Linux进程间通信——管道通信

目录

一、Linux进程间通信概述

1.UNIX平台进程通信方式

2.常用的进程间通信方式

二、无名管道PIPE

1.无名管道的特点:

2.无名管道的创建与关闭

创建无名管道

无名管道的读写

无名管道的读写特性

管道破裂例子

无名管道的大小

三、有名管道FIFO

1.有名管道的创建

 2.有名管道进行通信例子


 

一、Linux进程间通信概述

1.UNIX平台进程通信方式

早期进程间通信方式: AT&T的贝尔实验室,对Unix早期的进程间通信进行了改进和扩充,形成了“system V IPC”,其通

信进程主要局限在单个计算机内

BSD(加州大学伯克利分校的伯克利软件发布中心),跳过了该限制,形成了基于套接字(socket)的进程间通信机制 Linux继承了上述所有的通信方式

2.常用的进程间通信方式

1)传统的进程间通信方式 无名管道(pipe)、有名管道(fifo)和信号(signal) 2)System V IPC对象 共享内存(share memory)、消息队列(message queue)和信号灯(semaphore) 3)BSD 套接字(socket  用于网络通信)

二、无名管道PIPE

 

1.无名管道的特点:

1)只能用于具有亲缘关系的进程之间的通信;

 

2)半双工的通信模式,具有固定的读端和写端;

 

3)管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数

2.无名管道的创建与关闭

 

无名管道是基于文件描述符的通信方式(但这个文件在文件系统中不可见)。当一个管道建立时,它会 创建两个文件描述符fd[0]和fd[1]。 其中fd[0]固定用于读管道,而fd[1]固定用于写管道。

 

构成了一个半双工的通道。

 

创建无名管道

首先来看创建函数

 eg:

int main()
{
int pfd[2];
if (pipe(pfd) < 0) /*创建无名管道*/
{
printf("pipe create error\n");//利用返回值判断是否创建成功
return -1;
}
else
{
printf("pipe create success\n");
}
close(pfd[0]); /*关闭管道描述符*/
close(pfd[1]);
}

无名管道的读写

先举个例子实现子进程写入父进程读出

#include 
#include 
#include 
#include 
#include 
#include 
int main(int argc, char *argv[])
{ int pfd[2] = {0};//创建无名管道int ret = pipe(pfd);if(ret < 0){perror("pipe");exit(-1);}pid_t pid = fork();//创建子进程if(pid < 0){perror("fork");exit(-1);}if(pid == 0){char buf[64] = {0};//自定义缓冲区,用于存入终端输入信息while(1){fgets(buf, 64, stdin);buf[strlen(buf)-1] = '\0';    write(pfd[1], buf, strlen(buf));//子进程执行写操作}}else{char buf1[64] = {0};while(1)//父进程进行读操作{memset(buf1, 0, 64);//清空缓冲区操作,因为自定义缓冲区可能存在上次读入的数据read(pfd[0], buf1, 64);printf("father ---- %s\n", buf1);//展示读入的数据}wait(NULL);//回收子进程资源exit(0);}return 0;
} 

无名管道的读写特性

读特性:

当写端存在时:

管道有数据:返回读到的字节数

管道无数据:阻塞

当写端不存在时:

管道有数据:返回读到的数据

管道无数据:返回0;

写特性:

当读端存在时:

管道有空间:返回写入的字节数

管道无空间:阻塞,直到有空间为止

当读端不存在时:

无论管道是否有空间,管道破裂 SIGPIPE

管道破裂例子

#include 
#include 
#include 
#include 
#include 
#include int main(int argc, char *argv[])
{ int pfd[2] = {0};int ret = pipe(pfd);   //创建无名管道if(ret < 0){perror("pipe");exit(-1);}close(pfd[0]);         //关闭无名管道的读端pid_t pid = fork();     //创建子进程if(pid == 0){write(pfd[1], "hello", 5);sleep(5);}else{int status;wait(&status);printf("%d %d %d\n", WIFEXITED(status),WIFSIGNALED(status),WTERMSIG(status));//展示错误信息exit(0);}return 0;
} 

无名管道的大小

可以以下方式计算

#include 
#include 
int main(int argc, char *argv[])
{ int pfd[2] = {0};pipe(pfd);int ret = 0;int size = 0;char buf[1] = {0};while(1)//一直往管道里写数据{ret = write(pfd[1], buf, 1);//利用write返回值来计算,write会返回写入的字节数,这里我们一次就写一个0,然后循环进行累加,直到管道被塞满size = size+ret;printf("size = %d\n", size);}return 0;
} 

这里注意几个要点

1)当管道中无数据时,读操作会阻塞 2)向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将会一直阻塞。 3)只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号(通常Broken pipe错误)。

三、有名管道FIFO

1)无名管道只能用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围有名管道可以使互不相关的两个进程互相通信。

 

2)有名管道可以通过路径名来指出,并且在文件系统中可见进程通过文件IO来操作有名管道( 意思就是在系统文件中可见 )

 

3)有名管道遵循先进先出规则

 

4)不支持如lseek() 操作

1.有名管道的创建

 因为有名管道是一个文件,所以在操作有名文件时,要先创建一个有名文件,且操作时也有open和close操作

创建例子 

#include 
#include 
#include 
#include 
int main(int argc, char *argv[])
{ int ret = mkfifo("fifo", 0664);//文件属性一般为0664if(ret < 0){perror("mkfifo");exit(-1);}return 0;
} 

 2.有名管道进行通信例子

读操作进程

#include 
#include 
#include 
#include 
#include 
#include 
#include int main(int argc, char *argv[])
{ int fd = open("fifo", O_RDONLY);if(fd < 0){perror("open");exit(-1);}printf("open fifo ok!\n");char buf[64] = {0};while(1){memset(buf, 0, 64);read(fd, buf, 64);printf("%s\n", buf);}close(fd);return 0;
} 

写操作进程

#include 
#include 
#include 
#include 
#include 
#include 
#include int main(int argc, char *argv[])
{ int fd = open("fifo", O_WRONLY);if(fd < 0){perror("open");exit(-1);}printf("open fifo ok!\n");char buf[64] = {0};while(1){fgets(buf, 64, stdin);buf[strlen(buf)-1] = '\0';write(fd, buf, strlen(buf));}close(fd);return 0;
} 

注:两个进程要同在运行状态,管道的读写操作才能执行,如果只有一方运行,程序会一直程阻塞状态

 


本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!

相关文章

立即
投稿

微信公众账号

微信扫一扫加关注

返回
顶部