posix进程间通信

**************************************************************************************************
posix 信号量
信号量是一种是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。
本书讨论3中类型的信号量。
posix 有名信号量:使用posix IPC名字标识,可用于进程或者线程间的同步。
posix基于内存的信号量:存放在共享内存中,可用于进程或者线程间的同步。
system v信号量:在内核中维护,可用于进程或者线程间的同步。
函数sem_open创建一个新的有名信号量或者打开一个已存在的有名信号量。有名信号量总是
既可以用于线程间同步,又可以用于进程间同步。
#include<semaphore.h>
sem_t *sem_open (const char *name,int flag,.../*mode_t mode,unsigned int value*/);
返回:若成功则为指向信号量的指针,若出错则为SEM_FAILED
oflag参数可以是0,O_CREAT或O_CREAT|O_EXCL,如果指定了O_CREAT标志,那么第三个和第四个参数是需要的:其中
mode参数指定权限位,value参数指定信号量的初始值。该初始值不能超过ZSEM_VALUE_MAX(这个常值必须至少为32767)。
二值信号量的初始值通常为1,计数信号量的初始值则往往大于1.
若果指定了O_CREAT(而没有O_EXCL,那么只有当所需的信号量尚未存在时才初始化它。不过所需信号量已存在的条件下指定
O_EXCL并不是一个错误。该标志的意思仅仅是如果所需的信号量尚未存在,那就创建并初始化它。但是所需信号量已存在的
条件下指定O_CREAT|O_EXCL确是一个错误。
sem_open的返回值是指向某个sem_t数据类型的指针。该指针随后sem_close,sem_wait,sem_trywait,sem_post,以及sem_getvalue
的参数。
使用sem_open打开有名信号量,使用sem_close将其关闭。
#include<semaphore.h>
int sem_close(sem_t *sem_t);
返回:若成功则为0,若出错则为-1;
一个进程终止时,内核还对其上仍然打开着所有有名信号量自动执行这样的信号量关闭操作。不论该进程是自愿终止(通过
调用exit或_exit)还是非自愿的终止的,这种自动关闭都会发生。
关闭一个信号量并没有将他从系统中删除。这就是说posix有名信号量至少是随内核持续的:即使当前没有进程打开着某个
信号量,他的值仍然保持。
有名信号量使用sem_unlink从系统中删除。
#include<semaphore.h>
#sem_unlink(const char *name);
每个信号量都有一个计数器记录当前的打开次数(就像文件一样),sem_unlink类似于文件I/O的unlink函数:当引用计数
还是大于0时,name就能从文件系统中删除,然而其信号量的析构(不同于将他的名字从文件系统中删除)却要等到最后一个
sem_close发生为止。
sem_wait和sem_trywait函数
sem_wait函数测试所指定的信号量的值,如果该值大于0,那么就将他减1并立即返回。如果该值等于0,调用线程就被投入
睡眠中,直到该值变为大于0,这时再将它减1,函数随后返回。
#include<semaphore.h>
int sem_wait(sem_t *sem)
int sem_trywait(sem_t *sem)
均返回:若成功则为0,若出错则为-1;
sem_wait和sen_trywait的差别在于:当指定信号量的值已经是0时,后者并不将调用线程投入睡眠,相反,他返回一个错误;
sem_post和sem_getvalue函数
当一个线程使用完成某个信号量时,他应该调用sem_post。本函数把所指定信号量值加1,然后唤醒正在等待该信号量值变为
正数的任意线程。
#include<semaphore.h>
int sem_post(sem_t *sem);
int sem_getvalue(sem_t *sem,int *valp);
均成功:若成功则为0,若出错则为-1
sem_getvalue在由valp指向的整数中返回所指定信号量的当前值。如果该信号量当前值以上锁,那么返回值或0,或为某个负数
,其绝对值就是等待该信号量解锁的线程数。

前面的内容处理的是posix有名信号量。这些信号量是由一个name参数标识的,他通常指代系统文件中的某个文件。然而posix
也提供基于内存的信号量,它们由应用程序分配信号量的内存空间(也就是分配一个sem_t数据类型的内存空间),然后由系统
初始化它们的值。
#include<semaphore.h>
int sem_init(sem_t *sem,int shared,unsigned int value);
返回:若出错则为-1;
int sem_destory(sem_t  *sem);
返回:若成功则为0,若出错则为-1;
基于内存的信号量是由sem_init初始化的。sem参数指向该应用程序必须分配的sem_t变量。如果shared为0,那么待初始化的
信号量是在同一进程中个线程间共享的,否则该信号量是在进程间共享的。当shared为非0时,该信号量必须放在某种类型
的共享内存中,而即将使用他的所有进程要能访问共享内存区。跟sem_open一样,value参数是该信号量的初始值。
使用完一个基于内存的信号量后,我们调用sem_destory摧毁它。
sem_open不需要类似于shared的参数,是因为有名信号总是可以在不同的进程间共享的。注意基于内存的信号量不使用
任何类似于O_CREAT标志的东西,也就是说,sem_init总是初始化信号量的值。因此,对于一个给定的信号量,我们必须
小心保证只调用sem_init一次。对于一个只初始化过的信号量调用sem_init其结果是未定义的。
你的确保理解sem_open和sem_init之间的基本差异。前者返回一个指向某个sem_t变量的指针,该变量由sem_open函数本身分配
并初始化。后者的第一个参数指向某个sem_t变量的指针,该变量由调用者分配,然后由sem_init初始化。
当不需要使用与信号量关联的名字时,可改基于内存的信号量。彼此无亲缘关系的不同进程需要使用信号量时,通常使用
有名信号量。基于内存的信号量至少具有随进程的持续性,然而他们真正的持续性却取决于存放信号量内存的类型。只要含有
某个基于内存信号量的内存信号量的内存区有效时,该信号量就一直存在。
1>如果基于某个内存的信号量是由单个进程的各个线程共享的(sem_init的shared的参数为0),那么该信号量具有随进程持续性
,当该进程终止时它也消失。
2>如果某个基于内存的信号量是在不同的进程间共享的(sem_init的shared参数为1),那么该信号量必须放在共享内存区中,
因而只要共享内存区仍然存在,该信号量也就继续存在。posix共享内存区和system v共享内存区都具有随内核的持续性。这意味着
服务器创建一个共享内存区,再该内存区中初始化一个posix基于内存的信号量,然后终止。一段时间后,一个或多个客户
可打开该共享内存区,访问存在其中的基于内存的信号量。
小心,下面的代码并不像预期的那样工作。
sem_t mysem;
sem_init(&mysem,1,0)
if(fork()==0){//child
.....
sem_post(&mysem);
}
sem_wait(&mysem);//parent;wait for child
问题在于信号量mysem不在共享内存区中,fork出来的子进程通常不共享父进程的内存空间。子进程是在父进程内存空间
的副本上启动的,它跟共享内存区不是一回事。
进程间共享信号量
进程间共享信号量基于内存的信号量的规则很简单:信号量本身(其地址作为semz_init第一个参数的sem_t数据类型变量)
必须驻留在由所有希望共享他的进程所共享的内存区中,而且sem_init的第二个参数必须为1。
至于有名信号量,不同进程(不论彼此间有无亲缘关系)总是等够访问同一个有名的信号量,
只要他们在调用sem_open时,指定相同的名字就行了。即使对于某个给定的名字sem_open调用在每个进程中可能返回不同的指针
,使用该指针的信号量函数(sem_post和sem_wait)所引用的仍然是同一个有名的信号量。
我们在调用sem_open返回某个sem_t数据类型变量的指针后接着调用fork,情况又会怎么杨呢?posix中有关fork
函数这样描述的“若果在父进程中打开任何信号仍然应在子进程中打开。”这意味着如下的代码是正确的:
sem_t *mutex;/*global pointer that is copied across the fork*/
//parent creates named semaphore
mutex=sem_open(name,O_CREAT|O_EXCL,FILE_MODE,0)
if((child==fork())==0){
 //child
......
 sem_wait(mutex);
}
//parent
sem_post(mutex);
.....
我们必须仔细搞清楚什么时候可以或者不可以在不同进程间共享某个信号量的原因:
一个信号量的状态可以包含在它本身的sem_t数据类型中,但是它还可能使用其他信息
(例如文件描述符)。我们将在下一章看到,一个system v信号量的唯一句柄是由semget
返回他的整数标识符。知道这个标识符的任何进程都可以访问该信号量。system v信号量
的所有的状态都包含在内核中,他们的整数标识符只是告诉内核具体引用那个信号量。

***************************************************************************************
posix共享内存区
内存映射文件(memory-mmaped file):
由open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间中的一个文件)
共享内存区对象(shared——mmaped file):
由shm_open打开一个posix.1 IPC名字(也许是在文件系统中的一个路径名),
所返回的描述符由mmap函数映射到当前的进程地址空间。
这两种技术都要用到mmap技术,差别在于作为mmap的参数之一的描述符的获取手段:通过open或通过shm_pen。
posix内存映射文件
fd=open(pathname,....);
ptr=mmap(...,fd,.....);
posix共享内存区对象
fd=shm_open(name,...);
ptr=mmap(....,fd,.....);
posix内存共享区涉及到以下两个步骤要求。
(1)指定一个名字参数调用shm_open,以创建一个新的共享内存区对象或者打开一个已存在的共享内存区对象。
(2)调用mmap把这个共享内存区映射到调用进程的地址空间。传递给shm_open的名字参数随后由希望共享内存区的
 任何进程使用。
