LINUX管道

管道

很久之前,程间通信方式,都是通过文件。但是这种方式它有一个缺点:它的效率(速度)太低了。但是这种方式有一个天大的好处:简单不需要额外提供API函数(直接利用文件系统的API函数)

有人想,能不能改进一下?问题在哪里? 文件内容在外设硬件上。 => 访问速度太低了。

能不能把文件的内容放在内核中呢?=>管道: 管道文件(linux下有单独这一种文件),在文件系统中有一个名字,但是内容是在内核中。(普通文件在文件系统中有名字而且内容也在外设上)

无名管道(pipe)

它在文件系统中没有名字(没有inode),它的内容在内核中,访问pipe的方式是通过文件系统的API(read/write). 它不能用open(因为它没有名字),但是read/write又需要一个文件描述符,所以在创建这个pipe的时候,就必须要返回文件描述符。

 pipe在创建时,在内核中开辟一块缓冲区,作为pipe文件的内容的存储空间,创建管道会返回两个文件描述符(一个用来读,一个是用来写),并且它还有如下特点:

 (1) pipe有两端,一端是用来写,一端是用来读;(2) 按顺序读,不支持lseek;

(3) 内容读走了,就没有了;(4) pipe(无名管道)随内核持续性(内核结束,无名管道才结束)

函数接口

pipe, - create pipe,pipe用来在内核中创建一个无名管道,pipefd[2]用来保存创建好的无名管道的两个文件描述符.

#include

pipe创建的管道,默认是阻塞方式(没有东西就读不了,满了就写不了,会阻塞在那)

int pipe(int pipefd[2]);

参数pipefd:是整型数组。;pipefd[0] 保存读的文件描述符;pipefd[1] 保存写的文件描述符

返回值:成功返回0,失败返回-1,同时errno被设置。

//练习: 使用无名管道让父子进程发生通信 // : 父进程 ==》 子进程 : hello, I am Father // : 子进程 收到数据 输出打印 并发送数据给父进程 ==》 I get message , I am son // : 父进程 收到数据 输出打印 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> int main(int argc, char const *argv[]) { int pipe_fd[2]; int re = pipe(pipe_fd);//创建无名管道的位置必须在创建子进程之前 //这样当子进程复制时,返回的fd是相同的 if(re == -1)//错误处理 { perror("pipe false:"); return -1; } pid_t pid = fork(); //pipe(pipe_fd);//如果在这创建管道,父子进程会分别在自己的空间内创建管道,返回的fd毫无关系 if(pid>0)//父返回子pid { //pipe(pipe_fd);//如果在这创建管道,子进程没有创建管道只有个fd数组,但数组里是随机数 //父先发送数据 char str[] = {"hello I am father"};//定义栈空间的字符数组存储字符串 int re = write(pipe_fd[1],str,sizeof(str));//参数:fd,写的字符串,写的大小 //创建好了后用系统IO去读写这个管道 if(re > 0) { printf("father write success\n"); } //父后接受数据 char buf[40] = {0}; read(pipe_fd[0],buf,40);//参数:fd,读在哪,读的大小(有40读40,只要不超过40有多少读多少) printf("father buf=%s\n",buf); } else if(pid == 0) { //子先接受数据 char buf[40] = {0}; read(pipe_fd[0],buf,40);//参数:fd,读在哪,读的大小(有40读40,只要不超过40有多少读多少) printf("son buf=%s\n",buf); //子后发送数据 char str[] = {"I am son"};//定义栈空间的字符数组存储字符串 int re = write(pipe_fd[1],str,sizeof(str));//参数:fd,写的字符串,写的大小 if(re > 0) { printf("son write success\n"); } } else { perror("fork false :"); } }

上述代码结果

0

父进程发送的数据有可能被自己读到。原因: pipe本身是全双工的,但是两个进程用一个管道去实现全双工的通信,就必须以某种方式去同步, 不然自己很有可能读到自己写的数据。通常在工程项目中一般做成两个或多个管道,一个用于读,一个用于写,人为把pipe当成是半双工。

