读书人

linux 进程间通信步骤综合分析

发布时间: 2013-10-12 11:54:02 作者: rapoo

linux 进程间通信方法综合分析

进程间通信,常用的方法有,pipe、popen(pclose)、命名pipe、Unix域套接字、消息队列、信号量、记录锁、共享内存。

这么多方法,到底不同方法应用场景是怎样的呢。

1、相关进程(父子进程)

pipe,一定是最简单的,由于子进程可以继承父进程的描述符,所以用2个pipe,就可以实现父子进程间的通信,例子后续给出。

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #include <string.h>
  4. #define BUF_SIZE 1024
  5. int main()
  6. {
  7. char * str = "Hello world";
  8. char buf[BUF_SIZE] = {0};
  9. int file_pipes[2] = {0};
  10. int pipe_ret = pipe(file_pipes);
  11. if (-1 == pipe_ret)
  12. {
  13. printf("pipe failed\n");
  14. return -1;
  15. }
  16. pid_t fork_ret = fork();
  17. if (-1 == fork_ret)
  18. {
  19. printf("fork failed\n");
  20. return -1;
  21. }
  22. if (0 == fork_ret)
  23. {
  24. close(file_pipes[1]);
  25. int ret_num = read(file_pipes[0], buf, BUF_SIZE);
  26. printf("(%d), read %d bytes: %s\n", getpid(), ret_num, buf);
  27. sleep(1);
  28. return 0;
  29. }
  30. else
  31. {
  32. close(file_pipes[0]);
  33. int ret_num = write(file_pipes[1], str, strlen(str));
  34. printf("(%d), wirte %d bytes: %s\n", getpid(), ret_num, str);
  35. sleep(1);
  36. return 0;
  37. }
  38. return 0;
  39. }
运行结果:
./pipe2
(21653), wirte 11 bytes: Hello world
(21654), read 11 bytes: Hello world

<总结>需要注意的一点就是fork子进程, 子进程会继承父进程pipe(0)、pipe(1),其只能用一个pipe,所以需要close掉另外一个pipe,通过本例子,可以构造出一个父子进程同步的方法,fork之前pipe两个管道,一个用于父给子发送同步消息,一个用于子给父发送同步消息。

2、进程调用其它进程

输入参数、获取相应输出,用popen(pclose)最划算啦,例如程序用执行ls -l命令,要获取其输出,popen完全ok。下面给出一个简单的例子:

[cpp] view plaincopy
  1. #include <stdio.h>
  2. #include <stdlib.h>
  3. #define BUF_SIZE 1000
  4. int main()
  5. {
  6. FILE * read_fp = NULL;
  7. char buffer[BUF_SIZE] = {0};
  8. read_fp = popen("ls -l", "r");
  9. if (NULL == read_fp)
  10. {
  11. return -1;
  12. }
  13. fread(buffer, 1, BUF_SIZE, read_fp);
  14. printf("out put is \n %s \n", buffer);
  15. pclose(read_fp);
  16. return 0;
  17. }


3、不相干进程

a、如果仅仅用于传递消息,信息量少,Unix域套接字、消息队列都ok,当然是用起来,前者更方便一些,其是用方法和一般的socket方法基本一样;

UNIX域:可以参考我写的另外一个帖子http://blog.csdn.net/beginning1126/article/details/8895738,当中有说明和例子,这里就不赘述了。


b、如果两个进程之前需要共享一大段内存数据,这时再采用Unix域套接字、消息队列就太浪费了,因为两者再传递内存时,都少不了内存的拷贝,共享内存首选,当然用于共享内存的同步,信号量、记录锁都可以,例子后续给出

信号量:可以参考我写的另外一个帖子http://blog.csdn.net/beginning1126/article/details/12520253,当中有说明和例子,这里就不赘述了。


下面给出一个通过信号量做同步,然后用共享内存共享数据的例子:

例子代码比较简陋,功能也比较单一,仅仅用于说明如何通过信号量来达到共享内存的互斥。

进程1:

[cpp] view plaincopy
  1. #include <memory.h>
  2. #include <stdio.h>
  3. #include <unistd.h> //getpagesize( )
  4. #include <sys/ipc.h>
  5. #include <sys/shm.h>
  6. #include <sys/sem.h>
  7. #define MY_SHM_ID 1234
  8. #define MY_SEM_ID 4321
  9. #define MEM_SIZE 300
  10. union semun
  11. {
  12. int val;
  13. struct semid_ds * buf;
  14. unsigned short * array;
  15. };
  16. int semaphore_p(int sem_id) {
  17. struct sembuf sem_b;
  18. sem_b.sem_num = 0;
  19. sem_b.sem_op = -1;
  20. sem_b.sem_flg = SEM_UNDO;
  21. semop(sem_id, &sem_b, 1);
  22. return 0;
  23. }
  24. int semaphore_v(int sem_id) {
  25. struct sembuf sem_b;
  26. sem_b.sem_num = 0;
  27. sem_b.sem_op = 1;
  28. sem_b.sem_flg = SEM_UNDO;
  29. semop(sem_id, &sem_b, 1);
  30. return 0;
  31. }
  32. int set_semvalue(int sem_id) {
  33. union semun sem_union;
  34. sem_union.val = 1;
  35. if (-1 == semctl(sem_id, 0, SETVAL, sem_union))
  36. {
  37. return -1;
  38. }
  39. return 0;
  40. }
  41. int del_semvalue(int sem_id) {
  42. union semun sem_union;
  43. if (-1 == semctl(sem_id, 0, IPC_RMID, sem_union))
  44. {
  45. return -1;
  46. }
  47. return 0;
  48. }
  49. int main() {
  50. printf("page size: %d. \n", getpagesize());
  51. // create share memory
  52. int shmid = 0;
  53. shmid = shmget(MY_SHM_ID, MEM_SIZE, 0666 | IPC_CREAT);
  54. if (-1 == shmid) {
  55. printf("create share memroy failed. \n");
  56. return -1;
  57. }
  58. printf("create a shared memory success, shmid: %d. \n", shmid);
  59. // create sem id
  60. int sem_id = semget((key_t)MY_SEM_ID, 1, 0666 | IPC_CREAT);
  61. set_semvalue(sem_id);
  62. printf("get a sem id: %d\n", sem_id);
  63. // attach share memory
  64. unsigned char * mem = NULL;
  65. mem = shmat(shmid, (const void*)0, 0);
  66. if (-1 == (size_t)mem) {
  67. printf("failed in shmat. \n");
  68. return -1;
  69. }
  70. memset(mem, 0, MEM_SIZE);
  71. printf("attach shm: %p\n", mem);
  72. sleep(5);
  73. // get share memory status
  74. struct shmid_ds shmds;
  75. memset(&shmds, 0, sizeof(struct shmid_ds));
  76. int ret = 0;
  77. ret = shmctl(shmid, IPC_STAT, &shmds);
  78. if (-1 == ret) {
  79. printf("failed in shmctl. \n");
  80. return -1;
  81. }
  82. printf("size of memory segment is %d. \n", shmds.shm_segsz);
  83. printf("numbre of attaches %d. \n", (int)shmds.shm_nattch);
  84. while(1) {
  85. semaphore_p(sem_id);
  86. unsigned char * p = mem;
  87. int len = p[0];
  88. p[len+1] = 'w';
  89. p[0] += 1;
  90. semaphore_v(sem_id);
  91. sleep(1);
  92. if (len >= 254) {
  93. break;
  94. }
  95. }
  96. mem[0] = 'b';
  97. printf("result is %s\n", mem);
  98. del_semvalue(sem_id);
  99. // detach share memory
  100. ret = shmdt(mem);
  101. if(-1 == ret) {
  102. printf("failed in shmdt");
  103. return -1;
  104. }
  105. // delete share memory
  106. ret = shmctl(shmid, IPC_RMID, 0);
  107. if(-1 == ret) {
  108. printf("failded in delete shm. \n");
  109. return -1;
  110. }
  111. return 0;
  112. }

进程2:

