进程间的通讯(IPC (Inter-process communication))

一. 为什么要进行进程间的通讯(IPC (Inter-process communication))?

数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间
共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程)。
资源共享:多个进程之间共享同样的资源。为了作到这一点,需要内核提供锁和同步机制。

进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知道它的状态改变。

二. 进程通讯方式

1. 单工:单工就是指A只能发信号,而B只能接收信号,通信是单向的,就象灯塔之于航船——灯塔发出光信号而航船只能接收信号以确保自己行驶在正确的航线上。 

2. 半双工:半双工就是指A能发信号给B,B也能发信号给A,但这两个过程不能同时进行。最典型的例子就象我们在影视作品中看到的对讲机一样:
3. 全双工:比半双工又进了一步。在A给B发信号的同时,B也可以给A发信号。典型的例子就是打电话

三. linux常用的进程间的通讯方式

1. 管道(pipe)管道主要用于具有亲缘关系的进程间通信,允许一个进程和另一个与它有共同祖先的进程通信。比如fork或exec创建的新进程。在使用exec创建新进程时,需要将管道的文件描述符作为参数传递给exec创建的新进程。

pipe函数原型:

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

无名管道的原理是把无名管道当成文件,父进程创建(pipe)这个文件(无名管道),但这个文件在文件系统中不可见,它在内存中,所以只能通过子进程继承父进程的文件描述符(fd)来完成通信,这样无名管道只能用于父子进程间通信(有亲缘关系的进程间)。

<span style="font-size:18px;">int fd[2];
pipe(fd);</span>

该函数在数组上填上两个新的文件描述符后返回0,失败返回-1。

通过使用底层的read和write调用来访问数据。

向file_descriptor[1]写数据,从file_descriptor[0]中读数据。写入与读取的顺序原则是先进先出。


2. 命名管道(named pipe):命名管道是一种特殊类型的文件,它在系统中以文件形式存在。这样克服了管道的弊端,他可以允许没有亲缘关系的进程间通信

创建管道的两个系统调用原型:

#include 
#include 
int mkfifo(const char *filename,mode_t mode);
int mknod(const char *filename,mode_t mode|S_IFIFO,(dev_t) 0);

有名管道的原理是把有名管道当成文件,任意进程创建(mkfifo)这个文件(有名管道),这个文件在文件系统中可见,但内容在内存中,既然文件系统可见,那么任意进程就可以通过open来打来这个文件进行读写(read、write),这样有名管道能用于任意进程间通信。

int fd;
mkfifo(``fifo``, 0666)
fd = open(``fifo``, O_RDONLY);

具体操作方法只要创建了一个命名管道然后就可以使用open、read、write等系统调用来操作。创建可以手工创建或者程序中创建。

kill(pid, sig);

3. 信号(signal):信号是比较复杂的通信方式,用于通知接收进程有某种事件发生了,除了进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,用sigaction函数重新实现了signal函数)。

信号的原理是通过进程的pid进行通信,是软件中唯一的异步通信机制,它的关键是知道进程的pid。

===============================================================================================

共享内存、消息队列和信号灯是System V IPC对象,它们的通信原理都一样,是通过相同的key值生成相同的shmid、msgid和semid来实现任意进程间通信,那么它的关键是任意进程怎么知道这个key值,我们有个函数ftok能实现这个功能,它通过两个参数来生成key,任意进程只要这两个参数相同就能生成相同的key值,这样就能找到相同的IPC对象,实现任意进程间通信,这两个参数第一个必须是系统中现有的唯一id,比如目录的id和文件的id等,第二个参数是一个任意整数,这两个参数通过特定的计算可以生成唯一的key值。上面是任意进程间通信,还有一种是只有亲缘关系(父子进程)间通信时,key值没有必要用ftok来生成,我们有一个系统定义的参数IPC_PRIVATE可以实现父子进程间通信,这时的key值是0,当然父子进程间也可以用ftok,但这样就比较麻烦了。

// 任意进程间通信
key_t key;
int shmid;
int msgid;
int semid;

key = ftok(``.``, `*`);
// ``.``为当前目录,`*`为ascii码值42。

shmid = shmget(key, ......);
msgid = msgget(key, ......);
semid = semget(key, ......);

// 父子进程间通信

int shmid;
int msgid;
int semid;

shmid = shmget(IPC_PRIVATE, ......);
msgid = msgget(IPC_PRIVATE, ......);
semid = semget(IPC_PRIVATE, ......);


4. 消息队列:消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺。


5. 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制信号量结合使用,来达到进程间的同步及互斥


6. 内存映射:内存映射允许任何多个进程间通信,每一个使用该机制的进程通过把一个共享的文件映射到自己的进程地址空间来实现它


7. 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。


8. 套接字(Socket)更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。



  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值