1. **************************************************************************************************
  2. posix 信号量
  3. 信号量是一种是一种用于提供不同进程间或一个给定进程的不同线程间同步手段的原语。
  4. 本书讨论3中类型的信号量。
  5. posix 有名信号量:使用posix IPC名字标识,可用于进程或者线程间的同步。
  6. posix基于内存的信号量:存放在共享内存中,可用于进程或者线程间的同步。
  7. system v信号量:在内核中维护,可用于进程或者线程间的同步。
  8. 函数sem_open创建一个新的有名信号量或者打开一个已存在的有名信号量。有名信号量总是
  9. 既可以用于线程间同步,又可以用于进程间同步。
  10. #include<semaphore.h>
  11. sem_t *sem_open (const char *name,int flag,.../*mode_t mode,unsigned int value*/);
  12. 返回:若成功则为指向信号量的指针,若出错则为SEM_FAILED
  13. oflag参数可以是0O_CREATO_CREAT|O_EXCL,如果指定了O_CREAT标志,那么第三个和第四个参数是需要的:其中
  14. mode参数指定权限位,value参数指定信号量的初始值。该初始值不能超过ZSEM_VALUE_MAX(这个常值必须至少为32767)。
  15. 二值信号量的初始值通常为1,计数信号量的初始值则往往大于1.
  16. 若果指定了O_CREAT(而没有O_EXCL,那么只有当所需的信号量尚未存在时才初始化它。不过所需信号量已存在的条件下指定
  17. O_EXCL并不是一个错误。该标志的意思仅仅是如果所需的信号量尚未存在,那就创建并初始化它。但是所需信号量已存在的
  18. 条件下指定O_CREAT|O_EXCL确是一个错误。
  19. sem_open的返回值是指向某个sem_t数据类型的指针。该指针随后sem_closesem_waitsem_trywaitsem_post,以及sem_getvalue
  20. 的参数。
  21. 使用sem_open打开有名信号量,使用sem_close将其关闭。
  22. #include<semaphore.h>
  23. int sem_close(sem_t *sem_t);
  24. 返回:若成功则为0,若出错则为-;
  25. 一个进程终止时,内核还对其上仍然打开着所有有名信号量自动执行这样的信号量关闭操作。不论该进程是自愿终止(通过
  26. 调用exit_exit)还是非自愿的终止的,这种自动关闭都会发生。
  27. 关闭一个信号量并没有将他从系统中删除。这就是说posix有名信号量至少是随内核持续的:即使当前没有进程打开着某个
  28. 信号量,他的值仍然保持。
  29. 有名信号量使用sem_unlink从系统中删除。
  30. #include<semaphore.h>
  31. #sem_unlink(const char *name);
  32. 每个信号量都有一个计数器记录当前的打开次数(就像文件一样),sem_unlink类似于文件I/Ounlink函数:当引用计数
  33. 还是大于0时,name就能从文件系统中删除,然而其信号量的析构(不同于将他的名字从文件系统中删除)却要等到最后一个
  34. sem_close发生为止。
  35. sem_waitsem_trywait函数
  36. sem_wait函数测试所指定的信号量的值,如果该值大于0,那么就将他减1并立即返回。如果该值等于0,调用线程就被投入
  37. 睡眠中,直到该值变为大于0,这时再将它减1,函数随后返回。
  38. #include<semaphore.h>
  39. int sem_wait(sem_t *sem)
  40. int sem_trywait(sem_t *sem)
  41. 均返回:若成功则为0,若出错则为-;
  42. sem_waitsen_trywait的差别在于:当指定信号量的值已经是0时,后者并不将调用线程投入睡眠,相反,他返回一个错误;
  43. sem_postsem_getvalue函数
  44. 当一个线程使用完成某个信号量时,他应该调用sem_post。本函数把所指定信号量值加1,然后唤醒正在等待该信号量值变为
  45. 正数的任意线程。
  46. #include<semaphore.h>
  47. int sem_post(sem_t *sem);
  48. int sem_getvalue(sem_t *sem,int *valp);
  49. 均成功:若成功则为0,若出错则为-
  50. sem_getvalue在由valp指向的整数中返回所指定信号量的当前值。如果该信号量当前值以上锁,那么返回值或0,或为某个负数
  51. ,其绝对值就是等待该信号量解锁的线程数。
  52.  
  53. 前面的内容处理的是posix有名信号量。这些信号量是由一个name参数标识的,他通常指代系统文件中的某个文件。然而posix
  54. 也提供基于内存的信号量,它们由应用程序分配信号量的内存空间(也就是分配一个sem_t数据类型的内存空间),然后由系统
  55. 初始化它们的值。
  56. #include<semaphore.h>
  57. int sem_init(sem_t *sem,int shared,unsigned int value);
  58. 返回:若出错则为-;
  59. int sem_destorysem_t *sem);
  60. 返回:若成功则为0,若出错则为-;
  61. 基于内存的信号量是由sem_init初始化的。sem参数指向该应用程序必须分配的sem_t变量。如果shared0,那么待初始化的
  62. 信号量是在同一进程中个线程间共享的,否则该信号量是在进程间共享的。当shared为非0时,该信号量必须放在某种类型
  63. 的共享内存中,而即将使用他的所有进程要能访问共享内存区。跟sem_open一样,value参数是该信号量的初始值。
  64. 使用完一个基于内存的信号量后,我们调用sem_destory摧毁它。
  65. sem_open不需要类似于shared的参数,是因为有名信号总是可以在不同的进程间共享的。注意基于内存的信号量不使用
  66. 任何类似于O_CREAT标志的东西,也就是说,sem_init总是初始化信号量的值。因此,对于一个给定的信号量,我们必须
  67. 小心保证只调用sem_init一次。对于一个只初始化过的信号量调用sem_init其结果是未定义的。
  68. 你的确保理解sem_opensem_init之间的基本差异。前者返回一个指向某个sem_t变量的指针,该变量由sem_open函数本身分配
  69. 并初始化。后者的第一个参数指向某个sem_t变量的指针,该变量由调用者分配,然后由sem_init初始化。
  70. 当不需要使用与信号量关联的名字时,可改基于内存的信号量。彼此无亲缘关系的不同进程需要使用信号量时,通常使用
  71. 有名信号量。基于内存的信号量至少具有随进程的持续性,然而他们真正的持续性却取决于存放信号量内存的类型。只要含有
  72. 某个基于内存信号量的内存信号量的内存区有效时,该信号量就一直存在。
  73. 1>如果基于某个内存的信号量是由单个进程的各个线程共享的(sem_initshared的参数为0),那么该信号量具有随进程持续性
  74. ,当该进程终止时它也消失。
  75. 2>如果某个基于内存的信号量是在不同的进程间共享的(sem_initshared参数为1),那么该信号量必须放在共享内存区中,
  76. 因而只要共享内存区仍然存在,该信号量也就继续存在。posix共享内存区和system v共享内存区都具有随内核的持续性。这意味着
  77. 服务器创建一个共享内存区,再该内存区中初始化一个posix基于内存的信号量,然后终止。一段时间后,一个或多个客户
  78. 可打开该共享内存区,访问存在其中的基于内存的信号量。
  79. 小心,下面的代码并不像预期的那样工作。
  80. sem_t mysem
  81. sem_init(&mysem,,)
  82. if(fork()==){//child
  83. .....
  84. sem_post(&mysem);
  85. }
  86. sem_wait(&mysem);//parent;wait for child
  87. 问题在于信号量mysem不在共享内存区中,fork出来的子进程通常不共享父进程的内存空间。子进程是在父进程内存空间
  88. 的副本上启动的,它跟共享内存区不是一回事。
  89. 进程间共享信号量
  90. 进程间共享信号量基于内存的信号量的规则很简单:信号量本身(其地址作为semz_init第一个参数的sem_t数据类型变量)
  91. 必须驻留在由所有希望共享他的进程所共享的内存区中,而且sem_init的第二个参数必须为1
  92. 至于有名信号量,不同进程(不论彼此间有无亲缘关系)总是等够访问同一个有名的信号量,
  93. 只要他们在调用sem_open时,指定相同的名字就行了。即使对于某个给定的名字sem_open调用在每个进程中可能返回不同的指针
  94. ,使用该指针的信号量函数(sem_postsem_wait)所引用的仍然是同一个有名的信号量。
  95. 我们在调用sem_open返回某个sem_t数据类型变量的指针后接着调用fork,情况又会怎么杨呢?posix中有关fork
  96. 函数这样描述的“若果在父进程中打开任何信号仍然应在子进程中打开。”这意味着如下的代码是正确的:
  97. sem_t *mutex/*global pointer that is copied across the fork*/
  98. //parent creates named semaphore
  99. mutex=sem_open(name,O_CREAT|O_EXCL,FILE_MODE,)
  100. if((child==fork())==){
  101. //child
  102. ......
  103. sem_wait(mutex);
  104. }
  105. //parent
  106. sem_post(mutex);
  107. .....
  108. 我们必须仔细搞清楚什么时候可以或者不可以在不同进程间共享某个信号量的原因:
  109. 一个信号量的状态可以包含在它本身的sem_t数据类型中,但是它还可能使用其他信息
  110. (例如文件描述符)。我们将在下一章看到,一个system v信号量的唯一句柄是由semget
  111. 返回他的整数标识符。知道这个标识符的任何进程都可以访问该信号量。system v信号量
  112. 的所有的状态都包含在内核中,他们的整数标识符只是告诉内核具体引用那个信号量。
  113.  
  114. ***************************************************************************************
  115. posix共享内存区
  116. 内存映射文件(memory-mmaped file):
  117. open函数打开,由mmap函数把得到的描述符映射到当前进程地址空间中的一个文件)
  118. 共享内存区对象(shared——mmaped file):
  119. shm_open打开一个posix. IPC名字(也许是在文件系统中的一个路径名),
  120. 所返回的描述符由mmap函数映射到当前的进程地址空间。
  121. 这两种技术都要用到mmap技术,差别在于作为mmap的参数之一的描述符的获取手段:通过open或通过shm_pen
  122. posix内存映射文件
  123. fd=open(pathname,....);
  124. ptr=mmap(...,fd,.....);
  125. posix共享内存区对象
  126. fd=shm_openname,...);
  127. ptr=mmap(....,fd,.....);
  128. posix内存共享区涉及到以下两个步骤要求。
  129. ()指定一个名字参数调用shm_open,以创建一个新的共享内存区对象或者打开一个已存在的共享内存区对象。
  130. ()调用mmap把这个共享内存区映射到调用进程的地址空间。传递给shm_open的名字参数随后由希望共享内存区的
  131. 任何进程使用。
  132. #include<sys/mman.h>
  133. int shm_open(const char *name,int oflag,mode_t mode);
  134. 返回:若成功则为非负的描述符,若出错则为-;
  135. int shm_unlink(const char *name);
  136. 返回:若成功则为0,若出错则为-;
  137. name参数:共享内存区的名字,可以自己定义一个名字;
  138. oflag参数必须或者包含有O_RDONLY(只读)标志,或者含有O_RDWR(读写)标志,还可以指定如下标志:O_CREAT,O_EXCL,
  139. O_TRUNC.如果随O_RDWR指定O_TRUNC标志,而且所需的内存空间已经存在,那么他将被截断成0长度。
  140. mode参数指定权限位,他在指定了O_CREAT标志的前提下使用。注意与mq_opensem_open函数不同,shm_openmode参数
  141. 总是必须指定。如果没有指定O_CREAT标志,那么该参数可以指定为0
  142. shm_open的返回值是一个整数描述符,他随后用做mmap的第5个参数。
  143. shm_unlink函数删除一个共享内存区对象的名字。跟其他所有的unlink函数(删除文件系统中一个路径名的unlink,删除
  144. 一个posix消息队列的mq_unlink,以及删除一个posix有名信号量的sem_unlink)一样,删除一个名字不会影响对于其底层
  145. 支撑对象的现有引用,直到对于该引用全部关闭为止。删除一个名字仅仅防止后序的opensem_open调用取得成功。
  146.  
  147. ftruncatefstat函数
  148. 处理mmap的时候,普通文件或者共享内存区对象的大小都可以通过调用ftruncate修改
  149. #include<unistd.h>
  150. posix就该函数对普通文件和共享内存区对象的处理定义稍有不同。
  151. >对于一个普通文件:如果该文件大小大于length参数,额外的数据就被丢弃掉。如果该文件大小小于length,那么
  152. 该文件是否修改以及其大小是否增长是未加说明的。实际对于一个普通文件,把他的大小扩张到length字节的可移植方法是:
  153. lseek到偏移为length-1处,然后write 1个字节的数据。所幸的是几乎搜有的unix实现都支持使用ftruncate扩展一个文件。
  154. >对于一个共享内存区对象:ftruncate把该对象的大小设置成length字节。
  155. 我们调用ftruncate来指定新创建的共享内存区对象的大小,或者修改已存在的对象的大小。当打开一个已存在的共享内存
  156. 区对象是,我们可以调用fstat来获取有关该对象的信息。
  157. #include<sys/type.h>
  158. #include<sys/stat.h>
  159. int fstat(int fd,struct stat *buf);
  160. 返回:若成功则为0,若出错则为-;
    一般,使用基于内存的信号灯同步同进程多线程,使用有名信号灯同步多进程。

