Linux文件描述符fd详解&&重定向原理

什么是文件描述符fd?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    int fd1 = open(FILE_NAME(1), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd2 = open(FILE_NAME(2), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd3 = open(FILE_NAME(3), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd4 = open(FILE_NAME(4), O_RDWR | O_CREAT | O_TRUNC, 0666);
    printf("fd1=%d\n", fd1);
    printf("fd2=%d\n", fd2);
    printf("fd3=%d\n", fd3);
    printf("fd4=%d\n", fd4);
    close(fd1);
    close(fd2);
    close(fd3);
    close(fd4);
    return 0;
}

image-20230104173356557

可以看到,创建了四个文件,文件描述符分别是3、4、5、6,那么前面的数字去哪里了呢?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
	printf("fd1=%d\n", stdin->_fileno);
    printf("fd2=%d\n", stdout->_fileno);
    printf("fd3=%d\n", stderr->_fileno);
    return 0;
}

image-20230104173743020

其实文件描述符的0、1、2被标准输入、标准输出和标准错误给占用了。

一个系统中,会打开非常非常多的文件,这些文件OS也是需要进行管理的,而管理的方法就是先描述再组织,将所有文件共有的属性描述成结构体struct file __rcu,而在进程的PCB里有一个指针struct files_struct *files 指向结构体struct files_struct,该结构体中有一个指针数组struct file __rcu * fd_array[NR_OPEN_DEFAULT];属于当前进程的所有文件的结构体指针都在该数组中,而该数组的下表就是文件描述符fd!

image-20230104181210078

stdin、stdout、stderr默认占了该数组的前三个位置,而后面打开的文件则会依次填入该数组,所以fd会以0、1、2、3、4……的方式给出。

文件描述符的分配规则

我们每次用open打开文件,总是会用close来关闭文件。open会给我们返回fd,也就是新打开的文件的结构体指针已经填入到了那个数组中,那么close来关闭文件也就是将该数组对应下标的指针给释放。那么stdin、stdout、stderr是否可以关闭呢?

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    close(0);
    close(2);
    int fd1 = open(FILE_NAME(1), O_RDWR | O_CREAT | O_TRUNC, 0666);
    int fd2 = open(FILE_NAME(2), O_RDWR | O_CREAT | O_TRUNC, 0666);
    printf("fd1=%d\n", fd1);
    printf("fd2=%d\n", fd2);
    close(fd1);
    close(fd2);
    return 0;
}

image-20230104181951228

可以发现,当关闭了stdin、stderr对应的0和2后,我们新打开的文件分配的fd就变成了0和2,这也就说明了,fd的分配规则是从数组的0下标开始依次遍历,该位置上无文件则分配,有文件则跳过。

重定向的原理

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    close(1);
    int fd = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_TRUNC, 0666);
    const char *s = "hello world!\n";
    write(1, s, strlen(s));
    close(fd);
    return 0;
}

image-20230104183516508

该程序如果未关闭文件描述符1的话,也就是未关闭stdout的话,正常是应该往显示器上输出hello world!,但是关闭之后,hello world!就写入到了文件tag.txt1中!

原因是:关闭文件描述符1也就是将那个数组下标为1的指针关闭,而此时又open了一个文件,所以该文件分配的文件描述符就是1,但是write函数就是往1里面写,也就将本来写入到stdout里的内容写入到了tag.txt1中。

所以,输出重定向的原理就是关闭1,再让新打开的文件的文件描述符分配上1;

输入重定向的原理就是关闭0,再让新打开的文件的文件描述符分配上0。

但是这样的操作太过繁琐,于是就有一个函数,一步到位:dup2。

#include <unistd.h>

int dup2(int oldfd, int newfd);

参数:
    newfd是oldfd的拷贝,也就是将newfd位置上的指针变为oldfd位置上的指针。
返回值:
    成功返回newfd
    失败返回-1

于是上面的函数就可以改为:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#define FILE_NAME(n) "tag.txt"#n
int main()
{
    int fd = open(FILE_NAME(1), O_WRONLY | O_CREAT | O_TRUNC, 0666);
    dup2(fd, 1);
    const char *s = "hello world!\n";
    write(1, s, strlen(s));
    close(fd);
    return 0;
}
  • 0
    点赞
  • 2
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

云朵c

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值