Максимальный размер файла для файлов имеет не детерминированное влияние на процессы
Я запускаю собственное ядро 2.6.27, и я просто заметил, что основные файлы, созданные во время segfault, больше, чем ограничение размера жесткого ядра, установленное для процессов.
И что делает его более странным, так это то, что основной файл иногда усекается (но не до предела, установленного ulimit).
Например, это программа, которую я попытаюсь выполнить и сбой ниже:
int main(int argc, char **argv)
{
// Get the hard and soft limit from command line
struct rlimit new = {atoi(argv[1]), atoi(argv[1])};
// Create some memory so as to beef up the core file size
void *p = malloc(10 * 1024 * 1024);
if (!p)
return 1;
if (setrlimit(RLIMIT_CORE, &new)) // Set the hard and soft limit
return 2; // for core files produced by this
// process
while (1);
free(p);
return 0;
}
И вот выполнение:
Linux# ./a.out 1446462 & ## Set hard and soft limit to ~1.4 MB
[1] 14802
Linux# ./a.out 1446462 &
[2] 14803
Linux# ./a.out 1446462 &
[3] 14804
Linux# ./a.out 1446462 &
[4] 14807
Linux# cat /proc/14802/limits | grep core
Max core file size 1446462 1446462 bytes
Linux# killall -QUIT a.out
Linux# ls -l
total 15708
-rwxr-xr-x 1 root root 4624 Aug 1 18:28 a.out
-rw------- 1 root root 12013568 Aug 1 18:39 core.14802 <=== truncated core
-rw------- 1 root root 12017664 Aug 1 18:39 core.14803
-rw------- 1 root root 12013568 Aug 1 18:39 core.14804 <=== truncated core
-rw------- 1 root root 12017664 Aug 1 18:39 core.14807
[1] Quit (core dumped) ./a.out 1446462
[2] Quit (core dumped) ./a.out 1446462
[3] Quit (core dumped) ./a.out 1446462
[4] Quit (core dumped) ./a.out 1446462
Так произошло много вещей. Я установил жесткий предел для каждого процесса примерно на 1,4 МБ.
- Полученные файлы ядра значительно превышают это ограничение. Почему?
- И 2 из 4 основных файлов, созданных, усекаются, но точно
4096
байты. Что здесь происходит?
Я знаю, что основной файл содержит, среди прочего, выделенную стек и память кучи. Разве это не должно быть довольно постоянным для такой простой программы (дайте или возьмите не более нескольких байтов), и, следовательно, создайте согласованное ядро между несколькими экземплярами?
редактирует:
1 Запрошенный выход du
Linux# du core.*
1428 core.14802
1428 core.14803
1428 core.14804
1428 core.14807
Linux# du -b core.*
12013568 core.14802
12017664 core.14803
12013568 core.14804
12017664 core.14807
2 Добавление memset()
после malloc()
определило вещи в том, что основной файл теперь усечен до 1449984
(еще 3522
байтов за лимит).
Итак, почему раньше были ядра, что они содержали? Как бы то ни было, он не подвергался ограничениям процесса.
3 Новая программа также показывает интересное поведение:
Linux# ./a.out 12017664 &
[1] 26586
Linux# ./a.out 12017664 &
[2] 26589
Linux# ./a.out 12017664 &
[3] 26590
Linux# ./a.out 12017663 & ## 1 byte smaller
[4] 26653
Linux# ./a.out 12017663 & ## 1 byte smaller
[5] 26666
Linux# ./a.out 12017663 & ## 1 byte smaller
[6] 26667
Linux# killall -QUIT a.out
Linux# ls -l
total ..
-rwxr-xr-x 1 root root 4742 Aug 1 19:47 a.out
-rw------- 1 root root 12017664 Aug 1 19:47 core.26586
-rw------- 1 root root 12017664 Aug 1 19:47 core.26589
-rw------- 1 root root 12017664 Aug 1 19:47 core.26590
-rw------- 1 root root 1994752 Aug 1 19:47 core.26653 <== ???
-rw------- 1 root root 9875456 Aug 1 19:47 core.26666 <== ???
-rw------- 1 root root 9707520 Aug 1 19:47 core.26667 <== ???
[1] Quit (core dumped) ./a.out 12017664
[2] Quit (core dumped) ./a.out 12017664
[3] Quit (core dumped) ./a.out 12017664
[4] Quit (core dumped) ./a.out 12017663
[5] Quit (core dumped) ./a.out 12017663
[6] Quit (core dumped) ./a.out 12017663
Ответы
Ответ 1
Реализация сброса ядра может быть найдена в fs/binfmt_elf.c
. Я буду следовать коду в 3.12 и выше (он изменился с помощью commit 9b56d5438), но логика очень похожа.
Вначале код решает, сколько сбрасывать VMA (область виртуальной памяти) в vma_dump_size
. Для анонимного VMA, такого как куча brk
, он возвращает полный размер VMA. Во время этого шага основной предел не задействован.
Первая фаза записи дампа ядра затем записывает заголовок PT_LOAD
для каждого VMA. Это в основном указатель, в котором говорится, где найти данные в остальной части файла ELF. Фактические данные записываются в цикле for
и фактически являются второй фазой.
Во время второй фазы elf_core_dump
многократно вызывает get_dump_page
, чтобы получить указатель struct page
для каждой страницы адресного пространства программы, которое необходимо сбросить. get_dump_page
- общая функция полезности, найденная в mm/gup.c
. Комментарий к get_dump_page
полезен:
* Returns NULL on any kind of failure - a hole must then be inserted into
* the corefile, to preserve alignment with its headers; and also returns
* NULL wherever the ZERO_PAGE, or an anonymous pte_none, has been found -
* allowing a hole to be left in the corefile to save diskspace.
и фактически elf_core_dump
вызывает функцию в fs/coredump.c
(dump_seek
в вашем ядре, dump_skip
в 3.12+), если get_dump_page
возвращает NULL
. Эта функция вызывает lseek, чтобы оставить отверстие в дампе (фактически, поскольку это ядро, оно вызывает file->f_op->llseek
непосредственно на указателе struct file
). Основное отличие состоит в том, что dump_seek
действительно не подчинялся ulimit, а новый dump_skip
.
Что касается того, почему вторая программа имеет странное поведение, это, вероятно, связано с ASLR (рандомизация адресного пространства). Какой VMA усечен, зависит от относительного порядка VMA, который является случайным. Вы можете попробовать отключить его с помощью
echo 0 | sudo tee /proc/sys/kernel/randomize_va_space
и посмотрите, являются ли ваши результаты более однородными. Чтобы повторно использовать ASLR, используйте
echo 2 | sudo tee /proc/sys/kernel/randomize_va_space