APUE学习笔记——第八章 进程控制

来源:互联网 发布:南风知我意七微txt 编辑:程序博客网 时间:2024/05/17 07:16

本章的重点内容比较多,fork函数,wait和waitpid函数,exec函数,system函数等。

1、进程标识符

#include <unistd.h> pid_t getpid(void);    //调用进程的进程IDpid_t getppid(void);     //调用进程的父进程IDgid_t getgid(void);      //调用进程的实际组IDgid_t getegid(void);     //调用进程的有效组IDuid_t getuid(void);      //调用进程的实际用户IDuid_t geteuid(void);     //调用进程的有效用户ID

2、fork函数

该函数的作用就是:现有进程可以调用fork函数创建一个新的进程。

#include <unistd.h>

pid_t fork(void) //返回值:子进程中返回0,父进程中返回子进程的ID,出错返回-1。

现有进程创建子进程后,父子进程的执行顺序是不定的,如果想要子进程先执行,那么可以使用vfork函数。

父子进程对一些东西是可以共享的:

文件共享



这里偷懒了,直接截图。

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/types.h>#include <string.h>#include <error.h>#include <sys/wait.h>int main(){    int a = 8 , b = 90;    int status;    pid_t pid,pr;    if((pid = fork()) < 0)        perror("fork() error");    else if(pid == 0){//child process        a ++, b++;    }    else{//parent process        pr = wait(&status);//wait child process    }    printf("pid = %d, a = %d, b = %d\n",getpid(),a,b);    exit(0);}
3、wait和waitpid函数

当一个进程正常或者异常终止的时,内核就像其父进程发送SIGCHLD信号。
调用wait或waitpid的进程可能会发生以下情况:
如果其所有子进程都还在运行,则父进程阻塞;
如果一个子进程已经终止,正等待父进程获取其终止状态,则取得该子进程的终止状态立即返回。
如果他没有任何子进程,则立即出错返回。
#include <sys/wait.h>
pid_t wait(int *statloc)
pid_t waitpid(pid_t pid ,int *statloc, int options)
两个函数返回值:成功则返回进程ID,出错返回-1
两个函数区别:在一个子进程终止前,wait使其调用者(父进程)阻塞,而waitpid有个选项可以使调用者不阻塞。

对于waitpid函数中的pid参数的作用解释:
pid==-1 等待任一子进程
pid==0 等待其组ID等于调用进程组ID的任一子进程
pid>0 等待其进程ID与pid相等的子进程
pid<-1等待其组ID等于pid绝对值的任一子进程
waitpid的options常量说明:
WCONTINUED 若实现支持作业控制,那么由pid指定的任一子进程在暂停后已经继续,但其状态尚未报告,则返回其状态
WNOHANG    若由pid指定的子进程并不是立即可用,则waitpid不阻塞(父进程),此时其返回值为0
WUNTRACED  若某实现支持作业控制,而由pid指定的任一子进程已处于暂停状态,并且其状态自暂停以来为报告过,则返回其状态。
waitpid函数提供了wait函数三个没有的功能:
waitpid可以等待一个特定的进程,而wait则返回任一终止子进程的状态;
waitpid提供一个wait的非阻塞版本。有时用户希望取得一个子进程的状态(返回值为0),但不想阻塞(父进程)
waitpid支持作业控制(WCONTINUED WUNTRACED选项)
waitpid(-1,&status,0)等价于wait(&status)

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <error.h>#include <sys/wait.h>int main(){    int status,a = 9,b = 10;    pid_t pid,pr;    if((pid = fork()) < 0){        perror("fork error");    }else if(pid == 0) {        a ++; ++b;        printf("Child: %d\n",pid);        printf("pid: %d, ppid: %d, a: %d, b: %d\n",getpid(),getppid(),a,b);    }else{        printf("Parent: %d\n",pid);       // waitpid(pid,&status,WNOHANG);//非阻塞       waitpid(pid,&status,0);        printf("pid: %d, ppid: %d, a: %d, b: %d\n",getpid(),getppid(),a,b);    }    exit(0);}
此段代码肯定是子进程先执行,执行结果:

Child: 0
Parent: 3862//此处是子进程的ID
pid: 3862, ppid: 3861, a: 10, b: 11
pid: 3861, ppid: 2583, a: 9, b: 10

如果把注释去掉采用非阻塞方式,那么应该是有两种输出(本人运行了多次,都是父进程先执行),下面给出父进程先执行的运行结果:

Parent: 3971//子进程ID
pid: 3970, ppid: 2583, a: 9, b: 10
Child: 0
pid: 3971, ppid: 1, a: 10, b: 11//这里子进程后执行,但是子进程的父进程ID怎么变成了1?原因就是父进程执行结束后死亡,子进程就被系统超级进程init接管而init进程的ID是1。

4、fork进阶

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <error.h>#include <sys/wait.h>int main(){    int n = 3,i;    int status;    pid_t pid,pr;    printf("Main pid: %d\n",getpid());    for(i = 0 ;i < n ; i ++){        pid = fork();        if(pid == 0) {            printf("Create child process: ");            printf("i: %d, pid: %d, ppid: %d\n",i,getpid(),getppid());        }else if(pid < 0) {            perror("fork error");            exit(-1);        }else{            pr = wait(&status);            //pr = waitpid(pid,&status,WNOHANG);            printf("Parent process: ");            printf("i: %d, pid: %d, ppid: %d\n",i,getpid(),getppid());        }    }    exit(0);}
问题:此程序一共有多少个子进程产生(包括main)?


下面先看一个相关的面试题

#include <stdio.h>#include <unistd.h>#include <stdlib.h>#include <sys/types.h> int main(){pid_t   pid1;pid_t   pid2;pid1 = fork();pid2 = fork();printf("pid1=%d,pid2=%d\n",pid1,pid2);exit(0);}
要求如下:
已知从这个程序执行到这个程序的所有进程结束这个时间段内,没有其它新进程执行。
a、请说出执行这个程序后,将一共运行几个进程。
b、如果其中一个进程的输出结果是“pid1:1001, pid2:1002”,写出其他进程的输出结果(不考虑进程执行顺序)。

该程序执行过程:
0、fork函数产生两个返回值:子进程中返回0,父进程中返回子进程的ID;通过此判断条件判断若pid1>0&&pid2>0(假设是第一个输出结果),那么有如下进程执行顺序:
1、假设主程序(main)的进程ID为P0,那么执行到pid1=fork()时,P0启动一个子进程P1,fork函数有两个返回值,因此此时程序中有两个进程,pid1=0表示进程P1,pid1>0表示进程P0。
2、现在有父进程P0执行剩下的代码,P0启动一个子进程P2,此时pid1>0,pid2由fork函数产生两个值;
3、现在由P0进程执行剩下的代码,即输出函数,此时pid2>0,pid1>0,所以pid1=1001,pid2=1002;
4、现在由P2执行剩下代码,此时pid1>0,pid2=0,所以此时输出为pid1=1001,pid2=0;
5、现在由P1执行剩下的代码,P1启动一个子进程P3,此时pid1=0,pid2由fork函数产生两个值;
6、现在由P3执行剩下代码,此时pid2=0,pid1=0,所以此时输出是pid1=0,pid2=0
7、现在由P1执行剩下代码,此时pid2>0,pid1=0,根据进程ID递增原则,此时输出为pid1=0,pid2=1003
上述执行顺序的完整执行输出结果如下:
pid1:1001, pid2:1002
pid1:1001, pid2:0
pid1:0, pid2:0
pid1:0, pid2:1003
表述可能不是很清楚,给出链接http://www.cnblogs.com/leoo2sk/archive/2009/12/11/talk-about-fork-in-linux.html

再回到本节的那个程序,我代码中是先让子进程执行的,详细解释就不说了,同上面的分析类似,给出一个执行结果:

