linux pipe函数 重定向,I/O重定向和管道——《Unix/Linux编程实践教程》读书笔记(第10章)...

1、I/O重定向的概念与原因 及 标准输入、输出的标准错误的定义

所以的Unix I/O重定向都基于标准数据流的原理。三个数据了分别如下:

1)标准输入——需要处理的数据流

2)标准输出——结果数据流

3)标准错误输出——错误消息流

概念:所以的Unix工具都使用文件描述符0、1和2。标准输入文件的描述符是0,标准输出的文件描述符是1,而标准错误输出的文件描述符则是2。Unix假设文件描述符0、1、2已经被打开,可以分别进行读写操作。

通常通过shell命令行运行Unix系统工具时,stdin、stdout、stderr连接在终端上。因此,工具从键盘读取数据并且把输出和错误消息写到屏幕。

大部分的Unix工具处理从文件或标准输入读入的数据。如果在命令行上给出了文件名,工具将从文件读取数据。若无文件名,程序则从标准输入读取数据。从另一方面说,大多数程序并不接收输出文件名;它们总是将结果写到文件描述符1,并将错误消息写到文件描述符2。如果希望将进程的输出写道文件或另一个进程的输入去,就必须重定向相应的文件描述符。

重定向I/O的是shell而不是程序

最低可用文件描述符(lowest-available-fd)原则:文件描述符是一个数组的索引号。每个进程都有其打开的一组文件。这些打开的文件被保持在一个数组中。文件描述符即为某文件在此数组中的索引。当打开文件时,为此文件安排的描述符总是此数组中最低可用位置的索引。

将文件描述符0、1、2的概念和最低可用文件描述符原则结合使用,即可理解I/O重定向的工作原理

2、重定向标准I/O到文件

(1)如何将stdin定向到文件

a、close-then-open策略

close(0);

int fd = open(file, O_RDONLY);

b、open-close-dup-close策略

int oldfd = open(file, O_RDONLY);

#ifndef DUP2

close(0);

int newfd = dup(oldfd);

#else

int newfd = dup2(oldfd, 0);

#endif

close(oldfd);

man 2 dup

#include

int dup(int oldfd);

int dup2(int oldfd, int newfd);

系统调用dup复制了文件描述符oldfd。而dup2将oldfd文件描述符复制给newfd。两个文件描述符都指向同一个打开的文件。这两个调用都返回新的文件描述符,若发生错误,则返回-1。

c、open-dup2-close策略

(2)重定向到文件

共有3个基本的概念,利用它们是的Unix下的程序可以轻易地将标准输入、输出和错误信息输出连接到文件:

a、标准输入、输出以及错误输出分别对应于文件描述符0、1、2;

b、内核总是使用最低可用文件描述符;

c、文件描述符集合通过exec调用传递,且不会被改变。

例子:

/*

* who_to_file.c

* purpose: show how to redirect output for another program

* idea: fork, then in the child, redirect output, then exec

*/

#include

#include

#include

#include

int main(void)

{

pid_t pid;

int fd;

printf("about to run who into a file\n");

/* create a new process or quit */

if ((pid = fork()) == -1)

{

perror("fork");

exit(1);

}

/* child does the work */

if (pid == 0)

{

close(1);

fd = creat("userlist", 0644);

execlp("who", "who", NULL);

perror("execlp");

exit(1);

}

/* parent waits then reports */

else

{

wait(NULL);

printf("done running who. results in userlist\n");

}

return 0;

}

3、管道编程

(1)创建管道

man 2 pipe

#include

int pipe(int pipefd[2]);

系统调用pipe创建管道并将两端连接到两个文件描述符。pipefd[0]为读数据端的文件描述符,而pipefd[1]则为写数据端的文件描述符。

(2)技术细节:管道并非文件

a、从管道中读数据

管道读取阻塞:当进程试图从管道中读数据时,进程被挂起直到数据被写进管道。

管道的读取结束标志:当所以的写操作关闭了管道的写数据端时,试图从管道读取数据的调用返回0,意味着文件的结束。

多个读操作可能会引起麻烦:管道是一个队列。当进程从管道中读取数据之后,数据已经不存在了。

b、向管道中写数据

写入数据阻塞直到管道有空间去容纳新的数据

写入必须保证一个最小的块大小:POSIX标准规定内涵不会拆分小于512字节的块。而Linux则保证管道中可以存在4096字节的连续缓存。如果两个进程向管道写数据,并且没一个进程都限制其消息不打由于512字节,那么这些消息都不会被内核拆分。

若无读操作在读数据,则写操作执行失败:如果所以的读操作都已将管道的读取端关闭,那么对管道的写入调用将会执行失败。如果在这种情况下,数据还可以被接收的话,为了避免数据丢失,内核采用了两种方法来通知进程:“此时的写操作是无意义的”。首先,内核发送SIGPIPE消息给进程。若进程被终止,则无任何事情发生。否则write调用返回-1,并且将errno置为EPIPE。

例子:

/*

* pipe.c -

* demonstrates how to create a pipeline from one process to another

* takes two args, each a command, and connectes

* argv[1]s output to input of argv[2]

* usage: pipe command1 command2

* effect: command1 | command2

* limitations: commands do not take arguments

* users execlp() since known number of args

* note: exchange child and parent and watch fun

*/

#include

#include

#include

#define oops(m, x) { perror(m); exit(x); }

int main(int argc, char **argv)

{

int thepipe[2],

newfd,

pid;

if (argc != 3)

{

fprintf(stderr, "usage: ./pipe cmd1 cmd2\n");

exit(1);

}

if (pipe(thepipe) == -1)

oops("cannot get a pipe", 1);

if ((pid = fork()) == -1)

oops("cannot fork", 2);

if (pid > 0)

{

close(thepipe[1]);

if (dup2(thepipe[0], 0) == -1)

oops("could not redirect stdin", 3);

close(thepipe[0]);

execlp(argv[2], argv[2], NULL);

oops(argv[2], 4);

}

close(thepipe[0]);

if (dup2(thepipe[1], 1) == -1)

oops("could not redirect stdout", 4);

close(thepipe[1]);

execlp(argv[1], argv[1], NULL);

oops(argv[1], 5);

return 0;

}

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值