linux进程间通信(ipc)--匿名管道和命名管道

进程间进行交流(数据传输数据共享,进程间控制)
因为进程独立性,因此通信需要双方拥有公共的媒介才能通信,而这个媒介由操作系统提供因为通信场景不同,因此操纵系统也提供多种不同的进程间通信方式
进程间通信方式:
管道:匿名管道和命名管道–半双工单向通信(方向可以选择选择了就确定了无法改变)
,共享内存,消息队列,信号量
通信原理:
进程间通信方式是因为中间的公共媒介
管道:操作系统给进程提供的公共媒介,内存地址空间分两个一个用户空间一个内核空间,操作系统在内核开辟了一个缓冲区,(pipe),两个进程间同时访问内核的管道就可以实现通信,当做管道实现数据传输
管道在创建时候分为匿名管道和命名管道:
匿名管道这个管道没有名字,返回管道的操作句柄,一个操作句柄能够让你去操作它,句柄就是文件描述符,可以当做一个文件来做,一个管道创建之后内核就会给进程返回一个fd这个就是操作句柄
操作系统不能决定管道方向,操作系统给两个描述符一个读一个写想用哪个用哪个如果一个描述符则分辨不清不知道用于读还是写fd[2]
fd[0]用于读取数据fd[1]向管道写入数据就是管道的操作方式
因为匿名管道是匿名的,其他进程在内核中无法找到 这个管道,因此匿名管道实现进程间通信只能用于具有亲缘关系的进程(子进程通过父进程的Pcb-得到管道的操作句柄(两个文件描述符))
管道原理:操作系统在内核闯将一块缓冲区,并为用户提供管道的操作句柄(文件描述符)用户通过IO接口实现管道的操作,描述符有两个一个用读数据fd[0]
一个用于写数据
管道的实现
man2 pipe
int pipe(int pipefd[2]);
管道的创建
int main(){
6 //匿名管道的使用
7 //int pipe(int pipefd[2]);pipefd[0]读pipefd[1]写
8 //返回值成功0失败-1
9 //pipefd:用于接受管道的操作句柄(文件描述符)
10 int pipefd[2] ;
11 int ret=pipe(pipefd);
12 if(ret<0){
13 perror(“pipe error”);
14 return -1;
15 }
16 return 0;
17 }
创建管道一定在创建子进程之前只有管道创建在子进程之前才能复制之后就复制不到了
管道创建之后然后创建进程

int pipefd[2] ;
11 int ret=pipe(pipefd);
12 if(ret<0){
13 perror(“pipe error”);
14 return -1;
15 }
16 int pid=fork();//创建子进程
17 if(pid<0){
18 perror(“fork error”);
19 exit(-1);
20 }else if(pid==0){
21 sleep(1);
22 //子进程写数据
W> 23 char *ptr=“woainishijie~~~”;
24 write(pipefd[1],ptr,strlen(ptr));
25 }
26 else {
27 //父进程读数据
28 char buf[1024]={0};
read(pipefd[0],buf,1023);
30 printf(“child read buf:[%s]\n”,buf);
31 }
32 return 0;
33 }
chenyongjie@localhost ipc]$ ./pipe
child read buf:[woainishijie~~~]

不知道子进程先运行还是父进程运行
加上一个sleep子进程等一会写父进程先运行会发现程序没有直接退出而是等了1秒钟等父进程读到之后才退出为什么?
管道的特性:如果管道中没有数据那么如果read读取数据则会阻塞直到读取到数据返回
如果管道中数据满了那么如果就write写入数据,则会阻塞,直到能够写入数据
write len:65535

else if(pid==0){
21 sleep(1);
22 //子进程写数据
23 int len=0;
W> 24 char *ptr=“woainishijie~~~”;
25 while(1){
26 int ret=write(pipefd[1],ptr,strlen(ptr));
27 len+=ret;
28 printf(“write len:%d\n”,len);//统计了下总共写入多少数据
29 }
30 }
如果所有读端关闭了这个时候write写入数据则会触发异常–SIGPIPE信号会导致进程的退出
close(pipefd[0]);
22 sleep(1);
23 //子进程写数据
24 int len=0;
W> 25 char *ptr=“woainishijie~~~”;
26 while(1){
27 int ret=write(pipefd[1],ptr,strlen(ptr));
28 len+=ret;
29 printf(“write len:%d\n”,len);//统计了下总共写入多少数据
30 }
31 }
32 else {
close(pipefd[0]);}
while(1){
41 sleep(1);
42 printf("-----parent\n");
43 }
44 return 0;
45 }
子进程死循环如果能走下来就是父进程发现----parent,一直在打印

