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

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

1、进程标识符

#include <unistd.h> 
pid_t getpid(void);    //调用进程的进程ID
pid_t getppid(void);     //调用进程的父进程ID
gid_t getgid(void);      //调用进程的实际组ID
gid_t getegid(void);     //调用进程的有效组ID
uid_t getuid(void);      //调用进程的实际用户ID
uid_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);
}



发布了101 篇原创文章 · 获赞 26 · 访问量 46万+
展开阅读全文

没有更多推荐了,返回首页

©️2019 CSDN 皮肤主题: 大白 设计师: CSDN官方博客

分享到微信朋友圈

×

扫一扫,手机浏览