8
6
2010
0

Linux编程学习3——管道

【其中一些内容发早了,建议阅读本篇前看看下一篇《Linux的错误处理》再阅读本篇】

由于我在Windows下也没怎么用过管道,所以只能照猫画虎来写这个了。

管道是两个进程来进行通信的一种方式,所以被大量采用了。

我给一个输出ls ~输出的例子,因为不太懂管道,所以就是最常用的——捕获另一个程序的输出。

#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>

int main(  )
{
	FILE *fp;
	char cmd[] = "ls ~";
	char buf[PIPE_BUF];

	fp = popen( cmd , "r" ); //pipe open,打开为一个子进程(个人理解),并且是只读模式 
	if(!fp) //打开失败了
	{
		perror("不能创建管道");
		return 1;
	}
	while(fgets(buf,sizeof(buf),fp)!=NULL) //抓取管道缓冲区内容
		printf("%s",buf); //输出

	pclose(fp); //关闭管道
	return 0;
}

这里创建的是一个匿名管道,只适用于父子、兄弟进程的通信,这里我觉得popen创建的是一个子进程,有错请拍砖。

命名管道适用于任何一个进程间的通信(前提是两个进程都处理了管道代码,否则是无法让进程B接受进程A的信息,即使传送也是没有意义的)

因为LINUX特别的机制(也是UNIX的思想)——一切都是文件。连硬件都是,当然包括管道了。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>

//系统调用
#include <errno.h>
#include <fcntl.h>

#define FIFO_FILE "/tmp/myfifo"
#define ARRLEN 8192

void showerror( void ) //云风:只有参数列表为void的函数才是无参
{
	printf("Error %d: %s\n\n", errno, (char*)strerror(errno));
}

int main(  )
{
	int fd; //文件号
	char *buff=(char*)malloc(ARRLEN);//8kb的数据
	if(buff==NULL)  //内存不足
	{
		printf("致命错误:内存不足");
		return -1;
	}
	memset(buff,0,ARRLEN);
	//创建管道文件
	mkfifo(FIFO_FILE,O_CREAT | O_EXCL); //O_EXCL可执行,O_CREAT无E
	if (errno!=EEXIST && errno!=0)//不能创建但文件存在
	{
		puts("致命错误:不能创建有名管道");
		return 1;
	}
	fd=open(FIFO_FILE,O_RDWR|O_TRUNC,0); //读写|清零
	if(fd == -1)
	{
		showerror();
		return 2;
	}
	//写入管道
	strcpy(buff,"这条信息是使用管道写入的\n"
		"本管道在/tmp/myfifo\n"
		"程序结束后会自动删除这个管道\n");
	if(write(fd,buff,strlen(buff))==-1)
	{
		showerror();
		return 3;
	}
	//读入管道内容
	if(read(fd,buff,ARRLEN)==-1)
	{
		showerror();
		return 3;
	}
	printf("%s",buff);
	unlink(FIFO_FILE);

	return 0;
}

因为我们对/tmp/没有写入权限,所以你执行必须用root权限才可以。

我试试写一个Server和Client,在Server运行期间,不断抓取管道内容,直到client向里面写入数据,这时输出并退出。

#include <stdio.h>
#include <stdlib.h>
#include <memory.h>
#include <string.h>

//系统调用
#include <errno.h>
#include <fcntl.h>

#define FIFO_FILE "/tmp/myfifo"
#define ARRLEN 8192

void showerror( void ) //云风:只有参数列表为void的函数才是无参
{
	printf("Error %d: %s\n\n", errno, (char*)strerror(errno));
}

int main(  )
{
	int fd; //文件号
	char *buff=(char*)malloc(ARRLEN);//8kb的数据
	if(buff==NULL)  //内存不足
	{
		printf("致命错误:内存不足");
		return -1;
	}
	memset(buff,0,ARRLEN);
	//创建管道文件
	mkfifo(FIFO_FILE,O_CREAT | O_EXCL); //O_EXCL可执行,O_CREAT无E
	if (errno!=EEXIST && errno!=0)//不能创建但文件存在
	{
		puts("致命错误:不能创建有名管道");
		return 1;
	}
	fd=open(FIFO_FILE,O_RDWR|O_TRUNC,0); //读写|非阻塞|清零
	if(fd == -1)
	{
		showerror();
		return 2;
	}
	
	//抓取管道内容,每1秒一次
	while(1)
	{
		if(read(fd,buff,ARRLEN)==-1)
		{
			showerror();
			return 3;
		}
		if(strlen(buff))
			break;
		sleep(1000);
	}
	printf("%s",buff);
	unlink(FIFO_FILE);

	return 0;
}
#include <stdio.h>
#include <string.h>

#include <fcntl.h>
#include <errno.h>

#define FIFO_FILE "/tmp/myfifo"

void showerror(void)
{
	printf("Error %d: %s\n", errno, strerror(errno));
}

int main()
{
	int fd;
	char buff[] = "这些内容由管道Client写入\n";
	fd=open(FIFO_FILE,O_RDWR|O_TRUNC);
	if(fd == -1)
	{
		showerror();
		printf("服务端未打开?\n");
		return 1;
	}
	if(write(fd,buff,strlen(buff))==-1)
	{
		showerror();
		return -1;
	}
	return 0;
}

编译,首先执行sudo server,发现没有任何输出,而且迟迟不退出,这正是我们期望的,再打开一个终端GUI,执行sudo client,发现服务端处输出“这些内容由管道Client写入”,并且client和server都退出了,这也是我们期望的。不过我发现,循环内指示一秒抓取一次,说明在执行client时server应有一点延迟,然而我并未感觉到延迟,这可能是1秒太短暂了

正当我打算在server内使用remove删除FIFO_FILE时,意外发生了,/tmp/内并没有myfifo文件,这如何解释?我认为Server可以看作管道的拥有者,当unlink触发时,管道与拥有者断开链接,这时候,unlink就会轻松地将myfifo送上西天。你可能觉得这与mkfifo有关联,认为这会导致管道和程序连理某种不可告人的连接。我也曾经这样想过,不过不幸的是,再看看client,这下就明了了,实际上,它……仅……仅……是……一……个……文……件!!!!!!!或许你会觉得把文件还故弄玄虚叫成管道太麻烦,当然,你也可以用文件来搞定!共享读写就行了。

你又有什么关于Linux管道的看法,告诉我吧!毕竟,Windows的管道生成太麻烦了,竟然要填写什么结构,还要写具有超多参数的CreatePipe(与Windows的窗口类和CreateWindow[Ex]有点类似)

Category: | Tags: | Read Count: 1248

登录 *


loading captcha image...
(输入验证码)
or Ctrl+Enter

Powered by Chito | Hosted at is-Programmer | Theme: Aeros 2.0 by TheBuckmaker.com