Linux(高级编程)5————进程间通信1(匿名管道)

进程通信简介
IPC(进程间通信),前面我们学习了进程控制,了解进程间隔离的,各自有独立的虚拟内存空间无法直接访问对方的地址空间、进程调度和均衡则让进程互不干扰地使用处理器资源。但有时需要多个进程相互协作完成任务,而我们在没有学习进程间通信时我只能通过fork或exec传送打开的文件,或文件系统,进程之间的通信很不方便。
那么进程间通信的目的就很明确了:
1.数据传输:一个进程需要将自己的数据传递给另一个进程。
2.资源共享:多个进程之间需要共享部分资源。
3.通知事件:一个进程需要自己的所发生的事件传递给另一个进程,通知他们发生了什么事件(如:子进程退出需要通知父进程,父进程获取子进程状态)。一个进程需要发送一个或者一组消息给另一个进程。
4.进程控制:有些进程需要完全控制另一个进程的执行(如debug),此时进程希望能拦截另一个进程所有的陷入和异常,并及时直到它的状态。
进程间通信发展:
管道、System V进程间通信、POSIX进程通信
进程间通信的方式:

  • 管道

    1.管道
    2.匿名管道

  • System V IPC:

    1.System V 消息队列
    2.System V 共享内存
    3.System V 信号量

  • POSIX进程间通信:

    1.消息队列
    2.共享内存
    3.信号量
    4.互斥量
    5.条件变量
    6.读写锁

进程间通信管道(匿名管道):
这片博客的重点匿名管道从这就要开始了,管道是unix系统IPC最古老的方式,并且所有的Unix系统都支持这种通信机制。我们把进程连接到另一个进程的一个数据“流”称作管道,本片博客讲述无名管道。

  • 无名管道特点:
    1.管道是半双工(数据只能在一个方向上流动)。
    2.管道只能在具有亲缘关系的进程之间通信(即具有共同祖先的进程之间通信)。
    3.管道通信是面向字节流的。(字节流特点:数据无规则、无明显边界、收发灵活)。
    4.管道的生命周期随进程。
    5.管道自带同步与互斥(概念:1.临界资源及共享资源;2.临界区即对临界资源操作的代码;3.同步即临界资源访问的时序性控制;4.互斥即对临界资源访问的同一时间的唯一性控制,保护临界资源的安全性)。

  • 管道创建:

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

返回值:成功返回0,失败返回-1;

  • 管道的原理:
    在shell命令中当我们输入ls | more命令时会看到
    ls | more 即将ls的输出做为more的输入最终得到more的结果。
    在这里插入图片描述
    在这里插入图片描述
    对于单个进程的管道几乎是没有用的,通过管道我们可以完成父子进程间的通信。
    父子进程通过管道通信原理
    父进程读子进程写的原理过程:
    在这里插入图片描述
    管道使用实例:
    test.c:父进程读文件子进程写文件
//父进程读,子进程写
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<string.h>

int main(void)
{
	char* msg = "hello pipe futher and sub\n";
	char buf[1024];
	//创建管道
	int fd[2];
	if(pipe(fd) < 0)
	{
		print("pipe error!");
		exit(-1);
	}
	pid_t pid = fork();
	if(pid < 0)
	{
		print("fork error!");
		exit(-1);
	}
	//子进程写,父进程读
	if(pid == 0)
	{
		//子进程关闭读端
		close(fd[0]);
		//写操作
		write(fd[1],msg,strlen(msg));
		exit(1);
	}
	else
	{
		wait(NULL);
		//父进程关闭写端
		close(fd[1]);
		//读操作
	     size_t s = read(fd[0],buf,strlen(msg));
		if(s>0)
		{
			print(buf);
		}
		else
		{
			print("read error!");
			exit(-1);
		}
	}
	return 0;
}

可以发现父进程读子进程写这个程序分为一下步骤:
1.创建管道
2.创建子进程
3.子进程关闭读端,子进程进行写操作
4.子进程退出
5.父进程进行等待
6.父进程关闭写端,父进程读取管道内容
类似于上述的过程可以来做一个小程序,可以将一片英语文件全部转化问大写,子进程来完成转换工作,父进程完成写如工作。
代码如下:
translate.c:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<unistd.h>
#include<fcntl.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<assert.h>
#define IN "in"
#define OUT "out"

//小写字母翻译为大写字母
void translation(char* buf,char* tran_buf,int len)
{
	if(buf == NULL)
		return;
	int i = 0;
	for(i = 0;i<len;i++)
	{
		if('a'<=buf[i] && buf[i]<='z')
			buf[i] -= ('a'-'A');
		tran_buf[i] = buf[i];
	}

}


