Лаборатория Tarantool

Системное программирование

Лекция 5:

Файловая система. Виртуальная ФС в ядре. Файлы, их типы. I/O операции и их планировщики в ядре. Page cache. Режимы работы с файлом.

Version: 2

The presentation is outdated and not maintained anymore. Please, refer to the English version for the most actual slides.

Новости

Дедлайны:

  • планировщик корутин: 19 марта, штраф -10
  • shell: 2 апреля

Обратная связь:

Ссылка на анонимное голосование: https://PollEv.com/vladislavshp093
В нем можно отмечать все ли понятно, или совсем нет, или не до конца.

Файловые дескрипторы [1]

struct task_struct {
        /* ... */
        struct files_struct *files;
        /* ... */
};

struct files_struct {
	struct fdtable *fdt;
};

struct fdtable {
	unsigned int max_fds;
	struct file **fd;
};

struct file {
	struct path	  f_path;
	struct inode	  *f_inode;
	atomic_long_t	  f_count;
	unsigned int 	  f_flags;
	fmode_t		  f_mode;
	loff_t		  f_pos;
	const struct cred *f_cred;
};

Процесс хранит открытые файлы

В таблице дескрипторов

Таблица - обычный массив

Дескриптор в ядре - это структура. В user space - число. Индекс в массиве дескрипторов.

Файловые дескрипторы [2]

task 1

task 2

struct file

stdout
fd1
fd1
stdout
pos   = 0
count = 1

struct file

pos   = 0
count = 1

struct inode

struct file: stdout

pos   = 0
count = 1

struct file: stdout

pos   = 0
count = 1

struct inode

struct inode

Ядро Linux

Процессы

Аппаратура

Время

Файловая система

IPC

Сеть

Пользователи

Структуры данных

Виртуализация

Файловая система [1]

HDD

SSD

DRAM

DRAM

DRAM

Volatile

Non-volatile

Файловая система [2]

HDD

DRAM

Файловая система [3]

Sort

Sort

Sort

Файловая система [4]

DRAM 4 GB, 1700р

1 Gb - 425р

HDD 2000 GB, 3900р

1 GB - 1.95р

x217

SSD 250 GB, 4100р

1 GB - 16р

x26

Файловая система [5]

Что это?

1. " / "

2. FAT, ext, NSF, USF, NTFS ...

3. Partition

Файловая система [6]

Задачи

  • Именование
  • Формат хранения
  • API
  • Защита
  • Реализация

/home/v.shpilevoy/Work/Repositories/tarantool

struct super_operations {
   	struct inode *(*alloc_inode)(struct super_block *sb);
	void (*destroy_inode)(struct inode *);
	int (*write_inode) (struct inode *, struct writeback_control *wbc);
	int (*drop_inode) (struct inode *);
	void (*evict_inode) (struct inode *);
	int (*sync_fs)(struct super_block *sb, int wait);
	int (*freeze_fs) (struct super_block *);
	int (*unfreeze_fs) (struct super_block *);
	int (*statfs) (struct dentry *, struct kstatfs *);
	int (*remount_fs) (struct super_block *, int *, char *);
	void (*umount_begin) (struct super_block *);
};
struct super_operations ext2_sops = {
	.alloc_inode	= ext2_alloc_inode,
	.destroy_inode	= ext2_destroy_inode,
	.write_inode	= ext2_write_inode,
	.evict_inode	= ext2_evict_inode,
	.put_super	= ext2_put_super,
	.sync_fs	= ext2_sync_fs,
	.freeze_fs	= ext2_freeze,
	.unfreeze_fs	= ext2_unfreeze,
	.remount_fs	= ext2_remount,
};

Файловая система [7]

MOUNT(8)                  BSD System Manager's Manual                 MOUNT(8)

NAME
     mount -- mount file systems

SYNOPSIS
     mount [-adfruvw] [-t lfs | external_type]
     mount [-dfruvw] special | mount_point
     mount [-dfruvw] [-o options] [-t lfs | external_type] special mount_point

Реализация ФС - программа, не устройство.

ext, ext2, ext3, ext4, NTFS, FAT,
NFS, tmpfs, ramfs, procfs, sysfs

Файловая система [8] FUSE

NAME
       SSHFS - filesystem client based on ssh

SYNOPSIS
   mounting
       sshfs [user@]host:[dir] mountpoint [options]

   unmounting
       umount mountpoint
$> sudo mkdir /mnt/remote_dir
$> sudo sshfs username@xxx.xxx.xxx.xxx:/some/path/on/remote/serv \
              /mnt/remote_dir