posix进程间通信的更多相关文章

  1. POSIX 进程间通信 (可移植操作系统接口)

    1.什么是POSIX标准 Portable Operating System Interface for Computing System. 他是一个针对操作系统(准确地说是针对类Unix操作系统)的 ...

  2. linux进程间通信-概述

    一 进程间通信有如下的目的: 1.数据传输,一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M之间:2.共享数据,多个进程想要操作共享数据,一个进程对数据的修改,其他进程应该立刻看到 ...

  3. 【转】 Linux进程间通信

    一.进程间通信概述进程通信有如下一些目的:A.数据传输:一个进程需要将它的数据发送给另一个进程,发送的数据量在一个字节到几M字节之间B.共享数据:多个进程想要操作共享数据,一个进程对共享数据的修改,别 ...

  4. <转>Linux环境进程间通信(三)

    原文链接:http://www.ibm.com/developerworks/cn/linux/l-ipc/part3/index.html 原文内容: 消息队列(也叫做报文队列)能够克服早期unix ...

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

    https://www.ibm.com/developerworks/cn/linux/l-ipc/part3/ 继续坚持,或许不能深刻理解,但至少要保证有印象. ~~~~~~~~~~~~~~ 消息队 ...

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

    IBM上放的这个系统不错,刚好可以系统回温一下LINUX的系统知识. http://www.ibm.com/developerworks/cn/linux/l-ipc/part1/ 感觉年纪大了,前几 ...

  7. Linux环境进程间通信(四):信号灯

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  8. Linux环境进程间通信(三):消息队列

    linux下进程间通信的几种主要手段: 管道(Pipe)及有名管道(named pipe):管道可用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允 ...

  9. UNIX环境高级编程——进程间通信概念

    进程间通信 --- IPC1. 进程间通信的目的a. 数据传输: 一个进程需要将他的数据发送给另一个进程b. 资源共享: 多个进程之间共享同样的资源c. 通知事件: 一个进程需要向另一个或一组进程发送 ...