int main(void)
{
	//打开输入输出文件
	FILE* f_in = fopen(IN,"r");
	FILE* f_out = fopen(OUT,"w");
	int fd[2];
	assert(f_in != NULL && f_out != NULL);	
	pid_t pid = -1;
	//创建管道
	if(pipe(fd)<0)
	{
		perror("pipe");
		exit(-1);
	}
	//创建子进程
	if((pid = fork())<0)
	{
		perror("fork");
		exit(-1);
	}
	if(pid == 0)
	{
		ssize_t s = 0;
		char buf[1024];
		//关闭管道读端
		close(fd[0])
		while((s = fread(buf,sizeof(char),1023,f_in)))
		{
			if(s>0)
			{
				char out[1024] = {0};
				printf("%s\n",buf);
				translation(buf,out,s);
				write(fd[1],out,strlen(buf));
			}
			if(feof(f_in) == 0)
				break;
		}
		//关闭管道写端
		close(fd[1]);
		printf("write over!\n");
		exit(1);
	}
	//进程等待
	wait(NULL);
	ssize_t s = 0;
	char buf[1024];
	//关闭管道写端
	close(fd[1]);
	s = read(fd[0],buf,1024);
	//关闭管道读端
	close(fd[0]);
	printf("%s\n",buf);
	fwrite(buf,sizeof(char),strlen(buf),f_out);
	return 0;
}

  • 管道的读写规则:
    1.当管道写满数据时,写入数据:
    O_NONBLOCK:disable设置时,对管道的写操作会被阻塞。直到有数据被读取时才能继续写入数据。
    O_NONBLOCK:enable设置时,对管道的写操作会立即返回-1,errno值为EAGAIN。
    2.当管道为空时,读取数据:
    O_NONBLOCK:disable设置时,对管道的读操作会被阻塞。直到有数据被写入时才能继续读取数据。
    O_NONBLOCK:enable设置时,对管道的读操作会立即返回-1,errno值为EAGAIN。
    3.如果所有写端全部被挂起(所有写描述符被关闭)时,这时读取数据,读取完管道中的数据时,则会返回0.
    4.如果所有读端全部被挂起时,这时如果写入数据,则会触发异常,操作系统会给进程发送SIGPIPE信号(后面会解释),进程收到退出。
    5.当写入数据不大于PIPE_BUF时,Linux保证写入数据的原子性。
    6.当写入数据大于PIPE_BUF时,Linux不再保证写入数据的原子性。
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Linux系统提供了各种系统调用API用于进程之间的通信:    无名管道PIPE    命名管道FIFO    消息队列    共享内存    信号量    文件锁    信号signal....其中还包括system V和POSIX 两种接口标准,除此之外,Linux系统自身还扩展了自己的一套API接口用于进程间通信,比如signalfd、timerfd、eventfd等。本视频教程为《Linux系统编程》第05期,本期课程将会带领大家学习Linux下将近15种进程间通信IPC工具的使用,了解它们的通信机制、编程实例、使用场景、内核中的实现以及各自的优缺点。本课程会提供PDF版本的PPT课件和代码,学员购买课程后可到课程主页自行下载嵌入式自学路线指导图:------------------------------------------------------------------------------------------------------                   《嵌入式工程师自我修养》嵌入式自学系列教程                                          作者:王利涛------------------------------------------------------------------------------------------------------一线嵌入式工程师精心打造,嵌入式学习路线六步走: 第 1 步:Linux三剑客零基础玩转Linux+UbuntuGit零基础实战:Linux开发技能标配vim从入门到精通基础篇:零基础学习vim基本命令vim从入门到精通定制篇:使用插件打造嵌入式开发IDEmakefile工程实践基础篇:从零开始一步一步写项目的Makefilemakefile工程实践第2季:使用Autotools自动生成Makefile软件调试基础理论printf打印技巧Linux内核日志与打印使用QEMU搭建u-boot+Linux+NFS嵌入式开发环境第 2 步:C语言嵌入式Linux高级编程第1期:C语言进阶学习路线指南第2期:计算机架构与ARM汇编程序设计第3期:程序的编译、链接和运行原理第4期:堆栈内存管理第6期:数据存储与指针第7期:嵌入式数据结构与Linux内核的OOP思想第8期:C语言的模块化编程第9期:CPU和操作系统入门      搞内核驱动开发、光会C语言是不行的!      你还需要学习的有很多,包括:计算机体系架构、ARM汇编、程序的编译链接运行原理、CPU和操作系统原理、堆栈内存管理、指针、linux内核中的面向对象思想、嵌入式系统架构、C语言的模块化编程.....第 3 步:Linux系统编程第00期:Linux系统编程入门第01期:揭开文件系统的神秘面纱第02期:文件I/O编程实战第03期:I/O缓存与内存映射第04期:打通进程与终端的任督二脉第05期:进程间通信-------------------we are here!‍    第 4 步:Linux内核编程‍    练乾坤大挪移,会不会九阳神功,是一道坎。搞驱动内核开发,懂不懂内核也是一道坎。第 5 步:嵌入式驱动开发    芯片原理、datasheet、硬件电路、调试手段、总线协议、内核机制、框架流程....第 6 步:项目实战    嵌入式、嵌入式人工智能、物联网、智能家居...

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值