The OpenNET Project / Index page

[ новости /+++ | форум | теги | ]

форумы  помощь  поиск  регистрация  майллист  ВХОД  слежка  RSS
"Таймеры ядра Linux"
Вариант для распечатки  
Пред. тема | След. тема 
Форумы Программирование под UNIX (Public)
Изначальное сообщение [Проследить за развитием треда]

"Таймеры ядра Linux"  
Сообщение от Michelnok (ok) on 13-Янв-07, 22:02 
Всем привет!

Два вопроса про динамические таймеры ядра Linux.

1. Можно ли освобождать память, занятую структурой таймера, в функции таймера?
Т.е. можно ли вызывать kfree(timer) из самой timer->function?

2. Делается ли какая-то блокировка на время выполнения timer->function и (она же) в mod_timer?
Иначе говоря, если я напишу в какой-нибудь mainline() что-то типа:

spin_lock(lock);
<делаем что-то>
mod_timer(timer,...);
spin_unlock(lock);

И в то же время в timer->function у меня будет что-то типа:

spin_lock(lock);
<делаем что-то>
spin_unlock(lock);

... то не загоню ли я себя в deadlock, когда timer->function будет ждать на spin_lock(lock), а захватившая этот lock mainline() - на mod_timer()?

Исходник kernel/timer.c смотрел. Мало что понял, всё очень запутано :(
Подозреваю что и kfree(timer) можно безопасно вызывать из timer->function, и никакого deadlock не будет (spin_unlock(timer->lock) вроде как вызывается перед вызовом timer->function в __run_timers), но уверености нет...

Заранее спасибо за ответы.

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

 Оглавление

Сообщения по теме [Сортировка по времени, UBB]


1. "Таймеры ядра Linux"  
Сообщение от Michelnok (ok) on 18-Янв-07, 20:35 
Эх... Придется сказать UP %-|


Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

2. "Таймеры ядра Linux"  
Сообщение от timer (??) on 18-Янв-07, 20:39 
если вы аотом нигде не используете то можно
иначе спилить ветку на которой сидите - даже если вас страхуют снизу
чревато
Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

5. "Таймеры ядра Linux"  
Сообщение от Michelnok (ok) on 19-Янв-07, 14:39 
>если вы потом нигде не используете то можно

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

>иначе спилить ветку на которой сидите - даже если вас страхуют снизу чревато

Знаю.
Мне его просто больше негде удалять. Никаких событий (ни прерываний, ни запросов), связанных с тем чему мне надо сделать kfree, больше не будет.
Ну не делать же периодическую сборку мусора :-)

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

3. "Таймеры ядра Linux"  
Сообщение от BigHo on 19-Янв-07, 11:53 
Как понял речь идет о двух разных замках. deadlock блокировка в обычном случае может возникнуть в случае, если замки блокируются:
- в первом потоке - сперва lock1, и под его прикрытием - lock2;
- во втором потоке поряд обратый - сперва lock2, затем - lock1.

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

Это касается userlevel блокировок для любой ОС. Механизм ядра в этом смысле очень похож. Но возможны некоторые ньюансы, напрямую не связанные с проблемой взавимоисключающих блокировок. Если уже есть какая-то проблема, то опиши сиптоматику.

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

4. "Таймеры ядра Linux"  
Сообщение от Michelnok (ok) on 19-Янв-07, 14:34 
>Как понял речь идет о двух разных замках.

Да, один замок мой, я его захватываю как в mainline(), так и в timer->function. Вот о наличии второго я и спрашиваю. Дело в том что в структуре timer_list есть свой замок и он, естественно, используется ядром при операциях с таймером.

Насколько я понял из кода в kernel/timer.c, замок самого таймера не захватывается на время вызова timer->function. А в функции __mod_timer используется какая-то мутная последовательность блокировок, которая как раз и предназначена для избежания deadlock'а в ситуациях, похожих на мою (реализация несколько раз менялась уже в 2.6.*).

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

6. "Таймеры ядра Linux"  
Сообщение от BigHo on 19-Янв-07, 16:20 
>>Как понял речь идет о двух разных замках.
>
>Да, один замок мой, я его захватываю как в mainline(), так и в timer->function. Вот о наличии второго я и спрашиваю. Дело в том что в структуре timer_list есть свой замок и он, естественно, используется ядром при операциях с таймером.
>Насколько я понял из кода в kernel/timer.c, замок самого таймера не захватывается на время вызова timer->function. А в функции __mod_timer используется какая-то мутная последовательность блокировок, которая как раз и предназначена для избежания deadlock'а в ситуациях, похожих на мою (реализация несколько раз менялась уже в 2.6.*).

