进程间通信和同步:pipe、FIFO、消息队列、信号量、共享内存、信号
一、半双工管道(pipe)
关于管道详细介绍可参考http://www.cnblogs.com/nufangrensheng/p/3560130.html。
1、管道实现父子进程间通信实例:
/* pipe.c */ #include <unistd.h> #include <stdio.h> #include <limits.h> #include <sys/types.h> #include <errno.h> #include <stdlib.h> #define MAXLINE 1024 int main(void) { int fd[2], pid; char buf[MAXLINE]; if(pipe(fd) < 0) { perror("pipe error"); exit(1); } if((pid = fork()) < 0) { perror("fork error"); exit(1); } else if(pid == 0) /* child */ { close(fd[1]); /* read from parent */ if(read(fd[0], buf, MAXLINE) < 0) { perror("read error"); exit(1); } printf("read from parent: %s\n", buf); } else /* parent */ { close(fd[0]); /* send to child */ if(write(fd[1], "hello, i am your parent", 24) != 24) { perror("write error"); exit(1); } printf("send to child OK!\n"); wait(NULL); } }
编译运行结果:
2、管道实现父子进程间同步实例:
/* pipe_sync.c */ #include <sys/types.h> #include <errno.h> #include <unistd.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define BUFSIZE 1024 int fd1[2], fd2[2]; char c; void tell_wait() { if(pipe(fd1) < 0 || pipe(fd2) < 0) { perror("pipe error"); exit(1); } } void tell_parent() { if(write(fd1[1], "c", 1) != 1) { perror("write error"); exit(1); } } void wait_parent() { if(read(fd2[0], &c, 1) != 1) { perror("read error"); exit(1); } if(c != 'p') { printf("wait_parent: invalid data\n"); exit(1); } } void tell_child() { if(write(fd2[1], "p", 1) != 1) { perror("write error"); exit(1); } } void wait_child() { if(read(fd1[0], &c, 1) != 1) { perror("read error"); exit(1); } if(c != 'c') { printf("wait_child: invalid data"); exit(1); } } int main(void) { int pid; tell_wait(); if((pid = fork()) < 0) { perror("fork error"); exit(1); } else if(pid == 0) { printf("child: first\n"); tell_parent(); } else { wait_child(); printf("parent: after child\n"); } return(0); }
编译运行结果:
二、命名管道(FIFO)
在文件系统中命名管道是以设备特殊文件的形式存在的。
不同的进程可以通过命名管道共享数据。
关于FIFO详细介绍可参考http://www.cnblogs.com/nufangrensheng/p/3561632.html。
FIFO实现进程间通信实例:
/*************************** * **** FIFO server********** ***************************/ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #define FIFO "/home/zhu/network/fifo/myfifo" #define OPEN_MODE O_RDONLY int main(void) { int fifofd; char buf[80];
unlink(FIFO); /* 防止FIFO已存在 */
if(mkfifo(FIFO, 0777) == -1) { perror("mkfifo"); exit(1); } if((fifofd = open(FIFO, OPEN_MODE)) < 0) { perror("open"); exit(1); } read(fifofd, buf, sizeof(buf)); printf("message from client: %s\n", buf); close(fifofd); return(0); }
/*************************** * **** FIFO client********** ***************************/ #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #include <fcntl.h> #include <sys/types.h> #include <sys/stat.h> #define FIFO "/home/zhu/network/fifo/myfifo" #define OPEN_MODE O_WRONLY int main(void) { int fifofd; char s[] = "hello,server!"; if((fifofd = open(FIFO, OPEN_MODE)) < 0) { perror("open"); exit(1); } write(fifofd, s, sizeof(s)); printf("write message: %s\n", s); close(fifofd); return(0); }
编译成功后,我们首先运行服务器(创建FIFO,等待客户发来消息,此时FIFO服务器阻塞):
接着我们在另一个终端窗口运行客户程序,如下图所示,可以看出客户端已成功发送,服务器端也成功接收:
三、消息队列
消息队列是内核地址空间中的内部链表,通过Linux内核在各个进程之间传递内容。
关于消息队列详细介绍可参考http://www.cnblogs.com/nufangrensheng/p/3561820.html。
消息队列实现进程间通信实例:
/*************************** *******MSGQ server********** ***************************/ #include <sys/msg.h> #include <sys/ipc.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #define msqpath "/home/zhu/network/msgqueue/msq" #define proj_id 'b' struct mymesg { long mtype; char mtext[512]; }; int main(void) { key_t key; int msqid; struct msqid_ds buf; struct mymesg msg1; msg1.mtype = 1; sprintf(msg1.mtext, "hello"); if((key = ftok(msqpath, proj_id)) < 0) { perror("ftok"); exit(1); } if((msqid = msgget(key, IPC_CREAT)) < 0) { perror("msgget"); exit(1); } if(msgsnd(msqid, &msg1, sizeof(msg1), IPC_NOWAIT) < 0) { perror("msgsnd"); exit(1); } printf(“send message : hello\n”); if(msgctl(msqid, IPC_STAT, &buf) < 0) { perror("msgctl"); exit(1); } printf("message queue # of messages is: %d\n", buf.msg_qnum); return(0); }
/***************************** **********MSGQ client********* *****************************/ #include <sys/msg.h> #include <sys/ipc.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <fcntl.h> #include <unistd.h> #include <sys/types.h> #include <sys/stat.h> #define msqpath "/home/zhu/network/msgqueue/msq" #define proj_id 'b' struct mymesg { long mtype; char mtext[512]; }; int main(void) { key_t key; int msqid; struct msqid_ds buf; struct mymesg msg1; if((key = ftok(msqpath, proj_id)) < 0) { perror("ftok"); exit(1); } if((msqid = msgget(key, IPC_EXCL)) < 0) { perror("msgget"); exit(1); } if(msgrcv(msqid, &msg1, sizeof(msg1), 0, IPC_NOWAIT) < 0) { perror("msgrcv"); exit(1); } printf("receive message : %s\n", msg1.mtext); if(msgctl(msqid, IPC_STAT, &buf) < 0) { perror("msgctl"); exit(1); } printf("message queue # of messages is: %d\n", buf.msg_qnum); return(0); }
编译后运行结果如下:
四、信号量
信号量是一种计数器,用来控制对多个进程共享的资源所进行的访问。它们常常被用作一个锁机制,在某个进程正在对特定资源进行访问时,信号量可以防止另一个进程去访问它。
关于信号量详细介绍可参考http://www.cnblogs.com/nufangrensheng/p/3562046.html。
信号量实现资源控制实例:
#include <sys/types.h> #include <linux/sem.h> #include <linux/ipc.h> #include <sys/stat.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <errno.h> #include <unistd.h> #define sempath "/home/zhu/network/semaphore/sem" #define proj_id 'c' int main(void) { int semid, i; key_t key; union semun sem, getsem; sem.val = 3; if((key = ftok(sempath, proj_id)) < 0) { perror("ftok"); exit(1); } if((semid = semget(key, 1, IPC_CREAT)) < 0) { perror("semget"); exit(1); } semctl(semid, 0, SETVAL, sem); semctl(semid, 0, GETVAL, sem); printf("# of usable semphore: %d\n", sem.val); struct sembuf sops = {0, -1, IPC_NOWAIT}; for(i = 0; i < 4; i++) { printf(“%dth:”,i+1);
fflush(stdout); if(semop(semid, &sops, 1) < 0) { perror("semop"); exit(1); }
printf("ask for one semaphore:success!\n"); } return(0); }
编译运行结果如下(因为我们把信号量值设置为3,所以第四次资源请求失败):
注意,在上面的程序中,包含的头文件#include <linux/sem.h> 和#include <linux/ipc.h>。而不是#include <sys/sem.h> #include <sys/ipc.h>。否则出现“storage of size of 'sem' isn't know”的错误。详细介绍请参考http://hi.baidu.com/yuhongyangcn/item/f52545b33c1b55a1eaba93ac。
关于POSIX信号量详情可参考http://www.cnblogs.com/nufangrensheng/p/3564306.html。
注意使用POSIX信号量时,除了要包含头文件<semaphore.h>外,在编译选项中还有加上-lrt选项,否则出现“undefined reference to”这样的编译错误。
五、共享内存
共享内存是在多个进程之间共享内存区域的一种进程间通信的方式,它是在多个进程间对内存段进行映射的方式实现内存共享的。这是最快的IPC方式。
关于共享内存详细介绍可参考http://www.cnblogs.com/nufangrensheng/p/3563712.html。
共享内存实现父子进程间通信(这里为了简化、突出共享内存的使用方式,并没有加入同步处理,而只是简单地使用sleep模拟同步):
#include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h> #include <string.h> static char msg[] = "hello, share memory!"; int main(void) { key_t key; char i, *shms, *shmc; pid_t pid; int shmid; key = ftok("/home/zhu/network/shmm/shm", 'a'); shmid = shmget(key, 1024, IPC_CREAT | 0604); pid = fork(); if( pid > 0) { shms = (char *)shmat(shmid, 0, 0); memcpy(shms, msg, strlen(msg) + 1); sleep(5); shmdt(shms); } else if(pid == 0) { shmc = (char *)shmat(shmid, 0, 0); sleep(2); printf("the content in the share memory is : %s\n", shmc); shmdt(shmc); } return(0); }
运行结果:
六、信号
信号(signal)机制是UNIX系统中最为古老的进程之间的通信机制。它用于在一个或多个进程之间传递异步信号。
关于信号详细介绍可参考http://www.cnblogs.com/nufangrensheng/p/3514157.html。