linux 进程间通信方法综合分析进程间通信,常用的方法有,pipe、popen(pclose)、命名pipe、Unix域套接字、消息队
linux 进程间通信方法综合分析
进程间通信,常用的方法有,pipe、popen(pclose)、命名pipe、Unix域套接字、消息队列、信号量、记录锁、共享内存。
这么多方法,到底不同方法应用场景是怎样的呢。
1、相关进程(父子进程)
pipe,一定是最简单的,由于子进程可以继承父进程的描述符,所以用2个pipe,就可以实现父子进程间的通信,例子后续给出。
[cpp] view plaincopy
- #include <stdio.h>
- #include <stdlib.h>
- #include <string.h>
-
- #define BUF_SIZE 1024
-
- int main()
- {
- char * str = "Hello world";
- char buf[BUF_SIZE] = {0};
-
- int file_pipes[2] = {0};
-
- int pipe_ret = pipe(file_pipes);
- if (-1 == pipe_ret)
- {
- printf("pipe failed\n");
- return -1;
- }
-
- pid_t fork_ret = fork();
- if (-1 == fork_ret)
- {
- printf("fork failed\n");
- return -1;
- }
-
- if (0 == fork_ret)
- {
- close(file_pipes[1]);
- int ret_num = read(file_pipes[0], buf, BUF_SIZE);
- printf("(%d), read %d bytes: %s\n", getpid(), ret_num, buf);
- sleep(1);
- return 0;
- }
- else
- {
- close(file_pipes[0]);
- int ret_num = write(file_pipes[1], str, strlen(str));
- printf("(%d), wirte %d bytes: %s\n", getpid(), ret_num, str);
- sleep(1);
- return 0;
- }
- return 0;
-
- }
运行结果:
./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
- #include <stdio.h>
- #include <stdlib.h>
-
-
- #define BUF_SIZE 1000
-
- int main()
- {
- FILE * read_fp = NULL;
- char buffer[BUF_SIZE] = {0};
-
- read_fp = popen("ls -l", "r");
- if (NULL == read_fp)
- {
- return -1;
- }
-
- fread(buffer, 1, BUF_SIZE, read_fp);
- printf("out put is \n %s \n", buffer);
-
- pclose(read_fp);
-
- return 0;
-
- }
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
- #include <memory.h>
- #include <stdio.h>
- #include <unistd.h> //getpagesize( )
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
-
-
-
- #define MY_SHM_ID 1234
- #define MY_SEM_ID 4321
- #define MEM_SIZE 300
-
- union semun
- {
- int val;
- struct semid_ds * buf;
- unsigned short * array;
- };
-
- int semaphore_p(int sem_id) {
- struct sembuf sem_b;
- sem_b.sem_num = 0;
- sem_b.sem_op = -1;
- sem_b.sem_flg = SEM_UNDO;
-
- semop(sem_id, &sem_b, 1);
-
- return 0;
- }
-
- int semaphore_v(int sem_id) {
- struct sembuf sem_b;
- sem_b.sem_num = 0;
- sem_b.sem_op = 1;
- sem_b.sem_flg = SEM_UNDO;
-
- semop(sem_id, &sem_b, 1);
-
- return 0;
- }
-
-
- int set_semvalue(int sem_id) {
- union semun sem_union;
- sem_union.val = 1;
-
- if (-1 == semctl(sem_id, 0, SETVAL, sem_union))
- {
- return -1;
- }
- return 0;
- }
-
- int del_semvalue(int sem_id) {
- union semun sem_union;
-
- if (-1 == semctl(sem_id, 0, IPC_RMID, sem_union))
- {
- return -1;
- }
-
- return 0;
- }
-
-
-
- int main() {
- printf("page size: %d. \n", getpagesize());
-
- // create share memory
- int shmid = 0;
- shmid = shmget(MY_SHM_ID, MEM_SIZE, 0666 | IPC_CREAT);
- if (-1 == shmid) {
- printf("create share memroy failed. \n");
- return -1;
- }
- printf("create a shared memory success, shmid: %d. \n", shmid);
-
- // create sem id
- int sem_id = semget((key_t)MY_SEM_ID, 1, 0666 | IPC_CREAT);
- set_semvalue(sem_id);
- printf("get a sem id: %d\n", sem_id);
-
- // attach share memory
- unsigned char * mem = NULL;
- mem = shmat(shmid, (const void*)0, 0);
- if (-1 == (size_t)mem) {
- printf("failed in shmat. \n");
- return -1;
- }
- memset(mem, 0, MEM_SIZE);
- printf("attach shm: %p\n", mem);
-
- sleep(5);
-
- // get share memory status
- struct shmid_ds shmds;
- memset(&shmds, 0, sizeof(struct shmid_ds));
- int ret = 0;
- ret = shmctl(shmid, IPC_STAT, &shmds);
- if (-1 == ret) {
- printf("failed in shmctl. \n");
- return -1;
- }
- printf("size of memory segment is %d. \n", shmds.shm_segsz);
- printf("numbre of attaches %d. \n", (int)shmds.shm_nattch);
-
-
- while(1) {
- semaphore_p(sem_id);
- unsigned char * p = mem;
- int len = p[0];
- p[len+1] = 'w';
- p[0] += 1;
- semaphore_v(sem_id);
- sleep(1);
- if (len >= 254) {
- break;
- }
- }
-
- mem[0] = 'b';
- printf("result is %s\n", mem);
- del_semvalue(sem_id);
-
- // detach share memory
- ret = shmdt(mem);
- if(-1 == ret) {
- printf("failed in shmdt");
- return -1;
- }
-
- // delete share memory
- ret = shmctl(shmid, IPC_RMID, 0);
- if(-1 == ret) {
- printf("failded in delete shm. \n");
- return -1;
- }
-
- return 0;
- }
进程2:
[cpp] view plaincopy
- #include <memory.h>
- #include <stdio.h>
- #include <unistd.h> //getpagesize( )
- #include <sys/ipc.h>
- #include <sys/shm.h>
- #include <sys/sem.h>
-
- #define MY_SHM_ID 1234
- #define MY_SEM_ID 4321
- #define MEM_SIZE 300
-
- union semun
- {
- int val;
- struct semid_ds * buf;
- unsigned short * array;
- };
-
- int semaphore_p(int sem_id) {
- struct sembuf sem_b;
- sem_b.sem_num = 0;
- sem_b.sem_op = -1;
- sem_b.sem_flg = SEM_UNDO;
-
- semop(sem_id, &sem_b, 1);
-
- return 0;
- }
-
- int semaphore_v(int sem_id) {
- struct sembuf sem_b;
- sem_b.sem_num = 0;
- sem_b.sem_op = 1;
- sem_b.sem_flg = SEM_UNDO;
-
- semop(sem_id, &sem_b, 1);
-
- return 0;
- }
-
-
- int set_semvalue(int sem_id) {
- union semun sem_union;
- sem_union.val = 1;
-
- if (-1 == semctl(sem_id, 0, SETVAL, sem_union))
- {
- return -1;
- }
- return 0;
- }
-
- int del_semvalue(int sem_id) {
- union semun sem_union;
-
- if (-1 == semctl(sem_id, 0, IPC_RMID, sem_union))
- {
- return -1;
- }
-
- return 0;
- }
-
-
-
- int main() {
- printf("page size: %d. \n", getpagesize());
-
- // create share memory
- int shmid = 0;
- shmid = shmget(MY_SHM_ID, 0, 0);
- if (-1 == shmid) {
- printf("get share memroy failed. \n");
- return -1;
- }
- printf("get a shared memory success, shmid: %d. \n", shmid);
-
- // create sem id
- int sem_id = semget((key_t)MY_SEM_ID, 1, 0666 | IPC_CREAT);
- printf("get a sem id: %d\n", sem_id);
-
- // attach share memory
- unsigned char * mem = NULL;
- mem = shmat(shmid, (const void*)0, 0);
- if (-1 == (size_t)mem) {
- printf("failed in shmat. \n");
- return -1;
- }
- printf("attach shm: %p\n", mem);
-
-
-
- // get share memory status
- struct shmid_ds shmds;
- memset(&shmds, 0, sizeof(struct shmid_ds));
- int ret = 0;
- ret = shmctl(shmid, IPC_STAT, &shmds);
- if (-1 == ret) {
- printf("failed in shmctl. \n");
- return -1;
- }
- printf("size of memory segment is %d. \n", shmds.shm_segsz);
- printf("numbre of attaches %d. \n", (int)shmds.shm_nattch);
-
-
- while(1) {
- semaphore_p(sem_id);
- unsigned char * p = mem;
- int len = p[0];
- p[len+1] = 'r';
- p[0] += 1;
- semaphore_v(sem_id);
- sleep(1);
- if (len >= 254) {
- break;
- }
- }
-
- mem[0] = 'b';
- printf("result is %s\n", mem);
-
- // detach share memory
- ret = shmdt(mem);
- if(-1 == ret) {
- printf("failed in shmdt");
- return -1;
- }
-
- return 0;
- }
运行结果:
./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系统默认安装,需要添加附加包,本文章暂不讨论