[cpp] view plaincopy
  1. #include <memory.h>
  2. #include <stdio.h>
  3. #include <unistd.h> //getpagesize( )
  4. #include <sys/ipc.h>
  5. #include <sys/shm.h>
  6. #include <sys/sem.h>
  7. #define MY_SHM_ID 1234
  8. #define MY_SEM_ID 4321
  9. #define MEM_SIZE 300
  10. union semun
  11. {
  12. int val;
  13. struct semid_ds * buf;
  14. unsigned short * array;
  15. };
  16. int semaphore_p(int sem_id) {
  17. struct sembuf sem_b;
  18. sem_b.sem_num = 0;
  19. sem_b.sem_op = -1;
  20. sem_b.sem_flg = SEM_UNDO;
  21. semop(sem_id, &sem_b, 1);
  22. return 0;
  23. }
  24. int semaphore_v(int sem_id) {
  25. struct sembuf sem_b;
  26. sem_b.sem_num = 0;
  27. sem_b.sem_op = 1;
  28. sem_b.sem_flg = SEM_UNDO;
  29. semop(sem_id, &sem_b, 1);
  30. return 0;
  31. }
  32. int set_semvalue(int sem_id) {
  33. union semun sem_union;
  34. sem_union.val = 1;
  35. if (-1 == semctl(sem_id, 0, SETVAL, sem_union))
  36. {
  37. return -1;
  38. }
  39. return 0;
  40. }
  41. int del_semvalue(int sem_id) {
  42. union semun sem_union;
  43. if (-1 == semctl(sem_id, 0, IPC_RMID, sem_union))
  44. {
  45. return -1;
  46. }
  47. return 0;
  48. }
  49. int main() {
  50. printf("page size: %d. \n", getpagesize());
  51. // create share memory
  52. int shmid = 0;
  53. shmid = shmget(MY_SHM_ID, 0, 0);
  54. if (-1 == shmid) {
  55. printf("get share memroy failed. \n");
  56. return -1;
  57. }
  58. printf("get a shared memory success, shmid: %d. \n", shmid);
  59. // create sem id
  60. int sem_id = semget((key_t)MY_SEM_ID, 1, 0666 | IPC_CREAT);
  61. printf("get a sem id: %d\n", sem_id);
  62. // attach share memory
  63. unsigned char * mem = NULL;
  64. mem = shmat(shmid, (const void*)0, 0);
  65. if (-1 == (size_t)mem) {
  66. printf("failed in shmat. \n");
  67. return -1;
  68. }
  69. printf("attach shm: %p\n", mem);
  70. // get share memory status
  71. struct shmid_ds shmds;
  72. memset(&shmds, 0, sizeof(struct shmid_ds));
  73. int ret = 0;
  74. ret = shmctl(shmid, IPC_STAT, &shmds);
  75. if (-1 == ret) {
  76. printf("failed in shmctl. \n");
  77. return -1;
  78. }
  79. printf("size of memory segment is %d. \n", shmds.shm_segsz);
  80. printf("numbre of attaches %d. \n", (int)shmds.shm_nattch);
  81. while(1) {
  82. semaphore_p(sem_id);
  83. unsigned char * p = mem;
  84. int len = p[0];
  85. p[len+1] = 'r';
  86. p[0] += 1;
  87. semaphore_v(sem_id);
  88. sleep(1);
  89. if (len >= 254) {
  90. break;
  91. }
  92. }
  93. mem[0] = 'b';
  94. printf("result is %s\n", mem);
  95. // detach share memory
  96. ret = shmdt(mem);
  97. if(-1 == ret) {
  98. printf("failed in shmdt");
  99. return -1;
  100. }
  101. return 0;
  102. }

运行结果:

./shm1&

./shm2
page size: 4096.
get a shared memory success, shmid: 1605632.
get a sem id: 1966082
attach shm: 0x2aca081e4000
size of memory segment is 300.
numbre of attaches 2.
result is bwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwrwr

<总结1>:通过运行结果可以看出,w、r,个数相等,同时交替出现,信号量很好的完成对共享内存互斥的工作。当然例子代码很简陋,关于信号量的部分代码有重复,完全可以单独抽出来进行重构,是在懒得改了,看官见谅下吧。

<总结2>:虽然说信号量能够完成对共享内存的互斥,但是这还远远不能满足我们对共享内存同步的要求,比如说,一个写进程,一个读进程,写进程完成对共享内存的写操作之后需要通知读进程来读取内存,当读进程完成读取操作之后,需要通知写进程继续写内容,这种同步操作,如果通过信号量貌似费劲了一点,可以通过UNIX域socket来实现共享内存的同步操作。


c、命名管道,貌似也可以,但是没怎么用过。


<注>很多书中提到STREAMS,由于其不是linux系统默认安装,需要添加附加包,本文章暂不讨论

读书人网 >UNIXLINUX

热点推荐