嵌入式Linux进程间的通信方式

本文详细介绍了嵌入式Linux中进程间通信的两种主要方式——无名管道和有名管道,包括它们的性质、创建方法和读写规律。无名管道适用于有亲缘关系的进程,而有名管道通过路径名标识,允许不相关进程通信。此外,还提及了信号通信的重要性和常用信号处理方式。
摘要由CSDN通过智能技术生成

【1】分类
早期进程间通信方式:
无名管道
有名管道
信号通信

system V
消息队列
共享内存
信号灯集

BSD
scoket(套接字)通信

【2】本质
任何一个进程在32位操作里面都会有4G的虚拟空间,包含1G内核空间和3G用户空间,进程间能够通信,就是在内核空间进行读写数据。

共享内存效率最高,它是直接获取到物理内存的地址,对物理内存直接操作

前六种进程间通信方式只能实现在同一台主机的多个进程之间通信,而套接字通信可以实现在不同的主机的进程之间通信

【3】无名管道
(1)性质
只能用于具有亲缘关系的进程之间的通信,半双工的通信模式,具有固定的读端和写端。

管道可以看成是一种特殊的文件,对于它的读写可以使用文件IO如read、write函数。

管道是基于文件描述符的通信方式
当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。
fd[0]固定用于读管道,fd[1]固定用于写管道。

(2)创建无名管道

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

功能:创建一个无名管道
参数
fd:操作无名管道的数组,有两个成员,第一个负责读,第二个负责写
返回值
成功:0
失败:-1

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	//创建一个无名管道
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//printf("fd[0] = %d\n", fd[0]);
	//printf("fd[1] = %d\n", fd[1]);
	
	//无名管道可以使用文件io的函数直接操作
	//read write
	
	//fd[1]负责写数据
	//往无名管道写数据,下一次写的数据会放在第一个写的后面,以追加的方式写数据
	char s1[32] = "hello world";
	char s2[32] = "nihao beijing";

	write(fd[1], s1, sizeof(s1));
	write(fd[1], s2, sizeof(s1));

	//fd[0]负责读数据
	//从无名管道读取数据,读取的数据会从无名管道里面删除
	char buf[32] = {};
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);
	
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);

	return 0;
}

(3)无名管道的读写规律
如果读写端都存在,如果只读不写,如果管道内有数据,会正常读取,但是如果没有数据,就会一直阻塞。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果读写端都存在,如果只读不写,如果管道内有数据,会正常读取,但是如果没有数据,就会一直阻塞
	char s[32] = "hello world";
	write(fd[1], s, sizeof(s));

	char buf[32] = {};
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);
	
	read(fd[0], buf, sizeof(buf));
	printf("buf = %s\n", buf);

	return 0;
}

如果读写端都存在,如果只写不读,当管道写满是,就会阻塞,默认无名管道64k字节。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果读写端都存在,如果只写不读,当管道写满是,就会阻塞,默认无名管道64k字节
	char s[4] = "yes";
	int n = 0;
	while(1)
	{
		write(fd[1], s, 4);
		n++;
		printf("n = %d\n", n);
	}

	return 0;
}

如果只有读端没有写端,如果管道内有数据,则正常读取,如果没有数据,则read返回0。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果只有读端没有写端,如果管道内有数据,则正常读取,如果没有数据,则read返回0
	
	write(fd[1], "hello world", 32);

	close(fd[1]);

	char buf[32] = {};
	ssize_t bytes;
	bytes = read(fd[0], buf, sizeof(buf));
	printf("bytes = %d\n", bytes);
	printf("buf = %s\n", buf);	

	bytes = read(fd[0], buf, sizeof(buf));
	printf("bytes = %d\n", bytes);
	printf("buf = %s\n", buf);

	return 0;
}

如果只有写端没有读端,当运行到write时,就会产生SIGPIPE(管道破裂),当前信号默认的处理方式是结束整个进程,所以程序直接退出。

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main(int argc, const char *argv[])
{
	int fd[2];
	if(pipe(fd) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	//如果只有写端没有读端,当运行到write时,就会产生SIGPIPE(管道破裂),
	//当前信号默认的处理方式是结束整个进程,所以程序直接退出
	
	close(fd[0]);

	char s[4] = "yes";
	int n = 0;
	ssize_t bytes;
	while(1)
	{
		bytes = write(fd[1], s, 4);
		printf("bytes = %d\n", bytes);
		n++;
		printf("n = %d\n", n);
	}

	return 0;
}

以下是父子进程,通过无名管道实现互相通信:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>

#define N 32

int main(int argc, const char *argv[])
{
	int fd1[2], fd2[2];
	if(pipe(fd1) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	if(pipe(fd2) < 0)
	{
		perror("fail to pipe");
		exit(1);
	}

	pid_t pid;
	if((pid = fork()) < 0)
	{
		perror("fail to fork");
		exit(1);
	}
	else if(pid > 0) //父进程
	{
		//父进程先往管道写入数据
		char buf[N] = {};
		while(1)
		{
			fgets(buf, N, stdin);
			buf[strlen(buf) - 1] = '\0';

			write(fd1[1], buf, N);
			if(strncmp(buf, "quit", 4) == 0)
			{
				exit(0);
			}
			else 
			{
				read(fd2[0], buf, N);
				if(strncmp(buf, "quit", 4) == 0)
				{
					exit(0);
				}
				else 
				{
					printf("from child: %s\n", buf);
				}
			}
		}
	}
	else //子进程
	{
		//子进程先读取管道的数据
		char s[N] = {};
		while(1)
		{
			read(fd1[0], s, N);

			if(strncmp(s, "quit", 4) == 0)
			{
				exit(0);
			}
			else 
			{
				printf("from parent: %s\n", s);

				fgets(s, N, stdin);
				s[strlen(s) - 1] = '\0';

				write(fd2[1], s, N);
				if(strncmp(s, "quit", 4) == 0)
				{
					exit(0);
				}
			}
		}
	}

	return 0;
}

【4】有名管道
(1)性质
无名管道只能用于具有亲缘关系的进程之间,这就限制了无名管道的使用范围。有名管道可以使互不相关的两个进程互相通信。有名管道可以通过路径名来指出,并且在文件系统中可见。

进程通过文件IO来操作有名管道,有名管道遵循先进先出规则,不支持如lseek() 操作 。

有名管道还是在内核空间开辟区域,然后在本地创建一个管道文件用于标识内核空间的区域,对当前管道文件的操作就是对内核空间的操作。

(2)创建有名管道
方法1:使用mkfifo命令
mkfifo fifoname

方法2:使用mkfifo函数创建有名管道

	#include <sys/types.h>
	#include <sys/stat.h>
	int mkfifo(const char *pathname, mode_t mode);

功能:创建一个有名管道,产生一个管道文件
参数
pathname:文件名
mode:操作权限,一般为八进制数组成,例如0664
返回值
成功:0
失败:-1

#include <stdio.h>
#i
  • 3
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值