类Unix系统中,fd指的啥?
一、文件描述符(File Descriptor)简介
socket的fd指什么?
fd 是(file descriptor)即文件描述符,这种一般是BSD Socket的用法,用在Unix/Linux系统上。fd全称是file descriptor,是进程独有的文件描述符表的索引。
维基百科上介绍:
file descriptors index into a per-process file descriptor table maintained by the kernel, that in turn indexes into a system-wide table of files opened by all processes, called the file table. This table records the mode with which the file (or other resource) has been opened: for reading, writing, appending, and possibly other modes. It also indexes into a third table called the inode table that describes the actual underlying files
简单的说,就是内核为每个进程维护了一个file descriptor table,file descriptor是file descriptor table的索引,file descriptor table的表项又转而可以索引到系统级的file table,file table又可以索引到系统级的inode table,而这个inode table则真正的描述了底层的文件。系统级的file table还记录了每个文件被打开的方式:读、写、追加…。file descriptor table每个进程都有一个,所以fork的会被拷贝。
Linux 系统中,把一切都看做是文件,当进程打开现有文件或创建新文件时,内核向进程返回一个文件描述符,文件描述符就是内核为了高效管理已被打开的文件所创建的索引,用来指向被打开的文件,所有执行I/O操作的系统调用都会通过文件描述符。一个socket句柄,可以看做是一个文件,在socket上收发数据,相当于对一个文件进行读写,所以一个socket句柄,通常也用表示文件句柄的fd来表示。
Linux中一切皆文件,这句话我们都知道,可见文件的重要性,因此文件的管理在Linux中占据了很大比重,那么操作系统怎么管理文件的呢?
要管理一个东西,那么必须经历两个步骤——描述和组织。
当我们打开一个文件时,系统就会生成一个叫做file的结构体,系统正是用这个file结构体来描述单个文件的;既然已经描述了那么就要组织起来了,系统就会用一个数据结构把所有的文件都组织在一起以方便管理,而我们的进程要能访问这些文件,那么这个数据结构也要与进程关联起来。
在Linux中,进程与文件的模型是这样的:
进程的PCB中保存着一个指向文件组织结构的一个指针,而对于每个打开的文件,在文件组织结构的文件指针数组中都会有一个指针指向它,因此进程只要通过files指针访问到fd_array就可以进入打开的文件了。
所以,当我们用open函数打开一个文件时,其过程为:操作系统生成一个新的file结构体来保存这个文件的信息,并在files_struct的文件数组fd_array中生成一个指向这个file结构体的指针,然后向进程返回这个指针的下标值,而这个下标值正是文件描述符fd,在进程中也正是通过这个fd来访问目的文件。所以,fd就是指向所打开文件的指针在files_struct中的数组 fd_array里面的下标值。
必须注意的是files_struct与file都是在内核中存在的。FILE是C库中的文件描述结构体,这是一个用户层次的结构,里面封装了fd,其结构体定义为:
看到这里,我们能知道,C库中的FILE结构体也是通过fd来管理文件的,当C程序用fiopen打开一个文件时,其实质是通过调用open函数来进行操作的。也就是说FILE结构是对fd的封装,而C库中带f的一系列IO函数(如fopen)是对系统调用(如open)的封装。
二、文件描述符、文件、进程间的关系
1.描述:
- 每个文件描述符会与一个打开的文件相对应
- 不同的文件描述符也可能指向同一个文件
- 相同的文件可以被不同的进程打开,也可以在同一个进程被多次打开
2.系统为维护文件描述符,建立了三个表
-
进程级的文件描述符表
-
系统级的文件描述符表
-
文件系统的i-node表
3.通过这三个表,认识文件描述符
- 在进程A中,文件描述符1和30都指向了同一个打开的文件句柄(#23),这可能是该进程多次对执行
打开
操作 - 进程A中的文件描述符2和进程B的文件描述符2都指向了同一个打开的文件句柄(#73),这种情况有几种可能,1.进程A和进程B可能是父子进程关系;2.进程A和进程B打开了同一个文件,且文件描述符相同(低概率事件=_=);3.A、B中某个进程通过UNIX域套接字将一个打开的文件描述符传递给另一个进程。
- 进程A的描述符0和进程B的描述符3分别指向不同的打开文件句柄,但这些句柄均指向i-node表的相同条目(#1936),换言之,指向同一个文件。发生这种情况是因为每个进程各自对同一个文件发起了打开请求。同一个进程两次打开同一个文件,也会发生类似情况。
三、文件描述符限制
有资源的地方就有战争,“文件描述符”也是一种资源,系统中的每个进程都需要有“文件描述符”才能进行改变世界的宏图霸业。世界需要秩序,于是就有了“文件描述符限制”的规定。
如下表:
永久修改用户级限制时有三种设置类型:
soft
指的是当前系统生效的设置值hard
指的是系统中所能设定的最大值-
指的是同时设置了 soft 和 hard 的值
命令讲解:
- ulimit
- sysctl
四、检查某个进程的文件描述符相关内容
步骤(以nginx为例,*注意权限问题,此示例是在本地环境):
- 找到需要检查的进程id
ps aux|grep nginx
如图,找到的主进程id为 30802
1.查看该进程的限制
cat /proc/30802/limits
如图,在 Max open files 那一行,可以看到当前设置中最大文件描述符的数量为100001
2.查看该进程占用了多少个文件描述符
如上图所示:使用了17个文件描述符
实际应用过程中,如果出现“Too many open files” , 可以通过增大进程可用的文件描述符数量来解决,但往往故事不会这样结束,很多时候,并不是因为进程可用的文件描述符过少,而是因为程序bug,打开了大量的文件连接(web连接也会占用文件描述符)而没有释放。程序申请的资源在用完后及时释放,才是解决“Too many open files”的根本之道。
本文为joshua317原创文章,转载请注明:转载自joshua317博客 https://www.joshua317.com/article/56