当前位置: 代码迷 >> 综合 >> 【linux系统编程学习笔记】第三节:进程通信方式之匿名管道与有名管道
  详细解决方案

【linux系统编程学习笔记】第三节:进程通信方式之匿名管道与有名管道

热度:3   发布时间:2024-01-30 01:30:10.0

其实开心好简单 最紧要好玩

进程通信方式之管道

进程间通信方式

管道——无名管道/匿名管道

无名管道相关API

int pipe(int pipefd[2]);//创建无名管道pipe 

管道——有名管道

有名管道API


进程间通信方式

  1. 管道
    • 无名管道 pipe:适用于亲缘关系进程间的、一对一的通信
    • 有名管道 fifo :适用于任何进程间的一对一、多对一的通信
    • 套接字 socket:适用于跨网络的进程间通信
  2. 信号:异步通信方式
  3. system-V IPC对象
    1. 共享内存:效率最高的通信方式
    2. 消息队列:相当于带标签的增强版管道
    3. 信号量
      1. 信号量组:参数复杂,功能强大到臃肿
      2. POSIX有名信号量:适用于多进程,参数简单,接口明晰,老少咸宜
      3. POSIX无名信号量:适用于多线程,参数简单,接口明晰,童叟无欺

 

管道——无名管道/匿名管道

无名管道属性:

  1. 它只能作用于亲缘进程(匿名管道的创建他使用pipe创建,诞生出两个操作管道读写端的文件描述符,只有创建子进程的时候才会被继承过去,无法通过别的方式打开同一个管道)
  2. 半双工通信(在读的时候不能写入,在写入的时候不能读取)
  3. 操作无原子性
  4. 无法用lseek定位操作位置

无名管道相关API

int pipe(int pipefd[2]);//创建无名管道pipe 

#include <unistd.h>  int pipe(int pipefd[2]);

函数功能:

  • 随机创建一个匿名管道,并且返回管道的两个通信文件描述符给传入的数组

函数参数:

  • Pipefd:这是一个两个int型元素的数组,管道创建成功后会将读端文件描述符放到第0个元素中,将写端文件描述符放到第1个元素中

返回值:

  • 成功返回0,失败返回-1

注意:

  1. pipe 拥有两个文件描述符,一个专用于读fd[0],一个专用于写fd[1]
  2. 创建出来的 pipe 的描述符,只能通过子进程继承的方式传递给别的进程,因此只能用于亲缘进程间的通信,其他非亲缘进程无法获取 pipe 的描述符。
  3. 管道打开时,必须同时有读者和写者,否则 open 会阻塞
  4. 不能有多个进程同时对 pipe 进行写操作,否则数据有可能被覆盖
  5. 总结: pipe 适用于一对一的、具有亲缘关系的进程间的通信。

例程

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>int main(void)
{int pipe_fd[2];//管道文件描述符int retval;//管道返回值pid_t cpid_a, cpid_b;//子进程a、b的PIDchar buffer[1024];//ssize_t rd_ret,wr_ret;//读写返回值//创建一个无名管道//并且往pipe_fd数组当中的0元素填充管道的读端文件描述符//往1元素填充管道写端文件描述符retval = pipe(pipe_fd);if(retval == -1){perror("create pipe error\n");return -1;}bzero(buffer, sizeof(buffer));//清零cpid_a = fork();//创建子进程aif(cpid_a == 0){//往管道写入数据wr_ret = write(pipe_fd[1], "hello", strlen("hello"));if(wr_ret == -1){perror("A进程写入失败\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}cpid_b = fork();//创建子进程bif(cpid_b == 0){//读取管道的数据rd_ret = read(pipe_fd[0], buffer, sizeof(buffer));if(rd_ret == -1){perror("子进程读取失败\n");exit(EXIT_FAILURE);}printf("B子进程读取到=%s\n", buffer);//往管道写入数据wr_ret = write(pipe_fd[1], "hello world", strlen("hello world"));if(wr_ret == -1){perror("B进程写入失败\n");exit(EXIT_FAILURE);}exit(EXIT_SUCCESS);}//等待子进程子进程a、b结束,回收资源waitpid(cpid_a, NULL, 0);waitpid(cpid_b, NULL, 0);//读取管道的数据rd_ret = read(pipe_fd[0], buffer, sizeof(buffer));if(rd_ret == -1){perror("子进程读取失败\n");exit(EXIT_FAILURE);}printf("父进程读取到=%s\n", buffer);close(pipe_fd[0]);close(pipe_fd[1]);return 0;
}

 

