Перечитаем open(2):

       A call to open() creates a new open **file description**, an entry in
       the system-wide table of open files.  The open file description
       records the file offset and the file status flags.  A
       **file descriptor** is a reference to an open file description; this
       reference is unaffected if *pathname* is subsequently removed or
       modified to refer to a different file.
...
       When a file descriptor is duplicated (using dup(2) or similar),
       the duplicate refers to the same open file description as the
       original file descriptor, and the two file descriptors
       consequently share the file offset and file status flags.  Such
       sharing can also occur between processes: a child process created
       via fork(2) inherits duplicates of its parent's file descriptors,
       and those duplicates refer to the same open file descriptions.

То есть файловый дескриптор (число) — это индекс в таблицу в ядре, из которой ссылки ведут на file descriptions.

Разыщем соответствующе структуры данных в ядре Linux. Вот так выглядит таблица файловых дескрипторов:

struct fdtable {
	...
	struct file __rcu **fd;      /* current fd array */
	unsigned long *close_on_exec;
	unsigned long *open_fds;
	unsigned long *full_fds_bits;
	...
};

А вот и file description:

struct file {
	...
	struct path		f_path;
	struct inode		*f_inode;	/* cached value */
	const struct file_operations	*f_op;

  ...
	atomic_long_t		f_count;
	unsigned int 		f_flags;
	fmode_t			f_mode;
	struct mutex		f_pos_lock;
	loff_t			f_pos;
  ...

Ну и таблица виртуальных методов:

struct file_operations {
	struct module *owner;
	loff_t (*llseek) (struct file *, loff_t, int);
	ssize_t (*read) (struct file *, char __user *, size_t, loff_t *);
	ssize_t (*write) (struct file *, const char __user *, size_t, loff_t *);
	...
	__poll_t (*poll) (struct file *, struct poll_table_struct *);
	...
	int (*mmap) (struct file *, struct vm_area_struct *);
	unsigned long mmap_supported_flags;
	int (*open) (struct inode *, struct file *);
	int (*flush) (struct file *, fl_owner_t id);
	int (*release) (struct inode *, struct file *);
	int (*fsync) (struct file *, loff_t, loff_t, int datasync);
  ...

Что происходит с этими структурами при:

Подробности в документации по ядру:

Overview of the Linux Virtual File System - The Linux Kernel documentation

Давайте перечислим известные нам типы file descriptions: