博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
I/O重定向和管道——《Unix/Linux编程实践教程》读书笔记(第10章)
阅读量:7098 次
发布时间:2019-06-28

本文共 3963 字,大约阅读时间需要 13 分钟。

hot3.png

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);#endifclose(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;}

转载于:https://my.oschina.net/happynewyeah/blog/298504

你可能感兴趣的文章
dotnet rest实现
查看>>
K8S Deployment 命令
查看>>
[JavaScript] Imitate String.Format() in c#
查看>>
wxstring与其他类型转换
查看>>
Java synchronized详解
查看>>
Frameset使用教程
查看>>
局域网与internet
查看>>
request
查看>>
Beyond Compare乱码问题汇总
查看>>
线程和线程池
查看>>
Camstar开发常用数据库表及其关联
查看>>
html中的一些按钮之类的操作
查看>>
走进 AQS 瞧一瞧看一看
查看>>
NO18 linux开机自启动设置--开机流程--中文乱码--查看行数
查看>>
Java的四种内部类
查看>>
10-16C#for...循环语句(2)
查看>>
CentOS查看软件源提供的软件版本命令
查看>>
caffe 学习记录1及网络结构
查看>>
html5学习笔记——html新增属性(四)
查看>>
收藏的链接
查看>>