进程间通信(1)——管道和命名管道

进程间通信包括本地进程间通信网络进程间通信

  • 网络进程间通信:不同计算机的进程间通信,是基于socket进程的通信

  • 本地进程间通信:同一台计算机系统中各进程间的通信,包括下面五种

【Linux下的各种进程间通信方式】

  • 管道
  • 命名管道
  • 消息队列
  • 共享内存
  • 信号量

【1】管道

(1)概述

  • 管道实现数据以数据流的方式在进程间流动。在系统中其相当于文件系统上的一个文件,来缓存所要传输的数据。

  • 在某些特性上又不同于文件,例如,当数据读出后,则管道中就没有数据了,但文件没有这个特性。

  • 顾名思义,匿名管道在系统中没有实名,并不可以在文件系统中以任何方式看到该管道。它只是进程的一种资源,会随着进程的结束而被系统清除

  • 创建一个管道时生成了两个文件描述符,但对于管道中所使用的文件描述符并没有路径名,也就是不存在任何意义上的文件,它们只是在内存中跟某一个索引节点相关联的两个文件描述符

  • shell中,经常使用(l)来连接两个甚至多个命令

(2)管道的创建

Linux下使用pipe函数创建一个匿名管道,函数原型如下:

include <unistd.h>

int pipe(int fd[2]);

返回: 成功返回0,出错返回-1

fd[2]:长度为2的文件描述符数组

  • fd[0]是读出端的文件描述符
  • fd[1]是写入端的文件描述符
  • 当函数成功返回后,则自动维护了一个从fd[1]到fd[0]的数据通道

(3)管道的关闭

管道的关闭使用的是基于文件描述符close函数。

(4)一个使用管道的例子,create_pipe.c:

程序中先使用函数pipe建立管道,并使用管道传输数据,在程序结束部分,释放掉管道占用的文件资源(两个文件描述符)

include <unistd.h>

include <stdio.h>

include <stdlib.h>

int main(void)
{
int fd[2];/管道的文件描述符数组/
char str[256];
if((pipe(fd)) < 0)
{
printf("create the pipe failed!\n");
exit(1);/出错退出/
}
write(fd[1],"create the pipe successfully!\n",31);
/向管道写入端写入数据/
read(fd[0],str,sizeof(str));
/从管道输出端读出数据/
printf("%s",str);
printf("pipe file description are %d , %d\n",fd[0],fd[1]);
close(fd[0]);
close(fd[1]);
return 0;
}

运行结果:

hyx@hyx-virtual-machine:~/test$ ./create_pipe
create the pipe successfully!
pipe file description are 3 , 4

(5)管道的读写

使用read和write对管道进行操作

  • 管道读取规则

(1)如果管道写端不存在,则认为读到了数据末尾,读函数返回的读出字节数为0

(2)写端存在时,

  • 若请求字节数 > PIPE_BUF,则返回管道中现有的数据字节数
    (PIPE_BUF在include/linux/limits.h中定义)
  • 若请求字节数 < PIPE_BUF,则返回管道中现有数据字节数(此时管道中数据量小于请求数据量)
    或返回请求字节数(此时管道中数据量不小于请求数据量)
  • 管道写入规则:
  • 管道缓冲区一旦有空闲区域,写进程就会立即试图向管道写入数据,如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞

(6)管道在父子进程中的应用

parent _ pipe _ child.c (使用pipe和fork组合实现父子进程通信)

先使用pipe函数建立管道,再使用fork函数创建子进程

在父子进程中维护管道的数据方向,并在父进程中向子进程发送消息,在子进程中接收消息并输出到标准输出

include <stdlib.h>

include <unistd.h>

include <stdio.h>

include <sys/types.h>

include <limits.h>

define BUFSIZE PIPE_BUF /管道默认一次性读写的数据长度/

void err_quit(char msg)
{
printf(msg);
exit(1);
}
int main(void)
{
int fd[2];
char buf[BUFSIZE] = "hello my child!\n";/
写入管道的缓冲区/
pid_t pid;/
定义进程ID的变量/
int len;
if((pipe(fd)) < 0)/
创建管道/
err_quit("pipe failed\n");
if((pid = fork()) < 0)/
创建一个子进程/
err_quit("fork failed\n");
else if(pid > 0)
{
close(fd[0]);/
父进程中关闭管道的读出端/
write(fd[1],buf,strlen(buf));
exit(0);
}
else
{
close(fd[1]);/
子进程中关闭管道的写入端/
len = read(fd[0],buf,BUFSIZE);/
子进程从管道中读出数据/
if(len < 0)
err_quit("process failed when read a pipe\n");
else
write(STDOUT_FILENO,buf,len);/
输出到标准输出*/
exit(0);
}
}

