函数摘要
| 函数 | 说明 | 成功 | 失败 |
|---|---|---|---|
| 管道和FIFO | |||
| pipe | 单工通信 | 0 | -1 |
| popen | 执行一个shell命令,封装的PIPE和exec功能 | 文件指针 | NULL |
| pclose | 关闭标准I/O流,等待命令终止,然后返回shell的终止状态 | 返回cmdstring的终止状态 | -1 |
| mkfifo | 打开一个fifo文件 | 0 | -1 |
| mkfifoat | 打开一个fifo文件 | 0 | -1 |
| IPC的操作 | |||
| ftok | 路径和id创建一个IPC的key | 返回key | -1 |
| 消息队列 | 不推荐使用,FIFO代替 | ||
| msgget | 创建一个消息队列 | 返回消息队列ID | -1 |
| msgctl | 修改消息队列的msqid_ds | 0 | -1 |
| msgsnd | 发送消息 | 0 | -1 |
| msgrcv | 接受消息 | 消息数据部分的长度 | -1 |
| 信号量 | 不推荐使用,用记录锁代替 | ||
| semget | 获得一个信号量 | 信号量队列ID | -1 |
| semctl | 修改信号量的的semid_ds | 0 | -1 |
| semop | 执行信号集合上的操作数字 | 0 | -1 |
| 共享存储 | |||
| shmget | 创建一个共享存储 | 共享存储的ID | -1 |
| shmctl | 修改共享存储的shmid_ds | 0 | -1 |
| shmat | 连接一个共享存储的地址 | 指向共享存储段的指针 | -1 |
| shmdt | 将共享存储的地址分离 | 0 | -1 |
| unix域套接字 | |||
| socketpair | 创建一个UNIX域套接字传输管道(类似于pipe) | 0 | -1 |
名称解释
-
管道:
- UNIX系统IPC的最古老形式,所有UNIX系统都提供次通信机制
- 返回2个文件描述符
- 局限
- 历史上是半双工。不应预定假定系统支持全双工
- 只能在具有公共祖先的进程之间使用。通常,一个管道由一个进程创建,在进程调用fork之后,这个管道就能在父子进程之间使用。
-
FIFO:
- FIFO是一种文件类型。
- 未命名管道(pipe)只能在两个相关的进程之间使用,这两个相关的进程还要有一个共同的创建了他们的祖先进程。通过FIFO,不相关的进程也能交换数据。
-
XSI IPC:消息队列、信号量及共享存储器
-
IPC的标识符: 每个内核中的IPC结构都用一个非负证书的标识符加以引用。
- 与文件描述符不同,IPC标识符不是小的整数。当一个IPC结构被创建,然后又被删除时,与这种结构相关的标识符连续加1,直到达到一个整形数的最大值,然后又回转到0.
- 外部名:标识符是IPC对象的内部名。为了使多个合作进程能够在同一IPC对象上汇聚,需要提供一个外部名。为此每个IPC对象都与一个键(key)相关联,将这个键作为该对象的外部名。
- 获取方式
- 服务器进程可以指定键IPC_PRIVATE创建一个新IPC结构,将返回的标识符存放在(如一个文件)以便客户进程取用。缺点:文件系统操作需要服务器将整型标识符写到文件中,此后客户进程又要取得次标识符。
- 可以在一个公共头文件中定义一个客户进程和服务器进程都认可的键,然后服务器进程指定此键创建一个新的IPC结构。缺点:此键已被使用,则get函数出错。 服务器需删除已存在的IPC结构,然后试着创建它。
- 客户进程和服务器进程认同一个路径名和项目ID,调用ftok将这两个值变换为一个键,然后使用上面的方法中使用此键。
-
IPC的权限结构: XSI IPC为每个IPC结构关联一个ipc_perm结构,规定了权限和所有者。
struct ipc_perm {
uid_t uid; /*所有者(启动)userid*/
gid_t gid; /*所有者(启动)groupid*/
uid_t cuid; /*创建者userid*/
gid_t cgid; /*创建者userid*/
mode_t mode; /*访问模块*/
...
}

