Linux下进程间通信(一)----概述和管道通信


Linux下进程间通信的几种方式:

  • 管道(Pipe)和有名管道(FIFO)
  • 信号(Signal)
  • 消息队列
  • 共享内存(Shared Memory)
  • 信号量(Semaphore)
  • 套接字(Socket)

  1.管道通信

    管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,

  除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信。

  2.信号

    信号是比较复杂的通信方式,用于通知接收进程有某种事件发生,除了用于进程间通信

  外,进程还可以发送信号给进程本身;Linux除了支持UNIX早期信号语义函数signal外,还支

  持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现

  可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。

    信号是在软件层次上对中断机制的一种模拟,是一种异步通信方式。

    信号可以直接进行用户空间进程和内核进程之间的交互,内核进程也可以利用它来通知用

  户空间进程发生了哪些系统事件,它可以在任何时候发给某一进程,而无须知道该进程的状态。

    如果该进程当前并未处于执行态,则该信号就由内核保存起来,直到该进程恢复执行再传

  递给它;如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消时才传

  递给进程。 

  3.消息队列

    消息队列是消息的链接表,包括Posix消息队列System V消息队列。有足够权限的进程可以

  向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信

  息量少、管道只能承载无格式字节流,以及缓冲区大小受限等缺点。

  4.信号量/信号灯

    主要作为进程期间,以及同一进程的不同线程之间的同步手段。信号量是用来解决进程之间

  的同步与互斥问题的一种进程之间通信机制,包括一个称为信号量的变量和在该信号量下等待资

  源的进程等待队列,以及对信号量进行的两个原子操作(PV操作)。其中信号量对应于某一种资

  源,取一个非负的整型值。信号量的值是指当前可用的资源数量,若它等于0则意味着目前没有可

  用的资源。

  5.共享内存

    共享内存可以说是最有用的进程间通信方式,也是最快的IPC形式。两个不同进程A、B共享内

  存的意思是,同一块物理内存被映射到进程A、B各自的进程地址空间。进程A可以即时看到进程B

  对共享内存中数据的更新,反之亦然。由于多个进程共享同一块内存区域,必然需要某种同步机制,

  互斥锁和信号量都可以。采用共享内存通信的一个显而易见的好处是效率高,因为进程可以直接读

  写内存,而不需要任何数据的复制。

  6.套接字

    更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由UNIX系统的BSD分

  支开发出来的,但现在一般可以移植到其他类UNIX系统上,Linux和System V的变种都支持套接字。

管道通信

特点:

  • 管道是半双工的,数据只能向一个方向流动;需要双方通信时,需建立起两个管道。
  • 只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程)。
  • 单独构成一种独立的文件系统。管道对于管道两端的进程而言,就是一个文件,但它不是普通的文

  件,不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在于内存中。

  • 数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加

  在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

无名管道

建立

pipe函数
相关函数 mkfifo、popen、read、write、fork
表头文件 #include <unistd.h>
定义函数 int pipe(int filedes[2])
函数说明 pipe()会建立管道,并将文件描述符由参数filedes数组返回。filedes[0]为管道里的读取段filedes[1]则为管道的写入端
返回值 若成功则返回零,否则返回-1,错误原因存于errno中
错误代码
  • EMFILE:进程已用完文件描述符最大量
  • ENFILE:系统已无文件描述符可用
  • EFAULT:参数filedes数组地址不合法

 1 /*父进程借管道将字符串“hello!\n”传给子进程并显示*/
 2 #include <unistd.h>
 3 int main()
 4 {
 5     int filedes[2];
 6     char buffer[80];
 7     
 8     pipe(filedes);
 9     if (fork() > 0)
10     {
11         /*父进程*/
12         char s[] = "hello!\n";
13         write(filedes[1], s, sizeof(s));
14     }
15     else
16     {
17         /*子进程*/
18         read(filedes[0], buffer, 80);
19         printf("%s", buffer);
20     }
21 }

读写

  管道两端可分别用描述符fd[0]和fd[1]来描述,需要注意的是,管道的两端固定了任务,即一端只能用于

读,由描述符fd[0]表示,称其为管道读端;另一端则只能用于手写,由描述符fd[1]来表示,称其为管道写端。

如果试图从管道写端读取数据,或者向管道读端写入数据都将导致错误发生。一般文件的I/O函数都可以用于

管道,如close、read、write等等。

从管道中读取数据:

  • 如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0。
  • 当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数;如果

请求的字节数目不大于PIPE_BUF,则返回管道中现有数据字节数(此时,管道中数据量小于请求的数据量),

或者返回请求的字节数(此时,管道中数据量不小于请求的数据量)。

向管道中写入数据:

  向管道中写入数据时,Linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道

写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

有名管道

  有名管道和无名管道基本相同,但也有不同点:无名管道只能由父子进程使用;但是通过有名管道,不相关的

进程也能交换数据。

建立

#include <sys/types.h>
#include <sys/stat.h>

int mkfifo(const char *pathname, mode_t mode);

pathname: FIFO文件名
mode:属性(同文件操作)
一旦创建了一个FIFO,就可用open打开它,一般的文件访问函数(close、read、write等)都可用于FIFO。

当打开FIFO时,非阻塞标识(O_NONBLOCK)将对以后的读写产生影响:
1、没有使用O_NONBLOCK:访问要求无法满足时进程将阻塞。如果试图读取空的FIFO,将导致进程阻塞。
2、使用O_NONBLOCK:访问要求无法满足时不阻塞,立刻出错返回。errno是ENXIO。

优质内容筛选与推荐>>
1、软件测试
2、JavaScript可以根据浏览器类别决定是否注册函数?
3、数据仓库项目管理面试题整理(六)
4、关于回家坐车方便的信息
5、Jive学习(一)


长按二维码向我转账

受苹果公司新规定影响,微信 iOS 版的赞赏功能被关闭,可通过二维码转账支持公众号。

    阅读
    好看
    已推荐到看一看
    你的朋友可以在“发现”-“看一看”看到你认为好看的文章。
    已取消,“好看”想法已同步删除
    已推荐到看一看 和朋友分享想法
    最多200字,当前共 发送

    已发送

    朋友将在看一看看到

    确定
    分享你的想法...
    取消

    分享想法到看一看

    确定
    最多200字,当前共

    发送中

    网络异常,请稍后重试

    微信扫一扫
    关注该公众号