四种进程通讯方式总结

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程之间要交换数据必须通过内核,在内核中开辟一块缓冲区,进程 A 把数据从用户空间拷到内核缓冲区,进程 B 再从内核缓冲区把数据读走,内核提供的这种机制称为进程间通信。

1、管道

1.1 匿名管道

1)创建管道

​ 匿名管道只能用于有亲缘关系的父子进程之间,且只能单向通讯。

​ 管道随进程,进程在管道在,进程消失管道对应的端口也关闭,两个进程都消失管道也消失。

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

//例子
int fd[2];  
int ret = pipe(fd);

**fd:**参数返回两个文件描述符,fd[0] 指向管道的读端,fd[1] 指向管道的写端。

**返回值:**0:创建成功;-1:创建失败。

2)管道实现进程间通讯流程

  • 父进程创建管道,得到两个⽂件描述符指向管道的两端;
  • 父进程 fork 出子进程,子进程继承父进程的管道描述符;
  • 由于父子进程都有一套管道文件描述符,因此使用管道读端的需要关闭写端文件描述符,使用管道写端的需要关闭读文件描述符;
  • 管道通过环形队列实现,数据从写端流入从读端流出。

3)使用示例

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/types.h>
#include <sys/wait.h>

int main(void)
{
   
	int fd[2];
	int ret = 0;

	ret = pipe(fd);
	if(ret < 0){
   
		perror("creat pipe fail");
		exit(-1);
	}

	pid_t pid = fork();

	if(pid == 0){
   	//child
		close(fd[0]);	//减少一个引用计数
		char *msg = "I am child\n";
		for(int i = 0; i < 5; i++){
   
			write(fd[1],msg,strlen(msg));
			sleep(2);
		}
		close(fd[1]);
		_exit(0);
	}else{
   	//parent
		close(fd[1]);	//减少一个引用计数
		char buf[100];
		while(1){
   
			ssize_t count = read(fd[0], buf, sizeof(buf));
			if(count > 0){
   
				fputs(buf, stdout);
			}
			if(count == 0)	//关闭写描述符后,继续读会返回0
				break;
		}
		close(fd[0]);
		wait(NULL);
		exit(0);
	}
}

4)管道读取数据的四种情况

  • 读端不读,写端一直写:

    ​ 会导致管道里写满数据,满时将导致 write 阻塞。

  • 写端不写,读端一直读:

    ​ 会导致 read 阻塞。

  • 读端一直读,写端不写且关闭了文件描述符:

    ​ 当管道的剩余数据都被读取后,再次调用 read 会返回 0,而不是阻塞。

  • 写端一直写,读端不读且关闭了文件描述符:

    ​ 负责写的进程会收到 SIGPIPE 信号,通常会导致进程异常终止。


1.2 有名管道 FIFO

​ 不同于匿名管道,FIFO 有一个路劲名与之关联,允许无亲缘关系的进程访问同一个 FIFO,进行通信。

1)创建 FIFO

#include <sys/types.h>
#include <sys/stat.h>

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

**pathname:**FIFO 文件路径名。

**mode:**FIFO 文件权限,可由以下权限进行或运算组合:

S_IRUSR 00400 owner has read permission 
S_IWUSR 00200 owner has write permission 
    
S_IRGRP 00040 group has read permission 
S_IWGRP 00020 group has write permission 
    
S_IROTH 00004 others have read permission 
S_IWOTH 00002 others have write permission 

**返回值:**0:创建成功;-1:创建失败。

注意:

​ mkfifo() 函数只用来创建 FIFO 文件,如果文件路径名已存在,则会返回 EEXIST。如果想要打开 FIFO 文件则用 open() 函数,如果不确定 FIFO 文件是否存在则先调用 mkfifo() 来检查,再调用 open 函数。

2)打开 FIFO

​ 如果以某种方式(读/写)打开有名管道,系统将阻塞进程,直到有另一个进程(包括自己)以另一种方式(写/读)打开有名管道。一个进程可以以可读可写的方式打开有名管道,进程不会阻塞。

3)读操作

  • 如果管道中没有数据,读操作默认阻塞;
  • 如果管道中数据小于要读取数量,读出所有数据返回;
  • 如果管道中数据大于要读取数量,读出期望大小数据返回。

4)写操作

  • 如果管道中没有空间,写操作阻塞;
  • 如果管道中剩余空间小于要写入数据,写满空间后阻塞;
  • 如果管道中剩余空间大于要写入数据,写入数据后返回。

5)中途其中一个进程退出

  • 未退出一端是写操作,返回 SIGPIPE 信号;
  • 未退出一端是读操作,读操作不再阻塞返回 0。

6)使用示例

//写数据进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
   
	char PWD[100] = {
   0};
	char writeData[] = "fifo write\n";

	getcwd(PWD, sizeof(PWD));	//获取当前目录
	strcat(PWD,"/FIFO");		//在当前目录添加FIFO命名

	int ret = mkfifo(PWD, 0666);
	if(ret != 0){
   
		printf("已创建有名管道\n");
	}
	printf("%s\n",PWD);
	int fd = open(PWD, O_WRONLY);	//只写方式打开管道

	for(int i = 0; i < 5; ++i){
   
		write(fd, writeData, sizeof(writeData));
		printf("写入第%d次\n",i);
		sleep(2);
	}
	close(fd);
	printf("写进程结束\n");
	exit(0);
}
//读数据进程
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>

int main(void)
{
   
	char PWD[100] = {
   0};
	char buf[100];
	int i = 1;

	getcwd(PWD, 
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值