进程间通信有关知识整理

进程间通信(InterProcess Communication,IPC)

经典的IPC有如下几种:管道、FIFO、消息队列、信号量、共享存储

 

管道

局限型:

·半双工

·只能在具有公共祖先的两个进程之间使用

管道是通过调用pipe函数创建

#include <unistd.h>

int pipe(int fd[2]);

              若成功,返回0;若失败,返回-1

              fd返回两个文件描述符,fd[0]为读而打开,fd[1]为写而打开,fd[1]的输出是fd[0]的输入

 

单个进程中的管道几乎没有任何用处。

通常进程会先调用pipe,然后调用fork,从而创建从父进程到子进程的IPC通道。

 

fork之后做什么取决于我们需要的数据流的方向。

父进程到子进程   父fd[1] à 子fd[0]

子进程到父进程   父fd[0] à 子fd[1]

 

管道的一端被关闭后,下列两条规则起作用:

  1. 当读read一个写端已经被关闭的管道的时候,在所有数据被读取以后,read返回0,表示文件结束。
  2. 如果写write一个读端已经被关闭的管道,则产生信号SIGPIPE。忽略该信号或者捕捉该信号并从其处理程序返回,则write返回-1,errno设置为EPIPE

在写管道或者FIFO时,常量PIPE_BUF规定了内核的管道缓冲区大小,调用write时要求写的字节数小于等于PIPE_BUF。

 

popen、pclose函数

常见的操作是创建一个连接到另一进程的管道,然后读其输出或者向其发生输入,所以标准I/O库提供了popen函数和pclose函数。

这两个函数实现的操作是:创建一个管道,fork一个子进程,关闭未使用的管道端,执行shell运行命令,然后等待命令终止。

 

#include <stdio.h>

FIFE *popen(const char *command, const char *type);

int pclose(FILE *stream);

 

小结:

popen函数会创建一个管道,再fork一个子进程,在子进程中执行exec函数来执行command命令,然后返回stdout或者stdin的文件指针。

command命令是通过子进程执行的,那么stdin或者stdout文件指针也是子进程的进程片空间的。要将其返回给父进程则需要通过管道。

stdin是提供给程序写数据的,stdout是提供给程序读数据的。管道的读端跟stdout绑定,写端是跟stdin绑定。

 

协同进程

UNIX系统过滤程序从标准输入读取数据,向标准输出写数据,几个过滤程序通常在shell管道中线性连接。当一个过滤程序既产生某个过滤程序的输入,又读取某该过滤程序的输出时,则该过滤程序就成为了协同进程(coprocess)

 

popen只提供连接到另一个进程的标准输入或标准输出的一个单向管道,而协同进程则有连接到另一个进程的两个单向管道:一个接到其标准输入,另一个则来自其标准输出。

 

FIFO

又被称为有名管道。创建FIFO类似于创建文件。

#include <sys/stat.h>

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

int mkfifoat(int fd, const char *path, mode_t mode);

                                                 若成功,返回0;若失败,返回-1

当我们在用mkfifo或者mkfifoat创建FIFO时,要用open来打开它。正常的文件I/O函数都需要FIFO

类似于管道,若write一个尚无晋成为读而打开的FIFO,则产生信号SIGPIPE。若某个FIFO的最后一个写进程关闭了该FIFO,则将为该FIFO的读进程产生一个文件结束标志。在数据读出时,FIFO管道中同时清除数据,并且先进先出。

 

FIFO有以下用途:

·shell命令使用FIFO将数据从一条管道传送到另一条管道时,无需创建中间临时文件。
·客户进程-服务器进程应用程序中,FIFO用作汇聚点,在客户进程和服务器进程二者之间传递数据

 

消息队列

消息队列时消息的链接表,存储在内核中,由消息队列标识符标识。

msgget用于创建一个新队列或者打开一个现有队列。msgsnd将消息添加到队列尾端。每个消息包含一个正的长整型类型的字段、一个非负的长度以及实际数据字节数、所有这些都在将消息添加到队列的时候,也传送给msgsnd。msgrcv用于从队列中取消息。

 

msgget 其功能是打开一个现有队列或者创建一个新队列

#include <sys/msg.h>

int msgget(key_t key, int flag);

                                   成功,返回消息队列ID;出错,返回-1

 

msgctl函数时对队列执行多种操作。

#include <sys/msg.h>

int msgctl(int msqid, int cmd, struct msqid_ds *buf);

                                                 返回值,成功,返回0;失败,返回-1

 

调用msgsnd将数据放到消息队列当中

#include   <sys/msg.h>

int msgsnd(int msqid, const void *ptr, size_t nbytes, int flag);

每个消息是由3个部分组成:一个正的长整型类型的字段、一个非负的长度以及实际数据字节数(对应于长度)。消息总是放在队列的尾端。

 

msgrcv从队列中取用消息

#include <sys/msg.h>

ssize_t msgrcv(int msqid, void *ptr, size_t nbytes, long type, int flag);

                                   若成功,返回消息数据部分的长度,若失败,返回-1

参数解释:

ptr: 指向一个长整型数(其中存储的是返回的消息类型),其后跟随的是存储实际消息数据的缓冲区。

nbytes:指定数据缓冲区的长度

typpe参数有如下3种情况:

type == 0 返回队列中的第一个消息

type > 0  返回队列中消息类型为type的第一个消息

type < 0  返回队列中消息类型小于等于type绝对值的消息,如果这种消息有若干个,则取类型值最小的消息。

 

 

信号量

信号量是一个计数器,用于为多个进程提供对共享数据对象的访问。

为了获取共享资源,进程需要执行以下操作:

  1. 测试控制该资源的信号量
  2. 若此信号量的值为正,则进程可以使用该资源。
  3. 否则,若此信号量的值为0,则进程进入休眠状态,直至信号量大于0,进程被唤醒,返回步骤1

 

当进程不再使用由一个信号量控制的共享资源时,该信号量值增1.如果有进程正在休眠等待此信号量,则唤醒他们。

 

特点:

·信号量用于进程间同步,若要在进程间传递数据需要结合共享内存

·信号量基于操作系统的PV操作,程序对信号量的操作都是原子操作

·每次对信号量的PV操作不仅限于对信号量值加1或者减1,而且可以加减任意正整数

·支持信号量组

 

信号量通常实在内核中实现的。

共享存储

共享存储允许两个或多个进程共享一个给定的存储区。因为数据不需要再客户进程和服务器之间复制,所以这是最快的一种IPC。

信号量用于同步共享存储访问(也可以使用 记录锁或互斥量)

#include <sys/shm.h>

int shmget(key_t key, size_t size, int flag);

                            返回值,若成功,返回共享存储ID,若出错,返回-1

 

#include <sys/shm.h>

int shmctl(int shmid, int cmd, struct shmid_ds *buf);

                            返回值,若成功,返回0;若出错,返回-1

 

一旦创建了一个共享存储段,进程就可以调用shmat将其连接到他的地址空间中

#include <sys/shm.h>

void *shmat(int shmid, const void *addr, int flag);

                            返回值,若成功,返回指向共享存储段的指针;若出错,返回-1

 

当对共享存储段的操作已经结束时,则调用shmctl与该段进行分离

#include <sys/shm.h>

int shmdt(const void *addr);

                            返回值:若成功,返回0,若出错,返回-1

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值