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)
int oldfd;
oldfd = open("app_log",(O_RDWR|O_CREATE),0644);
dup2(oldfd,1);
close(oldfd);
函数mkfifo
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char* pathname, mode_t mode);
系统命令: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);