随机推荐

  1. Mac新建文件夹、txt文件、其他格式文件

    Mac新建txt,正好有人问我,我就把我自己的方法记录一下: 先cd到你指定的文件路径下: 新建文件夹: mkdir test 新建txt touch test.txt 新建无后缀格式文件 touch ...

  2. HDUOJ---Hamming Distance(4712)

    Hamming Distance Time Limit: 6000/3000 MS (Java/Others)    Memory Limit: 65535/65535 K (Java/Others) ...

  3. Python 多进程教程

    Python2.6版本中新添了multiprocessing模块.它最初由Jesse Noller和Richard Oudkerk定义在PEP 371中.就像你能通过threading模块衍生线程一样 ...

  4. 【LeetCode】52. N-Queens II

    N-Queens II Follow up for N-Queens problem. Now, instead outputting board configurations, return the ...

  5. 【LeetCode】150. Evaluate Reverse Polish Notation

    Evaluate Reverse Polish Notation Evaluate the value of an arithmetic expression in Reverse Polish No ...

  6. shell 基本学习

    1)查看当前shell echo $SHELL 2)查看兼容shell more /etc/shells 3) 脚本第一行 #!/bin/bash 4) 变量(变量名称的开头是一个字母或下划线符号,后 ...

  7. android 登陆界面

    LoginActivity.java package com.example.ruian; import android.app.Activity; import android.app.AlertD ...

  8. PLSQL_标准删除的方式Delete/Drop/Truncate区别和比较(概念)

    2014-06-02 Created By BaoXinjian

  9. BEGINNING SHAREPOINT&#174; 2013 DEVELOPMENT 第13章节--使用业务连接服务创建业务线解决方式 SP Apps中的BCS

    BEGINNING SHAREPOINT® 2013 DEVELOPMENT 第13章节--使用业务连接服务创建业务线解决方式  SP Apps中的BCS         之前的联系中,你安装了一个业 ...

  10. Android图片处理:识别图像方向并显示

    在Android中使用ImageView显示图片的时候发现图片显示不正.方向偏了或者倒过来了. 解决问题非常自然想到的分两步走: 1.自己主动识别图像方向,计算旋转角度. 2.对图像进行旋转并显示. ...