hyx@hyx-virtual-machine:~/test$ hello my child!

(7)管道在兄弟进程间的应用

brother_pipe.c:

  • 管道在兄弟进程间应用时,应该先在父进程中建立管道,然后调用fork函数创建两个子进程,在兄弟子进程中维护管道的数据方向

  • 在父进程中创建管道,并使用fork函数创建2个子进程。在第一个子进程中发送消息到第2个子进程,第2个子进程读出消息并处理。在父进程中,由于并不使用管道通信,所以什么都不做,直接关闭了管道的两端并退出

  • 创建第一个子进程时,并没有关闭管道的两端,创建第二个子进程时,关闭了管道

  • 创建第二个子进程时,子进程可以继承存活的管道,而不是一个两端已经关闭的管道,否则下面的读操作无法完成

    include <stdlib.h>

    include <unistd.h>

    include <stdio.h>

    include <sys/types.h>

    include <limits.h>

    define BUFSIZE PIPE_BUF /管道默认一次性读写的数据长度/

    void err_quit(char msg)
    {
    printf(msg);
    exit(1);
    }
    int main(void)
    {
    int fd[2];
    char buf[BUFSIZE] = "hello my brother!\n";/
    缓冲区/
    pid_t pid;
    int len;
    if((pipe(fd)) < 0) /
    创建管道/
    err_quit("pipe failed\n");
    if((fork()) < 0) /
    创建第一个子进程/
    err_quit("fork failed\n");
    else if(pid = 0) /
    子进程中/
    {
    close(fd[0]); /
    关闭不使用的文件描述符/
    write(fd[1],buf,strlen(buf));/
    写入消息/
    exit(0);
    }
    if((pid = fork()) < 0)
    err_quit("fork failed\n");
    else if(pid > 0) /
    父进程中/
    {
    close(fd[0]);
    close(fd[1]);
    exit(0);
    }
    else /
    第二个子进程中/
    {
    close(fd[1]); /
    关闭不使用的文件描述符/
    len = read(fd[0],buf,BUFSIZE);/
    读取消息/
    write("STDOUT_FILENO,buf,len");/
    将消息输出到标准输出*/
    exit(0);
    }
    return 0;
    }

【2】命名管道

(1)概述

  • 命名管道(named pipe)也称为FIFO,它是一种文件类型,创建一个FIFO文件类似于创建一个普通文件

  • FIFO解决了只有具有亲缘关系的进程间才能通信。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存放于文件系统中。这样,即使与FIFO的创建进程不存在亲缘关系的进程,只要可以访问该路径,就能够彼此通过FIFO相互通信(能够访问该路径的进程以及FIFO的创建进程之间),因此,通过FIFO不相关的进程也能交换数据

命名管道区别于管道主要是以下两点:

  • 命名管道可以用于任何两个进程间的通信,而并不限制这两个进程同源,因此命名管道的使用比管道的使用要方便灵活

  • 命名管道作为一种特殊的文件存放于文件系统中,而不是像管道一样存放于内存(使用完毕后消失)。当进程对命名管道的使用结束后,命名管道依然存在于文件系统中,除非对其进行删除操作,否则该命名管道不会消失

(2)FIFO管道通过mkfifo创建,函数原型:

include <sys/types.h>

include <sys/stat.h>

int mkfifo(const char *pathname,mode_t mode);

返回:成功返回0,出错返回-1

第一个参数是一个普通的路径名,即创建后FIFO文件的名字

第二个参数与打开普通文件的open()函数中的mode参数相同

如果mkfifo第一个参数是一个已经存在的路径名时,则返回EEXIST错误,所以般典型的调用代码首先会检查是否返回该错误,如果确实返回该错误,那么只要调用打开FIFO的函数就可以了。一般文件的I/O函数都可以用FIFO,如closeread、write等

(3)关于FIFO的程序示例

create_FIFO.c

