Сама ошибка сегментации висит

У меня были некоторые проблемы с сервером сегодня, и теперь я откидывал его до того, что он не может избавиться от процессов, которые получают segfault.

После того, как процесс получает seg-fault, процесс просто держится, а не убивается.

Тест, который должен вызвать ошибку Segmentation fault (core dumped).

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
 char *buf;
 buf = malloc(1<<31);
 fgets(buf, 1024, stdin);
 printf("%s\n", buf);
 return 1;
}

Скомпилируйте и установите разрешения с помощью gcc segfault.c -o segfault && chmod +x segfault.

Запуск этого (и нажатия на ввод 1 раз) на проблемном сервере приводит к зависанию. Я также запускал это на другом сервере с той же версией ядра (и большей частью тех же пакетов), и он получает seg-fault, а затем завершает работу.

Вот несколько последних строк после запуска strace ./segfault на обоих серверах.

Плохой сервер

"\n", 1024)                     = 1
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
# It hangs here....

Рабочий сервер

"\n", 1024)                     = 1
--- SIGSEGV {si_signo=SIGSEGV, si_code=SEGV_MAPERR, si_addr=0} ---
+++ killed by SIGSEGV (core dumped) +++
Segmentation fault (core dumped)
[email protected] { ~ }# echo $?
139

Когда процесс зависает (после его устранения), это выглядит так.

Невозможно

[email protected] { ~ }# ./segfault

^C^C^C

Запись из ps aux

root 22944 0.0 0.0 69700 444 pts/18 S+ 15:39 0:00 ./segfault

cat/proc/22944/stack

[<ffffffff81223ca8>] do_coredump+0x978/0xb10
[<ffffffff810850c7>] get_signal_to_deliver+0x1c7/0x6d0
[<ffffffff81013407>] do_signal+0x57/0x6c0
[<ffffffff81013ad9>] do_notify_resume+0x69/0xb0
[<ffffffff8160bbfc>] retint_signal+0x48/0x8c
[<ffffffffffffffff>] 0xffffffffffffffff

Еще одна забавная вещь: я не могу прикрепить strace к висящему процессу segfault. Фактически это убивает.

[email protected] { ~ }# strace -p 1234
Process 1234 attached
+++ killed by SIGSEGV (core dumped) +++

ulimit -c 0 сидит и ulimit -c, ulimit -H -c, а ulimit -S -c показывает значение 0

  • Версия ядра: 3.10.0-229.14.1.el7.x86_64
  • Distro-version: Red Hat Enterprise Linux Server release 7.1 (Maipo)
  • Запуск в vmware

Сервер работает как надо во всем остальном.

Обновление Выключение abrt (systemctl stop abrtd.service) устранило проблему с процессами, уже зависавшими после ядра-дампа, и новыми процессами ядра-демпинга. Запуск abrt снова не вызвал проблемы.

Обновление 2016-01-26 У нас возникла проблема, которая была похожа, но не совсем такая. Исходный код, используемый для тестирования:

#include <stdio.h>
#include <stdlib.h>
int main(int argc, char **argv)
{
 char *buf;
 buf = malloc(1<<31);
 fgets(buf, 1024, stdin);
 printf("%s\n", buf);
 return 1;
}

висел. Выходной сигнал cat /proc/<pid>/maps был

00400000-00401000 r-xp 00000000 fd:00 13143328                           /root/segfault
00600000-00601000 r--p 00000000 fd:00 13143328                           /root/segfault
00601000-00602000 rw-p 00001000 fd:00 13143328                           /root/segfault
7f6c08000000-7f6c08021000 rw-p 00000000 00:00 0
7f6c08021000-7f6c0c000000 ---p 00000000 00:00 0
7f6c0fd5b000-7f6c0ff11000 r-xp 00000000 fd:00 14284                      /usr/lib64/libc-2.17.so
7f6c0ff11000-7f6c10111000 ---p 001b6000 fd:00 14284                      /usr/lib64/libc-2.17.so
7f6c10111000-7f6c10115000 r--p 001b6000 fd:00 14284                      /usr/lib64/libc-2.17.so
7f6c10115000-7f6c10117000 rw-p 001ba000 fd:00 14284                      /usr/lib64/libc-2.17.so
7f6c10117000-7f6c1011c000 rw-p 00000000 00:00 0
7f6c1011c000-7f6c1013d000 r-xp 00000000 fd:00 14274                      /usr/lib64/ld-2.17.so
7f6c10330000-7f6c10333000 rw-p 00000000 00:00 0
7f6c1033b000-7f6c1033d000 rw-p 00000000 00:00 0
7f6c1033d000-7f6c1033e000 r--p 00021000 fd:00 14274                      /usr/lib64/ld-2.17.so
7f6c1033e000-7f6c1033f000 rw-p 00022000 fd:00 14274                      /usr/lib64/ld-2.17.so
7f6c1033f000-7f6c10340000 rw-p 00000000 00:00 0
7ffc13b5b000-7ffc13b7c000 rw-p 00000000 00:00 0                          [stack]
7ffc13bad000-7ffc13baf000 r-xp 00000000 00:00 0                          [vdso]
ffffffffff600000-ffffffffff601000 r-xp 00000000 00:00 0                  [vsyscall]

