[转自: http://blog.csdn.net/Paradise_for_why/article/details/5550619]

这一章就是著名的IPC,这个东西实际的作用和它的名字一样普及。例如我们浏览网页,打印文章,等等。

IPC总共有五种类型:

  1. 共享内存(Shared Memory):最容易理解的一种,就像一个特工把情报放在特定地点(内存),另一个特工再过来取走一样。

  2. 内存映射(Mapped Memory):和共享内存几乎相同,除了特工们把地点从内存改成了文件系统。

  3. 管道(Pipes):从一个进程到另一个进程的有序通信,用电话来比喻再恰当不过了。

  4. FIFOs:和管道和类似,唯一的区别是FIFOs比管道更神通一些,允许没有关系的进程之间的有序通信。

  5. 套接字(Sockets):为什么说浏览网页也是IPC?就是因为它。

5.1 共享内存(Shared Memory)

  • 共享内存是最快捷的进程间通信方式。访问共享内存的效率和访问进程自己的非共享内存的效率是相同的,而且这种通信方式不需要任何额外的系统调用。
  • 系统不会自动为共享内存处理同步问题,这个问题必须由用户自己解决。
  • 共享内存的步骤通常是:
    • 一个进程申请一块共享内存,即在它的页表中加入新的一项
    • 所有进程Attach该共享内存,即从申请内存的进程中拷贝对应的页表
    • 使用该内存进行通讯
    • 结束后所有进程detach该共享内存
    • 申请共享内存的进程在确定所有进程都detach后,释放该内存
  • 由于共享内存是通过页表来实现的,我们可以得出一个结论:共享内存的大小是页面大小的整数倍,页面的大小可以通过getpagesize()来得到,通常在Linux下该值是4KB
  • 相关的API函数:
    • 申请共享内存:shmget,返回共享内存segment的id
    • Attach,Detach函数:shmat,shmdt。需要共享内存segment的id
    • 释放申请的内存:shmctl。一定要记得释放!调用exit和exec会自动detach,但不会自动释放。
  • 使用 ipcs -m来观看当前系统存在的共享内存

  例子:原程序链接,依据这个例子进行简单修改一下

  1. /*
  2. * =============================================================================
  3. *
  4. * Filename: sharememory_read.c
  5. *
  6. * Description:
  7. *
  8. * Version: 1.0
  9. * Created: 2014年11月04日 19时52分28秒
  10. * Revision: none
  11. * Compiler: gcc
  12. *
  13. * Author: lwq (28120), scue@vip.qq.com
  14. * Organization:
  15. *
  16. * =============================================================================
  17. */
  18. #include <stdlib.h>
  19.  
  20. /**********************************************************
  21. *实验要求: 创建两个进程,通过共享内存进行通讯。
  22. *功能描述: 本程序申请和分配共享内存,然后轮训并读取共享中的数据,直至
  23. * 读到“end”。
  24. *日 期: 2010-9-17
  25. *作 者: 国嵌
  26. **********************************************************/
  27. #include <unistd.h>
  28. #include <stdlib.h>
  29. #include <stdio.h>
  30. #include <string.h>
  31. #include <sys/types.h>
  32. #include <sys/ipc.h>
  33. #include <sys/shm.h>
  34. #include <getopt.h>
  35. #include "sharememory.h"
  36.  
  37. void read_shm(struct shared_use_st *shared_stuff);
  38. void write_shm(struct shared_use_st *shared_stuff);
  39. void del_shm();
  40.  
  41. void usage(){
  42. fprintf(stderr, "\nusage: %s -r|-w\n\n"
  43. "-r read mode\n"
  44. "-w write mode\n"
  45. "\n", "shared_memory");
  46. exit();
  47. }
  48.  
  49. #define READ (1)
  50. #define WRITE (2)
  51. #define OPTNONE (0)
  52.  
  53. // 全局变量
  54. void *shared_memory=(void *);
  55.  
  56. /*
  57. * 程序入口
  58. * */
  59. int main(int argc, char **argv)
  60. {
  61. int running=RUNNING;
  62. struct shared_use_st *shared_stuff;
  63. int shmid;
  64. int operation=OPTNONE; /* 读/写操作 */
  65.  
  66. /*-----------------------------------------------------------------------------
  67. * getopt start
  68. *----------------------------------------------------------------------------*/
  69. int choice;
  70. while ()
  71. {
  72. static struct option long_options[] =
  73. {
  74. /* Use flags like so:
  75. {"verbose", no_argument, &verbose_flag, 'V'}*/
  76. /* Argument styles: no_argument, required_argument, optional_argument */
  77. {"version", no_argument, , 'v'},
  78. {"help", no_argument, , 'h'},
  79. {"read", no_argument, , 'r'},
  80. {"write", no_argument, , 'w'},
  81. {,,,}
  82. };
  83.  
  84. int option_index = ;
  85.  
  86. /* Argument parameters:
  87. no_argument: " "
  88. required_argument: ":"
  89. optional_argument: "::" */
  90.  
  91. choice = getopt_long( argc, argv, "vhrw",
  92. long_options, &option_index);
  93.  
  94. if (choice == -)
  95. break;
  96.  
  97. switch( choice )
  98. {
  99. case 'v':
  100.  
  101. break;
  102.  
  103. case 'h':
  104. usage();
  105. break;
  106.  
  107. case 'r':
  108. operation=READ;
  109. break;
  110.  
  111. case 'w':
  112. operation=WRITE;
  113. break;
  114.  
  115. case '?':
  116. /* getopt_long will have already printed an error */
  117. usage();
  118. break;
  119.  
  120. default:
  121. /* Not sure how to get here... */
  122. return EXIT_FAILURE;
  123. }
  124. }
  125. if (operation == OPTNONE) {
  126. usage();
  127. }
  128. /*-----------------------------------------------------------------------------
  129. * getopt end
  130. *----------------------------------------------------------------------------*/
  131.  
  132. /*创建共享内存*/
  133. shmid=shmget((key_t),sizeof(struct shared_use_st),|IPC_CREAT);
  134. if(shmid==-) {
  135. fprintf(stderr,"shmget failed\n");
  136. exit(EXIT_FAILURE);
  137. }
  138.  
  139. /*映射共享内存*/
  140. shared_memory=shmat(shmid,(void *),);
  141. if(shared_memory==(void *)-) {
  142. fprintf(stderr,"shmat failed\n");
  143. exit(EXIT_FAILURE);
  144. }
  145.  
  146. printf("Memory attached at 0%08x\n",(int)((intptr_t)shared_memory));
  147.  
  148. /*让结构体指针指向这块共享内存*/
  149. shared_stuff=(struct shared_use_st *)shared_memory;
  150.  
  151. /*控制读写顺序*/
  152. // lwq: 使之能读取上一条消息
  153. if (operation == READ && shared_stuff->written_by_you != HADWROTE)
  154. shared_stuff->written_by_you=HADREAD;
  155.  
  156. switch(operation) {
  157. case READ:
  158. read_shm(shared_stuff);
  159. break;
  160.  
  161. case WRITE:
  162. write_shm(shared_stuff);
  163. break;
  164.  
  165. default:
  166. usage();
  167. break;
  168. }
  169.  
  170. del_shm();
  171. exit(EXIT_SUCCESS);
  172. }
  173.  
  174. // 读取共享内存
  175. void read_shm(struct shared_use_st *shared_stuff){
  176. while() {
  177. if(shared_stuff->written_by_you == HADWROTE) {
  178. printf("You wrote:%s",shared_stuff->some_text);
  179. shared_stuff->written_by_you=HADREAD;
  180. if(strncmp(shared_stuff->some_text,"end",)==) {
  181. break;
  182. }
  183. }
  184. else {
  185. usleep();
  186. }
  187. }
  188. }
  189.  
  190. // 写入共享内存
  191. void write_shm(struct shared_use_st *shared_stuff){
  192. char buffer[BUFSIZ] = {};
  193. while() {
  194. while(shared_stuff->written_by_you!=HADREAD); /* 等待读写完成 */
  195. printf("Enter some text:");
  196. fgets(buffer,BUFSIZ,stdin);
  197. strncpy(shared_stuff->some_text,buffer,TEXT_SZ); /* 复制进去 */
  198. shared_stuff->written_by_you=HADWROTE;
  199. if(strncmp(buffer,"end",)==) {
  200. break;
  201. }
  202. }
  203. }
  204.  
  205. // 删除共享内存
  206. void del_shm(){
  207. /*删除共享内存*/
  208. if(shmdt(shared_memory)==-) {
  209. fprintf(stderr,"\nshmdt failed\n");
  210. exit(EXIT_FAILURE);
  211. }
  212. else {
  213. fprintf(stderr, "\ndelete shared_memory: 0x%08x\n", (int)((intptr_t)shared_memory));
  214. }
  215. }

sharememory.c

  编译:gcc sharememory.c -o sharememory

  执行:

    1. 以读取模式打开程序(进程1): ./sharememory -r

    2. 以写入模式打开程序(进程2): ./sharememory -w

  更新介绍:http://www.cs.cf.ac.uk/Dave/C/node27.html

5.2 进程信号量

  • 信号量(Semaphore)的概念前面已经介绍过了。Linux对用来同步进程的信号量采取了一种特别的实现方式。这些信号量也就被称为进程信号量(Process Semaphore)。(这一节下面所提到的所有信号量默认都是指进程信号量)
  • 相关的API函数:
    • 申请:semget
    • 释放:semctl。需要注意的是信号量不会被自动释放,我们必须显式释放它。
    • Wait和Post:semop
  • 使用ipcs -s来观看当前系统存在的信号量

  例子:原程序链接

  1. /*
  2. * =============================================================================
  3. *
  4. * Filename: semaphore_simple.c
  5. *
  6. * Description:
  7. *
  8. * Version: 1.0
  9. * Created: 2014年11月04日 21时21分25秒
  10. * Revision: none
  11. * Compiler: gcc
  12. *
  13. * Author: lwq (28120), scue@vip.qq.com
  14. * Organization:
  15. *
  16. * =============================================================================
  17. */
  18. #include <stdlib.h>
  19.  
  20. #include <unistd.h>
  21. #include <sys/types.h>
  22. #include <sys/stat.h>
  23. #include <fcntl.h>
  24. #include <stdlib.h>
  25. #include <stdio.h>
  26. #include <string.h>
  27. #include <sys/sem.h>
  28.  
  29. union semun
  30. {
  31. int val;
  32. struct semid_ds *buf;
  33. unsigned short *arry;
  34. };
  35.  
  36. static int sem_id = ;
  37.  
  38. static int set_semvalue();
  39. static void del_semvalue();
  40. static int semaphore_p();
  41. static int semaphore_v();
  42.  
  43. int main(int argc, char *argv[])
  44. {
  45. char msg[] = {};
  46. int i = ;
  47. int mypid=getpid();
  48.  
  49. //创建信号量
  50. sem_id = semget((key_t), , | IPC_CREAT);
  51.  
  52. if(argc > )
  53. {
  54. //程序第一次被调用,初始化信号量
  55. if(!set_semvalue()) {
  56. fprintf(stderr, "Failed to initialize semaphore\n");
  57. exit(EXIT_FAILURE);
  58. }
  59. }
  60. for(i = ; i < ; ++i) {
  61. //进入临界区
  62. if(!semaphore_p())
  63. exit(EXIT_FAILURE);
  64. //向屏幕中输出数据
  65. snprintf(msg, sizeof(msg)-, "%s, index: %03d, pid: %05d", argv[], i, mypid);
  66. printf("%s\n", msg);
  67. fflush(stdout);
  68. usleep();
  69. //离开临界区,休眠随机时间后继续循环
  70. if(!semaphore_v())
  71. exit(EXIT_FAILURE);
  72. }
  73.  
  74. if(argc > )
  75. {
  76. //如果程序是第一次被调用,则在退出前删除信号量
  77. sleep();
  78. del_semvalue();
  79. }
  80. exit(EXIT_SUCCESS);
  81. }
  82.  
  83. static int set_semvalue()
  84. {
  85. //用于初始化信号量,在使用信号量前必须这样做
  86. union semun sem_union;
  87.  
  88. sem_union.val = ;
  89. if(semctl(sem_id, , SETVAL, sem_union) == -)
  90. return ;
  91. return ;
  92. }
  93.  
  94. static void del_semvalue()
  95. {
  96. //删除信号量
  97. union semun sem_union;
  98.  
  99. if(semctl(sem_id, , IPC_RMID, sem_union) == -)
  100. fprintf(stderr, "Failed to delete semaphore\n");
  101. }
  102.  
  103. static int semaphore_p()
  104. {
  105. //对信号量做减1操作,即等待P(sv)
  106. struct sembuf sem_b;
  107. sem_b.sem_num = ;
  108. sem_b.sem_op = -;//P()
  109. sem_b.sem_flg = SEM_UNDO;
  110. if(semop(sem_id, &sem_b, ) == -)
  111. {
  112. fprintf(stderr, "semaphore_p failed\n");
  113. return ;
  114. }
  115. return ;
  116. }
  117.  
  118. static int semaphore_v()
  119. {
  120. //这是一个释放操作,它使信号量变为可用,即发送信号V(sv)
  121. struct sembuf sem_b;
  122. sem_b.sem_num = ;
  123. sem_b.sem_op = ;//V()
  124. sem_b.sem_flg = SEM_UNDO;
  125. if(semop(sem_id, &sem_b, ) == -)
  126. {
  127. fprintf(stderr, "semaphore_v failed\n");
  128. return ;
  129. }
  130. return ;
  131. }

semaphore_simple.c

  编译:gcc semaphore_simple.c -o semaphore_simple

  执行:./semaphore_simple XXX &; ./semaphore_simple YYY

5.3 内存映射

  • 内存映射使得不同的进程可以通过一个共享文件来互相通信。
  • 相关的API函数:
    • 映射:mmap
    • 同步:msync。用来指定对文件的修改是否被buffer。
    • 释放:munmap。在程序结束的时候会自动unmap
  • mmap的其他用法:
    • 可以替代read和write,有时使用内存映射后的效率比单纯使用I/O操作来的更快
    • 在内存映射文件中构建structure,修改structure再次将文件映射到内存中可以快速的将structure恢复到原来的状态
    • 把/dev/zero文件映射到内存中。该文件可以提供无限的0,并且写到该文件的所有内容将被直接丢弃

5.4 管道(Pipes)

  • 管道是单向的,即一个线程写,另一个线程读,无法互换
  • 如果写的速度太快,造成管道满了,那么写的线程就会被block;如果读的速度太快,造成管道空了,那么读的进程就会被block。因此事实上我们可以说管道自动实现了同步机制
  • 我们可以通过调用pipe函数来生成一对pipe file description。(为什么是一对?因为一个读一个写)。可是,生成的pipe file description无法传送给不相关的进程(因为做为file descriptor即使它拿到了也没法用)。但是我们注意到fork之后父进程所有的file descriptor在子进程中依然有效,因此管道最大的作用是在父子进程之间通信。或者更确切的说,是在有共同祖先的进程之间通信。
  • 典型的创建管道的流程如下:
    • 用pipe生成2个pipe file description(简称fds)。然后调用fork
    • 在父进程关闭fds[0](或fds[1]),并以只读(或只写)方式打开fds[1](或fds[0])。在子进程中关闭fds[1](或fds[0]),并以只写(或只读)方式打开fds[0](或fds[1])。打开的函数是fdopen。
    • 开始通信。结束后用close函数关闭剩下的fds。
  • 这里有一个技巧:可以利用管道来达成重定向stdin, stdout和stderr。注意到dup2这个API可以把一个file descriptor复制到另一个上。
  • 事实上,我们有一对更为简洁的函数popen/pclose来完成上面的一系列复杂的操作。popen有两个参数:
    • 第一个参数接受一个exec,子进程将执行这个exec
    • 第二个参数为”w”或者”r”,”w”表示父进程写子进程读,”r”则反之
    • 返回值为管道的一端,也就是一个file descriptor
    • pclose用来关闭popen返回的file descriptor
  • FIFO(First In First Out)文件事实上是一个有名字的管道,换句话说,他可以用来让“不相干”的程序互相通信。
    • 我们使用mkfifo函数来创建一个FIFO文件
    • 我们可以使用任何的低级I/O函数(open, write, read, close等)以及C库I/O函数(fopen, fprintf, fscanf, fclose等)来操作FIFO文件。
  • Linux的管道和Windows下的命名管道(Named Pipes)的区别
    • Windows的命名管道更像一个套接字(sockets),它可以通过网络让不同主机上的程序进行通信
    • Linux的管道允许有多个reader和writer,每个reader和writer进行读/写的最大容量为 PIPE_BUF(4KB),如果有多个writer同时写,他们写的东西会被分为一个一个的chunk(每个4KB)并允许交错写。(例如进程A有两个 chunk,A1,A2。进程B也有两个chunk,B1,B2。A和B同时写,则顺序可能为A1,B1,A2,B2) Windows的管道允许在同一个管道上有多个reader/writer对,他们之间读写的数据没有交叉。

5.5 套接字(Sockets)

  • 套接字的特点:

    • 它是双向通信的
    • 它是进程间通信的,包括其他机器上的进程
  • 套接字有三个参数:
    • 通讯类型(communication style)

      • 连接(connection)类型:保证所有的包按发送的顺序到达接受方。(类似于电话)如果包丢失或者抵达顺序错误,会自动重发。
      • datagram类型:所有包单独发送,可能会出现丢失或者晚发早到的现象。(类似于邮寄)
    • 命名空间(namespace):描述套接字的地址是如何表示的,例如本地就是文件名,internet上就是ip地址。
    • 协议(protocol):通讯协议,常用的有TCI/IP,AppleTalk等。
  • 相关的API(套接字也是通过file descriptor来表示的):
    • socket:创建一个socket
    • closes:销毁一个socket
    • connect:在两个socket之间创建一个连接。这个API通常由客户端调用。
    • bind:给服务器的一个套接字绑定一个地址,服务器端调用。
    • listen:让一个套接字开始侦听,准备接受请求,服务器端调用。
    • accept:接受一个连接请求,并且为该连接创建一个新的套接字,服务器端调用。
  • 服务器端的生命流程:
    • 创建一个connection类型的socket
    • 给该socket绑定一个地址
    • 调用listen来enable该socket(listen可以指定最多有多少个请求在等待队列中,如果等待队列满了,又有新的请求到达的时候,则该请求被拒绝)
    • 对于收到的连接请求调用accept来接受
    • 关闭socket
  • 本地socket(local socket)
    • 如果是同一台电脑上的两个进程需要通信的话,可以使用本地socket。这种情况下socket的地址是文件路径。注意进程必须对该路径拥有可写权限,否则无法建立连接
    • 完成之后使用unlink来关闭一个socket

〖Linux〗Linux高级编程 - 进程间通信(Interprocess Communication)的更多相关文章

  1. Linux环境高级编程--介绍

    从今天開始.将开启Linux环境高级编程(Advanced Programming Of Linux Enviroment)的学习笔记或者说总结,我将持续和大家分享自己的学习成果.本系列博客依托于li ...

  2. Linux C高级编程——文件操作之系统调用

    Linux C高级编程文件操作之系统调用 宗旨:技术的学习是有限的,分享的精神是无限的.           库函数是一些完毕特定功能的函数.一般由某个标准组织制作公布,并形成一定的标准.使用库函数编 ...

  3. linux 之进程间通信-------------InterProcess Communication

    进程间通信至少可以通过传送打开文件来实现,不同的进程通过一个或多个文件来传递信息,事实上,在很多应用系统里,都使用了这种方法.但一般说来,进程间 通信(IPC:InterProcess Communi ...

  4. Linux C高级编程——网络编程基础(1)

    Linux高级编程--BSD socket的网络编程 宗旨:技术的学习是有限的,分享的精神是无限的. 一网络通信基础 TCP/IP协议簇基础:之所以称TCP/IP是一个协议簇,是由于TCP/IP包括T ...

  5. Linux C高级编程——网络编程之以太网(2)

    Linux网络编程--以太网 宗旨:技术的学习是有限的,分享的精神是无限的. 1.以太网帧格式 源地址和目的地址是指网卡的硬件地址(也叫MAC地址),长度是48位,是在网卡出厂时固化的.用ifconf ...

  6. Linux Shell 高级编程技巧3----运行级别脚本介绍

    3.运行级别脚本介绍    3.1.运行级别        运行级别介绍:            0    关机            1    单用户模式            2    多用户模式 ...

  7. Linux Shell 高级编程技巧4----几个常用的shell脚本例子

    4.几个常用的shell脚本例子    4.0.在写脚本(同样适用在编程的时候),最好写好完善的注释    4.1.kill_processes.sh(一个杀死进程的脚本) #!/bin/bash c ...

  8. linux下socket编程-进程间通信

    一.什么是Socket Socket接口是TCP/IP网络通信的API,Socket接口定义了许多函数或例程,可以用它们来开发TCP/IP网络上的应用程序. Socket类型有两种:流式Socket ...

  9. Linux Shell 高级编程技巧2----shell工具

    2.shell工具    2.1.日志文件        简介            创建日志文件是很重要的,记录了重要的信息.一旦出现错误,这些信息对于我们排错是非常有用的:监控的信息也可以记录到日 ...

随机推荐

  1. 《Software Design中文版01》

    <Software Design中文版01> 基本信息 作者: (日)技术评论社 译者: 苏祎 出版社:人民邮电出版社 ISBN:9787115347053 上架时间:2014-3-18 ...

  2. [Hook] 免root,自己进程内,startActivity hook的几种姿势

    首先关于startActivity 我们平时会经常使用到 在activity内 直接startActivity(intent) 其实这里还有一种start方式 我们可能没怎么用过 getApplica ...

  3. Invalid Host header 的解决方案

    composer 显示:Invalid Host header的解决方案 I have tried this workaround: Edit the following line in node_m ...

  4. Hype cycle(Gartner 成熟度曲线)

        Hype cycle The hype cycle is a branded graphical presentation developed and used by the American ...

  5. ARCH模型

    ARCH模型的基本思想 ARCH模型的基本思想是指在以前信息集下,某一时刻一个噪声的发生是服从正态分布.该正态分布的均值为零,方差是一个随时间变化的量(即为条件异方差).并且这个随时间变化的方差是过去 ...

  6. [转]分析MySQL数据类型的长度【mysql数据字段 中length和decimals的作用!熟悉mysql必看】

    转载自:http://blog.csdn.net/daydreamingboy/article/details/6310907 分析MySQL数据类型的长度 MySQL有几种数据类型可以限制类型的&q ...

  7. RV32A指令集

    RV32A指令包括两类:AMO(atomic memory operation)指令,Load-Reserved/Store-Conditional指令 Category Fmt RV32I base ...

  8. matlib实现logistic回归算法(序一)

    数据下载:http://archive.ics.uci.edu/ml/datasets/Adult 数据描述:http://archive.ics.uci.edu/ml/machine-learnin ...

  9. [leetcode]Trapping Rain Water @ Python

    原题地址:https://oj.leetcode.com/problems/trapping-rain-water/ 题意: Given n non-negative integers represe ...

  10. ML、DL相关资源

    1. http://x-algo.cn/index.php/category/nlp/