#include<sys/mman.h>
int shm_open(const char *name,int oflag,mode_t mode);
返回:若成功则为非负的描述符,若出错则为-1;
int shm_unlink(const char *name);
返回:若成功则为0,若出错则为-1;
name参数:共享内存区的名字,可以自己定义一个名字;
oflag参数必须或者包含有O_RDONLY(只读)标志,或者含有O_RDWR(读写)标志,还可以指定如下标志:O_CREAT,O_EXCL,
或O_TRUNC.如果随O_RDWR指定O_TRUNC标志,而且所需的内存空间已经存在,那么他将被截断成0长度。
mode参数指定权限位,他在指定了O_CREAT标志的前提下使用。注意与mq_open和sem_open函数不同,shm_open的mode参数
总是必须指定。如果没有指定O_CREAT标志,那么该参数可以指定为0;
shm_open的返回值是一个整数描述符,他随后用做mmap的第5个参数。
shm_unlink函数删除一个共享内存区对象的名字。跟其他所有的unlink函数(删除文件系统中一个路径名的unlink,删除
一个posix消息队列的mq_unlink,以及删除一个posix有名信号量的sem_unlink)一样,删除一个名字不会影响对于其底层
支撑对象的现有引用,直到对于该引用全部关闭为止。删除一个名字仅仅防止后序的open或sem_open调用取得成功。