管道文件的读写特性:

 

有写者

没有写者

有数据

无数据

有数据

无数据

读操作

正常读取

阻塞

正常读取

立即返回0

 

有读者

没有读者

缓冲区未满

缓冲区已满

缓冲区未满

缓冲区已满

写操作

正常写入

阻塞

立即被系统发出的SIGPIPE杀死

备注:

  •     读者 —— 对管道拥有读权限的进程
  •     写者 —— 对管道拥有写权限的进程

应用场景:

  1. 根据调用第三方程序的特点,文件描述符会被继承过去第三方程序中,如果你的第三方程序想要引用也是可以的(不方便)(不具备第三方调度程序的实用性)
  2. 守护进程的特点是将进程变成一个后台服务,并非同步运行多个进程(不具备引用匿名管道的实用性)
  3. 灵活使用:管道的特点在于他本身拥有读写阻塞特性,你可以利用这一个特性制造出属于自己的特殊的缓冲区(数据的同步互斥特性)

管道——有名管道

有名管道属性:

  1. 他能够作用无任何亲缘关系的进程
  2. 全双工通信
  3. 操作有原子性
  4. 无法用lseek定位操作位置

有名管道API

int mkfifo(const char *pathname, mode_t mode);//创建有名管道文件

#include <sys/types.h>
#include <sys/stat.h>int mkfifo(const char *pathname, mode_t mode);

 

函数功能:

  • 创建一个有名管道文件

函数参数:

  • pathname:创建的管道文件名字(可以附加路径)(注意:一定得是linux下面的文件系统才能够创建出来,禁止使用windows的共享文件夹)
  • mode:创建出来的文件的权限值(参照open函数的mode参数)

返回值:

  • 成功返回0,失败返回-1

应用场景:

  1. 因为其操作具备原子性,经常拿过来作为一个多入口缓冲区
  2. 进程间通信

 读端fifo_read.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <strings.h>#define	FIFO_NAME	"myfifo"	//管道文件名(可加路径)int main(void)
{int retval;int fifo_fd;//有名管道描述符ssize_t rd_ret;char buffer[1024];//判断FIFO_NAME代表的文件是否存在,不存在则进入判断语句中if(access( FIFO_NAME, F_OK) != 0){//创建一个有名管道文件//FIFO_NAME:代表文件名,0644:代表创建文件的权限mkfifo(FIFO_NAME, 0644);}//打开管道fifo_fd = open(FIFO_NAME, O_RDWR);if(fifo_fd == -1){perror("打开管道出错\n");return -1;}while(1){bzero(buffer, sizeof(buffer));//读取管道内容rd_ret = read(fifo_fd, buffer, sizeof(buffer));if(rd_ret == -1){perror("读取管道数据异常");break;}printf("读到大小为%ld,内容为 %s 的数据\n", rd_ret, buffer);}close(fifo_fd);return 0;
}

 写段fifo_write.c

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>#define	FIFO_NAME	"myfifo"	//管道文件名(可加路径)int main(void)
{int retval;int fifo_fd;//有名管道描述符ssize_t wr_ret;char buffer[1024];//判断FIFO_NAME代表的文件是否存在,不存在则进入判断语句中if(access( FIFO_NAME, F_OK) != 0){//创建一个有名管道文件FIFO_NAME:代表文件名,0644:代表创建文件的权限mkfifo(FIFO_NAME, 0644);}fifo_fd = open(FIFO_NAME, O_RDWR);if(fifo_fd == -1){perror("打开管道出错\n");return -1;}while(1){//获取用户输入fgets(buffer, sizeof(buffer), stdin);//向管道写入数据wr_ret = write(fifo_fd, buffer, strlen(buffer));if(wr_ret == -1){perror("写入管道数据异常");break;}}close(fifo_fd);return 0;
}

 int access(const char *pathname, int mode);//检查指定文件权限或是否存在

#include <unistd.h>int access(const char *pathname, int mode);

 

函数功能:

  • 检查指定文件的权限(存在与否)

函数参数:

  • 指定查的文件名(可以附加路径)
  • mode:代表检查的权限
    1. R_OK:判断是否可读
    2. W_OK:判断是否可写
    3. X_OK:判断是否可执行
    4. F_OK:判断文件是否存在

返回值:

  • 如果权限可以,成功返回0,失败返回-1