$> cd /mnt/remote_dir
$> # you are on remote server

Подготовить папку для монтирования

Подключить удаленную ФС в локальную папку

Можно пользоваться как локальной папкой, а под капотом - ssh

File System In USEr Space

Файловая система [9]

$> cat /proc/filesystems
sysfs
rootfs
ramfs
proc
tmpfs
securityfs
pipefs
ext3
ext2
ext4
vfat
fuse
...

Файловая система [10]

ФС - программа, не зависит от устройства

Устройство

?

Что здесь?

Файловая система [11]

Что между ФС и устройством?

Драйвер

1 балл

Устройства хранения [1]

Символьное устройство

Блочное устройство

Сетевое устройство

Последовательный доступ

Произвольный доступ

Комбинированный доступ

Устройства хранения [2]

Контроллер

Считывающие головки

Магнитные диски

HDD - Hard Disk Drive

HDD [1] Магнитный диск

Стекло/пластик/металл

Кобальт/сталь

Кобальт/сталь

Оксид стали

Кобальт

0

1

1

0

xx нанометров

HDD [2] Магнитный диск

Запись

Чтение

HDD [3] Считывающая головка

HDD [4] Адресация

CHS - Cylinder - Head - Sector

Секторы

1

2

3

Цилиндр

Секторы

Цилиндр

Головки

Этаж - Кольцо - Сегмент

HDD [5] Адресация

LBA - Logical Block Addressing

0

N

Абстракция над любой адресацией. Устройство - непрерывный массив блоков байт.

Трансляция CHS в LBA:

LBA = (C * max\_heads + H) * max\_sectors + (S - 1)

HDD [6] Производительность

Скорости вращения в rpm - Revolutions Per Minute:

  • 15000 rpm - сервер
  • 7000-10000 rpm - домашний

>= 4 ms на полное вращение, 2 ms в среднем

Последовательные обращения - меньше пустых прокруток

vs

4 оборота

1 оборот

HDD [7] Отказоустойчивость

"Head crash"

Падение

Магнит

Устройства хранения

SSD - Solid State Drive

Ячейки Flash памяти

Контроллер

DRAM

SSD [1] Ячейка flash памяти - запись

Диэлектрик

Проводник

"Ловушка" для электронов

?

SSD [2] Ячейка flash памяти - запись

Диэлектрик

Проводник

Control Gate

Floating Gate

Source

Drain

SSD [3] Ячейка flash памяти - удаление

Диэлектрик

Проводник

Control Gate

Floating Gate

Source

Drain

Как прогнать электроны?

2 балла

SSD [4] Ячейка flash памяти - удаление

Диэлектрик

Проводник

SSD [5] Ячейка flash памяти - чтение

Диэлектрик

Проводник

С этой точки заряд в сумме 0 - ток не потечет

SSD [6] Отказоустойчивость

  • Заряд есть - тока нет. Это бит 0
  • Заряда нет - ток есть. Это бит 1

Утечка заряда

SSD [7] Адресация

LBA адресация

Чтения страницами по 512 - 8192 байт

SSD [8] Запись/удаление

1. В блоке X заняли страницы ABCD.

2. Записали новые страницы EFGH, обновили ABCD.

3. Чтобы снова можно было писать в ABCD, нужно очистить весь блок.

SSD [9] Запись/удаление

Write\_Amplification = \frac{data\_written\_by\_host}{data\_written\_by\_ssd}

Это проблема всех append-only сущностей

SSD

LSM-tree

SSD [10] Итог

Задачи SSD:

  • адресация LBA
  • кеширование
  • write leveling - "размазывание нагрузки"
  • параллельный доступ
  • сборка мусора

Скорость:

  • десятки микросекунд на чтение страницы
  • сотни - на запись
  • единицы миллисекунд на очистку блока

Файловая система [1]

Как понять, какая файловая система на устройстве?

В начале хранилища каждого устройства есть особый блок байтов, где указана метаинформация об ФС и ее "магическое число".

2 балла

Файловая система [2] MBR

MBR - Master Boot Record

Bootstrap code

Partition record 1

Partition record 2

Partition record 3

Partition record 4

512 байт

Носитель данных

MBR

Part. 1

Part. 2

Part. 3

Part. 4

struct part_record {
        lba_t start;
        lba_t end;
        /** Partition type. */
        int part_type;
        /** Filesystem header. */
        struct fs_header fs_header;
};

Файловая система [3] Разбиение

