配色: 字号:
进程间通信之管道(pipe、fifo)
2016-09-10 | 阅:  转:  |  分享 
  
进程间通信之管道(pipe、fifo)我们先来说说进程间通信(IPC)的一般目的,大概有数据传输、共享数据、通知事件、资源共享和进程控制等。但是我们知道,对于每一个进程来说这个进程看到属于它的一块内存资源,这块资源是它所独占的,所以进程之间的通信就会比较麻烦,原理就是需要让不同的进程间能够看到一份公共的资源。所以交换数据必须通过内核,在内核中开辟?块缓冲区,进程1把数据从?户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读?,内核提供的这种机制称为进程间通信。一般我们采用的进程间通信方式有

管道(pipe)和有名管道(FIFO)

信号(signal)

消息队列

共享内存

信号量

套接字(socket)

我们先来从最简单的通信方式来说起;

匿名管道pipe

---------------------------------------------------------------------------------------------------------------------------------

管道的创建

管道是一种最基本的进程间通信机制。管道由pipe函数来创建:



调用pipe函数,会在内核中开辟出一块缓冲区用来进行进程间通信,这块缓冲区称为管道,它有一个读端和一个写端。

pipe函数接受一个参数,是包含两个整数的数组,如果调用成功,会通过pipefd[2]传出给用户程序两个文件描述符,需要注意pipefd?[0]指向管道的读端,pipefd[1]指向管道的写端,那么此时这个管道对于用户程序就是一个文件,可以通过read(pipefd[0]);或者write(pipefd[1])进行操作。pipe函数调用成功返回0,否则返回-1..

那么再来看看通过管道进行通信的步骤:

》父进程创建管道,得到两个文件描述符指向管道的两端



》利用fork函数创建出子进程,则子进程也得到两个文件描述符指向同一管道



》父进程关闭读端(pipe[0]),子进程关闭写端pipe[1],则此时父进程可以往管道中进行写操作,子进程可以从管道中读,从而实现了通过管道的进程间通信。

???

?

示例代码:

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44 #include

#include

?#include

int?main(www.hunanwang.net)

{

int?_pipe[2];

int?ret=pipe(_pipe);

????if(ret<0)

????{

?????????perror("pipe\n");

????}

??pid_tid=fork();

??if(id<0)

{

???????perror("fork\n");

???}

???else?if(id==0)??//child

????{

????????close(_pipe[0]);

????????int?i=0;

????????char?mesg=NULL;

???????while(i<100)

???????{

???????????mesg="Iamchild";

???????????write(_pipe[1],mesg,strlen(mesg)+1);

???????????sleep(1);

???????????++i;

????????}

?????}

????else??//father

???{

???????close(_pipe[1]);

?????????int?j=0;

????????char?_mesg[100];

?????????while(j<100)

????????{

??????????memset(_mesg,''\0'',sizeof(_mesg));

??????????read(_pipe[0],_mesg,sizeof(_mesg));

??????????printf("%s\n",_mesg);

??????????j++;

????????}

????}

???return?0;

} 结果演示:

???

?

pipe的特点:

1.只能单向通信

2.只能血缘关系的进程进行通信

3.依赖于文件系统

4、生命周期随进程

5.面向字节流的服务

6.管道内部提供了同步机制

说明:因为管道通信是单向的,在上面的例子中我们是通过子进程写父进程来读,如果想要同时父进程写而子进程来读,就需要再打开另外的管道;

管道的读写端通过打开的?件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那上间通信,也可以fork两次,把,然后两个,总之?需要通过fork传递,它们才能通信。

四个特殊情况:

》如果所有指向管道写端的,?仍然有进程从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到

》如果有指向管道写端的,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

》如果所有指向管道读端的,这时有进程指向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终

》如果有指向管道读端的,?持有管道写端的进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再write会阻塞,直到管道中有空位置了才写

?

命名管道FIFO

---------------------------------------------------------------------------------------------------------------------------------

在管道中,只有具有血缘关系的进程才能进行通信,对于后来的命名管道,就解决了这个问题。FIFO不同于管道之处在于它提供FIFO的FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是,FIFO(firstinputfirstoutput)总是按照先进先出的原则

命名管道的创建

创建命名管道的系统函数有两个:mknod和mkfifo。两个函数均定义在头sys/stat.h,函数原型如下:#include#includeintmknod(constcharpath,mode_tmod,dev_tdev);intmkfifo(constcharpath,mode_tmode);

函数mknod参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模指明其存取权限;dev为设备值,该值取决于。这两个函数调0,失败都返回-1。下mknod函数创建了

umask(0);if(mknod("/tmp/fifo",S_IFIFO|0666)==-1){perror("mkfifoerror");exit(1);}函数mkfifo前两个参数的含义和mknod相同。下mkfifo的umask(0);if(mkfifo("/tmp/fifo",S_IFIFO|0666)==-1){perror("mkfifoerror!");exit(1);}"S_IFIFO|0666"指明创建0666,即创建者、与创建者同组的这umask对管道。命名管道创建后就可以使名管道时,必须先调open()将其打开。因为命名管道是是存在于内存中的特殊需要注意的是,调open()打开命名管道的进程可能会被阻塞。但如果同时式(O_RDWR)打开,则O_RDONLY)打开,则调open()

?

运行示例

-----------------------------------------------------------

那么此时我们早server.c中创建命名管道并打开,对管道中进行写操作,在client.c中进行读操作,把读到的内容进行打印,就实现了我们的使用命名管道通信。

?

1

2

3

4

5

6

7

8

9

10

11

12

13

14

15

16

17

18

19

20

21

22

23

24

25

26

27

28

29

30

31

32

33

34

35

36

37

38

39

40

41

42

43

44

45

46

47

48

49

50

51

52

53

54

55

56

57

58

59

60

61

62

63

64 Server.c:

#include

#include

#include

#include

#include

#include

#define_PATH_NAME_"/tmp/file.tmp"

#define_SIZE_100

????

?int?main()

?{

??????int?ret=mkfifo(_PATH_NAME_,S_IFIFO|0666);

??????if(ret==-1){

???????????printf("makefifoerror\n");

???????????return?1;

??????}

?????char?buf[_SIZE_];

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

?????int?fd=open(_PATH_NAME_,O_WRONLY);

?????while(1)

?????{

?????????//scanf("%s",buf);

?????????fgets(buf,sizeof(buf)-1,stdin);

?????????int?ret=write(fd,buf,strlen(buf)+1);

?????????if(ret<0){

?????????printf("writeerror");

?????????break;

?????????}

?????}

??????close(fd);

??????return?0;

?}???????????????????

?

Client.c:

#include

#include

#include

#include

#include

#include

#define_PATH_NAME"/tmp/file.tmp"

#define_SIZE_100

int?main(www.visa158.com)

{

????int?fd=open(_PATH_NAME,O_RDONLY);

????if(fd<0){

????????printf("openfileerror");

????????return?1;

????}

???char?buf[_SIZE_];

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

???while(1)

???{

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

???????if(ret<0){

???????????printf("readendorerror\n");

???????????break;

???????}

???printf("%s",buf);

???}

???close(fd);

???return?0;

?} 结果演示:

??

?????可以看到我实在服务端发送了“Hello”“Iamaerver”,在客户端可以收到此内容,完成简单的进程间通信。

献花(0)
+1
(本文系白狐一梦首藏)