[Linux] 进程间通信之管道(匿名管道和命名管道)

进程间通信的目的

  • 数据传输:一个进程需要将它的数据发送给另一个进程
  • 资源共享:多个进程之间共享同样的资源
  • 通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)
  • 进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

进程间通信的发展

管道system V IPCPOSIX IPC
匿名管道System V 消息队列消息队列
命名管道System V 共享内存共享内存
System V 信号量信号量、互斥量、条件变量
读写锁

管道

1) 相关概念

  1. 是什么? 从一个进程连接到另一个进程的一个数据流。
  2. 有多大? 从Linux2.6版本之后,其大小为65536bytes。

2) 匿名管道

特点

  1. 只能用于具有共同祖先的进程之间进行通信,通常是父子进程进行通信的一种方式,通过创建子进程实现通信。
  2. 管道提供流式服务,是半双工的,只能进行单项通信。
  3. 管道是基于文件实现的,文件随进程,管道的生命周期也随进程;进程退出,管道释放。
  4. 一般而言,内核会对管道提供同步与互斥。

读写规则

  1. 写端慢,写端若不关闭文件描述符且不写入,读端在一端时间内会阻塞。
  2. 读端慢,在实际写入时,若写入条件不满足,写端在一端时间内会阻塞。
  3. 如果写端关闭文件描述符,读端读到结尾时,会关闭读端的文件描述符,即,read会返回0。
  4. 如果读端关闭文件描述符,写端进程后续可能会被直接杀掉,变成僵尸进程:write操作会产生SIGPIPE信号,进而导致write进程退出。

实现从键盘读入数据写入管道(子进程),(父进程)读取数据写到屏幕的测试代码

int main(){
int fd[2] = {0};  //定义一个文件描述符数组:fd[0]--read,fd[1]--write
int pi = pipe(fd);  //创建匿名管道
if(pi != 0){
	printf("pipe error!\n");
	exit(1);
}

pid_t id = fork();  //创建子进程
if(id<0){
	printf("fork error!\n");
	exit(2);
}
else if(id==0){  //child:write
	close(fd[0]);  //关闭读端
	char* msg;
	while(1){
		//从键盘读入数据,写入管道
		fgets(msg, sizeof(msg), stdin);
		write(fd[1], msg, strlen(msg));
		sleep(1);  
	}
}
else{  //father:read
	close(fd[1]);  //关闭写端
	int count = 0;
	char buf[64];
	while(1){
		ssize_t s = read(fd[0], buf ,sizeof(buf)-1);   //s是读取的长度
		if(s > 0){
			buf[s] = 0;  
			printf("father get message: %s\n", buf);
		}
	}
	return 0;
}
}

3) 命名管道

特点

可以用于不相关进程之间的通信

创建方式

  1. 使用命令行:mkfifo pipe(创建了pipe命名管道)
  2. 从程序中创建(调用mkfifo函数):mkfifo(“pipe”, 0644);

使用方式

通过open函数打开管道进行使用

使用命名管道,实现文件拷贝的测试代码

//使用命名管道,对文件进行拷贝
//test_1.c文件中:
int main(int argc, char *argv[]){
 mkfifo("pipe", 0644);  //建立命名管道
 int fd = open("pipe", O_WRONLY);
 int infd = open("abc.txt", O_RDONLY); 
 char buf[64];
 int n;
 while ((n=read(infd, buf, 64))>0){
	write(fd, buf, n);  //从infd中读入读入数据到管道pipe
 }
 close(infd);
 close(fd);
 return 0;
}

//test_2.c文件中:
int main(){
 int infd;
 infd = open("pipe", O_RDONLY);  //打开同一个命名管道
 int outfd = open("def.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
 char buf[64];
 int n;
 while ((n=read(infd, buf, 64))>0){
	write(outfd, buf, n);  //从管道pipe中读出数据写入到outfd文件中,实现文件内容从infd到outfd之间的拷贝
 }
 close(infd);
 close(outfd);
 return 0;
}

利用命名管道,实现简单的Server&Client之间的通信

  1. Server端读取Client端发送的信息
int main(){
mkfifo("./myfifo", 0644);  //创建myfifo命名管道,并以只读的方式打开
int fd = open("./myfifo", O_RDONLY);
if(fd < 0){
	printf("open error...\n");
	exit(1);
}

char buf[64];
while(1){
	ssize_t s = read(fd, buf, sizeof(buf)-1);
	if(s > 0){
		buf[s] = 0;
		printf("Client say# %s", buf);
	}
	else if(s == 0){
		printf("Client stopping...\n");
	}
	else{
		printf("read error...\n");
		break;  //当前一次读取失败
	}
}
close(fd);
return 0;
}
  1. Client端发送信息给Server端
int main(){
int fd = open("./myfifo", O_WRONLY);
if(fd < 0){
	printf("open error...\n");
	exit(1);
}

char msg[64];
while(1){
	printf("Please Enter Message # ");  
	fflush(stdout);  //使用printf,要注意刷新标准输出
	ssize_t s = read(0, msg, sizeof(msg)-1);  //从键盘读数据
	if(s > 0){
		msg[s] = 0;
		write(fd, msg, strlen(msg)); 
	}
	else if(s == 0){
	}
	else{
		printf("write error...\n");
		break;  
	}
}
close(fd);
return 0;
}
  • 0
    点赞
  • 3
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值