Main pid: 4090
Create child process: i: 0, pid: 4091, ppid: 4090
Create child process: i: 1, pid: 4092, ppid: 4091
Create child process: i: 2, pid: 4093, ppid: 4092
Parent process: i: 2, pid: 4092, ppid: 4091
Parent process: i: 1, pid: 4091, ppid: 4090
Create child process: i: 2, pid: 4094, ppid: 4091
Parent process: i: 2, pid: 4091, ppid: 4090
Parent process: i: 0, pid: 4090, ppid: 2583
Create child process: i: 1, pid: 4095, ppid: 4090
Create child process: i: 2, pid: 4096, ppid: 4095
Parent process: i: 2, pid: 4095, ppid: 4090
Parent process: i: 1, pid: 4090, ppid: 2583
Create child process: i: 2, pid: 4097, ppid: 4090
Parent process: i: 2, pid: 4090, ppid: 2583


5、exec函数

fork函数创建子进程后,子进程往往调用一种exec函数以执行另一个程序。当调用exec函数时,该进程执行的程序完全替换为新进程,exec函数不新建进程,只是用一个全新的程序替换了当前的正文、数据、堆和栈段。执行完之后,进程ID不会改变。在进程间通信的时候,经常需要调用exec函数启动另外一个例程。

有6中不同的exec函数可供使用:

#include <unistd.h>
int execl(const char *pathname, const char *arg0,....,(char *)0)
int execv(const char *pathname,char *const argv[])
int execle(const char *pathname, const char *arg0,....,(char *)0,char *const envp[])
int execve(const char *pathname,char *const argv[],char *const envp[])
int execlp(const char *filename, const char *arg0,....,(char *)0)
int execvp(const char *filename,char *const argv[])
六个函数间区别:a、前四个取路径名为参数,后两个则取文件名为参数,当指定filename作为参数时,如果filename中包含/,则将其视为路径名;
否则就按照PATH环境变量,在它所指定的各目录中搜寻可执行文件
b、与参数表的传递有关(l表示list,v表示vector)

#include <stdio.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>#include <error.h>int main(){    char *env_init[] = {"USER=xkey","PATH=/home/xkey",NULL};    pid_t pid;    int status;    if((pid = fork()) == -1){        perror("fork error");        exit(-1);    }else if(pid == 0){        if(execle("/home/xkey/APUE/8/echoexec","echoexec","xkey","color",(char*)0,env_init) < 0)           perror("execle error");    }else {        pid_t pr = waitpid(pid,&status,0);        printf("waitpid return value: %d\n",pr);    }    char *argv[4];    argv[0] = "xkey";    argv[1] = "color";    argv[2] = 0;    pid = fork();    if(pid == 0) {       int val = execve("/home/xkey/APUE/8/echoexec",argv,env_init);       printf("execve value %d\n",val);    }else{        pid_t pr = waitpid(pid,&status,0);        printf("waitpid return value: %d\n",pr);    }    exit(0);}
子进程执行exec调用echoexec可执行文件。

#include <stdio.h>#include <stdlib.h>int main(int argc,char *argv[]){    int i;    extern char **environ;    for(i = 0 ; i < argc ; i ++){        printf("argv[%d] is: %s\n",i,argv[i]);    }     while(*environ!=NULL)        puts(*environ++);    exit(0);}


6、system函数

system函数可以在程序中很方便的执行一个命令字符串,例:system("date > file")

#include <stdlib.h>

int system(const char *cmdstring)

system在其实现中其实调用了fork、exec、waitpid函数,下面给出APUE书本上的没有信号处理的实现代码。

#include <stdio.h>#include <sys/types.h>#include <stdlib.h>#include <unistd.h>#include <sys/wait.h>#include <error.h>#include <errno.h>int system_(const char *cmdstr){    pid_t pid;    int status;    if(cmdstr == NULL) return 1;    if((pid = fork()) < 0){        status = -1;    }else if(pid == 0) {       execl("/bin/bash","sh","-c",cmdstr,(char*)0);       _exit(127);    }else{        while(waitpid(pid,&status,0) < 0) {            if(errno != EINTR){                status = -1;                break;            }        }    }    return status;}int main(){    int status;    if((status = system_("date")) < 0)        perror("system_ error");    system("date");    printf("exit status: %d\n",status);    if((status = system_("nocomd")) < 0)        perror("system_ error");    system("nosdfsd");    printf("exit status: %d\n",status);    if((status = system_("who")) < 0)        perror("system_ error");    printf("exit status: %d\n",status);    exit(0);}



原创粉丝点击