-
IPC的结构限制: 3种XSI IPC都有内核限制,可以通过重新配置内核来改变。
-
优点和缺点:
- IPC结构实在系统范围内起作用,没有引用计数器。
- 例如:创建一个消息队列,并且在该队列中放入了几则消息。那么该消息队列及其内容不会被删除,直到主动删除。
- IPC结构在文件系统中没有名字。
- IPC不使用文件描述符,所以不能使用多路转接I/O函数(select/poll)。没有某种形式的忙等待循环(busy-wait loop),就不能使一个服务器进程等待要放在消息队列中任意一个中的消息。
- IPC结构实在系统范围内起作用,没有引用计数器。
-
消息队列:消息队列是消息的链接表,存储在内核中,由消息队列标识符标识。
函数
管道
创建
#include <unistd.h>
int pipe(int fd[2]);
-- '成功:0;出错:-1'
- 特点:
- 在fork之前,先创建一个管道。
- 两个文件描述符:fd[0]为读而打开,fd[1]为写而打开。
- 父进程-->子进程:父进程关闭管道fd[0],子进程关闭fd[1]
- 子进程-->父进程:父进程关闭管道fd[1],子进程关闭fd[0]
- 当读(read)一个写端已被关闭的管道时,在所有数据都被读取后,read返回0,表示文件结束。
- 如果(write)一个读端已被关闭的管道,则产生信号SIGPIPE。如果忽略该信号或者捕捉该信号从其处理程序返回,则write返回-1,errno=EPIPE。
- 在写管道(或FIFO)时,常量PIPE_BUF规定了内核的管道缓冲区大小。多个进程同时写一个管道,字节超过PIPE_BUF,那么我们所写的数据可能会与其它进程所写的数据相互交叉。
![]()
popen和pclose
#include <stdio.h>
FILE *popen(const char *cmdstring, const char *type);
-- '成功:文件指针;出错:NULL'
int pclose(FILE *fp);
-- '成功:返回cmdstring的终止状态;出错:-1'
-
特点:
-
popen的功能:创建一个管道,fork一个子进程,关闭未使用的管道端,执行一个shell运行命令,然后等待命令终止。
-
popen先执行fork,然后调用exec执行cmdstring,并且返回一个标准I/O文件指针。
- type=r:文件指针连接到cmdstring的标准输出。
- type=w:文件指针连接到cmdstring的标准输入。
-
pclose:关闭标准I/O流,等待命令终止,然后返回shell的终止状态。
-

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'
-
参数:
- mode:与open函数中的mode相同。
- mkdfifoat的path:
- 绝对路径:fd被忽略
- 相对路径:fd是一个打开目录的有效文件描述符,路径名和目录有关。
- 相对路径且fd参数有一个特殊值AT_FDCWD:则路径名以当前目录开始,mkfifoat和mkfifo类似。
-
特点:
- 使用一个FIFO的时候,默认已知其路径
- 当mkfifo与mkfifoat创建FIFO时,要用open来打开它。
- 当open一个FIFO时,非阻塞标志(O_NONBLOCK) 会产生下列影响:
- 一般情况下(没有指定O_NONBLOCK),只读open要阻塞到某个其它进程为写而打开的FIFO为止。
- 一般情况下(没有指定O_NONBLOCK),只写open要阻塞到某个其它进程为读而打开的FIFO为止。
- 指定O_NONBLOCK,只读立即返回。如果没有写方向,则返回-1,errno=SIGPIPE
- 指定O_NONBLOCK,只写立即返回。如果没有读方向,则返回-1,errno=ENXIO
- 某个FIFO的最后一个写进程关闭了该FIFO,则将为FIFO的读进程产生一个文件结束标志。
- 一个给定的FIFO有多个写进程是常见的,如果不希望多个进程所写的数据交叉,则必须考虑原子写操作。
-
用途:
- shell命令使用FIFO将数据从一条管道传送到另一条时,无需创建中间临时文件。
- 客户进程-服务器进程应用程序中,FIFO作用汇聚点,在客户进程和服务器进程二者之间传递数据。
IPC的操作
ftok:路径和id创建一个IPC的key
#include <sys/ipc.h>
key_t ftok(const char *path ,int id);
-- '成功:返回键;出错:返回-1'
消息队列(不推荐使用,FIFO代替)
msgget:打开一个现有队列或创建一个新消息队列
#include <sys/msg.h>
int msgget(key_t key,int flag);
-- '成功:返回消息队列ID;出错:-1'
// msqid_ds结构
struct msqid_ds {
struct icp_perm msg_perm; /*see Section 15.6.2*/
msgqnum_t msg_qnum; /* # of message on queue */
msglent_t msg_qbytes; /* max # of bytes on queue */
pid_t msg_lspid; /* pid of last msgsnd() */
pid_t msg_lrpid; /* pid of last msgrcv() */
time_t msg_stime; /* last-msgsnd() time */
time_t msg_rtime; /* last-msgrcv() time */
time_t msg_ctime; /* last-change time */
...
}
msgctl:修改msqid_ds
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
-- '成功:0;出错:-1'
*参数:
| 参数 | 说明 |
|---|---|
| ICP_STAT | 取次队列的msqid_ds结构,并将它存放在buf指向的结构中 |
| ICP_SET | 取次队列的msg_perm.uid,msg_perm.gid,msg_perm.mode和msg_qbytes从buf指向的结构复制到与这个队列相关的msqid_ds结构中。 |
| ICP_RMID | 从系统中删除该消息对i额以及人在该队列中的所有数据。 |
msgsnd:将数据放到消息队列
#include <sys/msg.h>
int msgsnd(int msqid, const void *ptr, size_t nbytes,int flag);
--'成功:0;出错:-1'
//ptr设计成如下
struct mymesg {
long mtype;
char mtext[512];
};
-
参数:
- ptr:一个长整型数,包含了正的整型消息类型,其后紧接着的是消息数据。
- flag:指定是否阻塞。设置成IPC_NOWAIT,使得msgsnd立即出错返回EAGAIN。
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *ptr, size_t bytes, long type, int flag );
-- '成功:消息数据部分的长度;出错:-1'
-
参数:
- ptr:长整型数,消息的类型及存储实际消息数据的缓冲区。
- nbytes:长整型数,消息的类型。
- type: 指定想要哪一种消息:
- type0:队列中的第一个消息。
- type>0:返回队列中消息类型为type的第一个消息。
- type<0:返回队列中消息类型值小于等于type绝对值的消息,如果这种消息有若干个,则类型值最小的消息。
信号量(不推荐使用。记录锁代替)
-
作用:用于共享资源的同步。
-
信号量操作流程:
- 测试控制资源的信号量。
- 若此信号量的值为正,则进程可以使用该资源。若次信号量的值为0,则进程进入休眠状态,直到信号量大于0,进程被唤醒,从头开始。
-
常用的信号量呗称为二元信号量(binary semaphore)。
-
缺点:
- 信号量并非单个非负值,而必需定义为一个多个信号量值的集合。
- 创建(semget)是独立于它的初始化(semctl)的。不能原子化的创建和初始化一个信号量。
- 即使没有进程正则使用各种形式的XSI IPC,他们仍然是存在的。有得程序终止时没有释放已分配的信号量,必须考虑这种情况
创建
#include <sys/sem.h>
int semget(key_t key, int nsems, int flag );
-- '成功:信号量ID;出错:-1'
修改
#include <sys/sem.h>
int semctl(int semid, int semnum,int cmd, ..../*uion semun arg */);
-- '根据cmd返回'
执行信号集合上的操作数字
#include <sys/sem.h>
int semop(int semid, struct sembuf semoparray[], sieze_t nops);
--'成功:0;出错:-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 shmids_ds *buf);
-- '成功:0;出错:-1'
// 共享存储的结构
struct shmid_ds {
struct ipc_perm shm_perm; /*权限结构*/
size_t shm_setgsz; /*size of segment in bytes*/
pid_t shm_lpid; /*pid of last shmop()*/
pid_t shm_cpid; /*pid of creator*/
shmatt_t shm_nattch; /*number of current attaches*/
time_t shm_atime; /*last-attach time*/
time_t shm_dtime; /*last-detach time*/
time_t shm_ctime; /*last-change time*/
...
}
-
参数:
- cmd:
- IPC_STAT:取此段的shmid_ds结构,并将它存储在buf指向的结构中。
- IPC_SET:按buf指向的结构中的值设置共享存储段相关的shmid_ds结构中的3个字段:shm_perm.uid,shm_perm.gid和shm_perm.mode。
- IPC_RMID:从系统中删除该共享存储段。
- IPC_LOCK:在内存中对共享存储段加锁。
- IPC_UNLOCK:解锁。
- cmd:
连接
#include <sys/shm.h>
void *shmat(int shmid, const void *addr, int flag);
-- '成功:指向共享存储段的指针;出错:-1'
分离
#include <sys/shm.h>
int shmdt(const void *addr);
-- '成功:0;出错:-1'
-
特点:
- 使得shmid_ds.shm_nattach计数器减1.
UNIX域套接字
- 像套接字和管道的混合:全双工的作用。
- 和英特网域套接字区别:UNIX域套接字仅仅复制数据,他们并不执行协议处理,不需要添加或删除网络报头,无需计算验和,不要生成顺序号,无需发送确认报文。
- UNIX域套接字提供流和数据报两种结构。
- 命名UNIX域套接字:sockaddr_un的结构中 sun_path成员包涵一个路径名,当我们将一个地址绑定到一个UNIX域套接字时,系统会用该路径创建一个S_IFSOCK类型的文件。
- 例如:foo.socket.
- 唯一连接 :服务器进程可以使用标准bind、listen和accpet函数,为客户进程安排一个唯一UNIX域连接。
- 客户端进程通过使用connect与服务器进程连接,服务器进程结构式connect请求后,服务器进程和客户端之间就存在唯一连接。

#include <sys/socket.h>
int socketpari(int domain,int type, int protocol, int sockfd[2]);
--'成功:0;出错:-1'
//用例
int fd_pipe(int fd[2])
{
return (socketpair(AF_UNIX, SOCK_STREAM,0,fd));
}

浙公网安备 33010602011771号