ftruncate和fstat函数
处理mmap的时候,普通文件或者共享内存区对象的大小都可以通过调用ftruncate修改
#include<unistd.h>
posix就该函数对普通文件和共享内存区对象的处理定义稍有不同。
1>对于一个普通文件:如果该文件大小大于length参数,额外的数据就被丢弃掉。如果该文件大小小于length,那么
该文件是否修改以及其大小是否增长是未加说明的。实际对于一个普通文件,把他的大小扩张到length字节的可移植方法是:
先lseek到偏移为length-1处,然后write 1个字节的数据。所幸的是几乎搜有的unix实现都支持使用ftruncate扩展一个文件。
2>对于一个共享内存区对象:ftruncate把该对象的大小设置成length字节。
我们调用ftruncate来指定新创建的共享内存区对象的大小,或者修改已存在的对象的大小。当打开一个已存在的共享内存
区对象是,我们可以调用fstat来获取有关该对象的信息。
#include<sys/type.h>
#include<sys/stat.h>
int fstat(int fd,struct stat *buf);
返回:若成功则为0,若出错则为-1;
时间: 2024-10-12 09:21:05

posix进程间通信的相关文章

练习--LINUX进程间通信之无名管道PIPE

IBM上放的这个系统不错,刚好可以系统回温一下LINUX的系统知识. http://www.ibm.com/developerworks/cn/linux/l-ipc/part1/ 感觉年纪大了,前几年看的LINUX内核和系统的东东,忘了很多,要慢慢转化成永久记忆才可以. 今天,又拿起<LINUX内核设计与实现>,慢慢啃下去. ~~~~~~~~~~~~~~ 进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想

Linux进程间通信程序设计-1