Дополнительный вопрос по теме: в теле callback-функции таймера допустимо использовать spin_trylock механизм, вместо обычного spin_lock ? Если да, то в этом случае есть вариант обойти это гипотетически возможную проблему изменив режим блокировок.


Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

9. "Таймеры ядра Linux"  
Сообщение от Michelnok (ok) on 19-Янв-07, 17:59 
>Дополнительный вопрос по теме: в теле callback-функции таймера допустимо
> использовать spin_trylock механизм, вместо обычного spin_lock ?

Да, пожалуй так и сделаю для надежности (с соответствующей обработкой этой ситуации снаружи). А то не факт что внутренняя реализация таймеров не поменяется...

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

10. "Таймеры ядра Linux"  
Сообщение от BigHo on 20-Янв-07, 15:06 
>>Дополнительный вопрос по теме: в теле callback-функции таймера допустимо
>> использовать spin_trylock механизм, вместо обычного spin_lock ?
>
>Да, пожалуй так и сделаю для надежности (с соответствующей обработкой этой ситуации
>снаружи). А то не факт что внутренняя реализация таймеров не поменяется...

Я просто хотел описать одну древнюю хитрость: не входить рекурсивно в функцию с установленной блокировкой, а изменять некую переменную в структуре, которую _всегда_ изменяют только с установленной блокировкой. Таким образом можно реализовать функциональность spin_trylock/spin_unlock (spin_lock - невозможна в общем случае). Примерный код:


.   int
.   my_mutex_trylock(struct my_mutex *mtx) {
.       int ret = 0;
.
.       spin_lock(&mtx->mtx_lock);
.       if (mtx->mtx_lock_count < 0)
.           ret = -1; // замок уже кто-то заблокировал
.       spin_unlock(&mtx->mtx_lock);
.       return ret;
.   }

.   void
.   my_mutex_unlock(struct my_mutex *mtx) {
.       mtx->mtx_lock_count = 0;
.       spin_unlock(&mtx->mtx_lock);
.   }

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

11. "Таймеры ядра Linux"  
Сообщение от BigHo on 20-Янв-07, 15:51 
для

>.   my_mutex_trylock(struct my_mutex *mtx) {
>.       int ret = 0;
>.
>.       spin_lock(&mtx->mtx_lock);
>.       if (mtx->mtx_lock_count < 0)
>.           ret = -1; // замок уже кто-то заблокировал

тут конечно же нужно добавить:

.        else
.            mtx->mtx_lock_count = -1;

>.       spin_unlock(&mtx->mtx_lock);
>.       return ret;
>.   }
>

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

12. "Таймеры ядра Linux"  
Сообщение от Michelnok (ok) on 20-Янв-07, 16:21 
>.   void
>.   my_mutex_unlock(struct my_mutex *mtx) {
>.       mtx->mtx_lock_count = 0;
>.       spin_unlock(&mtx->mtx_lock);
>.   }

Не, тут что-то не так. spin_unlock делать нечему, мы же не захватывали mtx->mtx_lock между вызовами наших try_lock/unlock.
Я бы вообще реализовал эти функции так:

atomic_t my_mytex=1;

int my_mutex_trylock(atomic_t* mtx)
{
  return !atomic_dec_and_test(mtx); // 0 если у нас получилось "захватить" замок
}

void my_mutext_unlock(atomic_t* mtx)
{
  atomic_inc(mtx);
}

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

13. "Таймеры ядра Linux"  
Сообщение от Michelnok (ok) on 20-Янв-07, 16:24 

>int my_mutex_trylock(atomic_t* mtx)
>{

Тормоз я. Конечно же, тут:

  if(atomic_dec_and_test(mtx))
    return 0;
  atomic_inc(mtx);
  return -1;

>}

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

14. "Таймеры ядра Linux"  
Сообщение от BigHo on 21-Янв-07, 16:23 
так получается реализация подобная userspace вызову
pthread_rwlock_tryrdlock/pthread_rwlock_unlock. Реализацию, близкую к pthread_trylock/pthread_unlock имеют вызовы pthread_rwlock_trywrlock/pthread_rwlock_unlock.

