进程间通信--Linux
Linux进程间通信主要方法有:管道,命名管道,消息队列,信号量,共享内存,还有信号和套接字Socket。下面先简单介绍下
- 管道:半双工的通信方式,数据只能单方向流动,而且只能在父子进程间使用。数据只能由一个进程流向另一个进程(其中一个写管道,一个读管道),如果要进行全双工通信,需要建立两个管道。管道还有一些限制条件,比如管道的缓冲区大小是受限制的。管道所传送的是无格式的字节流,这就要求发送和接受方需要事先约定好数据的格式。
- 管道的创建
#include <unistd.h> int pipe(int fd[2])
一端只能用于读,由文件描述符fd[0]表示。另一端只能写,由文件描述符fd[1]表示。管道是一种文件,因此对于文件操作的I/O函数都可以用于管道,比如read(),write()。
一般情况下子进程可以共享父进程的文件描述符。但是如果子进程调用了exec函数执行另外一个应用程序的时候,就不能再共享了,这种情况下可以将子进程中的文件描述符重定向到标准输入,当新执行的程序从标准输入获取数据时实际上是从父进程中获取输入数据。dup和dup2函数提供复制文件描述符的功能。#include<unistd>
int dup(int oldfd);
int dup2(int oldfd, int newfd);
dup函数返回的文件描述符是当前可用的文件描述符中的最小值,而dup2函数则是可以利用参数newfd制定欲返回的文件描述符。
- 管道的创建
2 命名管道:允许非亲缘关系的进程间使用。
创建命名管道的系统函数有两个:mknod和mkfifo。均定义在头文件sys/stat.h中。
命名管道创建后,必须先调用open将其打开。因为命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊的文件。
需要注意的是,调用open()函数可能会被阻塞。如果用读写方式打开(O_RDWR),则一定不会导致阻塞;如果是以只读方式打开(O_RDONLY),则调用open函数的进程将会被阻塞直到有写方打开管道;如果以写方式打开(O_WRONLY),也会阻塞到直到有读方打开管道。
int mkfifo(const char *pathname, mode_t mode);命名管道FIFO其实就是一个特殊的文件FILE。
如果一个pipe没有进程去read,那么其他进程对这个pipe进行write操作的话,会产生SIGPIPE信号。而且对于一个FIFO,可以有多个写进程,所以原子操作非常重要,如果不想很多写进程写的数据相互覆盖的话。
常量PIPE_BUF表示系统在任意时刻在一个FIFO中可以存在的数据长度。
(1)当要写入的数据量不大于PIPE_BUF时,Linux将保证写入的原子性。如果此时管道空闲缓冲区不足以容纳要写入的字节数,则进入睡眠,直到当缓冲区中能够容纳要写入的字节数时,才开始进行一次性写操作。即写入的数据长度小于等于PIPE_BUF时,那么或者写入全部字节,或者一个字节都不写入,它属于一个一次性行为,具体要看FIFO中是否有足够的缓冲区。
(2)当要写入的数据量大于PIPE_BUF时,Linux将不再保证写入的原子性。FIFO缓冲区一有空闲区域,写进程就会试图向管道写入数据,写操作在写完所有请求写的数据后返回。
3 信号量(semophore):信号量是一种计数器,可以用来控制多个进程对共享资源的访问。它常常作为一种锁机制,防止某进程正在访问共享资源的时候,其他进程也访问该共享资源。因此可以作为进程间,或者同一进程内的不同线程之间的同步手段。
4 消息队列:消息队列就是消息的链表。存放在内核中,消息队列克服了信号量传递信息量少,管道只能承载无格式字节流以及缓冲区大小受限制等缺点。
5 信号(signal):信号是一种比较复杂的通信方式,用于通知接受进程某个事件已经发生。
6 共享内存:就是映射一段能被其他进程访问的一段内存,这段共享内存由一个进程创建但是多个进程都可以访问。共享内存是最快的IPC方式,它是针对其他IPC效率低而设计的。
7 套接字:可以再不同机器上进行进程通信。详情见Linux网络编程。