Part. i - 1

Part. i

Part. i + 1

Filesystem header

OS bootstrap code

Data block

1

... data blocks ...

Data block

N

Суперблок

struct fs_super_block {
        int32_t block_count;
        int32_t free_block_count;
        int32_t block_size;
        int32_t flags;
        int32_t mount_time;
        /* ... */
        int16_t magic;
        /* ... */
};
#define RAMFS_MAGIC		0x858458f6
#define TMPFS_MAGIC		0x01021994
#define EXT2_SUPER_MAGIC	0xEF53
#define EXT3_SUPER_MAGIC	0xEF53
#define EXT4_SUPER_MAGIC	0xEF53
#define MINIX_SUPER_MAGIC	0x137F
#define MSDOS_SUPER_MAGIC	0x4d44
#define NFS_SUPER_MAGIC		0x6969

Файловая система [4] FAT

FAT - File Allocation Table

Файл - однонаправленный список блоков

HDD

Файловая система [5] FAT

/**
 * Linux kernel,
 * fs/fat/fat.h
 * 30.09.2018.
 */
struct fat_entry {
	int entry;
	union {
		u8 *ent12_p[2];
		__le16 *ent16_p;
		__le32 *ent32_p;
	} u;
	int nr_bhs;
	struct buffer_head *bhs[2];
	struct inode *fat_inode;
};
/** uapi/linux/msdos_fs.h */
struct msdos_dir_entry {
	__u8	name[MSDOS_NAME];/* name and extension */
	__u8	attr;		/* attribute bits */
	__u8    lcase;		/* Case for base and extension */
	__u8	ctime_cs;	/* Creation time, centiseconds (0-199) */
	__le16	ctime;		/* Creation time */
	__le16	cdate;		/* Creation date */
	__le16	adate;		/* Last access date */
	__le16	starthi;	/* High 16 bits of cluster in FAT32 */
	__le16	time,date,start;/* time, date and first cluster */
	__le32	size;		/* file size (in bytes) */
};

Ядерная структура для цепочки файлов

Ядерная структура для каталога

Файловая система [6] Ext2

Ext2 - 2nd Extended filesystem

Эндрю Танненбаум - автор Minix и minixfs - прародителя ext

  • деление на блоки
  • fragmentation aware
  • хороша для flash памяти

Файловая система [7] Ext2

ext inode

name;
rights;
time;
-------
b1_addr;
b2_addr;
b3_addr;
...
b12_addr;
-------
ind1_addr;
ind2_addr;
ind3_addr;

block level 1

b1_addr;
b2_addr;
b3_addr;
...
bN_addr;

block level 2

ind1_1_addr;
ind1_2_addr;
...
ind1_N_addr;

block level 1

block level 1

block level 1

...

block level 3

ind2_1_addr;
ind2_2_addr;
...
ind2_N_addr;

block level 2

block level 2

block level 2

...

Файловая система [8] Ext2

Ext2 superblock

Block Group 1

Block Group

2

...

Block Group

N

Ext2 superblock

Block Bitmask

Inode Bitmask

Inode Table

... data blocks ...

Структура Ext2

Структура одной группы блоков

Битовая маска - если бит i = 0, то i-й объект (блок/inode) свободен

Подряд идущие struct ext2_inode

Файловая система [9] Ext2

/**
 * Linux kernel,
 * fs/ext2/ext2.h
 * 30.09.2018
 * 53 lines.
 */
struct ext2_inode {
	__le16	i_mode;
	__le16	i_uid;
	__le32	i_size;
	__le32	i_atime;
	__le16	i_links_count;
	__le32	i_blocks;
	__le32	i_flags;
	__le32	i_block[15];
        /* ... */
};

struct ext2_group_desc
{
	__le32	bg_block_bitmap;
	__le32	bg_inode_bitmap;
	__le32	bg_inode_table;
	__le16	bg_free_blocks_count;
	__le16	bg_free_inodes_count;
	__le16	bg_used_dirs_count;
	__le16	bg_pad;
	__le32	bg_reserved[3];
};
#define EXT2_MIN_BLOCK_SIZE 1024
#define	EXT2_MAX_BLOCK_SIZE 4096
#define	EXT2_NDIR_BLOCKS    12
#define	EXT2_IND_BLOCK	    EXT2_NDIR_BLOCKS
#define	EXT2_DIND_BLOCK	    (EXT2_IND_BLOCK + 1)
#define	EXT2_TIND_BLOCK	    (EXT2_DIND_BLOCK + 1)
#define	EXT2_N_BLOCKS	    (EXT2_TIND_BLOCK + 1)

