一、UINX哲学
一切皆文件是 Unix/Linux 的基本哲学之一。不仅普通的文件,目录、字符设备、块设备、 套接字等在 Unix/Linux 中都是以文件被对待;它们虽然类型不同,但是对其提供的却是同一套操作界面。
二、UNIX文件类型
下面列举了UNIX中常用到的文件类型,其实除了普通文件以外,其他的其实都不是真正意义上的文件。
三、查看进程文件
UNIX中提供一个文件作为我们对其操作的入口。有些文件我们甚至可以使用命令直接查看其内容。下面我们以查看进程文件为例子。
ubuntu@VM-0-4-ubuntu:~$ ps|grep 78415 #找到我们的例子进程
78415 pts/0 00:00:00 test
ubuntu@VM-0-4-ubuntu:~$ cd /proc/ #进入proc中
ubuntu@VM-0-4-ubuntu:/proc$ ls #找到对应pid的文件夹
1 17 3 608 663222 73937 74918 77582 91 diskstats keys mtrr sys
10 18 333 6096 663223 73938 75 78 92 dma key-users net sysrq-trigger
102 19 335 610 669 73939 75626 78415 920937 driver kmsg pagetypeinfo sysvipc
105 2 337 61649 670 74 757 78712 920954 execdomains kpagecgroup partitions thread-self
ubuntu@VM-0-4-ubuntu:/proc$ cd 78415/
ubuntu@VM-0-4-ubuntu:/proc/78415$ ls
arch_status cmdline exe loginuid mountstats oom_score_adj sched stack timers
attr comm fd map_files net pagemap schedstat stat timerslack_ns
autogroup coredump_filter fdinfo maps ns patch_state sessionid statm uid_map
auxv cpuset gid_map mem numa_maps personality setgroups status wchan
cgroup cwd io mountinfo oom_adj projid_map smaps syscall
clear_refs environ limits mounts oom_score root smaps_rollup task
ubuntu@VM-0-4-ubuntu:/proc/78415$ cd fd //查看本进程的所有文件描述符号
ubuntu@VM-0-4-ubuntu:/proc/78415/fd$ ll
total 0
dr-x------ 2 ubuntu ubuntu 0 Nov 8 23:02 ./
dr-xr-xr-x 9 ubuntu ubuntu 0 Nov 8 23:01 ../
lrwx------ 1 ubuntu ubuntu 64 Nov 8 23:04 0 -> /dev/pts/0
lrwx------ 1 ubuntu ubuntu 64 Nov 8 23:04 1 -> /dev/pts/0
lrwx------ 1 ubuntu ubuntu 64 Nov 8 23:04 2 -> /dev/pts/0
ubuntu@VM-0-4-ubuntu:/proc/78415$ cat smaps #可以通过该文件内存映射详细信息
5621b8cf0000-5621b8cf1000 r--p 00000000 fc:02 520197 /home/ubuntu/test
Size: 4 kB
KernelPageSize: 4 kB
MMUPageSize: 4 kB
Rss: 4 kB
Pss: 4 kB
Shared_Clean: 0 kB
Shared_Dirty: 0 kB
Private_Clean: 4 kB
...
netstat -anpt | grep 8888
lsof -p 27534 //用lsof查看该进程的所有文件描述符,其中的type字段就是表明它是什么类型,通过man losf
四、Linux 文件描述符到底是什么?
在编程中对上述的文件、设备、socket我们通常都可以使用open函数打开,open给我们返回一个fd(文件描述符),那么这个fd究竟是啥?
4.1 进程与文件描述符的关系
一个 Linux 进程启动后,会在内核空间中创建一个 PCB 控制块,PCB 内部有一个文件描述符表(File descriptor table),记录着当前进程所有可用的文件描述符,也即当前进程所有打开的文件。
除了文件描述符表,系统还需要维护另外两张表:
- 打开文件表(Open file table)
- i-node 表(i-node table)
文件描述符表每个进程都有一个,打开文件表和 i-node 表整个系统只有一个,它们三者之间的关系如下图所示。
4.2 文件描述符的本质
从本质上讲,这三种表都是结构体数组,0、1、2、73、1976 等都是数组下标。表中不难看出文件描述符是进程文件描述符的数组。
通过文件描述符,可以找到文件指针,从而进入打开文件表。该表存储了以下信息:
- 文件偏移量,也就是文件内部指针偏移量。调用 read() 或者 write() 函数时,文件偏移量会自动更新,当然也可以使用 lseek() 直接修改。
- 状态标志,比如只读模式、读写模式、追加模式、覆盖模式等。
- i-node 表指针。
打开文件表只是文件的中转站,通过打开文件表的 i-node 指针进入 i-node 表,该表包含了诸如以下的信息:
- 文件类型,例如常规文件、套接字或 FIFO。
- 文件大小。
- 时间戳,比如创建时间、更新时间。
- 文件锁。
4.3几种文件描述符情景
- 1、同一个进程的不同文件描述符可以指向同一个文件;(可能是通过调用 dup()、dup2()、fcntl() 或者对同一个文件多次调用了 open() 函数形成的。 )
- 2、不同进程可以拥有相同的文件描述符;(描述符正好分配到与其他进程打开该文件的描述符一样)
- 3、不同进程的同一个文件描述符可以指向不同的文件(一般也是这样,除了 0、1、2 这三个特殊的文件);
- 4、不同进程的不同文件描述符也可以指向同一个文件。(每个进程各自对同一个文件发起了 open() 调用。同一个进程两次打开同一个文件,也会发生类似情况)
4.4 socket 文件描述符
通过上面的知识,我们可以知道socket其实也是个文件,只不过这个文件用来进行网络通信的。我们获得sokcet的fd后,通过fd作为参数调用其他函数达到通信的目的。
fd本质上更像win中的句柄,是内核提供给用户来安全地操作文件的标识,不像指针,拥有了指针后你能瞎JB改。拥有了描述符后,你只能传入描述符给特定的接口,实际操作由内核读取用户输入的参数后来安全地执行。
五、思考
1、文件的个数有何限制,文件描述符的个数有限制吗?
免责申明:本文是根据网上多篇文章加上自己的理解整合而成,如有使用不妥之处还望告知。
参考:
http://m.biancheng.net/view/3066.html
https://www.zhihu.com/question/288808056/answer/463060065
https://www.cnblogs.com/bravery/archive/2012/06/27/2560611.html