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;
}
相关推荐
titxixYY40 分钟前
SElinux
linux·运维·服务器
香吧香2 小时前
getent使用小结
linux
麻瓜也要学魔法3 小时前
Linux关于vim的笔记
linux·笔记·vim
云边的快乐猫3 小时前
navicat远程连接虚拟机centos系统的mysql
linux·mysql·centos·虚拟机·navicat
望获linux3 小时前
在 ARM 平台上如何实现Linux系统的1秒启动
linux·服务器·开发语言·数据库·操作系统·嵌入式操作系统·arm平台
先天打工圣体的男人4 小时前
Linux中安装InfluxDB
linux·运维·服务器
dessler4 小时前
云计算&虚拟化-kvm-克隆(clone)虚拟机
linux·运维·云计算
陌小呆^O^4 小时前
CmakeList.txt之Linux-pthread
linux·运维·服务器
Sagice5 小时前
CVE-2019-13272(Linux本地内核提权)
linux·运维·服务器·网络·安全
Kendra9195 小时前
RHCE SELINUX安全管理
linux