linux named pipe mknod shell,进程间通信IPC—匿名管道(pipe)和命名管道(fifo)

管道内部如何实现-大小,组织方式,环形队列?

一.进程间通信有多种方式,本文主要讲解对管道的理解。管道分为匿名管道和命名管道。

(1)管道( pipe ):又称匿名管道。是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系。(2)命名管道 (named pipe或FIFO) : 有名管道也是半双工的通信方式,但是它允许无亲缘关系进程间的通信。

二.管道

1. 管道的特点:

(1)管道是半双工的,数据只能向一个方向流动;双方通信时,需要建立起两个管道;

(2)只能用于父子进程或者兄弟进程之间(具有亲缘关系的进程);

(3)单独构成一种独立的文件系统:管道对于管道两端的进程而言,就是一个文件,但它不是普通的文件,它不属于某种文件系统,而是自立门户,单独构成一种文件系统,并且只存在与内存中。

(4)数据的读出和写入:一个进程向管道中写的内容被管道另一端的进程读出。写入的内容每次都添加在管道缓冲区的末尾,并且每次都是从缓冲区的头部读出数据。

2.通信步骤

2.1创建管道

26833883_133829008404bu.png

利用pipe()函数创建管道,即在内核中开辟一块缓冲区(称为管道)用于通信。pipe函数调用成功返回0,调用失败返回-1。

管道是基于文件描述符的通信方式。当一个管道建立时,它会创建两个文件描述符fd[0]和fd[1]。其中fd[0]固定用于读管道,而fd[1]固定用于写管道,一般文件I/O的函数都可以用来操作管道(lseek除外)。管道的读写规则如下:

从管道中读取数据:如果管道的写端不存在,则认为已经读到了数据的末尾,读函数返回的读出字节数为0;

当管道的写端存在时,如果请求的字节数目大于PIPE_BUF,则返回管道中现有的数据字节数,如果请求的字节数目小于PIPE_BUF,返回请求的字节数(此时,管道中数据量大于请求的数据量)。注:(PIPE_BUF在include/linux/limits.h中定义,不同的内核版本可能会有所不同。Posix.1要求PIPE_BUF至少为512字节,red hat 7.2中为4096)。

向管道中写入数据:向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。如果读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

注:只有在管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIFPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是应用程序终止)。

2.2创建子进程

单独创建一个无名管道,并没有实际的意义。我们再fork一个子进程,然后通过管道实现父子进程间的通信(即两个进程中存在亲缘关系,这里的亲缘关系指的是具有共同的祖先)。

2.3父进程关闭管道读端,子进程关闭管道写端

3.代码验证

3.1检验管道大小1 #include

2 #include

3 #include

4 int main()

5 {

6     int fd[2];

7     int count=0;

8     if(pipe(fd)<0)

9     {

10         perror("Fail to create pipe");

11         exit(EXIT_FAILURE);

12     }

13     while(1)

14     {

15         write(fd[1],"a",sizeof(char));

16         printf("count=%d.\n",++count);

17     }

18     return 0;

19 }

运行结果:65536=64K

3.2读写规则探究

A.从管道中读取数据

<1>写端不存在时1 #include

2 #include

3 #include

4 int main()

5 {

6     int n;

7     int fd[2];

8     int count=0;

9     char buf[100]={0};

10

11     if(pipe(fd)<0)

12     {

13         perror("Fail to create pipe");

14         exit(EXIT_FAILURE);

15     }

16     close(fd[1]);

17

18     if((n=read(fd[0],buf,sizeof(buf)))<0)

19     {

20         perror("Fail to read pipe");

21         exit(EXIT_FAILURE);

22     }

23     printf("read %d bytes:%s\n",n,buf);

24     return 0;

25 }

运行结果:read 0 bytes:

<2>写端存在时

父进程向管道中写数据,子进程从管道中读取数据1 #include

2 #include

3 #include

4 #include

5

6 #define N 10

7 #define MAX 100

8 int child_read(int fd)

9 {

10     char buf[N];

11     int n=0;

12     while(1)

13     {

14         n=read(fd,buf,sizeof(buf));

15         buf[n]='\0';

16         printf("Read %d bytes:%s\n",n,buf);

17         if(strncmp(buf,"quit",4)==0)

18             break;

19     }

20     return 0;

21 }

22

23 int father_write(int fd)

24 {

25     char buf[MAX]={0};

26     while(1)

27     {

28         printf(">");

29         fgets(buf,sizeof(buf),stdin);

30         buf[strlen(buf)-1]='\0';

31         write(fd,buf,strlen(buf));

32         sleep(1);

33         if(strncmp(buf,"quit",4)==0)

34             break;

35     }

36     return 0;

37 }

38

39 int main()

