Linux基础篇 进程通信——管道

IPC(InterProcess Communication)进程间通信

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

linux下进程间通信的几种主要?手段简介:

1 管道(Pipe)及有名管道(named pipe):管道可?用于具有亲缘关系进程间的通信,有名管道克服了管道没有名字的限制,因此,除具有管道所具有的功能外,它还允许无亲缘关系进程间的通信;

2 信号(Signal):信号是比较复杂的通信?方式,用于通知接受进程有某种事件发?生,除了用于进程间通信外,进程还可以发送信号给进程本?身;linux除了?支持Unix早期信号语义函数sigal外,还支持语义符合Posix.1标准的信号函数sigaction(实际上,该函数是基于BSD的,BSD为了实现可靠信号机制,又能够统一对外接口,?用sigaction函数重新实现了signal函数);

3 报?文(Message)队列(消息队列):消息队列是消息的链接表,包括Posix消息队列system V消息队列。有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的消息。消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区?大?小受限等缺点。

4 共享内存:使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其它通信机制,如信号量结合使?用,来达到进程间的同步及互斥。

5 信号量(semaphore):主要作为进程间以及同一进程不同线程之间的同步手段。

6 套接口(Socket):更为一般的进程间通信机制,可用于不同机器之间的进程间通信。起初是由Unix系统的BSD分支开发出来的,但现在一般可以移植到其它类Unix系统上:Linux和System V的变种都支持套接字。

本篇文章先来学习下管道个命名管道。

管道是?一种最基本的IPC机制,由pipe函数创建:

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

调?用pipe函数时在内核中开辟一块缓冲区(称为管道)用于通信,它有一个读端一个写端,然后通过filedes参数传出给用户程序两个文件描述符,filedes[0]指向管道的读端,filedes[1]指向管道的写端(很好记,就像0是标准输?入1是标准输出一样)。所以管道在用户程序看起来就像一个打开的文件,通过read(filedes[0]);或者write(filedes[1]);向这个文件读写数据其实是在读写内核缓冲区。pipe函数调用成功返回0,调用失败返回-1。

我们来演示下Pipe的通信功能,首先

1. 父进程调?用pipe开辟管道,得到两个文件描述符指向管道的两端。

2. 父进程调?用fork创建?子进程,那么子进程也有两个文件描述符指向同一管道。

3. 父进程关闭管道读端,子进程关闭管道写端。父进程可以往管道里写,子进程可以从管道里读,管道是用环形队列实现的,数据从写端流入从读端流出,这样就实现了进程间通信。

代码如下:

#include<stdio.h>
  2 #include<unistd.h>
  3 #include<errno.h>
  4 #include<string.h>
  5
  6 int main()
  7 {
  8     int _pipe[2];
  9     int ret=pipe(_pipe);
 10     if(ret==-1)//
 11     {
 12         printf("creat pipe error!errno code is:%d\n",errno);
 13         return 1;
 14     }
 15     pid_t id = fork();
 16     if(id<0)
 17     {
 18         printf("fork error!");
 19         return 2;
 20     }
 21     else if(id==0)//child
 22     {
 23         close(_pipe[0]);
 24         int i=0;
 25         char* _mesg_c=NULL;
 26         while(i<100)
 27         {
 28             _mesg_c="i im coder";
 29             write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
 30             sleep(1);
 31             i++;
 32         }
 33     }
 34   else{//father
 35        close(_pipe[1]);
 36        char _mesg[100];
 37        int j=0;
 38        while(j<100)
 39        {
 40            memset(_mesg,‘\0‘,sizeof(_mesg));
 41            read(_pipe[0],_mesg,sizeof(_mesg));
 42            printf("%s\n",_mesg);
43            j++;
 44        }
 45         }
 46
 47     return 0;
 48 }

运行结果如下:

使?用管道有一些限制:

两个进程通过一个管道只能实现单向通信。比如上面的例子,父进程写子进程读,如果有时候也需要子进程写父进程读,就必须另开一个管道。

管道的读写端通过打开的?文件描述符来传递,因此要通信的两个进程必须从它们的公共祖先那?里继承管道?文件描述符。上?面的例子是父进程把?文件描述符传给子进程之后父子进程之间通信,也可以父进程fork两次,把?文件描述符传给两个子进程,然后两个子进程之间通信, 总之需要通过fork传递文件描述符使两个进程都能访问同一管道,它们才能通信。 也就是说,管道通信是需要进程之间有关系。

