进程——c语言

来源:互联网 发布:足彩数据 编辑:程序博客网 时间:2024/06/05 14:28

进程标识

每一个进程都有一个非负整数表示的唯一进程ID。ID为0的进程通常是调度进程,是内核的一部分。进程ID为1的进程通常是init进程,负责在自举内核后启动一个UNIX系统。init通常读取与系统相关的初始化文件(etc/rc*等)。init进程不会终止,是一个以超级用户权限运行的普通用户程序,不是内核中的系统进程。init会成为所有孤儿进程的父进程。


函数fork

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

由fork创建的进程被称为子进程。fork被调用一次,但是返回两次。子进程中返回0,父进程中则返回子进程的进程ID。子进程和父进程会继续执行fork之后的代码,子进程是父进程的副本,子进程会获得父进程的数据空间,堆和栈。注意是父进程的副本,而不是共享。
使用fork的例子:

if( (pid=fork())<0){//此处调用一个fork    //返回值小于0,出错啦}else if(pid == 0){    //此处在子进程中    doChildsWork();、    //exit(0); //这个代码使子进程结束。}else{    //此处在父进程中    doParentsWork();}doBothTodo();//此处代码父子进程都会执行。

函数wait和waitpid

当一个进程正常终止或异常终止时,内核就向其父进程发送SIGCHLD信号,父进程可以选择忽略此信号,或者提供一个该信号发生即被执行的信号处理函数。对于这种信号,系统的默认操作是忽略。
这里讲一下“僵死进程”:
在类UNIX系统中,僵尸进程是指完成执行(通过exit系统调用,或运行时发生致命错误或收到终止信号所致)但在操作系统的进程表中仍然有一个表项(进程控制块PCB),处于”终止状态”的进程。这发生于子进程需要保留表项以允许其父进程读取子进程的exit status:一旦退出态通过wait系统调用读取,僵尸进程条目就从进程表中删除,称之为”回收(reaped)”。但如果父进程没有调用wait,僵尸进程将保留进程表中的表项,导致了资源泄漏。
如果编写一个长期运行的程序,它fork了很多子程序,那么除非父进程等待取得子程序的终止状态,不然这些子进程终止后就会变成僵死进程。
下面给出wait和waitpid的声明:

#include <sys/wait.h>pid wait(int *statloc);pid waitpid9pid_t pid,int *statloc,int options);

调用wait和waitpid会发生:

  • 如果其所有子进程都在运行,则阻塞
  • 如果一个子进程已终止,正等待父进程获取其终止状态,则取得终止状态,立即返回。
  • 如果没有任何子进程,则立即出错返回。

如果进程因为接收到SIGCHLD信号而调用wait(信号处理函数),我们期待wait立即返回。但是如果在随即时间点调用wait,则进程可能会阻塞。
另外,这两个函数存在一些区别:

  • 在一个子程序结束前,wait使其调用者阻塞,而waitpid有一选项,可是调用者不阻塞。
  • waitpid并不等待其调用之后的第一个终止子程序,它有若干个选项可以控制它所等待的进程。

这两个函数的参数statloc是一个整型指针。如果statloc不是一个空指针,则终止进程的终止状态就存放在它所指向的单元内。如果不关心终止状态,则可将该参数设为空指针。
对于waitpid函数,pid参数的作用如下。

  • pid=-1,等待任意子程序,此时与wait相同
  • pid>0,等待进程ID与pid相同的子程序
  • pid=0,等待组ID和调用进程组ID的任意子程序
  • pid<-1,等待组ID等于pid绝对值的任一子程序

阅读unix网络编程之后发现一种处理结束的子进程防止僵死进程的方式:

signal(SIGCHLD,sig_chld);void sig_chld(int signo){    int statloc;    pid_t pid;    /**     * 下面的while循环中     * 使用了waitypid函数,第一个参数-1表示任意wait子进程,与wait相同     * 第二个参数用于保存进程终止状态     * 第三个参数是option,WNOHANG代表如果没有终止的子进程,不阻塞而是直接返回。     * 说一下使用while+WNOHANG的原因:     * unix中信号是不排队的,也就是说,如果在SIGCHLD信号处理的过程中,有其他子进程结束,产生SIGCHLD信号。     * 新的SIGCHLD信号是不递交的(也就是说,即使有多个子进程结束,即产生多个SIGCHLD,也只会处理一次)。     * 所以,我们需要在一次信号处理中,肯能需要处理多个终止子程序,因此使用while     * 至于WNOHANG选项,如果不加上,就阻塞在信号处理程序了。     */    while((pid=waitpid(-1,&statloc,WNOHANG))>0){        //todo        //信号处理函数不应该使用printf这类不可重入函数        printf("子进程%d结束",pid);    }    return;}

函数exec

当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序从其main函数处开始执行。因为调用exec并不创建新进程所以前后进程ID没有变化。

#include <unist.h>/* Execute the file FD refers to, overlaying the running program image.   ARGV and ENVP are passed to the new program, as for `execve'.  */extern int fexecve (int __fd, char *const __argv[], char *const __envp[])/* Execute PATH with arguments ARGV and environment from `environ'.  */extern int execv (const char *__path, char *const __argv[])/* Execute PATH with all arguments after PATH until a NULL pointer,   and the argument after that for environment.  */extern int execle (const char *__path, const char *__arg, ...)/* Execute PATH with all arguments after PATH until   a NULL pointer and environment from `environ'.  */extern int execl (const char *__path, const char *__arg, ...)    /* Execute FILE, searching in the `PATH' environment variable if it contains   no slashes, with arguments ARGV and environment from `environ'.  */extern int execvp (const char *__file, char *const __argv[])/* Execute FILE, searching in the `PATH' environment variable if   it contains no slashes, with all arguments after FILE until a   NULL pointer and environment from `environ'.  */extern int execlp (const char *__file, const char *__arg, ...)
原创粉丝点击