LINUX.ORG.RU

Корректное удаление мутекса в ядре

 ,


0

1

Привет, ЛОР. Вопрос, наверное, простой, только никак не могу найти ответ на него. Все форумы облазил, никто не знает.

Короче, дело происходит в ядре линукса. Нужно корректно удалить мутекс. Мутекс создается динамически, через mutex_init(). И весь вопрос в том, как его теперь корректно удалить.

Упрощенный пример:

//somewhere in the code:
typedef struct {
  struct mutex *lock;
  int ref_count;
} super_duper_mutex_struct;

super_duper_mutex_struct * create_sdms () {
   super_duper_mutex_struct * r = kmalloc (sizeof (super_duper_mutex_struct), GFP_KERNEL);

   if (r) {
      mutex_init(r->lock);
      r->ref_count = 1;
   }

   return r;
}


//in another place
void delete_sdms(super_duper_mutex_struct *r) {
   mutex_lock(r->lock);

   r->ref_count--;

   if (r->ref_count == 0) {
      mutex_unlock (r->lock);
      mutex_destroy(r->lock);
      kfree (r);
   } else mutex_unlock(r->lock);
}

В приведенном коде сразу несколько проблем.

  • Предположим есть 2 потока. Предположим, поток_1 успешно доходит до mutex_destroy() и пытается удалить мутекс. Поток_2 в это время встал на ожидание в mutex_lock(). Далее, поток_2 видит, что мутекс разблокирован врывается в секцию. Что произойдет далее неизвестно. Либо поток_1 попытается удалить заблокированный мутекс. Либо поток_1 уже удалит мутекс и тогда не понятно, что произойдет с потоком_2, который на этом мутексе висит в ожидании?
  • Проблема вторая связана с освобождением памяти, занимаемой структурой. Поток_1 освободит память из-под структуры. Поток_2 ворвется в секцию и попытается уменьшить уже освобожденный реф-каунтер.

Со второй проблемой, я еще могу справиться, как-то по-другому перефразировав код. Но с первой я вообще не понимаю. Как правильно удалить мутекс, если там будут висеть потоки?

Заранее спасибо за ответы, если вдруг кто-нибудь что-то знает на этот счет. :-)

★★★★★

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

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

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

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

alysnix ()
Последнее исправление: alysnix (всего исправлений: 2)

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

alysnix ()
Ответ на: комментарий от alysnix

Благодарю за ответ. Сам тоже прихожу к тем же мыслям.

Похожий по смыслу код мне встретился в windows-драйвере, который я сейчас переписываю под Линукс. Заставило почесать репу над сей конструкцией.

А мне говорили — да там просто названия функций на линуксовые поменять и все.

Короче, вот так для винды дрова пишут. А потом удивляются глюкам.

hibou ★★★★★ ()
Ответ на: комментарий от hibou

Короче, вот так для винды дрова пишут. А потом удивляются глюкам.

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

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

alysnix ()
Ответ на: комментарий от alysnix

В любом случае так писать — это риск. Потом изменят то место и потоки будут вставать на мутекс. А так, наверно да, там где-то ещё есть проверки. Я пока до них не дошёл.

hibou ★★★★★ ()
Ответ на: комментарий от hibou

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

то есть посмотрите тем, что делает поток сразу после захвата мьютекса

alysnix ()

Как поток может пытаться удалять мьютекс, пока другой на нем ждет? У мьютекса тогда должен быть рефкаунт 2.

Siborgium ★★★★ ()
Ответ на: комментарий от hibou

вообще в pthreads запрещено удалять залокнутый мьютекс. а как раз если он залокнут - на нем кто-то может стоять.

вообще ваш код взят отсюда, там пример нарисован. https://www.opennet.ru/man.shtml?topic=pthread_mutex_init&category=3&russian=5

alysnix ()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

короче пример там явно неправильный. там доступ к обьекту еще как-то защищается.

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

alysnix ()
Ответ на: комментарий от hibou

Похожий по смыслу код мне встретился в windows-драйвере, который я сейчас переписываю под Линукс.

в Linux инициализация в .probe удаление в .remove, из потоков естественно надо сначала выйти. И да, счётчик ссылок на мьютекс это какая-то шиза.

anonymous ()
Ответ на: комментарий от anonymous

И да, счётчик ссылок на мьютекс это какая-то шиза.

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

тогда создают некий врапер обьекта, с мьютексом и счетчиком ссылок. те треды, кто обьект юзает, увеличивает счетчик ссылок, а поюзав - уменьшает.

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