使?用管道需要注意以下4种特殊情况(假设都是阻塞I/O操作,没有设置O_NONBLOCK标志):

1. 如果所有指向管道写端的文件描述符都关闭了(管道写端的引?用计数等于0),而仍然有进程 从管道的读端读数据,那么管道中剩余的数据都被读取后,再次read会返回0,就像读到文件末尾一样。

代码和上面的代码类似,只是修改子进程中写入的循环条件,部分代码如下:

 else if(id==0)//child
 22     {
 23         close(_pipe[0]);
 24         int i=0;
 25         char* _mesg_c=NULL;
 26         while(i<10)
 27         {
 28             _mesg_c="i im coder";
 29             write(_pipe[1],_mesg_c,strlen(_mesg_c)+1);
 30             sleep(1);
 31             i++;
 32         }
            close(_pipe[1]);
 33     }

运行结果如下:

2. 如果有指向管道写端的文件描述符没关闭(管道写端的引用计数?大于0),而持有管道写端的 进程也没有向管道中写数据,这时有进程从管道读端读数据,那么管道中剩余的数据都被读取后,再次read会阻塞,直到管道中有数据可读了才读取数据并返回。

3. 如果所有指向管道读端的文件描述符都关闭了(管道读端的引?用计数等于0),这时有进程向管道的写端write,那么该进程会收到信号SIGPIPE,通常会导致进程异常终止。

#include <stdio.h>
  2 #include <unistd.h>
  3 #include <errno.h>
  4 #include <string.h>
  5 #include <sys/wait.h>
  6 void fun()
  7 {
  8     printf("zhongzhi\n");
  9 }
 10 int main()
 11 {
 12     signal(13,fun);
 13 int _pipe[2];
 14 int ret = pipe(_pipe);
 15 if(ret == -1){
 16 printf("create pipe error! errno code is : %d\n",errno);
 17 return 1;
 18 }
 19 pid_t id = fork();
 20 if( id < 0 ){
 21 printf("fork error!");
 22 return 2;
23 }else if( id == 0 ){ //child
 24 close(_pipe[0]);
 25 int i =0;
 26 char *_mesg_c=NULL;
 27 while(i<20){
 28 if( i < 10 ){
 29 _mesg_c="i am child!";
 30 write(_pipe[1], _mesg_c, strlen(_mesg_c)+1);
 31 }
 32 sleep(1);
 33 i++;
 34 }
 35 }else{ //father
 36 close(_pipe[1]);
 37 char _mesg[100];
 38 int j = 0;
 39 while(j<3){
 40 memset(_mesg, ‘\0‘, sizeof(_mesg));
 41 int ret = read(_pipe[0], _mesg, sizeof(_mesg));
 42 printf("%s : code is : %d\n",_mesg, ret);
 43 j++;
44 }
 45 close(_pipe[0]);
 46 sleep(10);
 47 if ( waitpid(id, NULL, 0)< 0)
 48 {
 49 return 3;
 50 }
 51 }
 52 return 0;
 53 }

运行结果如下:

4. 如果有指向管道读端的文件描述符没关闭(管道读端的引?用计数?大于0),?而持有管道读端的 进程也没有从管道中读数据,这时有进程向管道写端写数据,那么在管道被写满时再 次write会阻塞,直到管道中有空位置了才写?入数据并返回。

命名管道

1 概念

管道的一个不足之处是没有名字,因此,只能用于具有亲缘关系的进程间通信,在命名管道(named pipe或FIFO)提出后,该限制得到了克服。FIFO不同于管道之处在于它提供一个路径名与之关联,以FIFO的文件形式存储于文件系统中。命名管道是一个设备文件,因此,即使进程与创建FIFO的进程不存在亲缘关系,只要可以访问该路径,就能够通过FIFO相互通信。值得注意的是,FIFO(first input first output)总是按照先进先出的原则工作,第一个被写入的数据将?首先从管道中读出。

2 命名管道的创建与读写

