第六章:进程间通信目标:本章旨在向学员介绍Linux系统下进程间通信及同步的机制及方法:1)掌握进程间通信的主要方法:管道、命名管道、共享内存等2)掌握进程间同步的主要方法:信号量等时间:6学时教学方法:讲授PPT、实例练习6.1关于进程间通信与同步功能提供几种机制方便进程之间进行数据交换或者进程之间的同步方法进程间通信及同步的方式有:pipe、fifo、共享内存、信号量、socket通信6.1.1最简单的进程通信•在两个程序间最简单的数据传递方法是利用popen及pclose函数了。command参数:要运行的程序名和相应的参数open_mode参数:必须是“r”或者“w”#includestdio.hFILE*popen(constchar*command,constchar*open_mode);intpclose(FILE*stream_to_close);6.1.1最简单的进程通信•例程:读取外部程序的输出intmain(){FILE*read_fp;charbuffer[BUFSIZ+1];intchars_read;memset(buffer,„\0‟,sizeof(buffer));read_fp=popen(“uname–a”,“r”);if(read_fp!=NULL){chars_read=fread(buffer,sizeof(char),BUFSIZ,read_fp);if(chars_read0){printf(“Outputwas:-\n%s\n”,buffer);}pclose(read_fp);exit(EXIT_SUCCESS);}exit(EXIT_FAILURE);}#includeunistd.h#includestdlib.h#includestdio.h#includestring.h6.1.1最简单的进程通信•例程:将输出送往外部程序#includeunistd.h#includestdlib.h#includestdio.hintmain(){FILE*write_fp;charbuffer[BUFSIZ+1];sprintf(buffer,“Onceuponatime,therewas…\n”);write_fp=popen(“od–c”,“w”);if(write_fp!=NULL){fwrite(buffer,sizeof(char),strlen(buffer),write_fp);pclose(write_fp);exit(EXIT_SUCCESS);}exit(EXIT_FAILURE);}6.2进程间通信机制(IPC)进程间通信机制(IPC机制)有五类:信号量1共享内存2消息传递3管道4命名管道56.2.1管道父进程pipes[1]管道pipes[0]子进程功能两个或多个进程间通过管道,可以互相传递信息,利用read和write系统调用函数来进行读写操作。6.2.1管道•pipe调用filedes是一个有两个成员的整形数组,用来保存管道的文件描述符,如果调用成功。filedes[0]将用来从管道读取数据filedes[1]用来向管道写入数据。errno返回的错误说明:EMFILE:进程使用的文件描述符过多ENFILE:系统的文件表已满EFAULT:文件描述符无效#includeunistd.hintpipe(intfiledes[2]);6.2.1管道例程:pipe调用#includeunistd.h#includestdlib.h#includestdio.h#includestring.hintmain(){intdata_processed;intfile_pipes[2];constcharsome_data[]=“123”;charbuffer[BUFSIZ+1];memset(buffer,„\0‟,sizeof(buffer));if(pipe(file_pipes)==0){data_processed=write(file_pipes[1],some_data,strlen(some_data));printf(“Wrote%dbytes\n”,data_processed);data_processed=read(file_pipes[0],buffer,BUFSIZ);printf(“Read%dbytes:%s\n”,data_processed,buffer);exit(EXIT_SUCCESS);}exit(EXIT_FAILURE);}6.2.1管道实验:pipe与fork父进程pipes[1]管道pipes[0]子进程父进程pipes[0]pipes[1]管道pipes[1]pipes[0]子进程调用fork之后进行pipe通信6.2.1管道例程:跨越fork调用的pipe#includeunistd.h#includestdlib.h#includestdio.h#includestring.hintmain(){intdata_processed;intfile_pipes[2];constcharsome_data[]=“123”;charbuffer[BUFFSIZ+1];pid_tfork_result;memset(buffer,„\0‟,sizeof(buffer));if(pipe(file_pipes)==0){fork_result=fork();if(fork_result==-1){fprintf(stderr,“Forkfailure”);exit(EXIT_FAILURE);}if(fork_result==0){close(file_pipes[1]);data_processed=read(file_pipes[0],buffer,BUFSIZ);printf(“Read%dbytes:%s\n”,data_processed,buffer);exit(EXIT_SUCCESS);}else{close(file_pipes[0]);data_processed=write(file_pipes[1],some_data,strlen(some_data));printf(“Wrote%dbytes\n”,data_processed);}}exit(EXIT_SUCCESS);}6.2.2命名管道FIFO功能提供进程间数据交换的一种机制区别与pipe不同的是,命名管道不需要程序由一个共同的祖先进程启动特点命名管道是一种特殊类型的文件,因为是文件,具备了和文件相同的特点,有文件名、所有者,访问权限等,但行为和管道相同6.2.2命名管道FIFO•命令行方式创建FIFO管道$mknodfilenamep$mkfifofilename6.2.2命名管道FIFO•程序中创建FIFO管道参数filename为指定管道文件的名字参数mode给出了FIFO的访问权限#includesys/types.h#includesys/stat.hintmkfifo(constchar*filename,mode_tmode);intmknod(constchar*filename,mode_tmode|S_IFIFO,(dev_t)0);6.2.2命名管道FIFO•例程:创建命名管道FIFO#includeunistd.h#includestdlib.h#includesys/types.h#includesys/stat.hintmain(){intres=mkfifo(“/tmp/my_fifo”,0777);if(res==0)printf(“FIFOcreated\n”);exit(EXIT_SUCCESS);}6.2.2命名管道FIFO命令行访问FIFO总结:$cat/tmp/my_fifo&$echo“abcdefg”/tmp/my_fifo与通过pipe调用创建管道不同,FIFO是以命名文件的形式存在,而不是打开的文件描述符,所以对它进行读写操作之前必须先打开它。FIFO也用open和close函数打开和关闭,但多了额外的功能6.2.2命名管道FIFO•例程:打开FIFO文件#includeunistd.h#includestdlib.h#includestdio.h#includefcntl.h#defineFIFO_NAME“/tmp/my_fifo”intmain(){intres;intopen_mode=0;open_mode=O_RDONLY|O_NONBLOCK;res=open(FIFO_NAME,open_mode);if(res==-1){perror(“FailedtpopenFIFOfile”);exit(1);}printf(“FIFOfileisopened\n”);close(res);return0;}注意:打开管道文件,可以选择阻塞与非阻塞两种方式,用O_NONBLCOK表示.open(chonstchar*path,O_RDONLY)--open调用将阻塞,除非有一个进程以写方式打开同一个FIFO,否则它不会返回。open(chonstchar*path,O_RDONLY|O_NONBLOCK)--即使没有其他进程以写方式打开FIFO,这个open用也将成功并立即返回。open(chonstchar*path,O_WRONLY)--open调用将阻塞,除非有一个进程以读方式打开同一个FIFO,否则它不会返回。open(chonstchar*path,O_WRONLY|O_NONBLOCK)--调用总是立即返回,但如果没有其他进程以读方式打开FIFO,将返回一个错误-1并且FIFO也不会被打开。打开FIFO的主要限制是程序不能使用O_RDWR模型打开FIFO文件。6.2.2命名管道FIFO•例程:FIFO读、写程序#includeunistd.h#includestdio.h#includestdlib.h#includefcntl.h#includestring.h#defineFIFO_NAME/tmp/my_fifo“intmain(){charbuf[100];intfd;intnread;fd=open(FIFO_NAME,O_RDONLY);if(fd==-1){perror(open);exit(1);}memset(buf,0,sizeof(buf));if((nread=read(fd,buf,100))==-1){perror(read);exit(1);}printf(read%sfromFIFO\n,buf);return0;}#includeunistd.h#includestdlib.h#includestdio.h#includefcntl.h#includestring.h#defineFIFO_NAME/tmp/my_fifo“intmain(){intfd;intnwrite;charbuf[100];fd=open(FIFO_NAME,O_WRONLY);if(fd==-1){perror(FailedtoopenFIFOfile);exit(1);}printf(FIFOfileisopened\n);strcpy(buf,helloworld);if((nwrite=write(fd,buf,100))==-1){perror(write);exit(1);}close(fd);return0;}6.2.2命名管道FIFO练习:使用fork创建2个进程,一个用于读FIFO操作,另一个写FIFO操作,读写数据的内容与大小不做要求。6.2.3关于信号量功能确保程序对某个特定的资源具有独占式的访问的机制区别与线程信号量不一样特点信号量是特殊的变量,只能取正整数并且只允许有