您的位置:首页 > 运维架构 > Linux

linux IPC 通信 study 一:管道

2015-11-03 17:41 393 查看
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:

[cpp]
view plaincopy





#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)当有多个写进程时,写进程必须全部启动起来,然后再启动读进程,工作正常,但是一旦读进程跑起来的之后,再启动加入的写进程写入的内容反而不能被读出来。如果只有一个读一个写,读写任意一个先启动都不会影响。
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: