[linux]文件描述符再探

本文深入探讨了Linux系统中文件描述符的概念及其内部工作原理,包括进程级的文件描述符表、系统级的打开文件表及文件系统的i-node表等内容,并通过实例说明了不同文件描述符之间的关系。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

第一点也是最重要的一点:在linux上所有的硬件挂载/dev/mouse,pipe,文件,socket等都是文件!都是可以通过文件描述符来访问到的。


文件描述符file descriptor:是一个对文件的索引号,可以认为是一个公司里面的员工id号,通过id可以索引到员工,然后对员工进行一些操作。

首先要理解文件描述符,得有如下3个内核所维护的数据结构概念:

1.进程级的文件描述符

2.系统级的打开文件表

3.文件系统的i-node表


进程级的文件描述符:

针对于每一个进程,内核都会维护打开文件的描述符表,表中的每一条记录都记录了单个文件的相关信息,控制文件描述操作的标志,对打开句柄的引用,这里的句柄是指系统级打开文件表中的条目。


系统级的打开文件表

内核对所有打开的文件维护一个系统级的文件打开表,并将表中的各条目称之为打开句柄。一个句柄存储了打开文件的相关信息。包括文件偏移,打开文件状态flags,文件的访问模式,信号驱动I/O相关设置,对文件系统中的i-node的引用。


文件系统的i-node表:

目前linux的文件系统已经到了ext4,想知道自己系统的文件系统是什么 可以使用

df -aT
进行查看。一个文件在linux中可以有属性,权限,数据信息。ext文件系统通畅将这两部分的数据分别存放在不同的快,权限和属性放在inode中,实际数据则放在data block中。

一个文件会占用一个inode,inode记录文件的数据所在的block号码。如下图所示:


假设一个文件存放在inode4号,而这个文件实际放在了2,7,13,15这四个data block中,此时操作系统读出四个block就能将文件内容读出。




在程序中一个默认使用了3个文件描述符 0 ~ 2, 标准输入输出错误:STDIN_FILENO = 0,STDOUT_FILENO = 1,STDERR_FILENO = 2。

当用

int open(char* name,int flags,.../* mode_t mode */);
当开一个文件的时候,会返回最低的未使用的文件描述符序号,当标准输入,输出,错误被占用的情况下,将返回3;


上图中进程A的文件描述符fd 1 和 20是指向同一个句柄,这可能是因为使用了dup(),dup2(),fcntl()函数产生的

进程A的文件描述符 fd 2和 进程B的文件描述符 fd 2都指向同一个句柄,这可能是因为执行fork()之后,产生的内存拷贝,即A和B是父子进程关系,继承来的。

进程A的文件描述符 fd 0 和 进程B的文件描述符 fd 3 指向不同的句柄,但是指向同一个inode,可能是因为都打开了同一个文件,同一个进程两次打开一个文件也会发生这种情况。


总结:两个不同的文件描述符,若指向同一个打开文件句柄,则共享偏移,即通过一个文件描述符更改内容,另外一个文件描述符也是可以观察到的。


那么两条命令行

./run_script >result.log 2>result.log

./run_script >result.log 2>&1

第一条命令行 相当于 创建了两个文件描述符,但是不共享句柄,即偏移不共享,这就会产生十分怪异的情况,即输出结果是乱序的

第二条命两行 将文件描述符复制标准输入文件描述符,共享句柄,共享偏移,输出结果和控制台输出结果是一致的。

复制描述符 可以用

int dup(int oldfd);
int dup2(int oldfd,int newfd);

dup()函数会复制一个文件描述符,返回一个指向相同句柄的新文件描述符,新文件描述符从最低未使用的描述符选择。

比如:

close(STDIN_FILENO);
newfd = dup(STDOUT_FILENO);
这个时候 newfd就是0。因为标准输入所占用的文件描述符为0,这个时候关闭了,0变成未使用,那么dup之后将使得newfd将变成0即最小未使用的文件描述符。

dup2()所执行的功能即 若是指定的新的文件描述符存在,则关闭,然后返回,否则直接返回。




### 如何在 Linux 中查找正在使用或开放的端口 #### 使用 `ss` 命令 `ss` 是一种强大的工具,用于显示套接字统计信息。它能够提供比传统 `netstat` 更高效的性能,并且可以直接访问内核数据结构[^4]。 以下是常用的 `ss` 命令示例: 1. 显示所有监听状态的 TCP 和 UDP 端口: ```bash ss -tuln ``` 2. 仅显示 TCP 的监听端口: ```bash ss -tnl ``` 3. 仅显示 UDP 的监听端口: ```bash ss -unl ``` 上述命令中参数含义如下: - `-t`: 显示 TCP 连接。 - `-u`: 显示 UDP 连接。 - `-l`: 显示处于监听状态的连接。 - `-n`: 不解析主机名和服务名称,直接显示 IP 地址和端口号。 #### 使用 `netstat` 命令 尽管现代 Linux 发行版可能未预装 `netstat`,但它仍然是一个广泛使用的工具。如果需要使用该命令,则可以通过安装 `net-tools` 来实现[^3]。 以下是常见的 `netstat` 命令示例: 1. 显示所有监听状态的 TCP 和 UDP 端口: ```bash netstat -tuln ``` 2. 仅显示 TCP 的监听端口: ```bash netstat -tnl ``` 3. 仅显示 UDP 的监听端口: ```bash netstat -unl ``` 同样地,这里的参数定义与 `ss` 类似。 需要注意的是,在某些较的发行版本中(如 CentOS 或 RHEL),由于 `netstat` 已被弃用并推荐改用更高效的方式,因此建议优先考虑 `ss` 命令。 #### 使用 `lsof` 命令 除了 `ss` 和 `netstat` 外,还可以借助 `lsof` 查找当前系统上已打开的文件描述符及其关联的服务进程。对于网络端口而言,这通常意味着找到绑定到某个特定地址和端口的应用程序实例[^2]。 执行以下命令即可列出所有监听中的网络端口连同它们所属的进程 ID (PID) 及其他细节: ```bash lsof -i -P -n | grep LISTEN ``` 其中选项解释为: - `-i`: 展现涉及 Internet 协议的相关条目; - `-P`: 阻止将端口号转换成对应的协议名字; - `-n`: 抑制 DNS 查询从而加快速度; #### 总结 综上所述,有多种方法可以在 Linux测哪些端口正被占用或者对外暴露出来。最常用的技术包括但不限于调用专门设计用来完成这项工作的实用程序——比如前面提到过的那些 (`ss`, `netstat`, `lsof`) ——每种都有各自的优势所在[^1]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值