Номера 12 блоков данных, и три номера на непрямую адресацию

Номера блоков, где лежат битовые маски, таблица inode

Размеры блоков, размеры таблиц косвенности

Виртуальная файловая система [1]

/**
 * Linux kernel.
 * include/linux/fs.h
 * 30.09.2018.
 * 33 lines.
 */
struct super_operations {
   	struct inode *(*alloc_inode)(struct super_block *sb);
	void (*destroy_inode)(struct inode *);

   	void (*dirty_inode) (struct inode *, int flags);
	int (*write_inode) (struct inode *, struct writeback_control *wbc);
	int (*drop_inode) (struct inode *);
	void (*evict_inode) (struct inode *);
	void (*put_super) (struct super_block *);
	int (*sync_fs)(struct super_block *sb, int wait);
	int (*freeze_super) (struct super_block *);
	int (*freeze_fs) (struct super_block *);
	int (*thaw_super) (struct super_block *);
	int (*unfreeze_fs) (struct super_block *);
	int (*statfs) (struct dentry *, struct kstatfs *);
	int (*remount_fs) (struct super_block *, int *, char *);
	void (*umount_begin) (struct super_block *);

	int (*show_options)(struct seq_file *, struct dentry *);
	int (*show_devname)(struct seq_file *, struct dentry *);
	int (*show_path)(struct seq_file *, struct dentry *);
	int (*show_stats)(struct seq_file *, struct dentry *);
	long (*free_cached_objects)(struct super_block *,
				    struct shrink_control *);
};

Виртуальная файловая система [2]

write(fd, buf, size);

User space

Kernel space

file = find_file(fd);
file->write(buf, size);
file->inode->write_inode(buf, size);
ext2_write_inode(buf, size);
fat_write_inode(buf, size);
bdev_write_page(page);

Hardware

Виртуальная файловая система [3]

#include <stdio.h>
#include <dirent.h>

int main()
{
	DIR *dir = opendir(".");
	struct dirent *dirent = readdir(dir);
	while (dirent != NULL) {
		printf("name = %s, inode number = "\
                       "%d, type = %d\n",
                       dirent->d_name,
                       (int) dirent->d_ino,
		       (int) dirent->d_type);
		dirent = readdir(dir);
	}
	closedir(dir);
	return 0;
}
$> gcc 1_dirent.c
$> ./a.out
name = ., inode number = 19537325,
type = 4

name = .., inode number = 18730940,
type = 4

name = 2_fstat.c, inode number =
19537344, type = 8

name = a.out, inode number = 19641892,
type = 8

name = 1_dirent.c, inode number =
19537330, type = 8

Виртуальная файловая система [4]

#include <stdio.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv) {
	struct stat st;
	stat(argv[1], &st);
	printf("inode = %d, protection = %d, links = "\
		"%d, uid = %u, size = %d, blocks = "\
		"%d\n", (int)st.st_ino, (int)st.st_mode,
		(int)st.st_nlink, (unsigned)st.st_uid,
		(int)st.st_size, (int)st.st_blocks);
	if ((st.st_mode & S_IFDIR) == S_IFDIR)
		printf("the file is directory\n");
	if ((st.st_mode & S_IFREG) == S_IFREG)
		printf("the file is regular\n");
	if ((st.st_mode & S_IFLNK) == S_IFLNK)
		printf("the file is symbolic link\n");

	if ((st.st_mode & S_IRUSR) == S_IRUSR)
		printf("can read it\n");
	if ((st.st_mode & S_IWUSR) == S_IWUSR)
		printf("can write it\n");
	if ((st.st_mode & S_IXUSR) == S_IXUSR)
		printf("can execute it\n");

	printf("my uid: %d\n", (int)getuid());
	return 0;
}
$> gcc 2_fstat.c
$> ./a.out a.out
inode = 19642912, protection = 33261,
links = 1, uid = 502, size = 8536,
blocks = 24
the file is regular
can read it
can write it
can execute it
my uid: 502
$> ./a.out .
inode = 19537325, protection = 16877,
links = 5, uid = 502, size = 160,
blocks = 0
the file is directory
can read it
can write it
can execute it
my uid: 502
$> ./a.out 2_fstat.c 
inode = 19537344, protection = 33188,
links = 1, uid = 502, size = 838,
blocks = 8
the file is regular
can read it
can write it
my uid: 502

Планировщик I/O операций

