一、管道
管道:管道是一种半双工的通信方式,数据只能单方向流动,而且只能在具有亲缘关系的进程间使用,因为管道
传递数据的单向性,管道又称为半双工管道。进程的亲缘关系通常是指父子进程关系。
管道的特点决定了其使用的局限性:
- 数据只能由一个进程流向另一个进程(其中一个为写管道,另一个为读管道);如果要进行全双工通信,需要
建立两个管道。
- 管道只能用于父子进程或者兄弟进程间的通信,也就是说管道只能用于具有亲缘关系的进程间的通信,无亲缘
关系的进程不能使用管道。
管道的创建:
Linux下创建管道可以通过函数pipe来完成。该函数如果调用成功则返回0,并且数组中将包含两个新的文件描述符;
如果有错误发生,返回-1,该函数返回两个文件描述符:pipefd[0]和pipefd[1]。前者打开来读,后者打开来写。该函
数的原型为:
#include <fcntl.h>
#include <unistd.h>
int pipe(int pipefd[2]);
1. 下面是通过建立管道和创建父子进程间的通信:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <string.h> #include <sys/wait.h> int main() { int fd[2]; pid_t pid; int ret; ret= pipe(fd); if(ret== -1) { perror("pipe. "); exit(1); } pid= fork(); if(pid== -1) { perror("fork. "); exit(1); } else if(pid== 0) { char buff[256]; close(fd[1]); read(fd[0],buff,256); printf("Form parent say: %s ",buff); close(fd[0]); } else { char *say= "Hello Linux."; close(fd[0]); write(fd[1],say,strlen(say)+ 1); close(fd[1]); int status; wait(&status); } return 0; }
这是父子进程间利用管道进行通信的一个例子。
2. 下面是通过建立两个管道来实现父子进程间全双工通信:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> int main() { char *parent_talk[]= {"Hello","Can you tell me what time is it?","Ok,I must go,Bye",NULL}; char *child_talk[]= {"Hi","No problem","See you,Bye",NULL}; int fd1[2],fd2[2]; pid_t pid; int ret; ret= pipe(fd1); if(ret== -1) { perror("pipe1. "); exit(1); } ret= pipe(fd2); if(ret== -1) { perror("pipe2. "); exit(1); } pid= fork(); if(pid== -1) { perror("fork. "); exit(1); } else if(pid== 0) { char buff[256]; close(fd1[1]); close(fd2[0]); int i= 0; char *talk= child_talk[i]; while(talk!= NULL) { read(fd1[0],buff,256); printf("Parent say: %s ",buff); write(fd2[1],talk,strlen(talk)+ 1); talk= child_talk[++i]; } close(fd1[0]); close(fd2[1]); } else { char buff[256]; close(fd1[0]); close(fd2[1]); int i= 0; char *talk= parent_talk[i]; while(talk!= NULL) { write(fd1[1],talk,strlen(talk)+ 1); read(fd2[0],buff,256); printf("Child say: %s ",buff); talk= parent_talk[++i]; } close(fd1[1]); close(fd2[0]); int status; wait(&status); } return 0; }
这是通过建立两个管道来实现父子进程间全双工通信的例子。
二、有名管道
管道的有一个不足之处是没有名字,因此,只能用于具有亲缘关系的进程间通信,在有名管道(FIFO)提出后,该
限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。有名
管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相
互通信。需要注意的是,FIFO总是按照先进先出的原则工作,第一个被写入的数据将首先从管道中读出。
有名管道的创建:
Linux下有两种方式创建有名管道。一是在Shell下交互地建立一个有名管道,二是在程序中使用系统函数建立有
名管道。Shell方式下可使用mkfifo或mknod命令。创建有名管道的系统函数有两个:mkfifo和mknod。两个函数均定义
在头文件sys/stat.h中,函数的原型为:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname,mode_t mode);
int mknod(const char *pathname,mode_t mode,dev_t dev);
函数mkfifo参数中pathname为创建的有名管道的全路径名;mode为创建的有名管道的模式,指明其存取权限;函
数mknod参数中pathname为创建的有名管道的全路径名;mode为创建的有名管道的模式,指明其存取权限;dev为设
备值,该值取决于文件创建的种类,它只在创建设备文件时才会用到。这两个函数调用成功都返回0,失败都返回-1。
下面是使用有名管道建立一个客户端/服务器的例子:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> #define BUFFER_SIZE 256 const char *write_fifo= "write_fifo"; const char *read_fifo= "read_fifo"; int main() { if(access(write_fifo,F_OK)) { int ret= mkfifo(write_fifo,0755); if(ret== -1) { perror("mkfifo"); exit(1); } } int write_fd= open(write_fifo,O_WRONLY); if(write_fd== -1) { perror("open write_fifo."); exit(1); } int read_fd; while(1) { read_fd= open(read_fifo,O_RDONLY); if(read_fd== -1) { sleep(1); continue; } break; } char sendbuf[BUFFER_SIZE]; char recvbuf[BUFFER_SIZE]; while(1) { printf("Ser:"); scanf("%s",sendbuf); if(strcmp(sendbuf,"quit")== 0) { unlink(write_fifo); break; } write(write_fd,sendbuf,strlen(sendbuf)+ 1); read(read_fd,recvbuf,BUFFER_SIZE); printf("Cli:%s ",recvbuf); } close(write_fd); close(read_fd); return 0; }
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <fcntl.h> #include <sys/wait.h> #define BUFFER_SIZE 256 const char *write_fifo= "write_fifo"; const char *read_fifo= "read_fifo"; int main() { int read_fd= open(write_fifo,O_RDONLY); if(read_fd== -1) { perror("open write_fifio."); exit(1); } if(access(read_fifo,F_OK)) { int ret= mkfifo(read_fifo,0755); if(ret== -1) { perror("mkfifo"); exit(1); } } int write_fd= open(read_fifo,O_WRONLY); if(write_fd== -1) { perror("open read_fifo."); exit(1); } char sendbuf[BUFFER_SIZE]; char recvbuf[BUFFER_SIZE]; while(1) { read(read_fd,recvbuf,BUFFER_SIZE); printf("Ser:%s ",recvbuf); printf("Cli:"); scanf("%s",sendbuf); if(strcmp(sendbuf,"quit")== 0) { unlink(read_fifo); break; } write(write_fd,sendbuf,strlen(sendbuf)+ 1); } close(write_fd); close(read_fd); return 0; }