【Linux】文件描述符 | 重定向

目录

1.fd0、1、2

2.文件描述符

3.重定向

3.1 重定向的本质

3.2 dup2系统调用

例1:追加重定向

先close(1),再打开文件,打开文件的文件描述符为1

不强制刷新:只有write的数据被写入

强制刷新:观察到调用位置不同,结果不同

dup2

例2:输入重定向


通过对open函数的学习,我们知道了文件描述符就是一个小整数。

1.fd0、1、2

#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<assert.h>
#include<unistd.h>

#define FILE_NAME(number) "log.txt"#number
//宏参数,转换为字符串,两个字符串有自动连接特性
//
int main()
{
  printf("stdin->fd:%d\n",stdin->_fileno);
  printf("stdout->fd:%d\n",stdout->_fileno);
  printf("stderr->fd:%d\n",stderr->_fileno);

  umask(0);

  int fd0=open(FILE_NAME(1),O_WRONLY | O_CREAT |O_APPEND,0666);
  int fd1=open(FILE_NAME(2),O_WRONLY | O_CREAT |O_APPEND,0666);
  int fd2=open(FILE_NAME(3),O_WRONLY | O_CREAT |O_APPEND,0666);
  int fd3=open(FILE_NAME(4),O_WRONLY | O_CREAT |O_APPEND,0666);
  int fd4=open(FILE_NAME(5),O_WRONLY | O_CREAT |O_APPEND,0666);

  printf("FILE1 fd:%d\n",fd0);
  printf("FILE2 fd:%d\n",fd1);
  printf("FILE3 fd:%d\n",fd2);
  printf("FILE4 fd:%d\n",fd3);
  printf("FILE5 fd:%d\n",fd4);
  
  close(fd0);
  close(fd1);
  close(fd2);
  close(fd3);
  close(fd4);


  return 0;
}

bc07af56a8e14026ac168a6cc761dd3b.png

stdin,stdout,stderr(FILE*类型对象)里封装了文件描述符。

标准输入、标准输出、标准错误默认占据每个进程的文件描述符表的0,1,2,由该进程创建文件的fd只能从3开始编号。

0,1,2对应的物理设备一般是:键盘,显示器,显示器。重定向后0,1,2可以对应其他文件。


2.文件描述符

44struct files_struct {
48        atomic_t count;
49        struct fdtable *fdt;
50       struct fdtable fdtab;
54        spinlock_t file_lock ____cacheline_aligned_in_smp;
55        int next_fd;
56        struct embedded_fd_set close_on_exec_init;
57        struct embedded_fd_set open_fds_init;
58        struct file * fd_array[NR_OPEN_DEFAULT];//指针数组,指向文件描述符表

59};

FILE 是C语言文件结构定义,file是操作系统内核的文件结构体。

操作系统为了管理被打开的文件,为文件创建对应的 内核数据结构 标志文件struct file{},file里包含了文件的大部分属性,以链式结构管理

1.进程调用open()打开文件,会先初始化文件的file对象,把磁盘中的文件加载到内存。

2.在进程的文件描述符表fd_array[]找到最小未使用下标,在该下标位置填入file指针。数组下标就是文件描述符。给open返回文件描述符。

3.访问文件也通过文件描述符经过进程task_struct,找到文件描述符表里找。

d59e6263045646ad84aa774e6794dc20.png

分配规则

文件描述符表的数组从下标0开始,找到当前未被使用的最小下标。

例:如果close(0),取消指向,打开文件的文件描述符就会匹配到0。


3.重定向

b6c3117deab14280955634cb3924abc2.png

a1e0c4917f8f4e299200f7f0a08734ef.png

为什么没向显示器打印fd1?

文件操作以文件标识符为依据指定文件,系统默认0为标准输入,1为标准输出,2为标准错误。

printf/fprintf默认向stdout打印,stdout里封装的文件描述符是1。

所以printf/fprintf向标识符为1的文件打印。但当前进程的标识符1的指向已经变了,log.txt的fd为1,所以向文件打印。

强制刷新stdout的缓冲区,才能成功打印到文件,否则不能成功写入到文件。

了解缓冲区后的更多理解:

磁盘文件和显示器文件的缓冲区刷新策略不同。数据写到stdout的缓冲区里了,但是因为刷新策略是全缓冲,数据一直在stdout的缓冲区,没到内核缓冲区。

进程结束时刷新内核缓冲区,而数据没有写入到内核缓冲区。

所以只能强制调fflush去刷新。fflush先将stdout缓冲区数据写入到内核缓冲区(write()),再调系统接口强制刷新到外设(磁盘)。

3.1 重定向的本质

上层接口使用的fd不变,在内核中更改fd对应的指针(struct file*)。

常用的重定向指令:

1.>:输出

2.>>:追加

3.< :输入

3.2 dup2系统调用

dup2是一个更方便的重定向接口。

c81ae13193a34f459159645ecac2dae7.png

功能:newfd是oldfd的拷贝,拷贝oldfd。把oldfd下标存的内容拷贝给newfd。

例1:追加重定向

方法一:先close(1),再打开文件,打开文件的文件描述符为1

  • 不强制刷新:只有write的数据被写入

只写入了helloworld,原因:write直接将数据写入到内核缓冲区,fprintf/printf写入到stdout缓冲区,再根据刷新策略决定是否刷新到内核缓冲区。刷新策略是全缓冲,也就是没刷新到内核。也没强制刷新,只能等进程结束刷新内核缓冲区。现在内核只有helloworld。


  • 强制刷新:观察到调用位置不同,结果不同

后三行追加,虽然先调用的fprintf和printf,但是write的数据先到内核缓冲区,后调的强制刷新。


改变调用fflush的位置后,printf/fprintf的缓冲区数据先到内核缓冲区且先同步到外设。write等进程结束再同步到外设。

方法二:dup2

调用dup2重定向,再也不用自己调强制刷新了。但是还是顺序问题,等进程结束才帮忙刷新stdout的缓冲区。

例2:输入重定向

  • 20
    点赞
  • 19
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值