通信:因为进程有强大独立性,当想把自己数据交给另一个进程,需要通信。

通信本质:让不同进程看到相同资源。

匿名管道:管道:通过某种机制传递资源(数据),匿名管道只适用于有血缘关系的进程,一般用于父,子进程通信。

a.创建管道

b.创建子进程

c.父,子进程关闭自己不需要的文件描述符

注意:匿名管道提供单向通信,父读子写或父写子读,若要实现双向通信,可再创建一个管道。

#include<stdio.h>#include<string.h>#include<unistd.h>#include<sys/types.h>#include<stdlib.h>intmain(){int_pipefd[2]={-1,-1};intret=pipe(_pipefd);if(ret==-1){perror("pipe");return2;}pid_tid=fork();if(id<0){perror("fork");return2;}elseif(id==0){close(_pipefd[0]);charbuf[]="helloworld";intcount=5;while(count--){write(_pipefd[1],buf,strlen(buf));sleep(1);}}else{close(_pipefd[1]);charbuf[1024]={0};while(1){memset(buf,'\0',sizeof(buf));ssize_t_size=read(_pipefd[0],buf,sizeof(buf)-1);if(_size>0){buf[_size]='\0';printf("buf:%s\n",buf);}}}return0;}

运行结果:

使用管道需注意以下四种情况:

1.如果所有指向管道写端的文件描述符都关闭了,仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

1#include<stdio.h>2#include<string.h>3#include<unistd.h>4#include<sys/types.h>5#include<stdlib.h>6intmain()7{8int_pipefd[2]={-1,-1};9intret=pipe(_pipefd);10if(ret==-1)11{12perror("pipe");13return2;14}1516pid_tid=fork();17if(id<0)18{19perror("fork");20return2;21}22elseif(id==0)23{24close(_pipefd[0]);25charbuf[]="helloworld";26intcount=5;27inti=0;28while(count--)29{30if(i==3)31{32printf("Iwantsleep\n");33break;34}35write(_pipefd[1],buf,strlen(buf));36sleep(1);37i++;38}39close(_pipefd[0]);40}41else42{43close(_pipefd[1]);44charbuf[1024]={0};46while(1)47{48memset(buf,'\0',sizeof(buf));49ssize_t_size=read(_pipefd[0],buf,sizeof(buf)-1);50if(_size>0)51{52buf[_size]='\0';53printf("buf:%s\n",buf);54}55}56}57return0;58}

运行结果:

2.如果有指向管道写端的文件描述符没关闭,持有管道写端的进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

1#include<stdio.h>2#include<sys/wait.h>3#include<string.h>4#include<unistd.h>5#include<sys/types.h>6#include<stdlib.h>7intmain()8{9int_pipefd[2]={-1,-1};10intret=pipe(_pipefd);11if(ret==-1)12{13perror("pipe");14return2;15}1617pid_tid=fork();18if(id<0)19{20perror("fork");21return2;22}23elseif(id==0)24{25close(_pipefd[0]);26charbuf[]="helloworld";27intcount=5;28inti=0;29while(count--)30{31if(i==3)32{33printf("Iwantsleep\n");34sleep(10);35}36write(_pipefd[1],buf,strlen(buf));37sleep(1);38i++;39}40close(_pipefd[0]);41}42else43{44close(_pipefd[1]);45charbuf[1024]={0};46while(1)47{48memset(buf,'\0',sizeof(buf));49ssize_t_size=read(_pipefd[0],buf,sizeof(buf)-1);50if(_size>0)51{52buf[_size]='\0';53printf("buf:%s\n",buf);54}55}56}57return0;58}

运行结果:

3.如果所有指向管道读端的文件描述符都关闭了,这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。

