要明白Linux的运行机制,文件描述符属于一个绕不过去的核心概念,它不但是程序跟操作系统内核交换文件、网络等的关键抽象,还直接关乎程序的稳定性与性能,好多系统问题,像“打开文件过多”的错误,追根究底都跟文件描述符的管理紧密相连,本文会深入剖析文件描述符的各个方面,从基础定义到实战排查。

什么是Linux文件描述符

文件描述符,也就是fd,是个非负整数,它是进程去访问由内核管理的各类I/O资源时用到的“句柄”或者“门票”。当进程打开一个文件,或者创建一个网络套接字,甚至建立一个管道时,内核会创建一个内部数据结构,比如struct file,用来记录此次打开的相关信息,之后返回一个最小的、尚未被使用的整数给进程当作标识。而这个整数便是文件描述符。

shell文件描述符_linux 文件描述符_文件描述符linux

它在本质层面上是一个索引,此索引指向进程私有的“文件描述符表”里的某一项,该表项进而指向内核中全局的“打开文件表”,最终与具体的文件(i-node)或者网络连接等产生关联。所以,文件描述符自身并不涵盖文件数据red hat linux,它仅仅是进程用以告知内核“我要对哪个已打开的资源进行操作”的一种快捷方式。标准输入、输出以及错误流也各自对应着固定的文件描述符0、1和2。

文件描述符如何工作

当应用程序调用像open()或者socket()这样的系统调用时,会引发一次从用户态到内核态的转变。内核开展具体的打开举动,成功之后在当前进程的文件描述符表中寻得一个空闲地方,填入相关讯息,接着把该位置的索引也就是那个整数当作返回值传送给应用程序。此后,该进程的所有读写也就是read/write、控制也就是fcntl或者关闭也就是close该资源的操作,都经由传入这个文件描述符整数来达成。

shell文件描述符_linux 文件描述符_文件描述符linux

内核借助文件描述符,维持着每个打开资源的上下文详情,像当前文件的读写偏移量、访问模式(只读、只写之类)以及状态标志。同一文件能够被多个进程打开,每个进程会获取各自独立的文件描述符,它们或许指向内核里同一个打开文件表项,进而共享偏移量;也可能是各不相同的表项,具备独立的偏移量。这种分层的设计达成了资源的灵活共享与管理 。

如何查看文件描述符

对于正处于运行状态的进程而言,最为直接的查看途径乃是借助/proc文件系统,每个进程于/proc/ 。位于/目录之下,存在着一个名为fd的子目录,此子目录之中,包含着那些以文件描述符数字进行命名的符号链接linux数据恢复,而这些符号链接所指向的,乃是与该描述符实际产生关联的资源。运用命令ls -la /proc/。/,fd便能一眼清晰看出。比如说linux 文件描述符,瞧见0 -> /dev/pts/0意味着标准输入源自某个终端,4 -> socket:[XXXXXX]表明是一个网络套接字。

linux 文件描述符_文件描述符linux_shell文件描述符

除了去查看/proc,还能够使用lsof(也就是list open files)这个命令,去执行lsof -p 。标点符号。能够将那个进程所打开的全部文件描述符的详细情况罗列出来,其中涵盖类型,设备,大小以及路径等等,所呈现出的信息更加丰富多样,对于开展问题排查工作而言,另外一个具备实用价值的命令是ss -tulnp,或者是netstat -tulnp,它们能够把哪些进程正在使用哪些网络端口展示出来,而这些网络连接同样对应着文件描述符。

文件描述符有哪些限制

文件描述符归属一种有限的系统资源范畴,主要受到两个层面状况的限制。其一为进程级别的相关限制,也就是说,是针对单个进程当前能够同时予以打开的最大文件描述符数目限制情况。针对此,能够借助‘ulimit -n’的这样一个命令去观察查看当下 shell 环境的软限制方面的情形,另外,亦可借助‘ulimit -Hn’这一操作来查看相对应的硬的限制的那种情况。而在程序内部当中,是可以运用 getrlimit()以及 setrlimit()这两个系统调用的方式来实施相应的查询以及设置的操作行为的。

第二个存在的是系统级限制,也就是整个操作系统里所有进程能够去打开的文件描述符的总数目。这是由内核参数来决定的,能够借助cat /proc/sys/fs/file-max进行查看。在系统打开的文件总数快要接近这个上限之际,所有进程都有可能没办法再去打开新的文件。除此之外,针对于网络密集型应用,还需要留意/proc/sys/fs/nr_open(单个进程可以打开的最大数量)以及端口范围等相关联的限制。合理地去调整这些限制是系统调优的常规性操作。

文件描述符linux_linux 文件描述符_shell文件描述符

文件描述符泄漏如何处理

文件描述符泄漏,是指进程将文件描述符予以打开之后,却没能正确地去关闭,致使其持续占用资源,最终有可能达到上限,进而让程序崩溃或者使系统不稳定。典型的泄漏场景涵盖:于循环里打开文件却未关闭,异常分支路径未执行关闭操作,或者在使用某些第三方库时忘掉释放资源。长期运行的守护进程,像Web服务器、数据库,对于此问题格外敏感。

处理泄漏,首先得进行定位,能够定期监控可疑进程的fd数量变化linux 文件描述符,比如借助watch -n 1 'ls /proc/' 。借助于“/fd | wc -l’`”,结合lsof命令去查看持续增长的究竟是哪些种类的文件,在代码层面上,要保证每一个open/malloc分配都存在对应的close/free,并且运用RAII(资源获取即初始化)思想或者语言特性(比如Python的with语句)来自动管理资源,对于已经发生的泄漏而言,重启进程是一种直接的解决办法,不过要治本的话还是需要修复代码。

文件描述符与文件指针的区别

文件描述符linux_linux 文件描述符_shell文件描述符

低层的、系统级的概念之中,包含文件描述符,那是操作系统内核提供的原始接口,像read、write、闭合使用的整数值。至于文件指针(FILE*),它属于标准C库封装得来的抽象,是高层在文件描述符之上做此事,像glibc这样的库。FILE结构体的内部,有一个文件描述符在其中,再加上额外的缓冲区,配上错误指示器,携同文件结束指示器等,为了提升I/O效率 。

故而,运用fopen/fread/fwrite/fclose等这般之类的标准库函数时所操作的乃是文件指针,它们于用户所处状态之下提供了缓冲方面的功能,进而降低系统进行调用的频次。然而呢,在多线程的环境当中是需要额外去留意锁这一问题的。在底层编程的情况里,或者是网络编程之时,甚至是需要达到精细控制(像是非阻塞I/O、文件被锁定)的那样一种状况时,那么直接采用文件描述符以及与之相对应的系统调用会更为普遍。这两者能够借助fileno()以及fdopen()函数来实现相互之间的转换。

于你平常的开发或者运维工作里头,可曾碰到过因文件描述符方面的问题致使的故障?那你又是怎样去排查以及解决的?欢迎在评论区域分享你的实战经历,如若觉得这篇文章有帮助,同样也请点赞并分享给更多有可能遇上类似问题的伙伴。

Tagged:
Author

这篇优质的内容由TA贡献而来

刘遄

《Linux就该这么学》书籍作者,RHCA认证架构师,教育学(计算机专业硕士)。

发表回复