多进程编程

来源:互联网 发布:人事管理系统源码 编辑:程序博客网 时间:2024/05/16 05:53

fork()系统调用

#include<sys/types.h>#include<unistd.h>pid_t fork(void);

返回值:在父进程中返回子进程的pid,在子进程中返回零;

fork函数复制当前进程,在内核进程表创建一个新的进程表项,新的进程表项有很多属性与原进程相同,比如堆指针、栈指针和标志寄存器的值。但也有许多属性被赋予了新的值,比如该进程的PPID被设置为原进程的PID,信号位图被清除(原进程设置的信号处理函数不再对新进程起作用)。

子进程的代码与父进程完全相同,同时它还会复制父进程的数据(堆数据、栈数据和静态数据),数据的复制采用所谓的写时复制,即只有在任一进程(父进程或子进程)对数据执行了写操作时,复制才会发生(先是缺页中断,然后操作系统给子进程分配内存并复制父进程的数据)。即便如此,如果我们在程序中分配了大量内存,那么使用fork时也应该十分谨慎,尽量避免没必要的内存分配和数据复制。

此外,在创建子进程后,父进程中打开的文件描述符默认在子进程也是打开的,且文件描述符的引用计数加1。

exec系列系统调用

#include<unistd.h>extern char** environ;int execl(const char* path/*可执行文件完整路径*/, const char* arg,...);int execlp(const char* file/*可以接受文件名*/, const char* arg/*可变参数*/,...);int execle(const char* path, const char* arg,...,char* const envp[]/*设置新程序的环境变量*/);int execv(const char* path, char* const argv[]/*接受参数数组*/);int execvp(const char* file, char* const argv[]);int execve(const char* path, char* const argv[], char* const envp[]);

一般情况下,exec函数是不返回的,除非出错,其出错时返回-1,并设置errno。如果没出错,则原程序中的exec调用之后的代码不会执行,因此此时原程序已经被exec的参数指定的程序完全替换(包括代码和数据)

**exec函数不会关闭原程序打开的文件描述符,除非该文件描述符被设置了类似SOCK_CLOEXEC属性。

程序调用exec的时候,进程清空自身内存空间的text、global data、heap、stack,并根据新的程序文件重建text、global data、heap、stack(此时heap、stack大小为0),并开始运行;

处理僵尸进程

对于多进程程序而言,父进程一般需要跟踪子进程的退出状态,因此,当子进程结束运行时,内核不会立即释放该进程的进程表项,以满足父进程后续对该子进程退出信息的查询(如果父进程还在运行)。在子进程结束运行之后,父进程读取其退出状态之前,称该进程处于僵尸态。另外一种使子进程进入僵尸态的情况是:父进程结束或者异常终止,子进程继续运行,此时子进程的PPID将被操作系统设置为1,即init进程。init进程接管了该子进程,并等待它结束。在父进程退出之后,子进程退出之前,该子进程处于僵尸态。

由此可见,无论那种情况,如果父进程没有正确处理子进程的返回信息,子进程都将停留在僵尸态,并占据着内核资源。这绝对是不允许的,毕竟内核资源有限,下面这对函数在父进程中调用,以等待子进程的结束,并获取子进程的返回信息,从而避免僵尸进程的产生,或者使子进程的僵尸进程立即结束。

#include<sys/types.h>#include<sys/wait.h>pid_t wait(int* stat_loc); //阻塞进程,直到该进程的某个子进程结束运行为止, //返回子进程的pid,并将子进程的退出状态存储在stat_loc参数指向的内存中pid_t waitpid(pid_t pid,      //指定等待的子进程              int* stat_loc,  //子进程的退出信息              int options);   //WNOHANG,将调用设为非阻塞的

要在事件已经发生的情况下执行非阻塞调用才能提高程序的效率,对于waitpid函数,最好在某个子进程退出之后再调用它。那么子进程如何得知某个子进程已经退出了呢?这正是SIGCHID信号的用途,当一个进程结束时,它将给其父进程发送SIGCHID信号,因此,我们可以在父进程中捕获SIGCHLD信号,并在信号处理函数中调用waitpid函数以“彻底结束”一个子进程,代码示例:

static void handle_child(int sig){    pid_t pid;    int stat;    while((pid = waitpid(-1, &stat, WNOHANG)) > 0){      /*对结束的子进程的善后处理*/    }}

僵尸进程以及如何处理僵尸进程

管道

待续、、、

原创粉丝点击