演示了如何使用mkfifo创建一个FIFO

  • 程序中从命令行参数中得到一个文件名,然后使用mkfifo函数创建FIFO文件。新创建的FIFO只具有读写权限。由于FIFO文件的特性,所以它被隐性地规定不具有执行权限。

    include <sys/types.h>

    include <sys/stat.h>

    include <errno.h>

    include <stdio.h>

    include <stdlib.h>

    int main(int argc,char *argv[])
    {
    mode_t mode = 0666;
    if(argc != 2)
    {
    printf("USEMSG:Create_FIFO{FIFO name}\n");
    exit(1);
    }
    if((mkfifo(argv[1],mode)) < 0)
    {
    perror("failed to mkfifo!\n");
    exit(1);
    }
    else
    {
    printf("you successfully create a FIFO name is:%s\n",argv[1]);
    }
    return 0;
    }

    运行结果:

    hyx@hyx-virtual-machine:~/test$ ./create_FIFO
    USEMSG:Create _FIFO{FIFO name}
    hyx@hyx-virtual-machine:~/test$ ./create_FIFO testfifo
    you successful create a FIFO name is : testfifo
    hyx@hyx-virtual-machine:~/test$

(4)命名管道的读写

  • 与普通文件类似,可以使用系统调用open打开一个命名管道,使用read和write函数对命名管道进行读写,使用close关闭一个命名管道

(5)使用FIFO进行两个进程间通信的例子

write_fifo.c和read_fifo.c

  • 在程序write_fifo.c中打开一个名为fifo1的FIFO文件,并分10次向这个FIFO中写入数据。在程序read_fifo.c中先打开fifo1文件,然后读取里面的数据并输出到标准输出。

  • 运行程序时,要先使用 mkfifo -m 666 fifo1 创建fifo1文件

  • 因为分别进行读和写,所以在两个shell中进行

write_fifo.c

include <sys/types.h>

include <sys/stat.h>

include <unistd.h>

include <stdio.h>

include <stdlib.h>

include <limits.h>

include <fcntl.h>

define BUFES PIPE_BUF

int main(void)
{
int fd;
int n,i;
char buf[BUFES];
time_t tp;
printf("I am %d\n",getpid());
if((fd = open("fifo1",O_WRONLY)) < 0)
{
printf("Open failed!\n");
exit(1);
}
for(i = 0;i < 10;i++)
{
time(&tp);/提取系统当前时间/
n = sprintf(buf,"write_fifo %d sends %s",getpid(),ctime(&tp));/使用sprintf函数向buf中格式化写入进程ID和时间值/
printf("send msg:%s\n",buf);
if((write(fd,buf,n+1)) < 0)
{/写入到FIFO中/
printf("write failed!\n");
close(fd);/关闭FIFO文件/
exit(1);
}
sleep(3);/进程睡眠3秒,便于观察/
}
close(fd);/关闭FIFO文件/
exit(0);
}

read_fifo.c

include <sys/types.h>

include <sys/stat.h>

include <unistd.h>

include <stdio.h>

include <stdlib.h>

include <limits.h>

include <fcntl.h>

define BUFES PIPE_BUF

int main(void)
{
int fd;
int len;
char buf[BUFES];
mode_t mode = 0666; /FIFO文件的权限/
if((fd=open("fifo1",O_RDONLY)) < 0)/打开FIFO文件/
{
printf("open failed!\n");
exit(1);
}
while((len = read(fd,buf,BUFES)) > 0)/开始进行通信/
printf("read_fifo read:%s",buf);
close(fd);/关闭FIFO文件/
exit(0);
}
运行结果:

hyx@hyx-virtual-machine:~/test$ mkfifo -m 666 fifo1
hyx@hyx-virtual-machine:~/test$ ./write_fifo
I am 2964
send msg:write_fifo 2964 sends Thu Dec 3 16:29:26 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:29 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:32 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:35 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:38 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:41 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:44 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:47 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:50 2015

send msg:write_fifo 2964 sends Thu Dec 3 16:29:53 2015

hyx@hyx-virtual-machine:~/test$ ./read_fifo
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:26 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:29 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:32 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:35 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:38 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:41 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:44 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:47 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:50 2015
read_fifo read:write_fifo 2964 sends Thu Dec 3 16:29:53 2015

posted @ 2015-12-08 23:32  adacn  阅读(1359)  评论(0)    收藏  举报