Linux C:文件描述符、IO重定向、恢复标准输入输出

您所在的位置:网站首页 linux系统io Linux C:文件描述符、IO重定向、恢复标准输入输出

Linux C:文件描述符、IO重定向、恢复标准输入输出

2023-11-14 21:53| 来源: 网络整理| 查看: 265

目录

   

一、文件描述符  

二、IO重定向

三、重定向回终端、伪终端

四、恢复标准输入输出

    一、文件描述符  

     在Linux中,文件描述符是一个非负整数的数据类型。是FILE结构体中的一个成员属性。 每打开或者新建一个文件时,内核都会返回最小的且未被使用的非负整数,即文件描述符。例如,文件描述符 0,1,2,4,5...已经被该进程使用了,那么再打开一个文件返回的文件描述符就是3,再打开一个新文件就是6。如果文件描述符被关闭,那么文件描述符在下一次可能会重新被打开。

     FILE结构体大致如下

-----------FILE Structure--------- char fbuf[SIZE]; int counter,index.......... 一些其它属性 int fd; //文件描述符

Linux 为每个进程创建了文件描述符表(fd, 文件指针),其中文件指针,指向了系统级的打开文件表,通过该指针可以获取到文件偏移量和i-node指针信息,再通过文件偏移量和i-node指针,可以定位到文件系统中的i-node表,从而找到物理硬盘上的文件。换言之,在操作系统之上,通过进程号和文件描述符就可以定位到具体的文件。这个关系是一对多的,因为一个对象可以被多个指针指向,而一个指针只能指向一个对象。这就意味着,多个进程级的文件描述符可能会指向同一个系统级打开文件表,多个打开文件表项可能指向同一个i-node表。

二、IO重定向

在sh进程中有3个用于终端的IO文件流: stdin(标准输入),stdout(标准输出),stderr(标准错误)。

这3个流实际上指向文件结构体的指针, FILE * stdin , stdout ,stderr;

它们指向的FILE的区别在于它们的文件描述符分别是STDIN_FILENO, STDOUT_FILENO,STDERR_FILENO分别对应的值是0,1,2。标准输入默认来源于键盘,标准输出、标准错误的目标默认是屏幕。改变它们的来源或者目标就叫IO重定向。对应linux的操作符是   "","2>"。

所以通常程序的缺省情况,fd通常就已经打开了3个,如果想改变输入流或者输出流到文件,那么就要关闭对应的文件描述符。当重新open文件时,系统就会返回最小的且未被使用的文件描述符。

例如在filename.txt写入8878 . ,当关闭文件描述符0时,在打开文件,调用scanf函数就不再从键盘中输入,而把filename.txt中的内容当作输入。

#include #include #include #include int main(){ close(0); int fd = open("filename.txt",O_RDONLY); int item; scanf("%d",&item); printf("format= %d",item); }

关于scanf。当FILE结构体中fbuf为空时,才会向内核中发出read系统调用,通过文件描述符为0 和进程号找到对应的文件,把文件数据读到fbuf中。所以哪个文件获取到了文件描述符0,那么标准输入的来源就是这个文件。同理,哪个文件获取到了文件描述符1  (2),那么标准输出(错误)的目标就是这个文件。

三、重定向回终端、伪终端

      终端和伪终端通常包含屏幕和键盘。屏幕输出前,需要通常保存在/dev/ttyX  文件下 ,键盘输入后,输入的内容通常保存在 /dev/pts/# 下。

确定具体文件描述符打开的对应文件

例如在gdb调试代码时

通过   ps  -ef |grep a.out 找到进程号

查询   /proc/[进程号]/fd  中的内容  (需要root账号才可以登录fd目录)

查看到我的伪终端是:

/dev/pts/2

或者用 tty 命令查看伪终端

yu'shhi标准输入重定向回键盘:

close(0); int fd = open("/dev/pts/2",O_RDONLY);

四、恢复标准输入输出

在/dev目录下有/dev/stdin 文件,stdout文件,stderr文件

注意:标准输入文件/dev/stdin是个链接文件!,其他也一样,它们存放的是文件描述符的地址,链接文件类似指针,而标准IO文件类似于双重指针。文件描述符在Linux系统中也是一个链接文件。当close([文件描述符时]),其实删除的时文件描述符对应的链接文件。而/dev/stdin固定链接每个 /proc/self/fd/0,当0被删除了之后,/dev/stdin也就链接成了空文件。

也就是说下述代码得到结果并没有重新获得键盘输入:

#include #include #include #include int main(){ close(0); /**文件 /proc/[pid]/fd/0 消失*****/ int fd = open("filename.txt",O_RDONLY); /**文件 /proc/[pid]/fd/0 链接上了filename.txt */ int item; scanf("%d",&item); /*stdin 找到/proc/[pid]/fd/0 中读数据*/ close(0); /*文件 /proc/[pid]/fd/0消失,/dev/stdin为空链接*/ //这样做的意图是 /dev/stdin -> /proc/self/0 -> /dev/stdin 吗??? 显然目的不是这样的 int fd = open("/dev/stdin",O_RDONLY); /*打开空链接文件,并非是终端文件*/ scanf("%d",&item); printf("format= %d",item); }

所以如果要保证代码的通用性,变更stdin链接之前需要把终端文件用新的文件描述符保存起来。

(1)  int fd = dup(oldfd);   ,     int fd= dup2(oldfd,newfd);

当调用dup函数时,内核在进程中创建一个新的文件描述符,此描述符是当前可用文件描述符的最小数值,这个文件描述符指向oldfd所拥有的文件表项。   dup2和dup的区别就是可以用newfd参数指定新描述符的数值。   APUE用另外一个种方法说明了这个问题:   实际上,调用dup(oldfd)等效于,fcntl(oldfd, F_DUPFD, 0)   而调用dup2(oldfd, newfd)等效于,close(oldfd);fcntl(oldfd, F_DUPFD, newfd);

查看如下代码:

#include #include #include #include #include int main(){ int fd2=dup(0) ; //复制文件描述符0对应的表项给fd2 = 3,此时fd2 ,和0 都指向终端 close(0); //关闭文件描述符0 int fd = open("filename.txt",O_RDONLY); //filename.txt获取文件描述符0 int item; scanf("%d",&item); //从标准输入0中读数据 printf("format1= %d\n",item); //输出filename.txt中读出来的内容 dup2(fd2,0); //复制文件描述符fd2表项给0,此时0重新指向终端, // fopen("/dev/stdin", "r+"); scanf("%d",&item); //同时意味着/dev/stdin 也间接指向了终端 printf("format2= %d\n",item); }



【本文地址】


今日新闻


推荐新闻


CopyRight 2018-2019 办公设备维修网 版权所有 豫ICP备15022753号-3