linux 进程通信机制:分为基于system V 和posix。
常用的通信方式分为:
(1) 管道pipe和命名管道fifo.
(2) 信号signal
(3) 信号量semphore
(4) 消息队列 msg queue
(5) 共享内存share memory
(6) 套接字socket
详解如下:
1.1. 匿名管道通信pipe,它把一个进程的输入和另外一个进程的输出连接起来,读进程从头部读出数据,写进程从尾部写入数据,每个管道都是单向的,当需要双向通信时就需要建立起两个管道。管道两端的进程均将该管道看做一个文件,传输遵循“先入先出”(FIFO)的规则。数据从一个管道中读出后,便会从管道中删除,其他读进程再也不会读到该数据,当管道为空时,读进程会被阻塞,当管道满时,写进程也会被阻塞掉。管道实际上就是个只存在于内存中的文件,对这个文件的操作要通过两个已经打开文件进行,它们分别代表管道的两端。管道是一种特殊的文件,它不属于某一种文件系统,而是一种独立的文件系统,有其自己的数据结构。
默认情况下,匿名管道pipe是半双工通信(posix允许实现支持全双工管道)。
pipe只能在具有公共祖先的两个进程之间使用。
当要读管道时,在进程中要就爱那个写管道关闭,同理要写管道时,在进程中也要将读管道关闭。常量PIPE_BUF规定了内核的管道缓冲区大小。
sample code:
- #include <stdlib.h>
- #include <stdio.h>
- #include <unistd.h>
- #include <sys/types.h>
- #include <errno.h>
- int main(int argc, char **argv)
- {
- int ret = 0;
- int pipe_fd[2] = {0};
- char buf[128] = {0};
- char *p_write_buf;
- int num = 0;
- pid_t child_id;
- if (pipe(pipe_fd) < 0 ) {
- fprintf(stderr, "create pipe failed\n");
- return -1;
- }
- child_id = fork();
- if (child_id < 0){
- fprintf(stderr, "fork error\n");
- return -1;
- } else if(0 == child_id) { /*child process*/
- fprintf(stdout,"child process...\n");
- close(pipe_fd[1]);
- sleep(2);/*wait for father write pipe*/
- num = read(pipe_fd[0], buf, 128);
- if (num > 0)
- fprintf(stdout, "child read:%s\n", buf);
- close(pipe_fd[0]);
- exit(0);
- } else {/*father process*/
- fprintf(stdout, "father process..\n");
- close(pipe_fd[0]);
- ret = write(pipe_fd[1], "haha,", 5);
- //ret = write(pipe_fd[1], "write first", 20);
- fprintf(stdout, "father write1 ret = %d\n", ret);
- ret = write(pipe_fd[1], "pipe!", 5);
- //ret = write(pipe_fd[1], "write second", 20);
- fprintf(stdout, "father write2 ret = %d\n", ret);
- close(pipe_fd[1]);
- sleep(3);
- waitpid(child_id, NULL, 0);
- exit(0);
- }
- return ret;
- }
1.2 命名管道FIFO:不同于匿名管道,FIFO有一个pathname与之相关联,以文件的形式存在于文件系统中,无论是否是父子进程,都可以通过访问FIFO。
sample code:
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO "./myfifo"
int main(int argc,char** argv)
{
int ret = 0;
char buf[128] = {0};
int fd;
int nbytes_read;
int idx = 0;
if((mkfifo(FIFO,O_CREAT|O_EXCL)<0)&&(errno!=EEXIST))
fprintf(stdout, "cannot create fifo \n");
/*read as a file */
fd = open(FIFO,O_RDONLY|O_NONBLOCK,0);
if (fd == -1) {
fprintf(stdout, "open failed");
exit(1);
}
while(1) {
memset(buf,0,sizeof(buf));
nbytes_read = read(fd, buf, 128);
fprintf(stdout, "FIFO read %d times, len: %d ", idx++, nbytes_read);
if(nbytes_read == -1) {
if(errno==EAGAIN)
fprintf(stdout, "fifo empty\n");
}
fprintf(stdout, "context: %s \n", buf);
sleep(1);
}
pause();
unlink(FIFO);
close(fd);
return ret;
}
写fifo
#include <sys/types.h>
#include <sys/stat.h>
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#define FIFO "./myfifo"
main(int argc,char** argv)
{
int ret = 0;
int fd = 0;
char buf[128] = {0};
int nbytes_write = 0;
fd = open(FIFO, O_WRONLY|O_NONBLOCK, 0);
while (1) {
fprintf(stdout, "pls input:\n");
fgets(buf,128, stdin);
nbytes_write = write(fd, buf, strlen(buf));
printf("file write num = %d\n", nbytes_write);
if(nbytes_write == -1) {
if(errno==EAGAIN)
fprintf(stdout, "The FIFO has not been read yet.\n");
} else {
fprintf(stdout, "FIFO write: %s\n",buf);
}
}
close(fd);
return ret;
}
测试程序中有2 个问题:
(1)FIFO中的数据被读出来一次之后,程序不退出,再次读取就读不到任何内容了,写程序一直再写。
(2)FIFO中的内容被读走之后,FIFO buffer中的数据反而没有被清空,程序退出,再执行,还是能够读出同样的内容?
(3)当有多个写进程时,写进程必须全部启动起来,然后再启动读进程,工作正常,但是一旦读进程跑起来的之后,再启动加入的写进程写入的内容反而不能被读出来。如果只有一个读一个写,读写任意一个先启动都不会影响。