Ошибка страницы системного вызова read() не зависит от размера файла

Я читаю файлы разных размеров (1 КБ - 1 ГБ), используя read() в C, Но каждый раз, когда я проверяю page-faults с помощью perf-stat, он всегда дает мне одинаковые (почти) значения.

Моя машина: (fedora 18 на виртуальной машине, оперативная память - 1 ГБ, дисковое пространство - 20 ГБ)

uname -a
Linux localhost.localdomain 3.10.13-101.fc18.x86_64 #1 SMP Fri Sep 27 20:22:12 UTC 2013 x86_64 x86_64 x86_64 GNU/Linux

mount | grep "^/dev"
/dev/mapper/fedora-root on / type ext4 (rw,relatime,seclabel,data=ordered)
/dev/sda1 on /boot type ext4 (rw,relatime,seclabel,data=ordered)

Мой код:

 10 #define BLOCK_SIZE 1024
. . . 
 19         char text[BLOCK_SIZE];
 21         int total_bytes_read=0;
. . .

 81         while((bytes_read=read(d_ifp,text,BLOCK_SIZE))>0)
 82         {
 83                 write(d_ofp, text, bytes_read); // writing to /dev/null
 84                 total_bytes_read+=bytes_read;
 85                 sum+=(int)text[0];  // doing this just to make sure there's 
                                             // no lazy page loading by read()
                                             // I don't care what is in `text[0]`
 86         }
 87         printf("total bytes read=%d\n", total_bytes_read);
 88         if(sum>0)
 89                 printf("\n");

Perf-stat output: (показывает размер файла, время чтения файла и количество ошибок на странице)

[read]:   f_size:    1K B, Time:  0.000313 seconds, Page-faults: 150, Total bytes read: 980 
[read]:   f_size:   10K B, Time:  0.000434 seconds, Page-faults: 151, Total bytes read: 11172
[read]:   f_size:  100K B, Time:  0.000442 seconds, Page-faults: 150, Total bytes read: 103992
[read]:   f_size:    1M B, Time:  0.00191  seconds, Page-faults: 151, Total bytes read: 1040256
[read]:   f_size:   10M B, Time:  0.050214 seconds, Page-faults: 151, Total bytes read: 10402840 
[read]:   f_size:  100M B, Time:  0.2382   seconds, Page-faults: 150, Total bytes read: 104028372 
[read]:   f_size:    1G B, Time:  5.7085   seconds, Page-faults: 148, Total bytes read: 1144312092 

Вопросы:
1. Как можно с ошибками страницы для файла read() размером 1КБ и 1ГБ будет одинаковым? Поскольку я тоже читаю данные (кодовая строка № 84), я проверяю, действительно ли данные читаются.
2. Единственная причина, по которой я могу подумать, что он не сталкивается с таким количеством сбоев страниц, заключается в том, что данные уже присутствуют в основной памяти. Если это так, как я могу очистить его, чтобы при запуске моего кода он действительно показывал мне истинные ошибки страницы? В противном случае я никогда не смогу измерить истинную производительность read(),

Edit1:
echo 3 > /proc/sys/vm/drop_caches не помогает, вывод остается прежним.

Edit2: для mmap, выход perf-stat является:

[mmap]:   f_size:    1K B, Time:  0.000103 seconds, Page-faults: 14
[mmap]:   f_size:   10K B, Time:  0.001143 seconds, Page-faults: 151
[mmap]:   f_size:  100K B, Time:  0.002367 seconds, Page-faults: 174
[mmap]:   f_size:    1M B, Time:  0.007634 seconds, Page-faults: 401
[mmap]:   f_size:   10M B, Time:  0.06812  seconds, Page-faults: 2,688
[mmap]:   f_size:  100M B, Time:  0.60386  seconds, Page-faults: 25,545
[mmap]:   f_size:    1G B, Time:  4.9869   seconds, Page-faults: 279,519

1 ответ

Решение

Я думаю, что вы не поняли, что именно является ошибкой страницы. PageFault, согласно Википедии, является "ловушкой" (исключение), своего рода прерыванием, которое генерируется самим ЦП, когда программы пытаются получить доступ к чему-либо, которое не загружено в физическую память (но обычно уже зарегистрировано в виртуальной памяти с страница помечена как "не присутствует" P: текущий бит = 0).

