Linux子进程创建

来源:互联网 发布:贪心算法实例 编辑:程序博客网 时间:2024/05/16 04:56

最近在学操作系统,新学到了一些知识,记在下面。
以下是相关源代码:

#include"sys/types.h"#include"sys/file.h"#include"unistd.h"#include"string.h"#include"stdlib.h"#include"stdio.h"#include"wait.h"char r_buf[4]; //read bufferchar w_buf[4]; //write bufferint pipe_fd[2];pid_t pid1,pid2,pid3,pid4;int producer(int id);int consumer(int id);int main(int argc,int**argv){    if(pipe(pipe_fd)<0){        printf("pipe create error\n");        exit(-1);    }    else{        printf("pipe is created successfully\n");        if((pid1=fork())==0){           producer(1);           printf("child pid%d\n",getpid());        }        else if(pid1>0){           printf("parent pid%d,children pid%d\n",getpid(),pid1);        }        printf("after fork() 1\n");        if((pid2=fork())==0){           producer(2);           printf("child pid%d\n",getpid());        }        else if(pid2>0){           printf("parent pid%d,children pid%d\n",getpid(),pid2);        }        printf("after fork() 2\n");        if((pid3=fork())==0){           consumer(1);           printf("child pid%d\n",getpid());        }        else if(pid3>0){           printf("parent pid%d,children pid%d\n",getpid(),pid3);        }        printf("after fork() 3\n");        if((pid4=fork())==0){           consumer(2);           printf("child pid%d\n",getpid());        }        else if(pid4>0){           printf("parent pid%d,children pid%d\n",getpid(),pid4);        }        printf("after fork() 4\n");    }    close(pipe_fd[0]);  // 关闭管道,    close(pipe_fd[1]);  // 否则读者或写者永久等待    int i,pid,status;    // 等待子进程退出    for(i=0;i<4;i++){        pid = wait(&status);        printf("pid%d status %d\n",pid,WEXITSTATUS(status));    }    exit(0);            }int producer(int id){    printf("producer%d is running!\n",id);    close(pipe_fd[0]);  // 半双工写,关闭读    int i;    for(i = 0;i<10;i++){        sleep(1);  // other producer        if(id==1)            strcpy(w_buf,"aaa\0");        else            strcpy(w_buf,"bbb\0");        if(write(pipe_fd[1],w_buf,4)==-1)            printf("write to pipe error\n");    }    close(pipe_fd[1]);    printf("producer%d is over\n",id);    exit(id);}int consumer(int id){    close(pipe_fd[1]);  // 半双工读,关闭写    printf("consumer%d is running\n",id);    if(id==1)        strcpy(w_buf,"ccc\0");    else        strcpy(w_buf,"ddd\0");    while(1){        sleep(1);  // other consumer        strcpy(r_buf,"eee\0");        if(read(pipe_fd[0],r_buf,4)==0)            break;        printf("consumer%d get %s,while the w_buf is %s\n",id,r_buf,w_buf);    }    close(pipe_fd[0]);    printf("consumer%d is over!\n",id);    exit(id);}

在以上的程序中,主要是为了测试子进程的创建和进程间的通信,接下来对各个相关点进行分析:
1.fork
pid = fork().
fork() 在头文件unistd.h中。如果创建成功,创建一个新进程并返回新进程的进程ID(给父进程(大于0))和0(给新进程);如果不成功,返回-1给父进程。参数无,返回类型为pid_t。

父进程

if((pid1=fork())==0){           producer(1);           printf("child pid%d\n",getpid());        }        else if(pid1>0){           printf("parent pid%d,children pid%d\n",getpid(),pid1);        }        printf("after fork() 1\n");if((pid2=fork())==0){           producer(2);           printf("child pid%d\n",getpid());        }

由于子进程是父进程的一个副本,它们的程序计数器具有相同的值,所以在调用fork()之后两个进程都进入if判断,根据判断的结果执行相应的代码。如果pid1的值为0,那么正在执行的进程一定是子进程,该进程调用producer函数;如果pid的值大于0,那么正在执行的进程一定是父进程,该父进程执行printf语句。值得注意的是,在执行结果中,以上片段的最后一条printf语句只被执行了一次,原因在于子进程在producer函数中执行了exit函数,这导致子进程直接退出而不是返回原来的调用点,如果把exit换成return,那么子进程将执行在 “producer(1);” 语句后的代码。正是这个原因,使得第2个fork()没被子进程执行,否则产生的将不止4个进程,而是16个进程(总共有4个fork()调用)。

2.wait
pid = wait(&status_ptr).
wait()函数在头文件wait.h中。返回下一个子进程的进程ID并退出,退出状态写入由status_ptr指向的内存地址。参数int* status_ptr,返回类型pid_t。

int i,pid,status;    // 等待子进程退出    for(i=0;i<4;i++){        pid = wait(&status);        printf("pid%d status %d\n",pid,WEXITSTATUS(status));    }

wait函数返回退出的子进程的ID或0(如果不存在任何子进程),在以上程序片段中,它被保存在pid中。wait 函数在参数status(int 类型)中存储了进程的退出状态,包括退出代码;使用宏调用WEIXTSTATUS(status)可以从变量status中提取退出代码。如果调用进程中不存在任何子进程,退出代码显示出现错误。

3.pipe
pipe(filedes).
pipe函数在头文件unistd.h中。如果成功,创建新管道并返回0,读和写文件描述符写入数组参数中;如果失败返回-1。参数int filedes[2],返回类型int。

int pipe_fd[2];. . .f(pipe(pipe_fd)<0){        printf("pipe create error\n");        exit(-1);    }    else{        printf("pipe is created successfully\n");        . . .       }

已知的进程通信所用的最老的形式为管道(pipe),它由两个文件描述符构成,一个为读打开,另一个为写打开。共有两种类型的管道:半双工全双工,半双工管道只能向一个方向发送信息(->),而全双工管道可以向两个方向发送消息(<->)。在以上程序片段中,如果管道创建成功,将返回两个文件描述符到数组pip_fd中(pip_fd[0]为读描述符)。因为并非所有的操作系统都支持全双工管道,为保证信息只能向一个方向发送,即将从管道读数据的进程应该关闭写文件描述符,而即将向管道写数据的进程应该关闭读文件描述符。使用文件函数close可以关闭管道文件描述符,使用文件write函数可以向管道写文件描述符中写入信息,使用文件read函数可以从管道读文件描述符中读信息。
如果进程必须能够向两个方向发送信息,全双工管道可以用两个半双工管道模拟,其中的一个半双工管道用作读,另一个用作写。

以上便是此次总结,有些地方也许认识得还不够全面,欢迎各位指教。