如何深入理解文件描述符?

文件描述符是一个整数,用于标识 Linux 中的文件。
每个进程都有自己的文件描述符表。
读写操作由文件描述符执行。

文件描述符表记录文件对象指针。
文件对象包含打开模式和引用计数。
inode记录了资源类型和操作。

打开文件分配器文件描述符。
系统调用实现读和写操作。
关闭文件释放描述符。

复制文件描述符以共享文件对象。
文件描述符可以在进程之间传递。
重定向会更改标准输入和输出。

VFS提供了统一的文件系统接口。
Linux 通过 VFS 支持多个文件系统。

了解文件描述符和系统调用方法。
使用文件描述符来提高 Linux 编程技能。

文件描述符是什么

老实说,在 Linux 中与文件描述符斗争了这么多年之后,我对经典的 1 02 4 限制印象最深刻。
我记得当我第一次进入服务器编程时,我写了一个简单的Web服务器,但没过多久它就崩溃了,日志里全是“打开的文件太多”。
我当时就一头雾水,查了半天发现文件描述符已经用完了。
Linux的这个限制其实很有趣。
在/proc/self/fd目录下,可以直观的看到当前进程打开的文件。
每个小文件夹就是文件描述符,比较直观。

有趣的是 Windows 将其称为文件句柄。
概念相似,但实现细节不同。
我之前在跨平台项目中遇到过陷阱。
Linux使用“open”系统调用直接获取文件描述符,而Windows需要使用“CreateFile”,返回值是文件句柄。
最烦人的是你必须手动按下“CloseHandle”才能释放它。
在Linux下,直接“关闭”即可。
记得有一次写了兼容代码,调试了好几天。
最后,我意识到我忘记关闭Windows上的句柄,这导致了内存泄漏。

说起C语言中的FILE结构:我总觉得这个东西有点奇怪。
它既是一个指针,也是一个缓冲区和文件描述符。
我曾经重构过一个老项目,想把FILE结构改成直接使用文件描述符来匹配缓冲区,但是发现依赖太大了。
特别是,诸如“fflush”或“fprintf”之类的标准库函数基于FILE结构。
当时我不明白为什么 UNIX 是这样设计的。
也许当时效率是优先考虑的。

我确实遇到过C1 0K的陷阱。
有一次我在做高并发代理服务器的时候,直接使用查询模型来处理每个连接。
结果,文件描述符数组太小,系统调用的效率急剧下降。
后来还是用epoll(Linux上)比较好。
有趣的是,Epoll虽然解决了性能问题,但是代码复杂度急剧增加。
我记得有一次给实习生讲过Epoll事件回调部分,并对自己说,“回调顺序可能有点复杂”。
结果我说完之后愣了半秒——当时我还没有完全听懂。

我自己没有运行过这个,但我记得数据是Windows默认有1 02 4 个文件描述符。
然而,Windows系统管理员可以通过kernel.maxFileDescriptors等注册表项对其进行自定义,但普通用户权限是不够的。
Linux的/proc/sys/fs/file-max实际上是一个神器。
我在优化生产环境的时候,把这个值设置到了几万,才有信心。
然而,设置太高会带来风险。
我记得一位同事说价值几百万。
结果系统资源耗尽,整个机房陷入瘫痪。

Java 间接使用文件描述符,但它们包装得非常深。
我检查了源代码,发现Java的socket实现实际上使用了底层文件描述符。
然而,Java NIO 的选择器将文件描述符的管理抽象为“Channel”,并使用SelectionKey 用于跟踪准备情况事件。
老实说,Java NIO 的设计比我当年写的查询代码优雅得多,但学起来确实很难。

最后说一下文件描述符3 和4 ,之前在调试网络工具的时候,发现文件描述符4 对应的是一个临时文件。
当时我用Strace来跟踪整个过程。
我看到在 execve 系统调用之后,文件描述符 3 被重定向到 /dev/null ,并且 4 打开了一个临时文件。
这些细节在Linux系统编程中很常见,但是看多了就会习惯了。
我记得有一次我正在为一位客户解决问题,另一位参与者说:“我们的程序在打开文件后停止响应。
”我跟踪它,发现它卡在特定文件描述符4 的读取调用上——它是一个很久以前就已经关闭的设备文件。

