UNPv2:进程间通信(一)简介
2015-09-09 12:04
190 查看
想知道如何为网络开发软件,必须先理解进程间通信(IPC)。 ——Richard
Stevens
一个大功能或大应用,可以将其设计为单个庞大的程序,而更好的方式是将其设计为一组相互通信的程序片段。一般是用多个进程来实现,其中每个进程包含几个线程。这样,进程内部的线程之间需要通信,不同的进程之间需要通信。
进程间通信可以达到数据传输、数据共享、事件通知、进程控制等目的,其中:
数据传输:一个进程将数据发送给另一个进程。
数据共享:多个进程读写共享数据。
事件通知:一个进程向其他进程发送消息,通知它(们)发生了某种事件。
进程控制:一个进程希望完全控制另一个进程(如debug进程)的执行,能够拦截(如debug)进程的所有陷入和异常,并能够及时知道它的状态改变。
V IPC”,通信进程局限在单个主机内;后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制。Linux则把两者都继承下来。(转载)
Posix是“可移植操作系统接口”(PortableOperating System Interface),它并不是一个单一的标准,而是一个由电气与电子工程师学会及IEEE开发的一系列标准。Posix标准还是由ISO(国际标准化组织)和IEC(国际电工委员会)采纳的国际标准。
XSI是X/Open系统接口(X/OpenSystem
Interface)。Open Group是由X/Open公司(1984年成立)和开放软件基金会(OSF,1988年成立)于1996年合并而成的组织。它是由厂家、业界最终用户、政府部门和学术机构组成的国际组织。
因在System V系统上提出了进程通信的IPC机制(如消息队列、信号量和共享内存),故通常称为System
V IPC。又因为后来被收录到Unix的XSI标准之中故又称为XSI
IPC。所以System V IPC和 XSI IPC实际上指的是同一种东西。
左边的两个进程共享存留于文件系统中某个文件上的某些信息。当一个文件有待更新时,某种形式的同步是必要的。如read、write、lseek等,为访问这些信息,每个进程都得穿越内核。
中间的两个进程共享驻留于内核中的某些信息。如管道、System V消息队列、System
V信号量。访问共享信息的每次操作涉及对内核的一次系统调用。
右边的两个进程有一个双方都能访问的共享内存区。每个进程一旦设置好改共享内存区,就能根本不涉及内核而访问其中的数据。共享该内存区的进程需要某种形式的同步。
IPC对象通过它的标识符来引用和访问,该标识符是一个非负整数,它唯一的标识了一个IPC对象。当IPC对象被创建或删除时,对应的标识符连续加1,直到达到一个整型数的最大正值,然后又回转到0.
IPC对象的标识符只解决了内部访问一个IPC对象的问题,如何让多个进程都访问某一个特定的IPC对象还需要一个外部键(key),每个IPC对象都与一个键相关联。这样就解决了多个进程在一个IPC对象上汇合的问题。
ipcs输出有关System VIPC特性的各种信息,ipcrm则删除一个System
V消息队列、信号量或共享内存区。
lincoln@ubuntu:~$ ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
lincoln@ubuntu:~$ man ipcrm
IPCRM(1) Linux Programmer's Manual IPCRM(1)
NAME
ipcrm - remove a message queue, semaphore set or shared memory id
SYNOPSIS
ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] ...
对于一个给定的IPC类型,其可能的名字的集合称为它的命名空间(name
space)。命名空间非常重要,因为对于除普通管道以外的所有形式的IPC来说,名字是客户与服务器彼此连接以交换信息的手段。
3.6.1key_t键和ftok函数
从上述IPC命名空间可知,三种类型的SystemV
IPC使用key_t值作为它们的名字。头文件<sys/types.h>把key_t这个数据类型定义为一个整数(至少32位)。这个整数值通常是由ftok函数赋予的。
函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键。
该函数把从pathname导出的信息与id的低序8位组合成一个整数IPC键。ftok的典型实现调用stat函数,然后组合一下三个值:
1)stat结构的st_dev成员(pathname所在的文件系统的信息)
2)stat结构的st_info成员(pathname在文件系统内的索引节点号)
3)id的低序8位
如果pathname不存在,或者对于调用进程不可访问,ftok就返回-1.注意,路径名用于产生键的文件不能是在服务器存活期间由服务器反复创建并删除的文件,因为该文件每次创建时由系统赋予的索引节点号很可能不一样,于是对下一个调用者来说,由ftok返回的键也可能不同。
3.6.2ipc_perm结构
内核给每个IPC对象维护一个信息结构,其内容跟内核给文件维护的信息类似。
3.6.3创建与打开IPC通道
创建或打开一个IPC对象的三个XXXget函数,都有名为key和oflag的参数。
key是类型为key_t的IPC键。对于key值,应用程序有两种选择:
1)调用ftok,给它传递pathname和id。
2)指定key为IPC_PRIVATE,这将保证会创建一个新的、唯一的IPC对象。
oflag参数用于1)指定IPC对象的读写权限位,2)并选择是创建一个新的IPC对象还是访问一个已存在的IPC对象。
1)指定IPC对象的读写权限位
2)创建新IPC对象还是访问已存在IPC对象
参考:
《UNIX网络编程》卷2:进程间通信
《深刻理解Linux进程间通信》
Stevens
一个大功能或大应用,可以将其设计为单个庞大的程序,而更好的方式是将其设计为一组相互通信的程序片段。一般是用多个进程来实现,其中每个进程包含几个线程。这样,进程内部的线程之间需要通信,不同的进程之间需要通信。
进程间通信可以达到数据传输、数据共享、事件通知、进程控制等目的,其中:
数据传输:一个进程将数据发送给另一个进程。
数据共享:多个进程读写共享数据。
事件通知:一个进程向其他进程发送消息,通知它(们)发生了某种事件。
进程控制:一个进程希望完全控制另一个进程(如debug进程)的执行,能够拦截(如debug)进程的所有陷入和异常,并能够及时知道它的状态改变。
1进程间通信方式
Linux的进程通信方式都是从UNIX上继承下来的,而对Unix发展做出重大贡献的两大主力AT&T的贝尔实验室和BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间通信方面的侧重点有所不同。前者对Unix早期的进程间通信方式进行了系统的改进和扩充,形成了“SystemV IPC”,通信进程局限在单个主机内;后者则跳过了该限制,形成了基于套接口(socket)的进程间通信机制。Linux则把两者都继承下来。(转载)
Posix是“可移植操作系统接口”(PortableOperating System Interface),它并不是一个单一的标准,而是一个由电气与电子工程师学会及IEEE开发的一系列标准。Posix标准还是由ISO(国际标准化组织)和IEC(国际电工委员会)采纳的国际标准。
XSI是X/Open系统接口(X/OpenSystem
Interface)。Open Group是由X/Open公司(1984年成立)和开放软件基金会(OSF,1988年成立)于1996年合并而成的组织。它是由厂家、业界最终用户、政府部门和学术机构组成的国际组织。
因在System V系统上提出了进程通信的IPC机制(如消息队列、信号量和共享内存),故通常称为System
V IPC。又因为后来被收录到Unix的XSI标准之中故又称为XSI
IPC。所以System V IPC和 XSI IPC实际上指的是同一种东西。
2共享信息方式
一个系统上运行多个进程,每个进程都有各自的地址空间。UNIX进程间的信息共享可以有多种方式。左边的两个进程共享存留于文件系统中某个文件上的某些信息。当一个文件有待更新时,某种形式的同步是必要的。如read、write、lseek等,为访问这些信息,每个进程都得穿越内核。
中间的两个进程共享驻留于内核中的某些信息。如管道、System V消息队列、System
V信号量。访问共享信息的每次操作涉及对内核的一次系统调用。
右边的两个进程有一个双方都能访问的共享内存区。每个进程一旦设置好改共享内存区,就能根本不涉及内核而访问其中的数据。共享该内存区的进程需要某种形式的同步。
3 IPC
3.1 IPC对象
IPC对象可以是消息队列、信号量、共享存储等三个类型的任意一种。它是活动在内核级别的一种进程间通信的工具。IPC对象通过它的标识符来引用和访问,该标识符是一个非负整数,它唯一的标识了一个IPC对象。当IPC对象被创建或删除时,对应的标识符连续加1,直到达到一个整型数的最大正值,然后又回转到0.
IPC对象的标识符只解决了内部访问一个IPC对象的问题,如何让多个进程都访问某一个特定的IPC对象还需要一个外部键(key),每个IPC对象都与一个键相关联。这样就解决了多个进程在一个IPC对象上汇合的问题。
3.2 IPC对象查看与删除
由于System V IPC的三种类型不是以文件系统中的路径名标识的,因此使用标准的ls和rm程序无法看到它们,也无法删除它们。不过实现了这些类型IPC的任何系统都提供两个特殊的程序:ipcs和ipcrm。ipcs输出有关System VIPC特性的各种信息,ipcrm则删除一个System
V消息队列、信号量或共享内存区。
lincoln@ubuntu:~$ ipcs
------ Shared Memory Segments --------
key shmid owner perms bytes nattch status
------ Semaphore Arrays --------
key semid owner perms nsems
------ Message Queues --------
key msqid owner perms used-bytes messages
lincoln@ubuntu:~$ man ipcrm
IPCRM(1) Linux Programmer's Manual IPCRM(1)
NAME
ipcrm - remove a message queue, semaphore set or shared memory id
SYNOPSIS
ipcrm [ -M key | -m id | -Q key | -q id | -S key | -s id ] ...
3.3 IPC对象的持续性
IPC对象的持续性是该对象一直存在多长时间。如图四,展示了三种类型的持续性。3.4 IPC命名空间
当两个或多个无亲缘关系的进程使用某种类型的IPC对象彼此交换信息时,该IPC对象必须有一个某种形式的名字(name)或标识符(identifier),这样其中一个进程(往往是服务器)可以创建该IPC对象,其余进程则可以指定同一个IPC对象。对于一个给定的IPC类型,其可能的名字的集合称为它的命名空间(name
space)。命名空间非常重要,因为对于除普通管道以外的所有形式的IPC来说,名字是客户与服务器彼此连接以交换信息的手段。
IPC类型 | 命名空间 | 标识 |
管道 FIFO | 没有名字 路径名 | 描述符 描述符 |
Posix消息队列 Posix有名信号量 Posix基于内存的信号量 Posix共享存储区 | Posix IPC名字 Posix IPC名字 没有名字 Posix IPC名字 | mqd_t值 sem_t值 sem_t指针 描述符 |
System V消息队列 System V信号量 System V共享内存区 | key_t键 key_t键 key_t键 | System V IPC标识符 System V IPC标识符 System V IPC标识符 |
TCP套接字 UDP套接字 Unix域套接字 | IP地址和TCP端口 IP地址和UDP端口 路径名 | 描述符 描述符 描述符 |
3.5 POSIX IPC
| 消息队列 | 信号量 | 共享内存区 |
头文件 | <mqueue.h> | <semaphore.h> | <sys/mman.h> |
创建、打开或删除IPC的函数 | mq_open() mq_close() mq_unlink() | sem_open() sem_close() sem_unlink() | shm_open() shm_unlink() |
控制IPC操作的函数 | mq_getattr() mq_setattr() | | ftruncate() fstat() |
IPC操作函数 | mq_send() mq_receive() mq_notify() | sem_wait() sem_trywait() sem_post() sem_getvalue() | mmap() munmap() |
3.6 System V IPC
| 消息队列 | 信号量 | 共享内存区 |
头文件 | <sys/msg.h> | <sys/sem.h> | <sys/shm.h> |
创建或打开IPC的函数 | msgget() | semget() | shmget() |
控制IPC操作的函数 | msgctl() | semctl() | shmctl() |
IPC操作函数 | msgsnd() msgrcv() | semop() | shmat() shmdt() |
从上述IPC命名空间可知,三种类型的SystemV
IPC使用key_t值作为它们的名字。头文件<sys/types.h>把key_t这个数据类型定义为一个整数(至少32位)。这个整数值通常是由ftok函数赋予的。
函数ftok把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键。
#include <sys/ipc.h> key_t ftok(const char *pathname, int id);返回:成功返回IPC键,出错返回-1 |
1)stat结构的st_dev成员(pathname所在的文件系统的信息)
2)stat结构的st_info成员(pathname在文件系统内的索引节点号)
3)id的低序8位
如果pathname不存在,或者对于调用进程不可访问,ftok就返回-1.注意,路径名用于产生键的文件不能是在服务器存活期间由服务器反复创建并删除的文件,因为该文件每次创建时由系统赋予的索引节点号很可能不一样,于是对下一个调用者来说,由ftok返回的键也可能不同。
3.6.2ipc_perm结构
内核给每个IPC对象维护一个信息结构,其内容跟内核给文件维护的信息类似。
头文件<sys/ipc.h> struct ipc_perm { uid_t uid;//拥有者用户ID gid_t gid;//拥有者组ID uid_t cuid;//创建者用户ID dig_t cgid;//创建者组ID mode_t mode;//读写权限 ulong_t seq;//槽位使用序列号,即IPC对象的维护计数器 key_t key;//IPC键 }; |
创建或打开一个IPC对象的三个XXXget函数,都有名为key和oflag的参数。
int msgget(key_tkey, int oflag); int semget(key_tkey,int nsems, int oflag); int shmget(key_tkey, size_t size, int oflag); |
1)调用ftok,给它传递pathname和id。
2)指定key为IPC_PRIVATE,这将保证会创建一个新的、唯一的IPC对象。
oflag参数用于1)指定IPC对象的读写权限位,2)并选择是创建一个新的IPC对象还是访问一个已存在的IPC对象。
1)指定IPC对象的读写权限位
数字值 八进制 | 符号 | 说明 | ||
消息队列 | 信号量 | 共享内存区 | ||
0400 0200 | MSG_R MSG_W | SEM_R SEM_A(注:alter) | SHM_R SHM_W | 用户读 用户写 |
0040 0020 | MSG_R >> 3 MSG_W >>3 | SEM_R>>3 SEM_A>>3 | SHM_R>>3 SHM_W>>3 | 组读 组写 |
0004 0002 | MSG_R>>6 MSG_W>>6 | SEM_R>>6 SEM_A>>6 | SHM_R>>6 SHM_W>>6 | 其他用户读 其他用户写 |
oflag参数 | key不存在 | key已存在 |
无特殊标志,如0为参数 IPC_CREAT IPC_CREAT | IPC_EXCL | 出错,errno=ENOENT 成功,创建新对象 成功,创建新对象 | 成功,引用已存对象 成功,引用已存对象 出错,errno=EEXIST |
参考:
《UNIX网络编程》卷2:进程间通信
《深刻理解Linux进程间通信》
相关文章推荐
- SYSTEM V IPC——信号量笔记
- System V 消息队列
- 6.Android EditText 技巧
- 一篇搞定iOS 9适配--系列教程
- [sphinx][语音识别]学习笔记
- Hibernate 缓存机制
- RTP头简介
- 给定一个数组,除了两个数只出现一次以外,其他数都出现了两次。
- android dialog四周的黑边消除
- ubuntu 12.04 code::blocks 安装
- Codeforces 383A Milking cows
- TreeSet集合两种排序方式
- android工具网
- 混音器原理及Mixer API函数介绍
- iOS本地存储NSUserDefaults2
- 关于Arraylist的增量:ensureCapacity()
- 代码规范问题总结(四)
- winform中自定义窗体启动位置
- CentOS上如何把Web服务器从Apache换到nginx
- ScrollView嵌套ListView,GridView起始位置不是顶部