Пакетирование операций работы с диском - суть всех планировщиков

read_blocks(1);
read_blocks(2);

I/O планировщик

read_blocks(1, 2);
split_blocks(res);
res1
res2

I/O планировщик

read_blocks(3, 1, 4, 2);
read_blocks(1, 2, 3, 4);

Слияние

Сортировка

Linus I/O Elevator [1]

+

1. Найти запрос, смежный по началу/концу. Если найден, приклеить.

...

...

5-10

10-15

2. Найти отдаленных соседей. Если найдены - между ними.

...

...

5-8

10-15

1-3

3. Иначе в конец "очереди"

...

100-120

10-15

* если в очереди есть запрос, старше некоторого порога, то все новые идут в конец

Linus I/O Elevator [2]

Итог:

  • простой
  • нет гарантий задержки
  • нет защиты от write starving

Write starving - феномен "голодающих записей". Чтения более требовательны к скорости, но их предпочтение в планировщике морит записи "голодом" до устройства.

Deadline I/O Scheduler [1]

Merge/sort queue

Read FIFO queue

Write FIFO queue

req_t pick_next() {
        req_t ro = next_ro();
        req_t rw = next_rw();
        if (ro.deadline <= curr_time ||
            rw.deadline <= curr_time) {
                if (ro.deadline < rw.deadline)
                    return ro;
                return rw;
        }
        return next_merge_sort();
}

+

Положить в 2 из 3х очередей. У чтений дедлайн меньше в 10 раз.

Deadline I/O Scheduler [2]

Как Deadline I/O Scheduler решает проблему голодающих записей?

Вводится ограничение на число подряд выполненных чтений.

2 балла

Anticipatory I/O Scheduler

"Предупреждающий"

Как Deadline, но после выполнения чтения ждет несколько мс на случай новых чтений

/* ... */
while (read(fd, buf, size) != 0) {
    /* do something ... */
}
/* ... */

Типичная "читалка" - последовательные блокирующие чтения

...

CFQ I/O Scheduler

CFQ - Completely Fair Queuing

Merge/sort queue

Deadline queue

Process queues

Process queues

Process queues

Process queues

...

time slice

time slice

time slice

time slice

Почти как CFS для процессов

Другие планировщики

BFQ - Budget Fair Queuing. Это взвешенный CFQ, с приоритетами. Еще ближе к CFS.

Noop. Самый простой - только мерж. Нет сортировки, справедливости и тд. Одна очередь мержа.

I/O планировщики: итог [1]

CFQ на момент чтения лекции - по умолчанию. Хорошо управляется с интерактивными приложениями

Linus Elevator - наилучшая пропускная способность, но тотальная несправедливость

$> # Template:
$> # cat /sys/block/{device_name}/queue/scheduler
$>
$> cat /sys/block/sda/queue/scheduler 
noop deadline [cfq]

Посмотреть свой шедулер:

I/O планировщики: итог [2]

Как еще ускорить доступ к носителям?

Кэш

1 балл

Page cache [1]

Дерево закэшированных блоков

read(fd, buf, size);

если найдено - вернуть сразу

положить в кэш

Кэш ляжет в RAM, кэши процессора - доступ за наносекунды

Page cache [2] Write

no-write

write-back

write-through

Инвалидировать кэш, записать на носитель сразу.

Обновить кэш. На носитель записать, когда вытеснится из кэша.

Записать и в кэш, и на носитель сразу.

+ изгнание из кэша по LRU, классика

Буферизация вывода

int
printf(const char * restrict format, ...);

int
fprintf(FILE * restrict stream, const char * restrict format, ...);

int
fputs(const char *restrict s, FILE *restrict stream);

int
fflush(FILE *stream);

Это userspace понятия. В ядре буферизуется и кэшируется вообще все.

Заключение

Обратная связь: goo.gl/forms/TAeraYrqJcil7GDt1

и на портале Техносферы

В следующий раз:

Потоки. Отличие от процессов. Атомарные операции. Синхронизация. Атрибуты. Особенности многопоточных процессов. Вид в ядре.

Системное программирование 5

By Vladislav Shpilevoy

Системное программирование 5

Файловая система и ядро. Виртуальная ФС. Файлы и типы: block, char, network. Представление в ядре: inode. Секторы. Планировщики IO операций - elevator-ы: Linus, Deadline, Anticipatory, CFQ, Noop. Page cache. Page nowrite/writethrough/writeback. Работа с файлом - buffered, unbuffered, line.

  • 2,051