《Linux操作系统原理分析之Linux 进程管理 6》(10)
- 4 Linux 进程管理
- 4.6 Linux 管道
- 4.6.1 管道的概念
- 4.6.2 无名管道
- 1.终端使用
- 2.程序中使用
- 4.6.2 命名管道
- 1.终端使用
- 2.程序中使用
4 Linux 进程管理
4.6 Linux 管道
4.6.1 管道的概念
1、管道是 linux 进程通信的一种手段
2、管道通信时,一端写管道进程,另一端读管道进程,获取数据。
3、管道本质是共享文件。
4、管道以文件系统为基础实现。
5、数据在管道中先进先出方式,以字符流形态传送。
管道分两种:无名管道、命名管道。它们的内部结构是一致的,但是用方式不同。
无名管道:只能在父子进程之间通信
命名管道:可以在任意进程间通信。
4.6.2 无名管道
linux 管道可以在终端的命令行中使用,也可以在程序中使用。
1.终端使用
无名管道连接两个命令,如下命令中“|”就是管道
$ls –l | more
2.程序中使用
在程序设计中无名管道的建立由系统调用 pipe()实现,其定义:
#include <unstd.h>
Int pipe(int filedes[2]);
其中 filedes[]是具有两个元素的 int 型数组。在调用 pipe()建立一个无名管道后,使用两个文件标识号来表示管道的两端(一端写,一端读),并记入 filedes[]中。其中 filedes[0]是读取管道的文件标识号,filedes[1]是写入管道的文件标识号。
说明::::
父子进程使用无名管道通信是建立在子进程继承父进程资源的基础上。父子进程通信时,必须先建立管道,再创建子进程。
使用管道时必须确定管道通信的方向,且一旦确定后不能改变。
父子进程中一个进程只能使用一个文件标识号,所以另一个不使用的标识号可以使用系统调用close()关闭它。
举例:在该例中,父进程建立管道后创建一个子进程。子进程的任务是把一组字符串信息写入管道,父进程在子进程完成任务终止后,从管道中读取信息并显示在显示器上。
#include <stdio.h>
Main()
{
Pid_t pid;
Int fds[2];
Char buf1[50],buf2[50];
Pipe(fds); /*建立无名管道*/
If((pid=fork())<0)/*创建子进程失败,程序终止*/
{
Printf(“Fork() Error\n”);
Exit(1);
}
Else if(pid==0) /*子进程*/
{
Close(fds[0]); /*关闭不使用的文件标识号*/
Sprintf(buf1,”these are transmitted data\n”); /*把信息写入缓冲区 buf1*/
Write(fds[1],buf1,50); /*把缓冲区 buf1 中的信息写入管道*/
Exit(1) /*子进程终止*/
}
Else
{
Close(fds[1]); /*关闭不使用的文件标识号*/
Wait(0); /*等待子进程结束*/
Read(fds[0],buf2,50); /*读管道信息并把信息置入 buf2*/
Printf(“%s\n”,buf2);/*显示 buf2 中的信息*/
}
}
说明:
1、无名管道与一般文件不同,它没有纳入文件系统的目录,不占用外存空间,仅使用内存作为数据传输的缓冲区。
2、缓冲区的大小决定每次写入管道的字节数。该值由全局符号常量 PIPE BUF 确定。 缺省值为一个物理页面。
3、由文件系统管理,但此时没有文件系统知识基础,故暂时不讲详细管理
4.6.2 命名管道
又称 FIFO 管道。命名管道与无名管道的区别:命名管道有文件名,在文件系统中可见;可以实现任意进程间的通信。
1.终端使用
使用 mkfifo 建立一个命名管道。
例:$mkfifo myfifo;/*建立一个名字为 myfifo 的管道。
此时使用 ls 命令,就可以查看到该文件信息。
$ls –l myfifo
prw-r---- wang user 0 fen 22 13:45 myinfo /*第一个字符 p,表示是 FIFO 文件
在命名管道建立后,就可以用它在两个进程间进行通信。如:
$cut –c1-5< myfifo&
$cat file1 >myfifo
其中 cat 命令把文件 file1 的内容写入管道 myfifo,命令 cut 从管道 myfifo 中读出文件的内容进行裁剪后显示每行的前 1~5 个字符。
2.程序中使用
1)建立命名管道
方法一:调用 C 函数 mkfifo()实现的,其定义如:
#include <sys/state.h>
Int mkfifo(const char *path, mode_t mode);
其中:
path:指明要创建的命名管道的路径和名字;
mode:指明管道访问的权限。 创建成功返回 0,否则为负数。 另一种方法:使用
linux 系统调用 mknod() mknod()可以建立任何类型的文件,在建立命名管道时使用的形式如下:
mknod(path,mode|S_FIFO,0); 参数含义与上同,其中 S_FIFO 表示建立 FIFO 文件。
这两种方法作用基本相同:建立命名管道的目录结构、inode 节点、file 文件结构体等。
2)打开管道
由于任何进程都可以通过命名管道进行通信,所以在使用命名管道时,必须先打开它,由系统调用open()实现。
open(char *path, int mode)
其中: path:指明要使用的命名管道的路径和名字;
mode:指明管道访问的模式:O_RDONLY(只读)、O_WRONLY(只写)
创建成功返回文件标识号,否则为负数。 注:在使用文件操作对管道进行各种操作时,要使用文件标识号,而不是管道名。
例:有两个程序,其中 wrfifo.c 把一组信息写入管道,另一个程序 rdfifo.c 把管道中的信息读出后显示在
屏幕上。
/*读管道程序 rdfifo.c*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
Main(void)
{
Int fd,len;
Char buf[PIPE_BUF];
Mode_t mode=0666;
If(mkfifo(“fifo1”,mode)<0)
{
Printf(“mkfifo error\n”);
Exit(1);
}
If((fd=open“fifo1”,O_RDONLY))<0
{
Printf(“pipe open error\n”);
Exit(1);
}
While(len=(read(fd,buf,PIPE_BUF-1)>0)
Printf(“read fifo pipe: %s”,buf);
Close(fd);
}
/*写管道程序 wdfifo.c*/
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <limits.h>
Main(void)
{
Int fd,len;
Char buf[PIPE_BUF];
Mode_t mode=0666;
If((fd=open(“fifo1”,O_WRONLY))<0
{
Printf(“pipe open error\n”);
Exit(1);
}
For(i=0;i<3;i++)
{
Len=sprintf(buf,”write fifo pipe from %d at %d times\n”,getpid(),i+1);
Write(fd,buf,len+1);
}
Close(fd);
}
在 linux 终端上运行这两个程序:
$./rdfifo&
$./wrfifo
结果:
Write fifo pipe from 945 at 1 times
Write fifo pipe from 945 at 2 times
Write fifo pipe from 945 at 3 times