//设置2个管道完善全双工 #include <stdio.h> #include <sys/types.h> #include <unistd.h> int main() { int fd1[2]; //创建无名管道 int re = pipe(fd1);//创建一个管道 if(re == -1) { perror("pipe1 error"); return -1; } int fd2[2]; //创建无名管道 re = pipe(fd2);//创建二个管道 if(re == -1) { perror("pipe2 error"); return -1; } //创建子进程 pid_t pid = fork(); if(pid > 0)//父进程 { //sleep(2); char str[] = {"hello, I am Father"}; //发送数据 int re = write(fd1[1],str, sizeof(str));//对fd1写,那么子进程就从fd1读。同理自己要从fd2读 if(re > 0) { printf("Father: write success\n"); } char buf[32] = {0}; //接收数据 read(fd2[0],buf, 32); printf("Fathe:buf = %s\n", buf); } else if(pid == 0)//子进程 { char buf[32] = {0}; //接收数据 read(fd1[0],buf, 32); printf("Son:buf = %s\n", buf); //回发数据 char str[] = {" I get message , I am son"}; //发送数据 int re = write(fd2[1],str, sizeof(str)); if(re > 0) { printf("Son: write success\n"); } } else { perror("fork error:"); } }

pipe(无名管道)是不是任意两个进程都可以用pipe来通信呢?或者只能用于父子进程呢?都不对

原则上只要两个进程能获取同一个pipe的文件描述符就可以用pipe来通信。但是父子进程才可以利用fork()的复制特性对应到同一个pipe的文件描述符,所以pipe(无名管道)只能用于有亲缘关系(有无共同祖先,有共同祖先是指:进程2是进程1的子进程,进程3是进程2的子进程,那么进程1和3对应的也是同一个pipe的文件描述符)的进程间的通信。

它为什么会有这个限制呢?原因就是因为pipe没有名字,fd只能通过继承得到。假设它在文件系统中有一个“名字 inode”(那么就可以open文件获得fd),它就可以用于任意进程间的通信,有名管道。

有名管道

fifo是在pipe的基础上给fifo在文件系统中创建一个inode(它会在文件系统中有一个文件名),但是fifo文件的内容却是在内核中。fifo的文件名随文件系统持续性的,fifo的文件内容存在于内核,随内核持续性的。

fifo同pipe一样,除了fifo在文件系统中有一个文件名。

0

操作fifo:open,read/write,close。有文件名可以open获得fd了

fifo文件的创建

mkfifo - make a FIFO special file (a named pipe),mkfifo用来在文件系统中创建一个fifo(有名管道)的入口(inode)

#include

#include

int mkfifo(const char *pathname, mode_t mode);

pathname: 要创建的有名管道在文件系统中的名字

注意:1.不能创建在共享文件夹下面, Linux内部的特殊文件(无名管道不需要指明路径(名字))。2. 用绝对路径

mode: 创建的有名管道的权限,有两种方式指定:(1) S_IRUSR, ...。(2) 0660...

返回值:成功返回0,失败返回-1,同时errno被设置(errno == EEXIST ,表示该有有名管道文件存在)。

练习: 创建一个有名管道, p1往这个有名管道写入一串数据,写入的数据来源键盘. p2再从该有名管道中读取这一串数据. //p1与p2无任何关系,我们写一个文件p1再写一个文件p2,两个文件都运行那么就有两个进程了 //p1和p2都创建有名管道,只要有一个创建成功即可。对于p1或p2来说都有:1.创建无名管道,2.open 3.读或写 4.close //fifo1.c #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <errno.h> int main() { //创建有名管道 int re = mkfifo("/home/china/fifo1", 0777);// /home/china/fifo1非共享文件夹下的绝对路径 if(re == -1) { if(errno != EEXIST)//判断是否是P2创建fifo成功了,如果是就不影响继续执行 { perror("mkfifo error:"); return -1; } } //打开 int fd = open("/home/china/fifo1", O_WRONLY); if(fd == -1) { perror("open fifo error:"); return -1; } //P1写 char buf[128] = {0}; gets(buf);//从键盘输入 write(fd, buf, strlen(buf)+1); //关闭有名管道 close(fd); } //fifo2.c #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> #include <string.h> #include <errno.h> int main() { //创建有名管道 int re = mkfifo("/home/china/fifo1", 0777); if(re == -1) { if(errno != EEXIST) { perror("mkfifo error:"); return -1; } } //打开 int fd = open("/home/china/fifo1", O_RDONLY); if(fd == -1) { perror("open fifo error:"); return -1; } //写 char buf[128] = {0}; read(fd, buf,128); printf("buf = %s\n", buf); //关闭有名管道 close(fd); } 在两个终端分别编译运行p1,p2,在p1写入数据

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值