только в таком виде код нормальный.

то есть уменьшая счетчик, тред гарантирует, что больше не будет пытаться доступаться к этому обьекту

alysnix ()
Последнее исправление: alysnix (всего исправлений: 2)
Ответ на: комментарий от alysnix

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

и на каждый объект создавать отдельный мьютекс ? для стрельбы по ногам с взаимной блокировкой лучше не придумаешь. Тут вообще надо для начала знать что он пишет, свои потоки ядра создавать - это дурной тон, предпочтительней использовать workqueue

anonymous ()
Ответ на: комментарий от anonymous

и на каждый объект создавать отдельный мьютекс ?

смотря какой обьект…

ну например некий пакет данных, который обрабатывают несколько тредов паралельно. пока обрабатывается этот пакет, асинхронно приходит новый пакет…

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

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

зы.

так еще делают динамическую загрузку-выгрузку плагинов или модулей(я счас не про линукс, а вообще). какой-то тред просит загрузить модуль - делается врапер и там стоит мьютекс и счетчик ссылок и он увеличивается, а когда просит выгрузить модуль - если счетчик ссылок == ноль, то модуль физически выгружается. другие треды тоже хотят этот модуль, и как только последний его выгрузит - модуль будет из памяти выкинут. тут такая кухня работать будет.

alysnix ()
Последнее исправление: alysnix (всего исправлений: 1)
Ответ на: комментарий от alysnix

ну например некий пакет данных, который обрабатывают несколько тредов паралельно

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

anonymous ()
Ответ на: комментарий от anonymous

в заглавном посте говорится про драйвер,

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

alysnix ()
Последнее исправление: alysnix (всего исправлений: 1)

Предлагаю защитить динамический мютекс глобальным. В духе:


static struct mutex *g_mutex;  // initialise on module init

super_duper_mutex_struct * create_sdms () {

   mutex_lock(g_mutex);

   super_duper_mutex_struct * r = kmalloc (sizeof (super_duper_mutex_struct), GFP_KERNEL);

   if (r) {
      mutex_init(r->lock);
      r->ref_count = 1;
   }

   mutex_unlock(g_mutex);

   return r;
}

void use_sdms(super_duper_mutex_struct *r) {
    mutex_lock(g_mutex);
    mutex_lock(r->lock);
    mutex_unlock(g_mutex);
    use(r);
    mutex_unlock(r->lock);
}

void delete_sdms(super_duper_mutex_struct *r) {

   mutex_lock(g_mutex);
   if (r->lock == NULL) {
       mutex_unlock(g_mutex);
       return;
   }

   mutex_lock(r->lock);
   mutex_unlock(g_mutex);

   r->ref_count--;

   if (r->ref_count == 0) {
      mutex_lock(g_mutex);
      mutex_unlock (r->lock);
      mutex_destroy(r->lock);
      r->lock = NULL;
      kfree (r);
      mutex_unlock(g_mutex);
   } else mutex_unlock(r->lock);
imatveev13 ()

весь тред не читал.

Это достаточно простая и распространенная задача: поменять указатель на структуру, с которым работают несколько cpu.

Читай про rcu_dereference(), rcu_assign_pointer(), synchronize_rcu(), rcu_read_lock(), rcu_read_unlock()

https://www.kernel.org/doc/html/latest/RCU/whatisRCU.html

vel ★★★★★ ()

Изобретение динамических reference counting объектов в ядре затея опасная долгой отладкой и тормозами(мютексы не бесплатные). Если можно создать массив этих объектов чтоб точно хватило всем, жить будет проще.

Для готового ядерного reference counting навскидку:

https://www.kernel.org/doc/Documentation/kref.txt

http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2007/n2167.pdf

imatveev13 ()
Ответ на: комментарий от imatveev13

Изобретение динамических reference counting объектов в ядре затея опасная долгой отладкой и тормозами(мютексы не бесплатные).

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

alysnix ()

Для начала изучи документацию

Там вроде как есть atomic_dec_and_mutex_lock на случай подсчета ссылок. Если ты серьезно хочешь заморочиться с подсчетом ссылок.

Как правильно удалить мутекс, если там будут висеть потоки?

Не удалять его до завершения всех возможных лочащих потоков.

Насколько я понимаю, структура создается в потоке, иначе не имеет смыла использовать мьютексы.

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

shkolnick-kun ★★★★★ ()
Последнее исправление: shkolnick-kun (всего исправлений: 1)
Ограничение на отправку комментариев: только для зарегистрированных пользователей