Peng Lv

毋意,毋必,毋固,毋我。 言必行,行必果。

导航

GNU/Linux应用程序开发学习笔记(二)管道编程

 今天学习Linux编程第二章——管道编程

管道模型是一个古老但仍然有效的在进程之间提供半双工通信功能的机制,并且提供FIFO方式的通信模型。
    在GNU/Linux系统命令中'|'变表示可一种管道模型,比如  cat file.txt | less,表示的是将前一个命令的输出作为后一个进程的输入,这个简单的功能可以为系统管理提供强大的机制模型。注意,管道提供的是半双工模型,如果进程件需要全双工通信,于是必须考虑使用套接字API。


创建一个管道:

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

函数成功时返回0,失败时返回-1,成功时,fds数组返回这个管道的两个文件描述符,fds[0]为管道输出描述,fds[1]为管道的输入描述,并且我们可以用write和read来读取和写入管道。

 
关闭管道:

close(fds[0]);
这个操作其实不是很必要,因为当进程结束时,系统为管道分配的资源会自动释放,不过还是加上的好。
一个简单的管道例子:
 

#include <stdio.h>
#include
<unistd.h>
#include
<wait.h>
#include
<string.h>
#include
<stdlib.h>

#define MAXN_STR 40
#define PIPE_IN 0
#define PIPE_OUT 1

int main()
{
const char* str = {"This is a test."};
char buf[MAXN_STR];
int ret,my_pipe[2];
// 创建一个匿名管道,
ret = pipe(my_pipe);
//如果管道创建成功
if(ret == 0)
{
//写入管道
write(my_pipe[1],str,strlen(str)+1);
//读出管道
ret = read(my_pipe[0],buf,MAXN_STR);
printf(
"%s\n",buf);
}
return 0;
}


dup & dup2

int dup(int oldfd);
int dup2(int oldfd,int targetfd)
dup和dup2提供了复制文件描述符的功能,常用于stdin,stdout的重定向。dup允许复制一个描述符,传入一个已经存在的描述符,就会返回一个于该描述符相同的新描述符,而且这两个描述符共享相同的内部结构。dup2和dup相似,但是它允许调用着指向一个活动的描述符以及一个目标描述符的ID号,在dup2成功返回后,新的目标描述符是复制的第一个描述符(targetfd=oldfd)。现面看书上的一个例子:
int oldfd;
oldfd
= open("app_log",(O_RDWR|O_CREATE),0644);
dup2(oldfd,
1);
close(oldfd);
这里打开一个名为app_log的新文件,获得一个名为oldfd的文件描述符,将标识符为1的文件操作服(stdout)的内容设为oldfd,然后写入stdout的内容现在转而写入名为app_log的文件,然后,这里在复制oldfd装后立即关闭了这个描述符,着不会关闭刚刚打开的文件,因为现在文件描述符1(stdout)已指向这个文件。


函数mkfifo

#include <sys/types.h>
#include
<sys/stat.h>
int mkfifo(const char* pathname, mode_t mode);
函数mkfifo用于在文件系统中创建一个文件,提供FIFO功能,这就是命名管道,这个文件可以用于进程和进程之间的通信。函数成功时返回0,失败时返回-1,第一个参数是管道文件,第二个表示对该FIFO的读写授权设置。

 

系统命令:mkfifo

在linux系统命令中mkfifo可以创建一个命名管道,格式如下

mkfifo [option] name

具体man一下,这个管道文件操作也很方便,例如创建一个cmd_pipe文件,然后通过cat及其他命令来演示一下:

 

[]# mkfifo cmd_pipe
[]# cat cmd_pipe
[]# ll > cmd_pipe 

创建一个管道,用cat接受管道的输出,ll的内容作为管道的输入,便是整个过程了,其实就是一个缩小版的"|"命令。

============================================================


示例:

现在看三个程序,分别是创建一个命名管道,一个读管道进程,一个写管道进程:

创建命名管道 prog-create

//   prog-create
/*
通过mkfifo创建一个名为file_pipe的命名管道 */
#include
<stdio.h>
#include
<unistd.h>
#include
<wait.h>
#include
<string.h>
#include
<stdlib.h>
#include
<sys/types.h>
#include
<sys/stat.h>

#define MAXN_STR 40

char file_name[] = "file_pipe";
int main()
{
int ret;
// 创建命名管道
ret = mkfifo(file_name,S_IFIFO|0666);
if(ret != 0)
exit(
-1);
return 0;
}


读管道进程 prog-read

//   prog-read
/*
读管道进程
* 负责从管道中一行一行的读出数据并显示
*/
#include
<stdio.h>
#include
<unistd.h>
#include
<wait.h>
#include
<string.h>
#include
<stdlib.h>
#include
<sys/types.h>
#include
<sys/stat.h>

#define MAXN_STR 40
char file_name[] = "file_pipe";

int main()
{
FILE
* pfp;
char buf[MAXN_STR];
//打开管道文件
pfp = fopen(file_name,"r");
while(true)
{
//读取管道数据并显示
fgets(buf,MAXN_STR,pfp);
printf(
"GET: %s",buf);
}
fclose(pfp);
return 0;
}


写管道进程 prog-write

//   prog-write
/*
写管道进程
* 负责处理用户输入并写进管道文件
*/
#include
<stdio.h>
#include
<unistd.h>
#include
<wait.h>
#include
<string.h>
#include
<stdlib.h>
#include
<sys/types.h>
#include
<sys/stat.h>

#define MAXN_STR 40
char file_name[] = "file_pipe";
int main()
{
FILE
* pfp;
char str[MAXN_STR];
//打开管道文件
pfp = fopen(file_name,"w");
while(true)
{
scanf(
"%s",str);
if(strcmp(str,"end") == 0)
break;
//写入管道
fprintf(pfp,"%s\n",str);
//清空pfp缓存
fflush(pfp);
}
fclose(pfp);
return 0;
}

NOTE!

  此处注意prog-write中的while循环,里面用到了fflush()函数,原因是fprintf在写入管道时会使用缓存,这将会导致用户输入的信息不能及时的在prog-read中显示出来,要解决此种问题,可有3种方法:

  1.fflush(),这个函数可以及时清空缓存,使数据及时送到目的地

  2.setvbuf(),同理,这个函数可以设置文件流的缓存大小,设置成0就OK

  3.fopen()&fclose(),这个都知道,就是麻烦而已

把文件流的缓存设置为0会影响效率,所以缓存的大小还要依情况而定。

============================================================================================

总结之管道编程API

#include <unistd.h>
int pipe(int fds[2]);
int dup(int oldfd);
int dup2(int oldfd, int targetfd);
int mkfifo(const char* pathname, mode_t mode);

posted on 2011-02-07 02:04  Lvpengms  阅读(1335)  评论(0编辑  收藏  举报