嵌入式LINUX进程间通信详解

本文详细介绍了Linux系统中的进程间通信(IPC)方式,包括管道(无名管道和有名管道)、信号、共享内存和消息队列。这些通信机制用于不同进程之间的数据交换,确保系统的协调运行。文中通过实例代码展示了如何创建、使用和管理这些通信方式,强调了各种机制的特点和适用场景,帮助开发者理解Linux下进程通信的基本原理和实践。
摘要由CSDN通过智能技术生成

        学习过Linux相关知识,大家应该知道进程时一个程序的一次执行,时系统资源分配的最小单元。一般所说的进程是用户态的进程,而由于处于不同用户态的不同进程是彼此隔离的,就好比处于不同国家的人们,必须要通过某种方式来进行通信,例如人们现在广泛使用的手机等方式。本文将讲述如何建立这些不同的通信方式;

        Linux下的进程通信手段基本是从UNIX平台上的进程通信手段继承而来。而对UNIX发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间的通信方面的侧重点有所不同。前者是对UNIX早期的进程间通信手段进行了系统的改进和补充,进而形成了"System V IPC",其通信进程主要局限在单个计算机内;后者则跳过该限制,形成了基于套接字(Socket)的进程间通信机制;Linux则把这两者的优势都继承下来;

UNIX进程间通信(IPC)方式包括管道、FIFO及信号。

System V进程间通信(IPC)包括System V消息队列、System V信号量及System V共享内存区。

Posix 进程间通信(IPC)包括Posix消息队列、Posix信号量及Posix共享内存区。

现在在Linux中使用较多的进程间通信方式主要有以下几种。

管道(Pipe)及有名管道(Named Pipe):管道可用于具有亲缘关系进程间的通信。有名管道除具有管道所具有的功能外,还允许无亲缘关系进程间的通信。

信号(Signal):信号是在软件层次上对中断机制的一种模拟,它是比较复杂的通信方式,用于通知进程有某事件发生,一个进程收到一个信号与处理器收到一个中断请求效果上可以说是一样的。

消息队列(Messge Queue):消息队列是消息的链接表,包括Posix消息队列和System V消息队列。它克服了前两种通信方式中信息量有限的缺点,具有写权限的进程可以按照一定的规则向消息队列中添加新消息;对消息队列有读权限的进程则可以从消息队列中读取消息。

共享内存(Shared Memory):可以说这是最有效的进程间通信方式。它使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种通信方式需要依靠某种同步机制,如互斥锁和信号量等。

信号量(Semaphore):主要作为进程之间及同一进程的不同线程之间的同步和互斥手段。

套接字(Socket):这是一种更为通用的进程间通信机制,它可用于网络中不同机器之间的进程间通信,应用非常广泛。

1、管道

管道简介

管道是Linux中进程间通信的一种方式,它把一个程序的输出直接连接到另一个程序的输入。Linux的管道主要包括两种:无名管道和有名管道。

1.无名管道

无名管道是Linux中管道通信的一种原始方法,如图4.2(左)所示,它具有如下特点:

它只能用于具有亲缘关系的进程之间的通信(也就是父子进程或者兄弟进程之间)。

它是一个半双工的通信模式,具有固定的读端和写端。

管道也可以看成是一种特殊的文件,对于它的读写也可以使用普通的read()、write()等函数。但是它不是普通的文件,并不属于其他任何文件系统,并且只存在于内存中

无名管道(左)和有名管道(右)

 无名管道系统调用

1.管道创建与关闭说明

管道是基于文件描述符的通信方式,当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1],其中fd[0]固定用于读管道,而fd[1]固定用于写管道,如图4.3所示,这样就构成了一个半双工的通道。

无名管道的读写机制

管道关闭时只需将这两个文件描述符关闭即可,可使用普通的close()函数逐个关闭各个文件描述符。

2.管道创建函数

创建管道可以通过调用pipe()来实现。表4.1列出了pipe()函数的语法要点。

 pipe()函数语法要点

3.管道读写说明用

pipe()函数创建的管道两端处于一个进程中,由于管道是主要用于在不同进程间通信的,因此在实际应用中没有太大意义。实际上,通常先是创建一个管道,再调用fork()函数创建一个子进程,该子进程会继承父进程所创建的管道,这时,父子进程管道的文件描述符对应关系如图所示。

 父子进程管道的文件描述符对应关系

此时的关系看似非常复杂,实际上却已经给不同进程之间的读写创造了很好的条件。父子进程分别拥有自己的读写通道,为了实现父子进程之间的读写,只需把无关的读端或写端的文件描述符关闭即可。例如在图中将父进程的写端fd[1]和子进程的读端fd[0]关闭。此时,父子进程之间就建立起了一条“子进程写入父进程读取”的通道。

 关闭父进程fd[1]和子进程fd[0]

同样,也可以关闭父进程的fd[0]和子进程的fd[1],这样就可以建立一条“父进程写入子进程读取”的通道。另外,父进程还可以创建多个子进程,各个子进程都继承了相应的fd[0]和fd[1]。这时,只需关闭相应端口就可以建立其各子进程间的通道。

4.管道读写注意点

管道读写需注意以下几点:

只有在管道的读端存在时,向管道写入数据才有意义。否则,向管道写入数据的进程将收到内核传来的SIGPIPE信号(通常为Broken pipe错误)。

向管道写入数据时,Linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读取管道缓冲区中的数据,那么写操作将会一直阻塞。

父子进程在运行时,它们的先后次序并不能保证。因此,为了保证父子进程已经关闭了相应的文件描述符,可在两个进程中调用sleep()函数。当然这种调用不是很好的解决方法,在后面学到进程之间的同步机制与互斥机制后,请读者自行修改本小节的实例程序。

5.使用实例

在本例中,首先创建管道,之后父进程使用fork()函数创建子进程,最后通过关闭父进程的读描述符和子进程的写描述符,建立起它们之间的管道通信。

/* pipe.c */
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>
#include <stdio.h>
#include <stdlib.h>
#define MAX_DATA_LEN 256
#define DELAY_TIME 1
int main()
{
    pid_t pid;
    int pipe_fd[2];
    char buf[MAX_DATA_LEN];
    const char data[] = "Pipe Test Program";
    int real_read, real_write;
    memset((void*)buf, 0, sizeof(buf));
    if (pipe(pipe_fd) < 0) /* 创建管道 */
    {
        printf("pipe create error\n");
        exit(1);
        }
    if ((pid = fork()) == 0) /* 创建一个子进程 */
    {
   /* 子进程关闭写描述符,并通过使子进程暂停1s 等待父进程已关闭相应的读描述符 */
        clo
  • 24
    点赞
  • 39
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值