linux 进程操作

1、进程概念

进程状态图



②进程内存映像


  当父进程创建子进程时,子进程会复制父进程内存所有资源,从父进程处继承了整个进程的地址空间,包括进程上下文、代码段、进程堆栈、内存信息、打开的文件描述符、信号控制设定、进程优先级、进程组号、当前工作目录、根目录、资源限制和控制终端等。

二、和进程有关的函数

1.进程的创建—fork()

#include<unistd>

#incude<sys/types>

pid_tfork();

函数返回值:执行成功则建立一个新的进程,在子进程中则返回0,在父进程中回返回新建子进程的进程号(PID),失败则返回-1

备注:1.一般来说,fork()之后是父进程还是子进程先执行是不确定的.这取决于内核所使用的调度算法.如果要求父子进程之间同步,则要使用某种形式的进程间同步进步.

2.子进程创建后就具有了自己的地址空间,因此在子进程中对变量的做的操作(n++),没有对父进程造成影响,这两个变量具有同样的名字,但在内存中位置是不同的。新进程的信息是复制而来,而非指相同的内存空间,因子进程对这些变量的修改和父进程并不同步。但是出了文件描述符。

2.vfork()

vfork函数和fork函数作用基本类似,但是也有一些重要区别。

因为调用vfork函数的目的是调用exec函数,所以没有必要完全复制父进程的数据区,所以在子进程调用exec之前,子进程是和父进程共享数据段的。所以这时子进程修改变量会影响父进程的变量。

另外一点不同是在执行次序上,fork对父子进程执行次序不限制,而在vfork中,子进程先运行,父进程挂起,直至子进程调用execexit之后。

3.exec函数

exec函数的作用是清除父进程的可执行代码映像,用新程序的代码覆盖调用exec进程的代码。exec执行成功后,进程将从新程序的main函数入口开始执行。

exec()函数实际上是一个函数族,共有6个不同的exec()函数可供使用,它们是:

#include<unistd.h>

intexecl (const char *pathname, const char *arg0, …);

intexecv (const char *pathname, char *const argv[]);

intexecle (const char *pathname, const char *arg0, …, char *constenvp[]);

intexecve (const char *pathname, char *const argv[], char *constenvp[]);

intexeclp (const char *filename, const char *argv0,…);

intexecvp (const char *filename, char *const argv[]);

exec函数族使用区别:

查找方式

前四个函数的查找方式都是完整的文件目录路径,而最后两个函数(p结尾的函数)可以只给出文件名,系统就会自动从环境变量“$PATH”所指出的路径中进行查找.

参数传递方式

两种方式:逐个列举,将所有参数整体构造指针数组传递

以函数名的第五位字母来区分的,字母为"l"(list)的表示逐个列举的方式.其语法为char*arg;字母为"v"(vertor)的表示将所有参数整体构造指针数组传递,其语法为*constargv[].

环境变量

exec函数族可以默认系统的环境变量,也可以传入指定的环境变量.这里,"e"(Enviromen)结尾的两个函数execleexecve就可以在envp[]中指定当前进程所使用的环境变量.

参数pathname指出目标文件的路径,参数filename指出目标文件名;以列表显示时,arg0须与pathnamefilename相同,指示参数开始,最后以(char*0结束,表示参数读完。以向量显示时,argv[]第一个元素也是和filename一样,最后一个元素为一个空字符串。


exec函数执行失败,常见原因:


找不到文件或路径,此时errno被设置为ENOENT;

数组argvenvp忘记用NULL结束,此时errno被设置为EFAULT;

没有对应可执行文件的运行权限,此时errno被设置为EACCES.


#include<sys/types.h>

#include<unistd.h>

#include<stdio.h>

#include<stdlib.h>

intmain()

{

pid_tpid;

constchar *usr_envp[ ] = {"MYDEFINE=unknown","PATH=/tmp",(char *)0};

printf("Begin fork()\n");

pid= fork();

switch(pid)

{

case-1:

perror("forkfailed");

exit(1);

case0:

if(execle("/tmp/child","myarg1","my arg2",(char *)0, usr_envp)<0)

perror("execlefailed");

break;

default:

break;

}

if(waitpid (pid, NULL, 0) < 0)

perror("waitpidfailed");

printf("parent exiting\n");

exit(0);

}


4.进程的终止

一个进程正常终止有三种方式:


main()函数return返回;

调用exit()函数;

调用_exit()_Exit()函数.

exit()函数与_exit()函数最大的区别就在于exit()函数在终止当前进程之前要检查该进程打开过哪些文件,把文件缓冲区中的内容写回文件,就是上图中的"清理I/O缓冲"一项.


exit函数中可带状态码,用于在父进程中通过调用wait函数获得。

5.进程等待






wait()函数是用于使父进程(也就是调用wait()的进程)阻塞,直到一个子进程结束或者该进程接到了一个指定的信号为止.如果该父进程没有子进程或者他的子进程已经结束,wait()就会立即返回。

waitpid()的作用和wait()一样,但它并不一定要等待第一个终止的子进程,它还有若干选项,如可提供一个非阻塞版本的wait()功能,也能支持作业控制。

下面有几个宏可判别结束情况:

WIFEXITED(status)如果子进程正常结束则为非0.

WEXITSTATUS(status)取得子进程exit()返回的结束代码,一般会先用WIFEXITED来判断是否正常结束才能使用此宏.


WIFSIGNALED(status)如果子进程是因为信号而结束则此宏值为真.

WTERMSIG(status)取得子进程因信号而中止的信号代码,一般会先用WIFSIGNALED来判断后才使用此宏.


WIFSTOPPED(status)如果子进程处于暂停执行情况则此宏值为真.一般只有使用WUNTRACED时才会有此情况.

WSTOPSIG(status)取得引发子进程暂停的信号代码,一般会先用WIFSTOPPED来判断后才使用此宏.

6.僵死进程

前面的学习中,我们已经了解了父进程和子进程的概念,并已经掌握了系统调用exit的用法,我们知道exit系统调用是安全终止进程的系统调用,它在真正终止进程之前要进行一些列的检查,其实,在一个进程调用了exit之后,该进程并完全消失掉(终止),而是留下一个称为僵尸进程(Zombie)的数据结构。僵尸进程是非常特殊的一种进程,它已经放弃了几乎所有内存空间,没有任何可执行代码,也不能被调度,仅仅在进程列表中保留一个位置,记载该进程的退出状态等信息供其他进程收集,除此之外,僵尸进程不再占有任何内存空间。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值