《Unix/Linux编程实践教程》chapter10 I/O 重定向和管道

chapter10 I/O 重定向和管道

章节知识总结

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

所谓文件描述符,是一个数组的索引号。每个进程都有其打开的一组文件。这些打开的文件被保持在一个数组中。文件描述符即为某文件在数组中的索引。

最低可用文件描述符:当打开文件时,为此文件安排的描述符总是此数组中最低可用位置的索引。

stdin重定向到文件有三种方法:

方法一:close(0)…open()

close then open的方法首先将0号文件描述符关闭于键盘的连接,由于此时最低可用文件描述符为0,当open一个新文件时,给该新文件分配的文件描述符为0,从而实现了将stdin重定向到文件。

#include<stdio.h>
#include<unistd.h>  //for close()
#include<fcntl.h>   //for open()

int main()
{
    char line[100];

    //首先从键盘读取输入
    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);

    //close(0)-then-open(fd)
    close(0);
    if(open("/etc/passwd",O_RDONLY)==-1)
        perror("open");

    //之后从/etc/passwd读取输入
    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);

    return 0;
}
方法二/三:open(file)…close(0)…dup(fd,0)…close(fd) / open(file)…dup2(fd,0)…close(fd)

open(file)..close(0)..dup(fd)..close(fd)操作如下图所示:fd=open(file)首先分配一个文件描述符,之后close(0)使得此时最低可用文件描述符为0,通过dup(fd)fd和最低可用文件描述符(此时为0)两个文件描述符都连接到新文件,此时close(fd)则只有0连接到新文件,从而实现stdin重定向到文件。

dup2(fd,0)操作相当于close(0),dup(fd,0).

在这里插入图片描述

#include<stdio.h>
#include<unistd.h>  //for close() and dup()
#include<fcntl.h>   //for open()

//#define CLOSE_DUP

int main()
{
    char line[100];

    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);

    //open(fd)..close(0)..dup(fd)..close(fd)
    int fd;
    if((fd=open("/etc/passwd",O_RDONLY))==-1)
        perror("open");

    #ifndef CLOSE_DUP
        close(0);
        if(dup(fd)==-1)
            perror("dup");
    #else
        dup2(fd,0);
    #endif

    close(fd);

    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);
    fgets(line,100,stdin); printf("%s\n",line);

    return 0;
}

要将stdout重定向到文件也可以采用上述的方法,只是将open操作改为creat操作即可,creat操作同样适用最低可用文件描述符。

需要注意的是,文件描述符集合通过exec调用传递,且不会改变。 利用这个特性可以实现父子进程之间的通信。

//将子进程中exec调用程序的输出重定向到userlist
#include<stdio.h>
#include<unistd.h>      //for close()
#include<fcntl.h>       //for creat()

int main()
{
    int pid;
    printf("About to run who into a file\n");
    if((pid==fork())==-1){
        perror("fork");
    }
    if(pid==0){
        close(1);
        creat("userlist",0644);
        execlp("who","who",NULL);
        perror("execlp");
        exit(0);
    }
    else{
        wait(NULL);
        printf("Done running who. results in userlist.\n");
    }
    return 0;
}

管道是内核中的一个单向的数据通道。管道有一个读取端和一个写入端,通过pipe(int array[2])创建管道,其中array[0]为读数据端的文件描述符,而array[1]则为写数据段的文件描述符,类似于open调用,pipe调用也使用最低可用文件描述符。

在这里插入图片描述

pipefork结合起来,就可以连接两个不同的进程了。两个进程都可以读写管道,但是当一个程序读,另一个进程写的时候,管道的使用效率是最高的。

下面通过一个简单的例子实现指令pipe who sort,其中pipe是指令关键字,who指令通过父进程执行exec实现,sort指令通过子进程执行exec实现(非常好的一个例子)。

注意:只有共同父进程的进程之间才可以用管道连接。

#include<stdio.h>
#include<unistd.h>

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

int main(int ac,char* av[])
{
    int thepipe[2],newfd,pid;
    if(ac!=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("Cannot not redirect stdin",3);
        close(thepipe[0]);
        execlp(av[2],av[2],NULL);
        oops(av[2],4);
    }
    close(thepipe[0]);
    if(dup2(thepipe[1],1)==-1)
        oops("Cannot not redirect stdin",4);
    close(thepipe[1]);
    execlp(av[1],av[1],NULL);
    oops(av[1],5);
    return 0;
}

与文件相比,管道的不同点体现在:

从管道中读数据
  1. 管道读取阻塞: 当进程试图从管道中读取数据时,进程被挂起直到数据被写进管道
  2. 管道的读取结束标志: 当所有的写者关了管道的写数据端时,试图从管道读取数据的调用返回0,这意味着文件的结束
  3. 多个读者可能会引起麻烦: 多个进程读取一个管道可能会产生不可预见的错误
从管道中写数据
  1. 写入数据阻塞直到管道有空间去容乃新的数据:管道容纳数据的能力要比磁盘文件差的多。
  2. 写入必须保证一个最小的块大小(?)
  3. 若无读者在读取数据,则写操作执行失败:如果所有的读者都已将管道的读取端关闭,那么对管道的写入调用将会执行失败(此时内核采用两种方法来通知进程:一是发送SIGPIPE消息该进程,若进程被终止,则无任何事情发生。否则(即未对SIGPIPE信号进行处理),采用方法二,二是write调用返回-1,并且将errno置为EPIPE)。
系统调用
dup,dup2
用途复制一个文件描述符
头文件#include<unistd.h>
函数原型newfd=dup(oldfd); newfd=dup2(oldfd,newfd);
参数oldfd需要复制的文件描述符 ; newfd复制oldfd后得到的文件描述符
返回值-1:发生错误; newfd:新的文件描述符
pipe
用途创建管道
头文件#include<unistd.h>
函数原型result=pipe(int array[2])
参数array:包含两个int类型数据的数组
返回值-1:发生错误 ; 0:成功
  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
好的,下面是关于Unix/Linux编程实践的一些指南: 1. 学习命令行:Unix/Linux操作系统是命令行驱动的,因此学习命令行是非常要的。可以学习一些基本命令,如ls,cd,mkdir,rm等等。可以通过查看man页面来获取有关命令的详细信息。 2. 熟悉shell脚本:shell脚本是Unix/Linux系统上的一种编程语言,可以帮助自动化许多复性任务。可以编写脚本来自动执行常见任务,如备份文件,检查系统日志等。 3. 使用版本控制:版本控制是软件开发过程中的要组成部分。可以使用Git或SVN等工具来跟踪代码的变化,使其更容易管理和维护。 4. 学习编程语言:学习一种Unix/Linux系统上常用的编程语言,如C语言,Python等。这些语言都有大量的库和工具可供使用,可帮助您轻松地创建各种应用程序。 5. 编写文档和注释:在编写代码时,务必编写注释和文档。这将有助于其他开发人员了解您的代码,使其更易于维护和修改。 6. 熟悉网络编程Unix/Linux系统是网络应用程序的理想平台。学习如何编写网络应用程序,如Web服务器和客户端,将有助于您构建更复杂的应用程序。 7. 研究系统调用和库函数:Unix/Linux系统提供了许多系统调用和库函数,可帮助您执行各种任务。了解这些调用和函数将使您能够更好地控制系统,并编写更高效的代码。 希望这些指南能够帮助您开始学习Unix/Linux编程实践

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值