如果所有写端都关闭了,这时候read读取数据,将数据读完后继续读则会返回0

//匿名管道实现命令连接,数据传输
2 //ls |grep makefile,ls的运行结果作为后面的输入
3 #include <stdio.h>
4 #include <stdlib.h>
5 #include <unistd.h>
6 #include<string.h>
7 int main(){
8 int pipefd[2];
9 int ret=pipe(pipefd);
10 if(ret<0){
11 perror(“pipe error”);
12 return -1;
13 }
14 int pid1=fork();
15 if(pid10){
16 close(pipefd[0]);
17 //第一个子进程执行ls
18 //将第一个子进程的结果交给第二个子进程,将数据写到管道而不是标准输出–重定向
19 dup2(pipefd[1],1);//标准输出重定向到管道的写入端;
20 //因为ls的结果是写入到标准输入进行打印的,但是想要实现将一个数据交给另一个进程
//需要将数据写入到管道而不是标准输出,因此对标准输出进行重定向,重定向到
22 //管道的写入端
23 execlp(“ls”,“ls”,NULL);
24 exit(0);
25 }
26 int pid2=fork();
27 if (pid2
0){
28 close(pipefd[1]);
29 //第二个执行grepmake
30
31 dup2(pipefd[0],0);//将标准输入重定向到pipefd[0];因为grep要从标准输入读取数据
32 //但是我们的目的是让grep从管道读取处理结果进行过滤,这时候就要对标准输入进行
33 //重定向
34 execlp(“grep”,“grep”,“make”,NULL);
35 exit(0);
36 }
37 close(pipefd[0]);
38 close(pipefd[1]);
E> 39 wait(NULL);
wait(NULL);
41 return 0;
42 }
43
管道符的实现:连接连个命令第一个命令的输出结果作为第二个命令的输入数据进行处理ls |grep make
主程序创建两个子进程,在两个钟分别进行程序替换,让两个子进程分别运行ls和grep程序
ls是程序的浏览目录,将结过写入到标准输出
grep程序是标准输入读入数据,(循环读取数据),进行过滤
因为进程1执行ls程序将结果写入到标准输出,而不是管道,并没有将数据交给其他进程,
因此需要对进程1进行一个标准输出重定向,将ls的结果不是写入标准输出,而是写入到管道
因为进程2执行的grep程序是从标准输入读取数据,而不是管道,因此无法获取前面的命令结果
因此需要对进程2进行一个标准输入重定向,为了将本身从0指向标准输入读取数据改为管道
因为进程2不知道具体前面命令写了多少数据因此只能循环读取,读取多少处理多少
问题来了:加入管道中的数据已近被读完了,读不到数据会到值read阻塞,进程2无法退出,因此要将所有的管道写入端关掉,这时候读取不到数据返回0,认为没有数据了就退出了
管道的保护特性, 管道自带同步与互斥:当读写大小小于PIE_BU保证操作原子性(一次性完成不可打断);
非原子性a++加载到累加器返回寄存器这种属于非原子性
同步:保证临界资源(全局变量/公共资源)访问的时序可控性
互斥:保证临界资源同一时间的唯一访问特性

管道的生命周期随进程所有进程退出这个缓冲区没有意义管道则没有意义也会被操作系统释放
管道的半双特性

命名管道:
mkfifo test.fifo
[chenyongjie@localhost ipc]$ ls -l
prw-rw-r–. 1 chenyongjie chenyongjie 0 4月 11 08:44 test.fifo
这是一个管道文件以P开头命名管道有名字其实更多体现它在文件系统的可见性因为它有一个具体的文件,特点:所有的进程都可以通过打开这个文件而访问到管道,管道可以用到本机上面的任意进程间通信
命名管道:具有名字的管道–体现在文件系统的可见性(可见于文件系统)
因为创建命名管道会在文件系统穿件一个管道文件,所有进程都可以通过进程打开管道文件进而获取管道操作的句柄,因此命名管道可以实现同一主机上任意进程间通信
makefifo创建管道文件
命名管道原理:依然是内核的缓冲区只是通过文件向所有进程都提供了能够访问管道的方法而已
代码实现一个管道文件
man 3 mkfifo