40 {

41     int fd[2];

42     if(pipe(fd)<0)

43     {

44         perror("Fail to create pipe");

45         exit(EXIT_FAILURE);

46     }

47     pid_t pid=fork();

48     if(pid<0)

49     {

50         perror("Fail to fork");

51         exit(EXIT_FAILURE);

52     }

53     else if(pid==0)

54     {

55         close(fd[1]);

56         child_read(fd[0]);

57     }

58     else

59     {

60         close(fd[0]);

61         father_write(fd[1]);

62     }

63     exit(EXIT_SUCCESS);

64     return 0;

65

66 }

运行结果:

2e79f74b24d7703c39f226981db5b5ab.png

解释:因为buf大小为N=10,后面还加了一个'\0',超出了数组分配的内存空间(这是代码的一个错误),所以这儿的0应为10.

从以上验证我们可以看到:

<1>当写端存在时,管道中没有数据时,读取管道时将阻塞

<2>当读端请求读取的数据大于管道中的数据时,此时读取管道中实际大小的数据

<3>当读端请求读取的数据小于管道中的数据时,此时放回请求读取的大小数据

B.向管道中写入数据:

向管道中写入数据时,linux将不保证写入的原子性,管道缓冲区一有空闲区域,写进程就会试图向管道写入数据。当管道满时,读进程不读走管道缓冲区中的数据,那么写操作将一直阻塞。

注意:只有管道的读端存在时,向管道中写入数据才有意义。否则,向管道中写入数据的进程将收到内核传来的SIGPIPE信号,应用程序可以处理该信号,也可以忽略(默认动作则是使应用程序终止)。1 #include

2 #include

3 #include

4 #include

5

6 int main()

7 {

8     int n=0;

9     char buf[1000*6]={0};

10     int fd[2];

11     if(pipe(fd)<0)

12     {

13         perror("Fail to create pipe");

14         exit(EXIT_FAILURE);

15     }

16     pid_t pid=fork();

17     if(pid<0)

18     {

19         perror("Fail to fork");

20         exit(EXIT_FAILURE);

21     }

22     else if(pid==0)

23     {

24         close(fd[1]);

25         sleep(3);

26         close(fd[0]);

27         printf("Read port close\n");

28         sleep(1);

29     }

30     else

31     {

32         close(fd[0]);

33         while(1)

34         {

35             n=write(fd[1],buf,sizeof(buf));

36             printf("write %d bytes to pipe\n",n);

37         }

38     }

39     exit(EXIT_SUCCESS);

40     return 0;

41 }

运行结果:

633ff731e593cfcf64e11364021261a3.png

三.命名管道

1. 管道的特点:

(1)FIFO不同于管道之处在于它提供一个路径名与之关联。

(2)以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。

(3)FIFO(first input first output)总是按照先进先出的原则工作

2.通信步骤

2.1创建管道

Linux下有两种方式创建:

(1)在Shell下交互地建立一个命名管道;(用mknod或mkfifo命令,例如:mknod namedpipe)

(2)在程序中使用系统函数建立命名管道。(系统函数有两个:mknod和mkfifo)

db10231cc61f0f7bde994be6f7e67754.png

命名管道创建后就可以使用了,命名管道和管道的使用方法基本是相同的。只是使用命名管道时,必须先调用open()将其打开。因为命名管道是一个存在于硬盘上的文件,而管道是存在于内存中的特殊文件。

3.代码验证//写端

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7

8

9 #define _PATH_ "/tmp/file.tmp"

10 #define _SIZE_ 100

11

12 int main()

13 {

14     int ret=mkfifo(_PATH_,0666|S_IFIFO);

15     if(ret==-1)

16     {

17         printf("mkfifo error|n");

18         return 1;

19     }

20     int fd=open(_PATH_,O_WRONLY);

21     if(fd<0)

22     {

23         printf("open file error!\n");

24     }

25     char buf[_SIZE_];

26     memset(buf,'\0',sizeof(buf));

27     while(1)

28     {

29         scanf("%s",&buf);

30         int ret=write(fd,buf,sizeof(buf));

31         if(ret<0)

32         {

33             printf("write error!\n");

34             break;

35         }

36         if(strncmp(buf,"quit",4)==0)

37         {

38             break;

39         }

40     }

41

42     close(fd);

43     return 0;

44 }

45

//读端

1 #include

2 #include

3 #include

4 #include

5 #include

6 #include

7

8 #define _PATH_ "/tmp/file.tmp"

9 #define _SIZE_ 100

10

11 int main()

12 {

13     int fd=open(_PATH_,O_RDONLY);

14     if(fd<0)

15     {

16         printf("open file error!\n");

17         return 1;

18     }

19     char buf[_SIZE_];

20     memset(buf,'\0',sizeof(buf));

21     while(1)

22     {

23         int ret=read(fd,buf,sizeof(buf));

24         if(ret<=0)

25         {

26             printf("read end or error!\n");

27             break;

28         }

29         printf("%s\n",buf);

30         if(strncmp(buf,"quit",4)==0)

31         {

32             break;

33         }

34     }

35

36     close(fd);

37     return 0;

38 }

输出结果:

767c9f9a1bd643f2af7515c1e5202a4d.png

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值