Linux系统应用编程---进程间通信(一)【管道pipe fifo】

0. 进程间通信(IPC inter process communication)的概念

进程间通信就是在不同进程之间传播或交换信息,那么不同进程之间存在着什么双方都可以访问的介质呢?

1)进程的用户空间是互相独立的,一般而言是不能互相访问的,唯一的例外是共享内存区。【内存】

2)系统空间是“公共场所”,各进程均可以访问,所以内核也可以提供这样的条件。【内核】

3)双方都可以访问的外设。在这个意义上,两个进程当然也可以通过磁盘上的普通文件交换信息。【磁盘】

1. 管道的工作原理

2. pipe(有名管道)

函数原型

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

1)管道作用于有血缘关系的进程之间,通过fork来传递

2)调用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]固定用于管道的读端,filedes[1]固定用于管道的写端(很好记,就像0是标准输入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。

3)pipe函数调用成功返回0,调用失败返回-1。

4)无名管道是单工的工作方式,即同一时刻要么只能读管道,要么只能写管道。父子进程虽然同时拥有管道的读端和写端,但只能使用其中一个。
 

程序1:实现父进程写,子进程读父进程写的内容,并将读到的内容输出到标准输出。

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <wait.h>
 
int main(void)
{
	int fd[2];
	char str[1024] = "hello world.\n";
	char buf[1024];
	pid_t pid;
	
	//fd[0]读端, fd[1]写端
	if(pipe(fd) < 0){
		perror("pipe");
		exit(1);
	}
	
	pid = fork();
	//父写子读
	if(pid > 0){		//父进程
		close(fd[0]);	//关闭父进程的读端
		sleep(2);
		write(fd[1], str, strlen(str));
		wait(NULL);
	}
	else if(pid == 0){//子进程
		int len;
		close(fd[1]);	//关闭子进程的写端
		len = read(fd[0], buf, sizeof(buf));
		//sprintf(str, "child %s", buf);
		write(STDOUT_FILENO, buf, len);		//将子进程读到的内容输出到标准输出
	}
	else{
		perror("fork");
		exit(1);
	}
	
	return 0;	
}

使用pipe管道时要注意:

1、写管道关闭,读端读完管道里面内容时,再次读,返回0,相当于读到EOF

2、写端未关闭,写端暂时无数据;读端读完管道里数据时,再次读,阻塞

3、读端关闭,写端写管道,产生SIGPIPE信号,写进程默认情况下回终止进程

4、读端为读管道数据,当写端写满管道数据后,再次写,阻塞
 

3. fifo(有名管道)

3.1 fifo原理

有名管道,解决无血缘关系的进程通信,有名管道fifo特点:

1)当只写打开FIFO管道时,如果没有FIFO没有读端打开,则open写打开会阻塞。
2) FIFO内核实现时可以支持双向通信。(pipe单向通信,因为父子进程共享同一个file结构体)
3)FIFO可以一个读端,多个写端;也可以一个写端,多个读端。
 

 

通过mkfifo指令创建管道文件(注意在WinShare目录下不能创建管道文件,因为windows没有管道文件)

fifo_write.c----向管道文件中写数据

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#incude <fcntl.h>
 
void sys_err(char *str, int exitno)
{
	perror(str);
	exit(exitno);
}
 
int main(int argc, char * argv[])
{
	int fd;
	char buf[1024] = "hello world.\n";
	if(argc < 2){
		printf("./a.out fifoname.\n");
		exit(1);
	}
	
	//fd = open(argv[1], O_RDONLY);
	fd = open(argv[1], O_WRONLY);
 
	if(fd < 0)
		sys_err("open", 1);
	
	write(fd, buf, strlen(buf));
	close(fd);
	
	return 0;	
}

fifo_read.c---读取管道文件中的数据

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <stdlib.h>
 
void sys_err(char *str, int exitno)
{
	perror(str);
	exit(exitno);
}
 
int main(int argc, char *argv[])
{
	int fd, len;
	char buf[1024];
	if(argc < 2){
		printf("./a.out fifoname.\n");
		exit(1);
	}
	
	//fd = open(argv[1], O_RDONLY);
	fd = open(argv[1], O_RDONLY);
	if(fd < 0)
		sys_err("open", 1);
	
	len = read(fd, buf, sizeof(buf));
	write(STDOUT_FILENO, buf, len);
	close(fd);
	
	return 0;
	
}

执行的时候开两个终端窗口,先执行fifo_write.c,向管道中写数据,可以看到此时是阻塞的;

打开另外一个终端窗口,执行fifo_read.c,可以看到读出管道的数据,并打印在终端。

同时可以看到读写前后管道文件的大小是不变的

已标记关键词 清除标记
相关推荐
©️2020 CSDN 皮肤主题: 大白 设计师:CSDN官方博客 返回首页