Хотя речь идет о вызовах ядра, можно реализовать тройку вызовов, близких к:
- pthread_rwlock_tryrdlock (my_lock_rd);
- pthread_rwlock_trywrlock (my_lock_wr);
- pthread_rwlock_unlock (my_unlock_rd и my_unlock_wr).

struct my_lock {
.   ...
.   int mtx_lock_count; // если < 0, значит открыт на запись,
.                       // если > 0, то открыт на чтение,
.                       // если == 0, свободен от блокировок.
.   spinlock_t mtx_lock;
.   ...
};

// смысл mtx_lock_count в том, что читающих процессов может быть несколько,
// и только один - записывающий. Можно договориться использовать значение -1 в
// качестве обозначения блокировки на запись, и любое положительное число -
// блокировка на чтение.

int
my_lock_rd(struct my_lock *mtx) {
.   int ret = 0;
.
.   spin_lock(&mtx->mtx_lock);
.   if (mtx->mtx_lock_count >= 0)
.       mtx->mtx_lock_count++;
.   else
.       ret = mtx->mtx_lock_count;
.   spin_unlock(&mtx->mtx_lock);
.   return ret;
}

int
my_lock_wr(struct my_lock *mtx) {
.   int ret = 0;
.
.   spin_lock(&mtx->mtx_lock);
.   ret = mtx->mtx_lock_count; // вернем информацию о том, какая блокировка
.                              // имеет место быть. В нашем случае, ошибкой
.                              // является ненулевой код возврата.
// чтобы произвести WRITE блокировку, замок должен быть свободен
// от любых блокировок. Иногда для WRITE используется термин
// "исключающая блокировка", а для READ - "конкурирующая блокировка".
.   if (mtx->mtx_lock_count == 0)
.       mtx->mtx_lock_count = -1; // метка блокировки на запись.
.   spin_unlock(&mtx->mtx_lock);
.   return ret;
}

// разблокирование - существенно более легкая задача.
// можно произвести кучу проверок, но зачем ?

void
my_unlock_rd(struct my_lock *mtx) {
.   spin_lock(&mtx->mtx_lock);
.   mtx->mtx_lock_count--;
.   spin_unlock(&mtx->mtx_lock);
}

void
my_unlock_wr(struct my_lock *mtx) {
.   spin_lock(&mtx->mtx_lock);
.   mtx->mtx_lock_count = 0;
.   spin_unlock(&mtx->mtx_lock);
}

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

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

15. "Таймеры ядра Linux"  
Сообщение от BigHo on 21-Янв-07, 18:11 
.. и завершаю свою мысль :)

atomic_* использует те же MUTEX блокировки, что и в предложенном способе, при этом обеспечивает меньший функционал (только READ или WRITE блокировка), и менее понятна (IMHO)..

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

7. "Таймеры ядра Linux"  
Сообщение от BigHo on 19-Янв-07, 16:24 
> А в функции __mod_timer используется какая-то мутная последовательность блокировок, которая как раз и предназначена для избежания deadlock'а в ситуациях, похожих на мою (реализация несколько раз менялась уже в 2.6.*).

в каком файле определена эта функция ?

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

8. "Таймеры ядра Linux"  
Сообщение от BigHo on 19-Янв-07, 16:54 
>> А в функции __mod_timer используется какая-то мутная последовательность блокировок, которая как раз и предназначена для избежания deadlock'а в ситуациях, похожих на мою (реализация несколько раз менялась уже в 2.6.*).

нашел, почитал. На выходе __mod_timer ни один замок не остается запертым. Т.е. на этот замок имеешь право не обращать внимание, поскольку для данного потока и данной callback функций этот замок всегда открыт (я посмотрел код только для __mod_timer. То что вне неё - не на моей совести). В этом случае deadlock исключен.

Высказать мнение | Ответить | Правка | Наверх | Cообщить модератору

Архив | Удалить

Индекс форумов | Темы | Пред. тема | След. тема
Оцените тред (1=ужас, 5=супер)? [ 1 | 2 | 3 | 4 | 5 ] [Рекомендовать для помещения в FAQ]




Партнёры:
PostgresPro
Inferno Solutions
Hosting by Hoster.ru
Хостинг:

Закладки на сайте
Проследить за страницей
Created 1996-2024 by Maxim Chirkov
Добавить, Поддержать, Вебмастеру