详解Linux环境下实现对文件读写操作

Linux文件读写,主要步骤: 1 . 使用 open() 打开文件。
路径和模式是关键。
2 、读写时要注意缓冲区大小。
3 . close()操作后,不要让资源逃逸。
4 . 小心处理错误,perror()有助于发现问题。
5 .文件指针位置必须管理好,lseek()可以帮助你。
6 . 缓冲区大小必须适当,避免溢出。
7 . 使用fcntl()或flock()锁定文件以确保数据不混乱。
8 . mmap()内存映射,快速读写大文件。
9 . 非阻塞I/O以提高响应能力。
1 0.对于调试和优化,strace、lsof和gdb是好帮手。
1 1 .安全第一,权限检查和输入验证至关重要。
1 2 .保护敏感数据并记得清除内存。
1 3 、学习系统调用和标准库,打牢基础。

你自己看看,如果你记住了这些步骤,你就已经准备好进行 Linux 文件操作了。

一起深入理解 Linux shell 中 2>&1 的含义叭~

哎呀,我得谈谈这个。
当我在 Linux 服务器上摆弄 Java 时,我也遇到了这种头痛。

我记得在北京的一个冬天。
该公司的系统已经过时,需要维护。
我是新来的,对这些东西知之甚少。
要编译 Java 文件,命令是 javac MyProgram.java && java MyProgram。
结果,终端显示了一堆无效字符。
乍一看,这是一条名为error1 的错误消息,但看不到正常输出out1 我去查看日志文件,但是log.txt中只有out1 我对当时发生的事情感到困惑。

后来我向一位老同事请教,他叫醒了我。
这位朋友说他用了javac MyProgram.java 2 >&1 >log.txt。
正确的?这样写,首先>log.txt将标准输出重定向到log.txt,然后2 >&1 将标准错误重定向到标准输出log.txt。
但问题是重定向是连续的。
首先,如果您运行> log.txt,则标准输出已经指向log.txt。
在本例中,2 >&1 尝试将标准错误重定向到标准输出。
标准输出已经被占用,因此没有地方容纳标准错误,它只打印到终端。

当时我很困惑,为什么 javac MyProgram.java 2 >&1 > log.txt 可以工作,而 javac MyProgram.java > log.txt 2 >&1 却不能呢?这位朋友说和文件描述符的顺序有关系。
Linux中的文件描述符从0开始。
0是标准输入,1 是标准输出,2 是标准错误。
First > log.txt 相当于文件描述符1 (标准输出)指向log.txt。
然后2 >&1 使文件描述符2 (标准错误)指向log.txt,这是文件描述符1 (标准输出)指向的位置。
但相反的情况,首先2 >&1 ,标准错误已经指向标准输出(通常是终端),然后>log.txt,如果你试图将标准输出指向log.txt,你就会搞砸。

后来我尝试了一下,果然成功了。
如果您编写 javac MyProgram.java 2 >&1 > log.txt,标准输出将首先指向 log.txt,标准错误也将指向 log.txt。
这是正确的。
如果你写javac MyProgram.java > log.txt 2 >&1 ,标准输出会先指向log.txt,然后标准错误也会尝试指向log.txt,但此时log.txt已经被标准输出占用了,所以出了问题。

所以你必须记住这一点。
编写命令时顺序非常重要。
您所说的 javac MyProgram.java 2 >&1 > log.txt 是正确的。
无法写入 javac MyProgram.java > log.txt 2 >&1 这就像给管道加油一样,你必须按顺序进行。

还有,你提到的&>和>&,我之前没关注过这两个。
你这么一说,我就得回去仔细研究了。
反正我写命令的时候主要习惯用2 >&1 >log.txt。
它简单、清晰且无错误。

简单来说,你需要理解如下。
首先重定向标准输出,然后使用2 >&1 将标准错误重定向到标准输出指向的位置。
顺序不能乱。
我回去尝试一下你提到的简化方法,看看是否真的可以简化。
我得看看。

顺便说一句,您应该查看您提到的参考链接并了解其他人如何使用这些命令。
Linux 系统的学习永无止境。