Linux进程通信机制:
- 管道
- 信号量
- 消息队列
- 共享内存
- socket通信
管道其实质是由内核管理的一个缓冲区
形象地认为管道的两端连接着两个进程:
- 一个进程进行信息输出,将数据写入管道;
- 另一个进程进行信息输入,从管道中读取信息。
管道分为:
- 匿名管道:只能用于有亲缘关系的进程间通信,进程退出后管道会被销毁。
- 命名管道:命名管道与进程的联系较弱,相当于一个读写内存的接口,进程退出后,命名管道依然存在。
匿名管道的使用流程如下:
①在进程中创建匿名管道,pipe函数;
②关闭进程中不使用的管道端口,close函数;
③在待通信的进程中分别对管道的读、写端口进行操作,read/write函数;
④关闭管道,close函数。
#includeint pipe(int pipefd[2]);
功能:创建匿名管道
参数说明:
- pipefd:传入参数,一个文件描述符数组;Linux将管道抽象为一个特殊文件。
返回值说明:
- 成功:返回0.
- 不成功:返回-1。
【案例1】使用pipe()实现父子进程间通信,父进程作为读端,子进程作为写端。
#include#include #include #include #include #include int main(){ int tempFd[2];//定义文件描述符数组 int tempRet=pipe(tempFd);//创建管道 if(tempRet == -1){ perror("pipe"); exit(1); } pid_t tempPid=fork(); if(tempPid > 0){//父进程—读 close(tempFd[1]);//关闭写端 char tempBuf[64]={0}; tempRet = read(tempFd[0], tempBuf, sizeof(tempBuf));//读数据 close(tempFd[0]); write(STDOUT_FILENO, tempBuf, tempRet);//将读到的数据写到标准输出 wait(NULL); } else if(tempPid == 0){//子进程—写 close(tempFd[0]);//关闭读端 char *tempStr="hello,pipen"; write(tempFd[1], tempStr, strlen(tempStr)+1);//写数据 close(tempFd[1]); }//of if return 0; }//of main
分析如下:
pipe()创建管道后读端的文件描述符为fd[0],写端的文件描述符为fd[1];
调用fork后父子进程共享文件描述符,文件描述符与管道的关系如图所示:
父进程进行读操作,子进程进行写操作;使用close()函数关闭父进程的写端与子进程的读端。
【案例2】使用管道实现兄弟进程间通信,兄弟进程实现命令“ls | wc –l”的功能。
- 在实现本案例时会用到重定向函数dup2:
#includeint dup2(int oldfd, int newfd);
- 其功能是将参数oldfd的文件描述符复制给newfd
- 若函数调用成功则返回newfd
- 否则返回-1,并设置errno。
#include#include #include int main(){ int tempFd[2]; int tempRet = pipe(tempFd); if(tempRet == -1){ perror("pipe err"); exit(1); }//of if int i; pid_t tempPid, tempWpid; for(i=0; i<2; i++){//2个子 if((tempPid = fork()) == 0){ break; }//of if }//of if if(2 == i){//父进程,回收子进程 close(tempFd[0]); //关闭读 close(tempFd[1]); //关闭写 tempWpid = wait(NULL); printf("wait child 1 success,pid=%dn", tempWpid ); tempPid = wait(NULL); printf("wait child 2 success,pid=%dn", tempPid); } else if(0 == i){//子进程1—写 close(tempFd[0]); dup2(tempFd[1], STDOUT_FILENO);//定向到标准输出 execlp("ls", "ls", NULL); } else if(1 == i){//子进程2—读 close(tempFd[1]); dup2(tempFd[0], STDIN_FILENO); execlp("wc", "wc", "-l", NULL); }//of if return 0; }//of main
兄弟进程间通信,进程文件描述符与管道的关系如图所示:
实线所示的箭头为编程中需要保留的文件描述符
Linux标准I/O库两个函数popen()和pclose(),可完成管道通信的流程。
#includeFILE *popen(const char *command, const char *type); int pclose(FILE *stream);
功能:
popen函数的功能是:
- 调用pipe()函数创建管道;
- 调用fork()函数创建子进程;
- 之后在子进程中通过execve()函数调用shell命令执行相应功能。
pclose函数的功能:
- 关闭popen()打开的I/O流;
- 通过调用wait()函数等待子进程命令执行结束;
- 返回shell的终止状态,防止产生僵尸进程。
参数说明:
popen函数的参数:
- command:命令;
- type:指定命令类型(输入w/输出r);
pclose函数的参数:
- stream:I/O流。
返回值说明:
popen函数的返回值:
- 成功:管道文件的描述符;
- 不成功:返回-1。
pclose函数的返回值:
- 成功:0;
- 不成功:-1。
【案例3】使用popen与pclose函数实现管道通信。
#include2.2 命名管道#include #include int main(){ FILE *tempRfp,*tempWfp; char temBuf[100]; tempRfp = popen("ls","r"); //读取命令执行结果 tempWfp = popen("wc -l","w"); //将管道中的数据传递给进程 while(fgets(temBuf, sizeof(temBuf), tempRfp)!=NULL){ fputs(temBuf, tempWfp); }//of while pclose(tempRfp); pclose(tempWfp); return 0; }//of main
#include#include int mkfifo(const char *pathname, mode_t mode);
功能:创建命名管道(FIFO文件),命名管道与系统中的一个路径名关联,以文件的形式存在于文件系统中,通过FIFO的路径名访问FIFO文件,实现进程间通信。
参数说明:
- pathname:管道文件的路径名,通过FIFO路径名访问FIFO文件;
- mode:指定FIFO的权限;
返回值说明:
- 成功:0;
- 不成功:-1,并设置errno。
【案例4】使用FIFO实现没有亲缘关系进程间的通信。没有亲缘关系的进程间通信,需要两段程序来实现:
- fifo_write.c实现FIFO的写操作;
- fifo_read.c实现FIFO的读操作。
fifo_write.c #include#include #include #include #include #include #include int main(int paraArgc,char *paraArgv[]){ if(paraArgc < 2){ //判断是否传入文件名 printf("./a.out fifonamen"); exit(1); }//of if int tempRet = access(paraArgv[1], F_OK); //判断fifo文件是否存在 if(tempRet == -1){ //若fifo不存在就创建fifo int tempFIFO = mkfifo(argv[1], 0664); if(tempFIFO == -1){ //判断文件是否创建成功 perror("mkfifo"); exit(1); } else{ printf("fifo creat success!n"); }//of if }//of if int tempFd = open(argv[1], O_WRONLY); //读写方式打开 while(1){ //循环写入数据 char *tempStrp="hello,world!"; write(tempFd, tempStrp, strlen(tempStrp)+1); sleep(1); }//of while close(tempFd); return 0; }//of main fifo_read.c #include #include #include #include #include #include #include int main(int paraArgc,char *paraArgv[]){ if(paraArgc < 2){ //判断是否传入文件名 printf("./a.out fifonamen"); exit(1); }//of if int tempRet = access(argv[1], F_OK); //判断fifo文件是否存在 if(tempRet == -1){ //若fifo不存在就创建fifo int tempFIFO = mkfifo(argv[1], 0664); if(tempFIFO == -1){ //判断文件是否创建成功 perror("mkfifo"); exit(1); } else{ printf("fifo creat success!n"); }//of if }//of if int tempFd = open(argv[1], O_RDONLY); //只读方式打开 if(tempFd == -1){ perror("open"); exit(1); }//of if while(1){ //不断读取fifo中的数据并打印 char temBuf[1024]={0}; read(tempFd, temBuf, sizeof(temBuf)); printf("buf=%sn", temBuf); }//of while close(tempFd); //关闭文件 return 0; }//of main