一.进程间通信概述: 1.目的:为什么要进行进程间通信? 1)数据传输:一个进程需要把他的数据发送给另一个进程. 2)资源共享:协调共享资源. 3)通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件. 4)进程控制 2.发展 Linux进程间通信(IPC)由以下几部分发展而来: 1)UNIX进程间通信 2)基于System V进程间通信(System 5是UNIX操作系统众多版本中的一支) 3)POSIX进程间通信(POSIX可移植的操作系统接口) 3.分类 现在Linux

Linux进程间通信的几种方式总结--linux内核剖析(七)

进程间通信概述 进程通信的目的 传输数据 一个进程须要将它的数据发送给还有一个进程.发送的数据量在一个字节到几M字节之间 共享数据 多个进程想要操作共享数据,一个进程对共享数据 通知事 一个进程须要向还有一个或一组进程发送消息.通知它(它们)发生了某种事件(如进程终止时要通知父进程). 资源共享 多个进程之间共享相同的资源.为了作到这一点,须要内核提供锁和同步机制. 进程控制 有些进程希望全然控制还有一个进程的执行(如Debug进程),此时控制进程希望能够拦截还有一个进程的全部陷入和异常,并能够

练习--LINUX进程间通信之消息队列MSG

https://www.ibm.com/developerworks/cn/linux/l-ipc/part3/ 继续坚持,或许不能深刻理解,但至少要保证有印象. ~~~~~~~~~~~~~~ 消息队列(也叫做报文队列)能够克服早期unix通信机制的一些缺点.作为早期unix通信机制之一的信号能够传送的信息量有限,后来虽然POSIX 1003.1b在信号的实时性方面作了拓广,使得信号在传递信息量方面有了相当程度的改进,但是信号这种通信方式更像"即时"的通信方式,它要求接受信号的进程在某

进程间通信(IPC)

概述 1.Linux使用的进程间通信方式主要包括: (1)管道(pipe)和有名管道 (2)信号(signal) (3)共享内存 (4)消息队列 (5)信号量 (6)套接字(socket) 2.问:为什么进程间需要通信? (1)数据传输 一个进程需要将它的数据发送给另外一个进程 (2)资源共享 多个进程间共享同样的资源 (3)通知事件 一个进程需要向另外一个或一组进程发送消息,通知它们发生了某种事件 (4)进程控制 某些进程希望能够控制另一个进程的执行(比如Debug进程),此时,控制进程希望能

【转】 Linux进程间通信

一.进程间通信概述进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别的进程应该立刻看到.C.通知事件:一个进程需要向另一个或一组进程发送消息,通知它(它们)发生了某种事件(如进程终止时要通知父进程).D.资源共享:多个进程之间共享同样的资源.为了作到这一点,需要内核提供锁和同步机制.E.进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程

Linux进程间通信(IPC)简介

Linux IPC的发展 Linux下的进程通信手段基本上是从UNIX平台上的进程通信手段继承而来的.而对UNIX发展做出重大贡献的两大主力AT&T的贝尔实验室及BSD(加州大学伯克利分校的伯克利软件发布中心)在进程间的通信方面的侧重点有所不同.前者是对UNIX早期的进程间通信手段进行了系统的改进和扩充,形成了"system V IPC",其通信进程主要局限在单个计算机内:而BSD则跳过了该限制,形成了基于套接口(socket)的进程间通信机制.而Linux则把两者的优势都继承

linux进程间通信-概述

一 进程间通信有如下的目的: 1.数据传输,一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M之间:2.共享数据,多个进程想要操作共享数据,一个进程对数据的修改,其他进程应该立刻看到:3.通知事件,一个进程需要向另一个或一组进程发送消息,通知它们发生了某件事情:4.资源共享,多个进程之间共享同样的资源.为了做到这一点,需要内核提供锁和同步机制:5.进程控制,有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有陷入和异常,并能够及时知

Linux进程间通信—消息队列

四.消息队列(Message Queue) 消息队列就是消息的一个链表,它允许一个或者多个进程向它写消息,一个或多个进程向它读消息.Linux维护了一个消息队列向量表:msgque,来表示系统中所有的消息队列. 消息队列克服了信号传递信息少,管道只能支持无格式字节流和缓冲区受限的缺点. 消息队列用于运行于同一台机器上的进程间通信,它和管道很相似,是一个在系统内核中用来保存消息的队列,它在系统内核中是以消息链表的形式出现.消息链表中节点的结构用msg声明. 事实上,它是一种正逐渐被淘汰的通信方式,