Linux下有两种方式创建命名管道。一是在Shell下交互地建立一个命名管道,二是在程序中使?用系统函数建?立命名管道。Shell方式下可使用mknod或mkfifo命令,下面命令使用mknod创建了一个命名管道:mknod namedpipe创建命名管道的系统函数有两个:mknod和mkfifo。两个函数均定义在头?文件sys/stat.h,

函数原型如下:

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

函数mknod参数中path为创建的命名管道的全路径名:mod为创建的命名管道的模式,指明其存取权限;dev为设备值,该值取决于文件创建的种类,它只在创建设备文件时才会?用到。这两个函数调用成功都返回0,失败都返回-1。下面使用mknod函数创建了一个命名管道:

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

“S_IFIFO|0666”指明创建?一个命名管道且存取权限为0666,即创建者、与创建者同组的?用户、其他用户对该命名管道的访问权限都是可读可写( 这?里要注意umask对生成的管道?文件权限的影响 )。命名管道创建后就可以使用了,命名管道和管道的使用方法基本是相同的。只是使?用命名管道时,必须先调用open()将其打开。因为命名管道是一个存在于硬盘上的?文件,而管道是存在于内存中的特殊?文件。 需要注意的是,调用open()打开命名管道的进程可能会被阻塞。但如果同时用读写方式(O_RDWR)打开,则?一定不会导致阻塞;如果以只读方式(O_RDONLY)打开,则调?用open()函数的进程将会被阻塞直到有写方打开管道;同样以写?方式(O_WRONLY)打开也会阻塞直到有读?方式打开管道。

3、总结

mkfifo函数的作?用是在?文件系统中创建?一个?文件,该?文件?用于提供FIFO功能,即命名管道。

前边讲的那些管道都没有名字,因此它们被称为匿名管道,或简称管道。对?文件系统来说,

匿名管道是不可见的,它的作?用仅限于在?父进程和?子进程两个进程间进?行通信。?而命名管

道是?一个可见的?文件,因此,它可以?用于任何两个进程之间的通信,不管这两个进程是不

是?父?子进程,也不管这两个进程之间有没有关系。

下面我们通过程序演示下FIFO的功能

FIFO server端:

 1 #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
  8 #define _PATH_ "./file.tmp"
  9 #define _SIZE_ 100
 10
 11 int main()
 12 {
 13     int ret=mkfifo(_PATH_,0666|S_IFIFO);
 14     printf("%d\n",ret);
 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         return 1;
 25     }
 26     char buf[_SIZE_];
 27     memset(buf,‘\0‘,sizeof(buf));
 28     while(1)
 29     {
 30         scanf("%s",buf);
 31         int ret=write(fd,buf,sizeof(buf));
 32         if(ret<=0)
 33         {
 34             printf("raed end or error!\n");
 35             break;
 36         }
 37         printf("%s\n",buf);
 38         if(strncmp(buf,"quit",4)==0)
 39         {
 40             break;
 41         }
 42     }
 43     close(fd);
 44     return 0;
 45 }

FIFO client端:

 1 #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
  8 #define _PATH_ "./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("raed 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     close(fd);
 36     return 0;
 37 }

运行结果如下:

时间: 2024-08-25 12:54:59

Linux基础篇 进程通信——管道的相关文章

Linux系统编程@进程通信(一)

进程间通信概述 需要进程通信的原因: 数据传输 资源共享 通知事件 进程控制 Linux进程间通信(IPC)发展由来 Unix进程间通信 基于System V进程间通信(System V:UNIX系统的一个分支) POSIX进程间通信(POSIX:可移植操作系统接口,为了提高UNIX环境下应用程序的可移植性.很多其他系统也支持POSIX标准(如:DEC OpenVMS和Windows).) 现在Linux使用的进程间通信方式包括: 管道(pipe).有名管道(FIFO) 信号(signal) 消

C++技术问题总结-第2篇 进程通信

进程通信的方法 进程通信基本方法有如下几种. 1.消息传递机制. 2.共享内存. 3.管道和邮槽. 4.剪贴板. 5.Socket通信. 1.消息传递机制 使用WM_COPYDATA实现进程间的传递数据. 函数原型 LRESULT SendMessage(HWND hWnd,UINT Msg,WPARAM wParam,LPARAM IParam) 参数 hWnd:其窗口程序将接收消息的窗口的句柄.如果此参数为HWND_BROADCAST,则消息将被发送到系统中所有顶层窗口,包括无效或不可见的非

