Linux下用fork()派生的子进程通过pipe管道通讯的实例详解("生产者-消费者"问题)

//基于fork()系统调用

//#include "sys/types.h"//包含pid_t等的声明

//#include "sys/file.h"

//#include "unistd.h"//包含pipe(),fork()等的声明

    

/*在一般的较新的linux系统(如Ubuntu)中,以上头文件的引入不是必须的,用gcc编译时不会报error*/

/*如果出现关于pid_t的错误,可以包含以上头文件中任意一个,或者包含stdlib.h*/

/*如果出现类似于“不兼容的隐式声明”或“incompatible implicit declaration of built-in function ***”的警告(warning),包含以下三个头文件即可消除警告*/

    

#include "stdlib.h"//包含exit(),pid_t等的声明

#include "string.h"//包含strcpy()等的声明

#include "stdio.h"//包含printf()等的声明

    

char r_buf[4];//管道读取缓冲

char w_buf[4];//管道写入缓冲

    

int pipe_fd[2];//管道读写文件指针

pid_t pid1,pid2,pid3,pid4;//用于保存子进程号,此程序中并没有用到,只有一个简单的赋值

    

int producer(int id);//“生产者”

int consumer(int id);//“消费者”

    

int main(int argc,char **argv)

{

     //pipe()成功返回,失败返回-1

     if(pipe(pipe_fd)<0){//文件指针fd[0]和fd[1]分别用于管道文件的读和写

         printf("Pipe create error.\n");

         exit(-1);

     }

     else{

         printf("Pipe is created successfully !\n");

         if((pid1=fork())==0)

              producer(1);//对于fork()得到的子进程,pid1=0,于是调用一个生产者,以下三句类似

         if((pid2=fork())==0)

              producer(2);

         if((pid3=fork())==0)

              consumer(1);

         if((pid4=fork())==0)

              consumer(2);

     }

     close(pipe_fd[0]);//去掉之后,对程序结果没有影响

     close(pipe_fd[1]);//需要加上这句,否则会有读者永远等待

    

     int i,pid,status;

     /*等待四个子进程结束,否则主程序先于子程序结束(但是子进程仍在进行,对结果无影响)*/

     /*对于每个子进程,此处返回的pid对应于上面的进程号pid1,pid2,pid3和pid4,而status则对应于子进程结束时发送给系统的返回值*/

     /*注意返回值的低半字为零,高半字为exit(int ret)中指定的值;简单的说,就是256*ret */

    

     for(i=0;i<4;i++)

         pid = wait(&status);

     //exit()函数要做的工作是退出处理函数(我认为,相当于return),然后清理I/O缓冲,最后调用exit系统调用

     exit(0);//main函数中,exit(int ret)等同于return ret;

}

   

 

 

int producer(int id){

     printf("Producer %d is running !\n",id);

     close(pipe_fd[0]);//关读

     int i = 0;

     for(i=1;i<10;i++){

         sleep(3);

         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);

     /*结束(子)进程,并给系统(父进程)一个返回值;不可以用return,因为return只是返回函数调用处*/

     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);//一个消费者读取管道后暂时挂起,给另一个消费者运行的机会

         strcpy(r_buf,"eee\0");

         //read()返回值为0表示到了文件结尾;返回-1表示读取过程有错误;一般情况返回读取的字节总数

         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);

}

   

 

 

     以上代码中,关闭读文件操作符pipe_fd[0]的操作不是必须的,去掉之后,不会影响程序的运行结果,但是为了管道操作的规范性,需要加上close(pipe_fd[0])的操作

     由于只有当read函数读到管道文件的结尾(写文件操作结束,read函数返回0)时,消费者进程consumer才能退出死循环,并结束,因此如果少了关闭写文件操作符的close(pipe_fd[1])操作,就会导致有消费者进程永远等待,无法返回。

posted @ 2010-12-17 11:18  flyxiang  阅读(942)  评论(0编辑  收藏  举报