Pagefault - это плохо, потому что он заставляет процессор останавливать выполнение пользовательской программы и переключаться на ядро. И сбои страниц в режиме ядра встречаются не так часто, потому что ядро ​​может проверить наличие страницы, прежде чем получить к ней доступ. Если функция ядра хочет записать что-то на новую страницу (в вашем случае read syscall), он будет распределять страницу, явно вызывая распределитель страниц, а не пытаясь получить к ней доступ и вызывая ошибку страницы. Меньше прерываний и меньше кода для выполнения с явным управлением памятью.

--- читать дела ---

Ваше чтение обрабатывается sys_read из fs / read_write.c. Вот цепочка вызовов (возможно, не точная):

472 SYSCALL_DEFINE3(read, unsigned int, fd, char __user *, buf, size_t, count)
479                 ret = vfs_read(f.file, buf, count, &pos);
  vvv
353 ssize_t vfs_read(struct file *file, char __user *buf, size_t count, loff_t *pos)
368                         ret = file->f_op->read(file, buf, count, pos);
  vvv

фс / ext4 / file.c

626 const struct file_operations ext4_file_operations = {
628         .read           = do_sync_read,

... do_sync_read -> generic_file_aio_read -> do_generic_file_read

мм / filemap.c

1100 static void do_generic_file_read(struct file *filp, loff_t *ppos,
1119         for (;;) {
1120                 struct page *page;
1127                 page = find_get_page(mapping, index);
1128                 if (!page) {
1134                                 goto no_cached_page;  
  // osgx - case when pagecache is empty  ^^vv
1287 no_cached_page:
1288                 /*
1289                  * Ok, it wasn't cached, so we need to create a new
1290                  * page..
1291                  */
1292                 page = page_cache_alloc_cold(mapping);

включают в себя / Linux / pagemap.h

233 static inline struct page *page_cache_alloc_cold(struct address_space *x)
235         return __page_cache_alloc(mapping_gfp_mask(x)|__GFP_COLD);
  vvv
222 static inline struct page *__page_cache_alloc(gfp_t gfp)
224         return alloc_pages(gfp, 0);

Так что я могу отследить это read() системный вызов заканчивается выделением страницы (alloc_pages через прямые звонки. После размещения страницы, read() syscall выполнит DMA-передачу данных с жесткого диска на новую страницу и затем вернет их пользователю (учитывая случай, когда файл не кэшируется в pagecache). Если данные уже были в кеше страницы, read() (do_generic_file_read) будет повторно использовать существующую страницу из кэша страниц без фактического чтения с жесткого диска, создавая дополнительное сопоставление.

После read() возвращается, все данные находятся в памяти, и доступ для чтения к ним не будет генерировать ошибку страницы.

--- случай mmap ---

Если вы перепишите тест, чтобы сделать mmap() в вашем файле, а затем доступ (text[offset]) нет страницы вашего файла (она не была в pagecache), произойдет настоящая ошибка страницы.

Все счетчики страниц (perf stat а также /proc/$pid/stat) обновляются ТОЛЬКО в том случае, когда ЦП генерирует реальные ловушки при возникновении ошибок. Вот обработчик x86 для страницы сбоя arch / x86 / mm / fault.c, который будет работать

1224 dotraplinkage void __kprobes
1225 do_page_fault(struct pt_regs *regs, unsigned long error_code)
1230         __do_page_fault(regs, error_code);
  vvv
1001 /*
1002  * This routine handles page faults.  It determines the address,
1003  * and the problem, and then passes it off to one of the appropriate
1004  * routines.
1005  */
1007 __do_page_fault(struct pt_regs *regs, unsigned long error_code)
 /// HERE is the perf stat pagefault event generator VVV 
1101         perf_sw_event(PERF_COUNT_SW_PAGE_FAULTS, 1, regs, address);

а где-то позже вызовет обработчик Pagefault handle_mm_fault -> handle_pte_fault -> __do_fault кончающийся на vma->vm_ops->fault(vma, &vmf);,

это fault виртуальная функция была зарегистрирована в mmap и я думаю, что это filemap_fault, Эта функция будет выполнять фактическое распределение страниц (__alloc_page) и чтение с диска в случае пустого кэша страниц (это будет считаться "основным" отказом страницы, потому что для этого требуется внешний ввод / вывод) или будет переназначение страницы из кэша страниц (если данные были предварительно выбраны или уже находятся в кэше страниц, считаются "второстепенными" "pagefault, потому что это было сделано без внешнего ввода-вывода и, как правило, быстрее).


PS: проведение экспериментов на виртуальной платформе может что-то изменить; например, даже после очистки дискового кэша (pagecache) в гостевой Fedora echo 3 > /proc/sys/vm/drop_caches данные с виртуального жесткого диска все еще могут кэшироваться ОС хоста.

Другие вопросы по тегам