Linux基础篇--linux基础和帮助用法

Linux基础篇--linux基础和帮助用法 本章概要 用户登录 终端 Shell介绍 执行命令 简单命令 Tab键补全 命令行历史 bash快捷键 使用gnome-terminal whatis command--help manandinfo 本地帮助文档/usr/share/doc/ 官网在线文档 其它网站和搜索 用户登录 root用户   一个特殊的管理帐户   也被称为超级用户   root已接近完整的系统控制   对系统损害几乎有无限的能力   除非必要,不要登录为root 普通用户

Linux进程通信——管道

进程间通信(IPC:Inner Proceeding Communication) 进程是操作系统实现程序独占系统运行的假象的方法,是对处理器.主存.I/O设备的抽象表示.每个进程都是一个独立的资源管理单元,每个进程所看到的是自己独占使用系统的假象,因此各个进程之间是不能够直接的访问对方进程的资源的,不同的进程之间进行信息交互需要借助操作系统提供的特殊的进程通信机制. 进程之间的通信,从物理上分,可以分为同主机的进程之间的通信和不同主机间的进程之间的通信.从通信内容方式上分,可以分为数据交互.同

linux下的进程通信之管道

概念:管道是由内核管理的一个缓冲区,相当于我们放入内存中的一个纸条.管道的一端连接一个进程的输出.这个进程会向管道中放入信息.管道的另一端连接一个进程的输入,这个进程取出被放入管道的信息. 优点:不需要加锁,基于字节流不需要定义数据结构 缺点:速度慢,容量有限,只能用于父子进程之间,使用场景狭窄 基本原理: 一个缓冲区不需要很大,它被设计成为环形的数据结构,以便管道可以被循环利用.当管道中没有信息的话,从管道中读取的进程会等待,直到另一端的进程放入信息.当管道被放满信息的时候,尝试放入信息的进程

Linux间的进程通信;以及子进程的创建

1 "-----第六天-----------------------------------------------------------------------------" 2 3 1.版本控制:svn/git; 4 5 2.进程的概念: 6 1)程序和进程: 7 每个进程操作系统会为它分配 0-4G 的虚拟内存空间(32位操作系统): 其中0-3G为用户内存空间,进程可以对它进行读写操作: 3G - 4G 为系统内核空间,进程没有读写权限. 8 进程只能读写用户空间,没有权限读

进程通信----管道(pipe)

 Linux管道的实现机制 在Linux中,管道是一种使用非常频繁的通信机制.从本质上说,管道也是一种文件,但它又和一般的文件有所不同,管道可以克服使用文件进行通信的两个问题,具体表现为: ·      限制管道的大小.实际上,管道是一个固定大小的缓冲区.在Linux中,该缓冲区的大小为1页,即4K字节,使得它的大小不象文件那样不加检验地增长.使用单个固定缓冲区也会带来问题,比如在写管道时可能变满,当这种情况发生时,随后对管道的write()调用将默认地被阻塞,等待某些数据被读取,以便腾出足够的

linux 单机跨进程通信

一般来说通过网络通信(比如tcp,udp)或者共享内存的方式肯定可以实现跨进程通信,但现在这里要说的是比较偏但实用的几个方法:利用unix域通信(普通网络连接),利用unix域通信(socketpair通信),以及pipe方式. 一. 利用unix域通信(普通网络连接) socket API原本是为网络通讯设计的,但后来在socket的框架上发展出一种IPC机制,就是UNIX Domain Socket.虽然网络socket也可用于同一台主机的进程间通讯(通过loopback地址127.0.0.

Linux基础篇和服务器篇教程

基础篇 服务器篇 视频内容很详细,看完后就是中级攻城师了!!! 视频内容很详细,看完后就是中级攻城师了!!! 视频内容很详细,看完后就是中级攻城师了!!! 如有需要留言邮箱!!!记得推荐文章,让跟多的人看到!! 如有需要留言邮箱!!!记得推荐文章,让跟多的人看到!! 如有需要留言邮箱!!!记得推荐文章,让跟多的人看到!!