File descriptors are OS entries or handles identified by positive integers and used to represent files. You can look at file descriptors as links between an integer and an OS "resource", below a list of common "resources":
regular file path on disk
memfd - anonymous regular files (no path on disk, live only in RAM)
eventfd - and integer used by kernel as an event wait/notify mechanism to notify Ring 1 applications
path to an OS device - any device involved in functioning of app, I will show examples below as it's easier this way
NOTE: file descriptors are bound to a PID.
There are three major tables in which FDs(file descriptors) are involved and those are in ascending order:
________________________
| file descriptor table | == contains only file descriptors, can be found in /proc/$PID/fd where $PID is the process ID for which
‾‾‾‾‾‾‾‾‾‾|‾‾‾‾‾‾‾‾‾‾‾‾‾‾ you want to check FD table
|
______|_______
| file table | == contains all FDs from fd table and in addition some open files for which the kernel has not allocated
‾‾‾‾‾‾|‾‾‾‾‾‾‾ a FD; Most often the number of open files without a FD is much higher than the ones with allocated FD
|
______|________
| inode table | == kernel table which contains open file tables for all processes
‾‾‾‾‾‾‾‾‾‾‾‾‾‾‾
As an example I will usee vim to open a text file and then analyze these tables for this process
[tony-fx503vd Documents]# ls
simple_text_file.txt
[tony-fx503vd Documents]# vim simple_text_file.txt
I want to leave this vim opened and to not use a different terminal, one way to do this is to issue semicolon ":" and then sh, so I will run a shell using vim binary
After hiting enter you will go be brought back to terminal but you are now in another shell instance. I'll use pgrep vim to find out PID and then go to file descriptor table for vim
[tony-fx503vd Documents]# cd /proc/$(pgrep vim)/fd
[tony-fx503vd fd]# pwd
/proc/3462/fd
[tony-fx503vd fd]# ls -l
total 0
lrwx------ 1 root root 64 sep 19 17:37 0 -> /dev/pts/2
lrwx------ 1 root root 64 sep 19 17:37 1 -> /dev/pts/2
lrwx------ 1 root root 64 sep 19 17:37 2 -> /dev/pts/2
lrwx------ 1 root root 64 sep 19 17:37 4 -> /root/.cache/vim/swap/%root%Documents%simple_text_file.txt.swp
For vim process there are 4 file descriptors, 0 , 1 and 2 because these are generic numbers and are present for all processes. From kernel point of view, file descriptors number 0, 1 and 2 represent by definition STD_IN, STD_OUT and STD_ERR. These three FDs are linked to /dev/pts/2 which is a character file, meaning they live only in the memory and pts stands for pseudo-terminals.
For more information about how to use these generic FDs please check Generic FDs
FD number 4 points towards a path on the system where the vim swap file for our opened file is located. Purpose of this swap file is to store changes you've made to the buffer, meaning that if you've added some text in this file and haven't written changes on disk with "w", in case if vim or OS crash you can recover those changes.
So far we have seen contents of file descriptor table, next we'll check the file table for vim process and for that we will use lsof command.
╰─ lsof -p 3462
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
vim 3462 root cwd DIR 254,0 4096 6029435 root/Documents
vim 3462 root rtd DIR 254,0 4096 2 /
vim 3462 root txt REG 254,0 4601928 13375594 usr/bin/vim
vim 3462 root mem REG 254,0 5994144 13388621 usr/lib/locale/locale-archive
vim 3462 root mem REG 254,0 165824 13375642 usr/lib/libcrypt.so.2.0.0
vim 3462 root mem REG 254,0 1953472 13379232 usr/lib/libc.so.6
vim 3462 root mem REG 254,0 4006272 13635947 usr/lib/perl5/5.36/core_perl/CORE/libperl.so
vim 3462 root mem REG 254,0 26384 13385670 usr/lib/libgpm.so.2.1.0
vim 3462 root mem REG 254,0 34680 13375602 usr/lib/libacl.so.1.1.2301
vim 3462 root mem REG 254,0 470232 13374435 usr/lib/libncursesw.so.6.3
vim 3462 root mem REG 254,0 944600 13379249 usr/lib/libm.so.6
vim 3462 root mem REG 254,0 216184 13379220 usr/lib/ld-linux-x86-64.so.2
vim 3462 root 0u CHR 136,3 0t0 6 dev/pts/3
vim 3462 root 1u CHR 136,3 0t0 6 dev/pts/3
vim 3462 root 2u CHR 136,3 0t0 6 dev/pts/3
vim 3462 root 4u REG 254,0 4096 6048577 root/.cache/vim/swap/%root%Documents%simple_text_file.txt.swp
We know from the previous commands that PID of vim is 3462, -p is a lsof option that stands for process, so list open files for process 3462 is the meaning of above command. On the output, I shall focus on FD and NAME columns, more details about the others you will find in lsof command.
We can see that the last 4 rows from output represent the file descriptor table which we've seen above. Each ID has the letter 'u' near it which means that they are open with read & write permissions.
First open file FD type is cwd (current working directory) and represents the path from which vim command was executed. Second open file FD type is rtd (root directory) and its name is enough to describe it. Third FD type is "txt" (program text), code or data used for running process, in our case it is the path on disk for vim binary.
The remaining open files are all FD type mem (memory mapped file), they are segments of virtual memory. As you can see almost all of them are .so extension. In our case they represent all dynamic linked libraries used by vim binary to run and are all loaded in RAM.
As for our last table, the inode table, as far as I know there is no easy way or a straight-forward command to display inode-table. Just remember that each file in a filesystem has an unique inode number. This number is unique only within that filesystem.
╰─ lsof -p 3462
COMMAND PID USER FD TYPE DEVICE SIZE/OFF NODE NAME
vim 3462 root 4u REG 254,0 4096 6048577 /root/.cache/vim/swap/%root%Documents%simple_text_file.txt.swp
As an example let's check the swap file for vim, we can see in NODE column the value 6048577, which represents this unique inode number. One way to find out this inode is to add to ls command the -i option. If we navigate to /root/.cache/vim/swap folder and use ls -li:
╭─░▒▓ ~/.cache/vim/swap ─────────────────────────────────────────── ✔ root@tony-fx503vd 11:38:55 ▓▒░─╮
╰─ cd /root/.cache/vim/swap ─╯
╭─░▒▓ ~/.cache/vim/swap ─────────────────────────────────────────── ✔ root@tony-fx503vd 11:38:59 ▓▒░─╮
╰─ ls -li ─╯
total 1
6048577 -rw-r--r-- 1 root root 4096 sep 24 11:38 %root%Documents%simple_text_file.txt.swp
We see in the first column of output inode number for this file, value 6048577. Another way of finding corresponding file on disk using inode number is with find command and -inum option, generic syntax is "find ${filesystem_mountpoint} -inum ${inode} -print"
╭─░▒▓ /home/tony/Desktop ─── 1 ✘ root@tony-fx503vd 11:36:26 ▓▒░─╮
╰─ find / -inum 6048577 -print ─╯
/root/.cache/vim/swap/%root%Documents%simple_text_file.txt.swp
Well, I've just said that for each file on a file system there's a unique inode number, so how would this find actually be useful you might ask. If we create hard links to a file on a filesystem, those hardlinks will have the same inode value and using this find you can see all existing hardlinks for a file. I shall use ln command to create a hardlink to our swap file
╭─░▒▓ /home/t/Desktop ─── 1 ✘ 5s root@tony-fx503vd 11:42:28 ▓▒░─╮
╰─ ln /root/.cache/vim/swap/%root%Documents%simple_text_file.txt.swp swap_file_hard_link
╰─ ls ─╯
swap_file_hard_link
╰─ cat swap_file_hard_link ─╯
b0VIM 9.0�b(c�K\�roottony-fx503vd~root/Documents/simple_text_file.txt 3210#"! U#
╭─░▒▓ /home/t/Desktop ─── 1 ✘ 5s root@tony-fx503vd 11:44:50 ▓▒░─╮
╰─ ls -li ─╯
total 4
6048577 -rw-r--r-- 2 root root 4096 sep 24 11:38 swap_file_hard_link
This hard link has indeed the same node id 6048577 and it will be displayed together with its source if I execute again the same find command.
╭─░▒▓ /home/tony/Desktop ────────── ✔ root@tony-fx503vd 11:46:12 ▓▒░─╮
╰─ find / -inum 6048577 -print ─╯
/home/tony/Desktop/swap_file_hard_link
/root/.cache/vim/swap/%root%Documents%simple_text_file.txt.swp
That's about it regarding the three file tables in the OS, of course there's a lot more to know about these and perhaps I shall create in the future some more specific tutorials.
I now hope that you understand the difference between and open file and a file descriptor. They are often confused meaning that some people use file descriptor when talking about an open file which is not accurate.