Linux 基础入门操作 第九章 进程间通信之管道

第九章 进程间通信

进程通信有如下一些目的:

A、数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几 M 字节之间

B、共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程,应该立刻看到。

C、通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。

D、资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。

E、进程控制:有些进程希望完全控制另一个进程的执行(如 Debug 进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

现在 linux 使用的进程间通信方式:

1)管道(pipe)和有名管道(FIFO)

2)信号(signal)

3)消息队列

4)共享内存

5)信号量

6)套接字(socket)

9.1 管道

管道是 Linux 支持的最初 Unix IPC 形式之一,具有以下特点:

(1)管道是半双工的,数据只能向一个方向流动;需要双方通信时,需要建立起两个管道;

(2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);

(3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的

文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

(4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添

加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

c 复制代码
#include <unistd.h>
int pipe(int fd[2])

返回的 fd[0]用于读,fd[1]用于写。因此,一个进程在由 pipe()创建管道后,一般再 fork 一个子进程,然后通过管道实现父子进程间的通信(因此也不难推出,只要两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先,都可以采用管道方式来进行通信)。

管道的局限:

  • 只支持单向数据流;
  • 只能用于具有亲缘关系的进程之间;
  • 没有名字;
  • 管道的缓冲区是有限的(管道制存在于内存中,在管道创建时,为缓冲区分配一个页面大小);
  • 管道所传送的是无格式字节流,这就要求管道的读出方和写入方必须事先约定好数据的格式。

9.1.1 单项管道

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void read_from_pipe(int fd)
{
	char message[100];
	read(fd,message,100);
	printf("The messiage from parent is %s\n",message);
}

void write_to_pipe(int fd)
{
	char *message = "hello world\n";
	write(fd,message,strlen(message)+1);
}
int main()
{
	int fd[2];
	pid_t pid;
	int stat_val;
	if(pipe(fd))
	{
		printf("error");
		exit(1);
	}
	pid=fork();
	switch(pid)
	{
		case -1:
			printf("fork error");
			exit(1);
		case 0:
			printf("The child process is:%d\n",getpid());
			close(fd[1]);
			read_from_pipe(fd[0]);
			exit(0);
		default:
			printf("The father process is:%d\n",getpid());
			close(fd[0]);
			write_to_pipe(fd[1]);
			wait(&stat_val);
		exit(0);
	}
return 0;
}

9.1.2 全双工通讯,两个通道

这里用pipe1 父进程发送子线程 , ,pipe2 子线程发送父父线程

c 复制代码
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
void child_rw_pipe(int readfd,int writefd)
{
	char *message1="from child process\n";
	write(writefd,message1,strlen(message1)+1);
	char message2[100];
	read(readfd,message2,100);
	printf("child read is: %s\n",message2);
}
void parent_rw_pipe(int readfd,int writefd)
{
	char *message1 = "from parent process\n";
	write(writefd,message1,strlen(message1)+1);
	char message2[100];
	read(readfd,message2,100);
	printf("parent read is: %s \n",message2);
	}
int main()
{
	int pipe1[2],pipe2[2];
	pid_t pid;
	int stat_val;
	if(pipe(pipe1))
	{
		printf("error");
		exit(1);
	}
	if(pipe(pipe2))
	{
		printf("error");
		exit(1);
	}
	pid=fork();
	switch(pid)
	{
		case -1:
			printf("fork error");
			exit(1);
		case 0:
			printf("The child process is:%d\n",getpid());
			close(pipe1[1]);
			close(pipe2[0]);
			child_rw_pipe(pipe1[0],pipe2[1]);
			exit(0);
		default:
			printf("Thefather process is:%d\n",getpid());
			close(pipe1[0]);
			close(pipe2[1]);
			parent_rw_pipe(pipe2[0],pipe1[1]);
			wait(&stat_val);
			exit(0);
	}
return 0;
}

9.2 有名管道

FIFO 不同于管道之处在于它提供一个路径名与之关联,以 FIFO 的文件形式存在于文件系统中。这样,即使与 FIFO 的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过 FIFO 相互通信(能够访问该路径的进程以及 FIFO 的创建进程之间),因此,通过 FIFO 不相关的进程也能交换数据。

c 复制代码
include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int mkfifo(const char * pathname, mode_t mode)

mkfifo()会依参数 pathname 建立特殊的 FIFO 文件,该文件必须不存在,而参数 mode 为该文件的权限(mode%~umask) ,因此 umask 值也会影响到 FIFO 文件的权限。Mkfifo()建立的 FIFO 文件其他进程都可以用读写一般文件的方式存取。当使用 open()来打开 FIFO 文件时,O_NONBLOCK 旗标会有影响

1、当使用 O_NONBLOCK 旗标时,打开 FIFO 文件来读取的操作会立刻返回,但是若还没有其他进

程打开 FIFO 文件来读取,则写入的操作会返回 ENXIO 错误代码。

2、没有使用 O_NONBLOCK 旗标时,打开 FIFO 来读取的操作会等到其他进程打开 FIFO 文件来写入才正常返回。同样地,打开 FIFO 文件来写入的操作会等到其他进程打开 FIFO 文件来读取后才正常返回。

下面用server 发送,client 接收实现全双工通讯。

server.c

c 复制代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define FIFO_READ "readfifo"
#define FIFO_WRITE "writefifo"
#define BUF_SIZE 1024
int main(void)
{
	int wfd,rfd;
	char buf[BUF_SIZE];
	int len;
	umask(0);
	if(mkfifo(FIFO_WRITE,S_IFIFO|0666)){
		printf("can't create FIFO %s beause %s",FIFO_WRITE,strerror(errno));
		exit(1);
	}
	umask(0);
wfd = open(FIFO_WRITE,O_WRONLY);
if(wfd == -1){
printf("open FIFO %s error :%s",FIFO_WRITE,strerror(errno));
exit(1);
}
while((rfd = open(FIFO_READ,O_RDONLY)) == -1){
sleep(1);
}
while(1) {
printf("Server:");
fgets(buf,BUF_SIZE,stdin);
buf[strlen(buf)-1] = '\0';
if(strncmp(buf,"quit",4) == 0) {
close(wfd);
unlink(FIFO_WRITE);
close(rfd);
exit(0);
}
write(wfd,buf,strlen(buf));
len = read(rfd,buf,BUF_SIZE);
if(len > 0) {
buf[len] = '\0';
printf("Client:%s\n",buf);
}
}
}

linux中的 umask 函数主要用于:
在创建新文件或目录时,屏蔽掉新文件或目录不应有的访问允许权限。文件的访问允许权限共有9种,分别是:r w x r w x r w x(它们分别代表:用户读 用户写 用户执行 组读 组写 组执行 其它读 其它写 其它执行)。
其实这个函数的作用,就是设置允许当前进程创建文件或者目录最大可操作的权限,比如这里设置为0,它的意思就是0取反再创建文件时权限相与,也就是:(~0) & mode 等于八进制的值0666 & mode了,这样就是给后面的代码调用函数mkfifo给出最大的权限,避免了创建目录或文件的权限不确定性。

S_IFIFO|0666"指明创建一个命名管道且存取权限为0666,即创建者、与创建者同组的用户、其他用户对该命名管道的访问权限都是可读可写(这里要注意umask对生成的管道文件权限的影响)

client.c

c 复制代码
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <errno.h>
#include <stdlib.h>
#define FIFO_READ "writefifo"
#define FIFO_WRITE "readfifo"
#define BUF_SIZE 1024
int main(void)
{
	int wfd,rfd;
	char buf[BUF_SIZE];
	int len;
	umask(0);
	if(mkfifo(FIFO_WRITE,S_IFIFO|0666)){
		printf("can't create FIFO %s beause %s",FIFO_WRITE,strerror(errno));
		exit(1);
	}
	while((rfd = open(FIFO_READ,O_RDONLY)) == -1) {
		sleep(1);
	}
		Wfd = open(FIFO_WRITE,O_WRONLY);
	if(wfd == -1){
		printf("open FIFO %s error :%s",FIFO_WRITE,strerror(errno));
		exit(1);
	}
	while(1){
		len = read(rfd,buf,BUF_SIZE);
		if(len > 0) {
		buf[len] = '\0';
		printf("Server:%s\n",buf);
		}
		printf("Client:");
		fgets(buf,BUF_SIZE,stdin);
		buf[strlen(buf)-1] = '\0';
		if(strncmp(buf,"quit",4) == 0) {
			close(wfd);
			unlink(FIFO_WRITE);
			close(rfd);
			exit(0);
		}
		write(wfd,buf,strlen(buf));
	}
	return 0;
}
相关推荐
徐同保1 天前
nginx转发,指向一个可以正常访问的网站
linux·服务器·nginx
HIT_Weston1 天前
95、【Ubuntu】【Hugo】搭建私人博客:_default&partials
linux·运维·ubuntu
实心儿儿1 天前
Linux —— 基础开发工具5
linux·运维·算法
oMcLin1 天前
如何在SUSE Linux Enterprise Server 15 SP4上通过配置并优化ZFS存储池,提升文件存储与数据备份的效率?
java·linux·运维
王阿巴和王咕噜1 天前
【WSL】安装并配置适用于Linux的Windows子系统(WSL)
linux·运维·windows
布史1 天前
Tailscale虚拟私有网络指南
linux·网络
水天需0101 天前
shift 命令详解
linux
wdfk_prog1 天前
[Linux]学习笔记系列 -- 内核支持与数据
linux·笔记·学习
Xの哲學1 天前
深入剖析Linux文件系统数据结构实现机制
linux·运维·网络·数据结构·算法
深圳市恒讯科技1 天前
Linux 文件权限指南:chmod 755、644、drwxr-xr-x 解析
linux·服务器·xr