1#include<stdio.h>2#include<sys/wait.h>3#include<string.h>4#include<unistd.h>5#include<sys/types.h>6#include<stdlib.h>7intmain()8{9int_pipefd[2]={-1,-1};10intret=pipe(_pipefd);11if(ret==-1)12{13perror("pipe");14return2;15}1617pid_tid=fork();18if(id<0)19{20perror("fork");21return2;22}23elseif(id==0)24{25close(_pipefd[0]);26charbuf[]="helloworld";27intcount=5;28while(count--)29{30write(_pipefd[1],buf,strlen(buf));31sleep(1);32}33close(_pipefd[0]);34}35else36{37close(_pipefd[1]);38charbuf[1024]={0};39inti=5;40while(i--)41{42if(i==3)43{44close(_pipefd[0]);45}46memset(buf,'\0',sizeof(buf));47ssize_t_size=read(_pipefd[0],buf,sizeof(buf)-1);48if(_size>0)49{50buf[_size]='\0';51printf("buf:%s\n",buf);52}53}54intstatus=0;55pid_t_pid=waitpid(id,&status,0);56printf("waitpid:%d,returnstatus:%d\n",id,status&0xff);57}58//printf("%d,%d\n",_pipefd[0],_pipefd[1]);59return0;60}

运行结果:


4.如果有指向管道读端的文件描述符没关闭,持有管道读端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再次write会阻塞,直到管道中有空位置了才写入数据并返回。

1#include<stdio.h>2#include<sys/wait.h>3#include<string.h>4#include<unistd.h>5#include<sys/types.h>6#include<stdlib.h>7intmain()8{9int_pipefd[2]={-1,-1};10intret=pipe(_pipefd);11if(ret==-1)12{13perror("pipe");14return2;15}1617pid_tid=fork();18if(id<0)19{20perror("fork");21return2;22}23elseif(id==0)24{25close(_pipefd[0]);26charbuf[]="helloworld";27intcount=5;28while(count--)29{30write(_pipefd[1],buf,strlen(buf));31sleep(1);32}33close(_pipefd[0]);34}35else36{37close(_pipefd[1]);38intstatus=0;39pid_twait=waitpid(id,&status,0);40if(wait==id)41printf("wait:%d,status:%d\n",wait,status);42}43return0;44}

总结匿名管道特点:

单向通信,适用于有血缘关系的进程通信。

管道提供流式服务(数据按序,不规定一次读多少)

管道内部有保证数据读完(完整性)。

进程间同步

通过路径看到共同资源

在shell下交互的建立

调用函数

//client:1#include<stdio.h>2#include<unistd.h>3#include<sys/types.h>4#include<sys/stat.h>5#include<stdlib.h>6#include<string.h>7#include<fcntl.h>8intmain()9{10umask(0);11intfd=mkfifo("./.tmp",S_IFIFO|0666);12if(fd<0)13{14perror("mkfifo");15exit(1);16}17int_fd=open("./.tmp",O_WRONLY);18if(_fd<0)19{20perror("mkfifo");21exit(1);22}23while(1)24{25charbuf[1024];26fflush(stdout);27memset(buf,'\0',sizeof(buf));28printf("client:");29size_tsize=read(0,buf,sizeof(buf)-1);30buf[size]='\0';31write(_fd,buf,strlen(buf));32}33return0;34}//server:1#include<stdio.h>2#include<string.h>3#include<sys/types.h>4#include<sys/stat.h>5#include<fcntl.h>6#include<unistd.h>7#include<stdlib.h>8intmain()9{10int_fd=open("./.tmp",O_RDONLY);11if(_fd<0)12{13perror("open");14exit(1);15}16charbuf[1024];17while(1)18{19memset(buf,'\0',sizeof(buf));20ssize_t_size=read(_fd,buf,sizeof(buf)-1);21if(_size>0)22{23buf[_size]='\0';24printf("server:%s",buf);25}26else27{28printf("readerror\n");29break;30}31}32close(_fd);33return0;34}

//Makefile编写:.PHONY:all2all:clientserver34client:client.c5gcc-o$@$^6server:server.c7gcc-o$@$^8.PHONY:clean9clean:10rm-fclientserver.tmp

结果验证:打开两个终端,不同进程可以通信。