nt main (){
7 //int mkfifo(const char pathname, mode_t mode);
8 //pathname命名管道文件路径名 mode_tmode创建管道文件的权限
9 //返回值成功0失败-1
E> 10 umask(0);
W> 11 char
fifo="./test.fifo";
E> 12 int ret=mkfifo(fifo,0664);
13 if(ret<0){
14 perror(“mkfifo error”);
15 return -1;
16 }
17 return 0;
18 }
运行后会发现一个test.fifo表示创建成功
怎么操作?需要打开文件,管道实际是一块内核的缓冲区命名管道虽然是个文件名但是> 没有进程打开文件没有进程操作管道(缓冲区)代表 这块缓冲并么有直接创建创建了,因.为当创建了但是没有操作相当于在内核当中占用了一块内存是不合适的 所以只有当进程打开时候它才会判断到底值创建管道还是打开管道,也就是真正打开文件时候才会判断是创建缓冲区还是去连接这块缓冲区

int main (){
E> 8 umask(0);
W> 9 charfifo="./test.fifo";E> 10 int ret=mkfifo(fifo,0664); 11 if(ret<0){ 12 if(errno!=EEXIST){//如果文件不存在则打印错误,如果文件存在打开文件 13 perror(“mkfifo error”);
14 return -1;
15 }
16 }
17 printf(“i want you open ~~~\n”);
18 int fd=open(fifo,O_RDONLY);
19 if(fd<0){
20 perror(“open error”);
21 }else{
22 printf(“open fifo success!!\n”);
23 }
24 return 0;
25 }
[chenyongjie@localhost ipc]$ ./fifo
i want you open ~~~
会发现一直卡在open没有打开
命名管道open的打开特性:
如果命名管道文件以只读方式打开但是这个文件又没有被其他进程以写的方式打开,则阻塞到其他进程以写的方式打开这个管道文件

如果命名管道文件以只写方式打开但是这个文件又没有被其他进程以读的方式打开,则阻塞到其他进程以读的方式打开这个管道文件

命名管道读写特性雷同与匿名管道
mv fifo.c fifo_read.c改名
打开两个终端
写数据
int main (){
E> 8 umask(0);
W> 9 charfifo="./test.fifo";
E> 10 int ret=mkfifo(fifo,0664);
11 if(ret<0){
12 if(errno!=EEXIST){//如果文件不存在则打印错误,如果文件存在打开文件
13 perror(“mkfifo error”);
14 return -1;
15 }
16 }
17 printf(“i want you open ~~~\n”);
18 int fd=open(fifo,O_WRONLY);
19 if(fd<0){
20 perror(“open error”);
21 }else
22 printf(“open fifo success!!\n”);
23 while (1){
24 char buf[1024]={1};
25 scanf("%s",buf);
26 write(fd,buf,strlen(buf));
printf(“write buf:[%s]\n”,buf);
28 }
29 close(fd);
30 return 0;
31 }
读数据
int main (){
E> 8 umask(0);
W> 9 char
fifo="./test.fifo";
E> 10 int ret=mkfifo(fifo,0664);
11 if(ret<0){
12 if(errno!=EEXIST){//如果文件不存在则打印错误,如果文件存在打开文件
13 perror(“mkfifo error”);
14 return -1;
15 }
16 }
17 printf(“i want you open ~~~\n”);
18 int fd=open(fifo,O_RDONLY);
19 if(fd<0){
20 perror(“open error”);
21 }else{
22 printf(“open fifo success!!\n”);
23 while(1){
24 char buf[1024]={0};
25 int ret=read(fd,buf,1023);
26 if(ret>0){
printf(“client sya:[%s]\n”,buf);
28 }else if(ret==0){
29 printf(" write is closed\n");
30 return -1;
31 }else{
32 perror(“read errno”);
33 return -1;
34 }
35 }
36 }
37 close(fd);
38 return 0;
39 }
在写的终端运行
$ ./fifo_write
i want you open ~~~
open fifo success!!
nihaoa
write buf:[nihaoa]
我爱你
write buf:[我爱你]
那么在读的另一个终端一起运行的结果为
./fifo_read
i want you open ~~~
open fifo success!!
client sya:[nihaoa]
client sya:[我爱你]
write is closed
当写端先关闭关闭读端返回0
当读端先关闭则写端在写入后退出,(触发异常)
[chenyongjie@localhost ipc]$ ./fifo_read
i want you open ~~~
open fifo success!!
^C
./fifo_write
i want you open ~~~
open fifo success!!
nihao
[chenyongjie@localhost ipc]$
匿名管道和命名管道的区别:命名管道可以用于同一主机任意进程间通信,但是匿名管道只能用于具有亲缘关系的进程间通信–因为命名管道有名字(有文件可见于文件系统之中)

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值