Как определить из вывода strace, какая часть моей программы не может получить мьютекс

Я работаю над встроенной системой Linux (3.12. Что-то), и наше приложение через некоторое время начинает загружать процессор. Я бегал strace в нашем приложении, и именно тогда, когда проблема возникает, я вижу много строк, похожих на это в strace выход:

[48530666] futex(0x485f78b8, FUTEX_WAIT_PRIVATE, 2, NULL) = -1 EAGAIN (Resource temporarily unavailable) <0.009002>

Я почти уверен, что это курящий пистолет, который я ищу, и есть какая-то гонка. Однако теперь мне нужно выяснить, как определить место в коде, которое пытается получить этот мьютекс. Как я могу это сделать? Наш код скомпилирован с GCC и содержит отладочные символы.

В настоящее время я думаю (что я еще не пробовал) распечатать строку в stdout и очистить ее, прежде чем пытаться захватить любой мьютекс в нашей системе, ожидая, что строка будет напечатана прямо перед тем, как strace пожалуется на получение блокировки. … но в коде есть много мест, которые должны быть оснащены таким образом.

РЕДАКТИРОВАТЬ: Еще одна странная вещь, которую я только что понял, это то, что наша программа не начинает загружать процессор, пока не прошло какое-то случайное время с момента его запуска (от 5 минут до 5 часов и где-то между ними). За это время есть нуль futex происходят системные вызовы. Почему они вдруг начинают? Из того, что я прочитал, я думаю, что, возможно, они используются должным образом в пользовательском пространстве, пока что-то не даст сбой и не вернется к созданию futex() Системный вызов …

Какие-либо предложения?

1

Решение

Если вы постоянно и часто блокируете мьютекс на короткое время из разных потоков, как, например, Защищая глобальный логгер, вы можете вызвать так называемый конвой потоков. Проблема не возникает, пока два потока не конкурируют за блокировку. Первый получает блокировку и удерживает ее в течение короткого времени, затем, когда ему требуется блокировка во второй раз, он выгружается, потому что второй уже ждет. Второй делает то же самое. Временной интервал, доступный каждому потоку, внезапно сокращается до времени между двумя попытками блокировки, что вызывает много переключений контекста и соответствующее замедление. Кроме того, все, кроме одного потока, всегда блокируются в мьютексе, эффективно отключая любое параллельное выполнение.

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

Что касается вызовов futex (), идея состоит в том, чтобы опросить атомарный флаг и после некоторых вращений использовать фактический мьютекс ОС. Атомный флаг доступен без дорогого переключения между пользовательским пространством и пространством ядра. Для более длительных перерывов, используя выгрузку ядра (с futex()) избегает блокировки процессора при опросе. Это объясняет, почему программе не нужны futex() звонки в обычном режиме.

0

Другие решения

Вам, в основном, необходимо создать основной файл в данный момент.

Тогда вы можете загрузить программу + ядро ​​в GDB и посмотреть на него

man gcore

или же

generate-core-file

В течение этого времени происходит 0 системных вызовов. Почему они вдруг начинают?

Это связано с тем, что неоспоримый мьютекс, реализованный через futex, не делает системный вызов, только атомарный прирост, исключительно в пространстве пользователя. Только блокировка CONTESTED видна как системный вызов

0

По вопросам рекламы [email protected]