Однако меньший c-код (int main(void){*(volatile char*)0=0;}), чтобы вызвать segfault, вызвал segfault и не зависал...

Ответы

Ответ 1

ПРЕДУПРЕЖДЕНИЕ - этот ответ содержит ряд предположений, основанных на неполной информации. Надеюсь, он по-прежнему полезен, хотя!

Почему появляется segfault?

Как показывает трассировка стека, ядро ​​занято созданием дампа ядра разбитого процесса.

Но почему это так долго? Вероятное объяснение заключается в том, что метод, который вы используете для создания segfaults, приводит к тому, что процесс имеет массивное виртуальное адресное пространство.

Как указано в комментариях MM, результат выражения 1 < 31 равен undefined стандартами C, поэтому трудно сказать, какое фактическое значение передается malloc, но на основе последующее поведение я предполагаю, что это большое число.

Обратите внимание, что для успеха malloc вам не нужно, чтобы у вас было столько ОЗУ в вашей системе - ядро ​​расширит виртуальный размер вашего процесса, но фактическая RAM будет выделена только тогда, когда ваша программа действительно обратится к этой ОЗУ.

Я считаю, что вызов malloc преуспевает или, по крайней мере, возвращает, потому что вы заявляете, что он segfaults после нажатия кнопки enter, поэтому после вызова fgets.

В любом случае segfault приводит к ядру для выполнения дампа ядра. Если процесс имеет большой виртуальный размер, это может занять много времени, особенно если ядро ​​решает сбросить все страницы, даже те, которые никогда не затрагивались процессом. Я не уверен, что он это сделает, но если бы это произошло, и если в системе не хватило оперативной памяти, ему пришлось бы начать замену страниц в и из памяти, чтобы сбрасывать их на основной дамп. Это создаст высокую нагрузку ввода-вывода, которая может привести к тому, что процесс окажется невосприимчивым (и общая производительность системы будет снижена).

Возможно, вы сможете проверить некоторые из них, просмотрев каталог дампа abrtd (возможно /var/tmp/abrt или проверить /etc/abrt/abrt.conf), где вы можете найти основные дампы (или, возможно, частичные дампы ядра), которые были созданы.

Если вы можете воспроизвести поведение, вы можете проверить:

  • /proc/[pid]/maps, чтобы увидеть карту адресного пространства процесса и посмотреть, действительно ли он большой
  • Используйте инструмент, например vmstat, чтобы проверить, не меняется ли система, количество операций ввода-вывода и состояние ожидания IO Wait.
  • Если вы выполняли sar, вы можете увидеть подобную информацию даже за период до перезапуска abrtd.

Почему создается дамп ядра, хотя ulimit -c равен 0?

В соответствии с этот отчет об ошибках, abrtd инициирует сбор дампа ядра независимо от настроек ulimit.

Почему это не запускалось снова, когда arbtd был запущен еще раз?

Есть несколько возможных объяснений. Во-первых, это будет зависеть от количества свободной ОЗУ в системе. Возможно, что один основной дамп большого процесса не займет столько времени и не будет восприниматься как зависание, если достаточно свободной ОЗУ, и система не подталкивается к обмену.

Если в ваших первоначальных экспериментах у вас было несколько процессов в этом состоянии, тогда симптомы были бы намного хуже, чем в случае, когда только один процесс был неудачным.

Другая возможность заключается в том, что конфигурация abrtd была изменена, но служба еще не перезагружена, поэтому, когда вы ее перезапустили, она начала использовать новую конфигурацию, возможно, изменив ее поведение.

Также возможно, что обновление yum обновило abrtd, но не перезапустило его, так что при его перезапуске новая версия была запущена.