进程间通信----管道

来源:互联网 发布:axure mac安装教程 编辑:程序博客网 时间:2024/06/08 11:57

一,进程间通讯原理

每个进程各自有不同的用户地址空间,任何一个进程的全局变量在另一个进程中都看不到,所以进程间交换数据,必须通过内核,在内核中开辟一块缓冲区,进程1把数据从用户空间拷到内核缓冲区,进程2再从内核缓冲区把数据读走,内核提供的这个机制称为进程间通信(IPC)
这里写图片描述

二,进程间通信——管道

(一),匿名管道

匿名管道是一种最基本的IPC进制,由pipe函数创建:

#include <unistd.h>int pipe(int fd[2]);

pipe函数调用成功返回0,调用失败返回-1,参数为文件描述符,fd[0]指向管道读端,fd[1]指向管道写端,管道其实就像一个打开的文件可通过read(fd[0]);或write(fd[1])进行读写操作,

(一)验证匿名管道思路:
(1)父进程调用pipe开辟管道,得到两个文件描述符指向管道两端。
(2)父进程调用fork创建子进程,子进程两个文件描述符指向同一管道读写端。
(3)父进程关闭写端,子进程关闭读端,子进程往管道里写,父进程从管道里读,实现进程通信。
验证代码:

int main()  5 {  6     int fds[2];//fds[0]为管道读取端;fds[1]为管道写入端;  7     if (pipe(fds)<0)    //管道创建失败返回-1;  8     {  9         perror("pipe"); 10         return 1; 11     } 12     //管道创建成功返回0; 13     //实现进程间通讯;父子进程间通讯; 14     pid_t id = fork(); 15     if(id == 0) 16     { 17         //child->write 18         close(fds[0]);//fds[1]->write point 19         const char* msg = "hello father,i am child,hello pipe!"; 20         while(1) 21         { 22             write(fds[1],msg,strlen(msg)); 23             sleep(1); 24         } 25     } 26     else    //father->read; 27     { 28         close(fds[1]);//fds[0]->read point 29         char buf[1024]; 30         while(1) 31         { 32             ssize_t s = read(fds[0],buf,sizeof(buf)-1); 33             if(s >0) 34             { 35                 buf[s-1] = 0; 36                 printf("ywx->#%s\n",buf); 37             } 38         } 39     } 40     return 0; 41 }

这里写图片描述
可以看出,父进程可以输出子进程写的内容。
(二),匿名管道特点
(1)常用于具有血缘关系的进程通信,如父子进程
(2)单向通信
(3)基于字节流
(4)生命周期随进程
(5)同步
(三),几种特殊情况(大家自行验证)
(1)读端不读,写端一直写,则写满管道,写端则阻塞。
(2)写端不写,读端一直读,则读空管道,读端则阻塞。
(3)读端一直读,写端被关闭,则读到文件结尾就停止。
(4)写端一直写,读端被关闭,则写端会被操作系统终止。

(二),命名管道

命名管道克服了只有血缘关系之间才可以通信,任意两个进程都可以进行通信。
命名管道有两种方式创建。一是在shell下交互地建立一个命名管道,二是程序中使用系统函数建立命名管道,shell方式下可以使用mknod或mkfifo函数。如下

#include <sys/type.h>#include <sys/stat.h>int mknod(const char* path,mode_t mod,dev_t dev);int mkfifo(const char* path,mode_t mod);

两个函数调用成功返回0,失败返回-1,
第一个参数path 为创建命名管道的全路径名(文件名),mod为创建的命名管道的权限;dev为设备值,取决文件创建种类,只在创建设备文件时才会用到。
mknod函数创建命名管道

umask(0);if (mknod("/tmp/fifo",S_IFIFO|0666)==-1){    perror("mknod error");}exit(1);

mkfifo函数创建命名管道

umask(0);if (mkfifo("/tmp/fifo",S_IFIFO|0666)==-1){    perror("mkfifo error");}exit(1);

“S_IIFIO|0666”指明创建一个命名管道取权限0666,(注意umask对文件权限的影响),命名管道创建成功后,必须以open()打开,这和匿名管道不同,这点是因为命名管道是一个存在于硬盘的文件,而匿名管道是存在内存中的特殊文件。注意Open打开命名管道进程可能会阻塞。同时以读写方式打开(O_RDWR)打开,不会阻塞,以只读方式打开(O_RDONLY),则调用open函数的进程将会被阻塞知道有写方式打开管道,同时以写方式(O_WRITE)打开也会阻塞直到有读写方式打开管道。

(一),验证命名管道

思路:创建命名管道,进行两个进程之间通信
代码如下:
server.c

#include <stdio.h>  2 #include <sys/types.h>  3 #include <sys/stat.h>  4 #include <unistd.h>  5 #include <fcntl.h>  6 #include <string.h>  7 #define _PATH_ "./mypipe"  8 #define _SIZE_ 1024  9 int main() 10 { 11     int fd = open(_PATH_,O_WRONLY); 12     if(fd <0) 13     { 14         printf("open file error!\n"); 15         return 1; 16     } 17     char buf[_SIZE_]; 18     while(1) 19     { 20         printf("please Enter#"); 21         fflush(stdout); 22         ssize_t s = read(0,buf,sizeof(buf)-1); 23         if(s>0) 24         { 25             buf[s-1] = 0; 26             write(fd,buf,strlen(buf)); 27  28         } 29     } 30     close(fd); 31     return 0; 32 } 33 

client.c

#include <stdio.h>  2 #include <sys/types.h>  3 #include <sys/stat.h>  4 #include <string.h>  5 #include <fcntl.h>  6 #include <unistd.h>  7 int main()  8 {  9     umask(0); 10     if(mkfifo("./mypipe",0666|S_IFIFO)<0) 11     { 12         perror("mkfifo"); 13         return 1; 14     } 15  16     int fd = open("./mypipe",O_RDONLY); 17     if(fd<0) 18     { 19         perror("open"); 20         return 2; 21     } 22     char buf[1024]; 23     memset(buf,'\0',sizeof(buf)); 24     while(1) 25    { 26        ssize_t s = read(fd,buf,sizeof(buf)-1); 27        if (s >0) 28        { 29             buf[s] = 0; 30             printf("server say# %s\n",buf); 31        } 32        else if(s== 0) 33        { 34             printf("server quit!  client begin quit!\n "); 35             break; 36        } 37     } 38     close(fd); 39     return 0; 40 } 41 

运行结果如下:
这里写图片描述
这里写图片描述
这样就实现了进程间通信了。

(二,)命名管道特点

(1)任意两个进程之间都可以通信,无血缘限制。
(2)同步
(3)基于字节流
(4)生命周期随进程
(5)单向通信

原创粉丝点击