【Linux】进程间通信的方式-管道

进程之间的通信方式主要有六种,包括管道,信号量,消息队列,信号,共享内存,套接字

管道

        管道是 Unix 中最古老的进程间通信的形式,我们把从一个进程连接到另一个进程的数据流称为一个“管道”

例如,我们统计登录用户个数

       其中 who 命令和 wc 命令都是两个程序,当它们运行起来后就变成了两个进程,who 进程通过标准输出将数据打到“管道”当中,wc 进程再通过标准输入从“管道”当中读取数据,至此便完成了数据的传输,进而完成数据的进一步加工处理

        管道:管道是半双工的,双方需要通信的时候,需要建立两个管道。管道的是指是一个内核缓冲区,进程以先进先出的方式从缓冲区存储数据:管道一端的进程顺序地将进程数据写入缓冲区,另一端的进程则顺序地读取数据,该缓冲区可以看做一个循环队列,读和写的位置都是自动增加的,一个数据只能被读一次,读出以后再缓冲区都不复存在了。当缓冲区读空或写满时,有一定的规则控制相应的读进程或写进程是否进入等待队列,当空的缓冲区有新数据写入或满的缓冲区有数据读出时,就唤醒等待队列中的进程继续读写。管道是最容易实现的。

匿名管道

        匿名管道仅限于本地关联进程之间的通信

 注意:

        这里父子进程看到的同一份文件资源是由操作系统来维护的,所以当父子进程对该文件进行写入操作时,该文件缓冲区当中的数据并不会进行写时拷贝

        管道虽然用的是文件的方案,但操作系统一定不会把进程进行通信的数据刷新到磁盘当中,因为这样做有 IO 参与会降低效率,而且也没有必要。也就是说,这种文件是一批不会把数据写到磁盘当中的文件,换句话说,磁盘文件和内存文件不一定是一一对应的,有些文件只会在内存当中存在,而不会在磁盘当中存在

pipe函数

        函数描述

                创建匿名管道

        函数原型

                int pipe(int pipefd[2]) 

        函数参数

                pipefd 是一个传出参数,用于返回两个指向管道读端和写端的文件描述符

                        pipefd[0]:管道读端的文件描述符

                        pipefd[1]:管道写端的文件描述符

        函数返回值

                成功返回0

                失败返回-1,设置error

        在创建匿名管道实现父子进程间通信的过程中,组要pipe函数和fork函数搭配使用,具体步骤如下:

1、父进程调用 pipe 函数创建管道

 

2、父进程创建子进程

3、父进程关闭写端,子进程关闭读端

注意:

        管道只能够进行单向通信,因此当父进程创建完子进程后,需要确认父子进程谁读谁写,然后关闭相应的读写端

        从管道写端写入的数据会被存到内核缓冲,直到从管道的读端被读取

/创建管道
int pipefd[2];
pipe(pipefd);
//创建子进程
int pid = fork();
if (pid == 0) {//子进程写数据,关闭读端
	close(pipefd[0]);
	slepp(3);
	int write_count = write(pipefd[1], "hello", 5);
	printf("write_count = %d\n", write_count);
}
else {//父进程读数据,关闭写端
	close(pipefd[1]);
	char buff[64];
	int read_count = read(pipefd[0], buff, sizeof(buff));
	printf("read count = %d\n", read_count);
	if (read_count > 0) {
		buff[read_count] = '\0';
		printf("buff:%s\n", buff);
	}
}
while (1);
  • 管道中没有数据:write 返回成功写入的字节数,读端进程阻塞在 read 上
  • 管道中有数据没满:write 返回成功写入的字节数,read 返回成功读到的字节数
  • 管道写满:写端进程阻塞在 write 上,read 返回成功读到的字节数
  • 写端全部关闭: read 正常读,返回成功读到的字节数(没有数据返回0,不阻塞)
  • 读端全部关闭:写端进程 write 会异常终止进程(被 SIGPIPE 信号杀死)
#include<iostream>
using namespace std;

int pipefd[2];
pipe(pipefd);
int pid = fork();
if (pid == 0) {
	close(pipefd[0]);
	int write_count;
	while (1) {//模拟管道写满
		write_count = write(pipefd[1], "hello", 5);
		printf("write_count = %d\n", write_count);
	}
}
else {
	close(pipefd[1]);
	char buff[10];
	int read_count = read(pipefd[0], buff, sizeof(buff));
	printf("parent read count = %d\n", read_count);
	while (1);
}

read:

管道中没有数据且写端没有关闭:read阻塞

管道中有数据:read 返回成功读到的字节数

write:

管道没满并且读端没有关闭:write 返回成功写入的字节数

管道已满并且读端没有关闭:write 阻塞

读端全部关闭:写端进程 write 会异常终止进程

命名管道 

        命名管道和匿名管道一样,都是内存文件,只不过命名管道在磁盘有一个简单的映像,但这个映像的大小永远为0,因为命名管道和匿名管道都不将通信数据刷新到磁盘当中

创建命名管道使用 mkfifo 命令

mkfifo fifo

mkfifo函数

         函数描述

                程序中创建命名管道

        头文件

                #include<sys/types.h>

                #include<sys/stat.h.

        函数原型

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

        函数参数

                pathname:表示要创建的命名管道文件

                mode:表示创建命名管道文件的默认权限

        函数返回值:

                成功,返回0

                失败,返回-1

命名管道在父子进程间通信

#include<iostream>
using namespace std;

#include<sys/stat.h>

mkfifo("./fifo", 0644);
int fd = open("./fifo", O_RDWR);
int pid = fork();
if (pid == 0) {
	write(fd, "hello fifo", 10);
}
else if (pid > 0) {
	char buf[1024];
	int ret = read(fd, buf, sizeof(buf));
	printf("read count = %d,buf= %s\n", ret buf);
}

命名管道在没有血缘关系的进程间通信:
wfifo.c

int fd = open("./fifo",O_WRONLY);
printf("fd = %d\n",fd);
write(fd,"hello fifo",10);

rfifo.c

int fd = open("./fifo",O_RDONLY);
char buf[1024];
int ret = read(fd,buf,sizeof(buf));
printf("read ccount = %d,buf = %s\n",ret,buf);

命名管道的打开规则:

  • 读进程打开 FIFO,没有写进程打开
    • 没有 O_NONBLOCK,阻塞直到有写进程打开该 FIFO
    • 有 O_NONBLOCK,立刻返回成功 
  • 写进程打开 FIFO,没有读进程打开
    • 没有 O_NONBLOCK,阻塞直到有读进程打开该 FIFO
    • 有 O_NONBLOCK,立刻返回失败 
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值