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;
}
相关推荐
一切皆是定数44 分钟前
Linux驱动开发——LED驱动开发
linux·驱动开发·b树
小小不董1 小时前
图文深入理解Oracle DB Scheduler
linux·运维·服务器·数据库·oracle
不烦下雨c2 小时前
[网络]抓包工具介绍 tcpdump
linux·tcpdump
南瓜小米粥、2 小时前
通过fdisk初始化Linux数据盘
linux·运维·服务器
秋风起,再归来~2 小时前
【Linux庖丁解牛】—Linux基本指令(中)!
linux·指令
Eternal-Student2 小时前
预处理、编译、汇编、链接
linux·汇编·windows
sp_wxf3 小时前
Stream流
linux·服务器·windows
LYK_HAHA3 小时前
centos常用知识和命令
linux·运维·centos
可涵不会debug4 小时前
【Linux】信号知识三把斧——信号的产生、保存和处理
linux·运维·信号
jyan_敬言4 小时前
【Linux】Linux命令与操作详解(一)文件管理(文件命令)、用户与用户组管理(创建、删除用户/组)
linux·运维·服务器·c语言·开发语言·汇编·c++