linux系统学习之进程

本小结记录下进程的学习。
进程要有四个重要的系统函数,分别是fork(),exit(),wait(),exec系列函数。
首先来看fork,函数原型为pid_t fork();主要用于一个进程(父进程)创建一个新进程(子进程)。子进程几乎是父进程的翻版,它拥有和父进程相同的堆,栈,数据段,执行文本段(程序段)。但是在内部实现的时候,子进程并不直接复制父进程的这些内容,因为大多数情况下,创建一个子进程之后,子进程就执行exec系列函数,堆,栈什么的就要改变了,所以开始直接从父进程复制过来很浪费,真实情况是父,子进程共享同一代码段。系统调用fork()为子进程创建代码段时,其进程级页表项指向和父进程相同的物理内存页帧。对于数据段,堆,栈,采用写时复制技术。修改时,再各自拷贝。
一般可以通过fork()返回值区分父,子进程。父进程中,fork返回子进程ID,子进程,返回0.-1代表出错。
父,子进程可以实现文件共享,他们维持同样的打开文件句柄。这些打开的文件句柄包括文件偏移量,和文件状态标志。如果子进程改变了文件的偏移量,也会影响到父进程的相应描述符。但要注意,可能会有竞争现象。因为不确定父进程还是子进程先执行。

在进程终止的时候,调用exit()函数,该函数会刷新stdio流缓冲区,并将状态保存在status中。void exit(int status),虽然status为int型,它仅有低8位可供父进程使用。0表示进程正常退出,非0表示进程异常退出。

父进程可以用wait函数来等待任一子进程。

pid_t wait(int *status)

如果调用之前并没有子进程终止,调用就一直阻塞,直到子进程终止,如果调用的时候子进程终止已经终止,wait立即返回该僵尸进程的状态。
如果status非空,子进程终止消息通过status所指的整形变量返回。
wait返回终止的子进程pid号。
由于wait只能阻塞等待子进程终止,而且不能等待特定子进程终止,由此有了wait函数的升级版。
如果wait()不关心子进程的退出状态,那么可以将status设置为NULL即可。

pid_t waitpid(pid_t pid,int *status,int optition)

wait在子进程未退出之前,父进程一直阻塞等待,而且无法等待一个特定的子进程退出,它只是等待下一个子进程退出。
waitpid会暂停目前进程的运行,直到信号来到或者子进程结束。
如果不在意子进程返回的状态,可以将status设置为NULL。
其中optition有一些选项可以控制选择,可以为0或者一些其它。其中,设置为WNOHANG代表若子进程结束返回其pid,若没结束,则立即返回,返回0.
pid即为需要等待的具体子进程;
if pid>0,表示等待ID为pid号的进程
if pid=0,则等待与父进程同进程组的所有子进程。
if pid<-1,等待与pid绝对值相等的所有子进程
if pid==-1 等待任意子进程,与wait()等价。

孤儿进程:
一个子进程的父进程先挂了,它就成了孤儿,就成了孤儿进程。之后该子进程将有进程号为1的进程之祖—-init收养。
僵尸进程:
如果父进程在调用wait去获取子进程的退出状态之前,子进程已经挂了。那么父进程怎么去获得子进程的状态呢。内核通过将子进程转换为僵尸进程来处理。子进程死而不僵,内核释放它大部分资源,在进程表中保存一条记录,记录下子进程的ID,终止状态,资源数据等等。保证父进程可以wait()回收。那么如果父进程就根本就没有调用wait怎么办呢?直接将子进程抛弃了,那么只有等待父进程也挂了,子进程由init接管,它会自动调用wait()。那如果父进程他一直老不死怎么办?比如一些网络服务器,它创建众多服务子进程,自己不死,那么系统长久运行就有很多僵尸进程在进程表里,阻碍新进程创建了。因此,在长寿的父进程中,一定要调用wait()进行回收子进程。

SIGCHLD在子进程退出时,默认会发送该信号给父进程,默认情况下,父进程会忽略此信号,但是为了知道子进程何时退出,可以捕捉该信号。

execve()函数
int execve(const char *pathname,char *const argv[],char *const envp[])
pathname 为准备载入当前进程空间的新程序的路径名,
argv为传递给新进程的命令行参数,由字符串指针组成,以NULL结束。
最后一个参数envp制定新程序 的环境列表,也以NULL结束,字符串格式为name=value。
当然还有一些exec系列函数,本质是一样的,只是提供的接口不一样而已,在此就不一一列出了。

你可能感兴趣的:(linux)