LINUX.ORG.RU

schedule_task() ?


0

0

Не подскажите, для чего в коде ядра в некоторых местах вызывается 
 "int schedule_task(struct tq_struct *task)"  ?
Вставляет в список, но какой, для чего, в каком случае ?

Например, при создании  kernel_thread() нужно использовать schedule_task(), 
или нет ?  
kernel_thread() вроде записывает в списки процессов, но это 
похоже другое. Может всё это связано ?
Подскажите если не сложно, может можно где прочитать об этом.
anonymous

Это для выполнения отложенных процедур в контексте рабочей нити ядра. К kernel_thread отношения не имеет.

anonymous
()

taskqueue устарело, workqueue наше фсио. См. include/linux/workqueue.h и kernel/workqueue.c

Это аналог Io{Allocate,Queue,Free}WorkItem в оффтопике. Обычно с помощью workqueue откладывают относительно длительные операции, которые невозможно выполнить немедленно, но создание отдельного ядерного потока не рационально.

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

>Обычно с помощью workqueue откладывают относительно длительные операции, которые невозможно выполнить немедленно

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

либо нужен контекст, а мы находимся в прерывании

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

Именно это я и имел ввиду.

workqueue заменяет даже 2 механизма оффтопика - системные рабочие потоки и DPC для ISR.

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

>DPC для ISR

DPC скорее ближе к bottomhalf/softirq.

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

Я разбираюсь с ядром 2.4.X.
При создании нити ядра с помощью  kernel_thread()  в некоторых случаях
используется  механизм, например: 

./kernel/kmod.c:371:
__call_usermodehelper(void *data) {
  struct subprocess_info *sub_info = data;
  pid_t    pid = kernel_thread(____call_usermodehelper, sub_info, 
                                CLONE_VFORK | SIGCHLD);
}

st()  {
  struct subprocess_info   sub_info = { ... };
  struct  tq_struct     tqs = {
                routine:        __call_usermodehelper,
                data:           &sub_info,
  };
  schedule_task(&tqs);
  . . . . .
}

таким образом можно создать нить ядра и работает.
Если же я просто вызываю:
 kernel_thread(____call_usermodehelper, sub_info, CLONE_VFORK|SIGCHLD);

то не работает.
Мне казалось что  kernel_thread() - интерфейс для создания нитей ядра
и достаточно её вызвать с нужными параметрами. Но не проходит. :(
Я не понимаю почему ?  Не подскажете в чем может быть проблема ?

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

Тут все зависит от контекста в котором вызывается kernel_thread(). Нужен именно контекст ядерной нити, а не пользовательской. Внутри у kernel_thread обычный do_fork. А вы вероятно пытаетесь клонировать пользовательский процесс, сделавший сисвызов и перешедший в режим ядра.

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

>Внутри у kernel_thread обычный do_fork. 
Да, я в курсе, там все делается, создается новая task_struct,
в неё копируется текущаяя и заполняются нужные поля.

>А вы вероятно пытаетесь клонировать пользовательский процесс,
Вроде не пользовательский.
Пытаюсь kernel_thread() вызвать из  init(), 
который создается как нить в rest_init().  Может неправильно ?

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

А кто вас научил использовать флаг CLONE_VFORK? man 2 clone!

Поток с init() зависает, пока ваш тред не вызовет execve или _exit

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

>А кто вас научил использовать флаг CLONE_VFORK? man 2 clone!

Извините, ввел в заблуждение, это был пример кода, не корректный.
Я использую:
kernel_thread(example_thread2,NULL,CLONE_FS|CLONE_FILES|CLONE_SIGNAL);
static void example_thread2(void *data) {
  //wait_queue_head_t   queue; 
  int     id = *((int*)data);
  printk("hi, here is the kernel thread #%d\n", id);
  for(;;) {
     printk("thread #%d: thread woke up\n", id);
    //set_current_state(TASK_INTERRUPTIBLE);
    schedule();
  } // for(;;)
}

Нить запустилсь.
Я добавил  'schedule()'.  
Не знаю нужно ли это делать в данном случае или нет.

Вообще мне нужно запустить несколько нитей и чтоб планировщик 
переключал управление между ними.

Я не очень понимаю:
- нужно ли передавать управление планировщику в нити: 
  call  schedule();
- как осуществить задержку или как задать нити квант времени 
   
Может подскажите, как это делается или в каком направлении копать?

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

Задержки хорошо описаны в LDD3 http://lwn.net/Kernel/LDD3/

Ядерные нити планируются точно так же как и обычные, по приоритетам. Вызывать специально schedule() надо в случаях: 1. когда нити больше нечего делать, а ждать, когда кончится квант времени долго; 2. нужно заблокировать нить до некоторого события

Второй пункт от первого отличается тем, что нить исключается из выбора планировщиком set_current_state(TASK_[UN]INTERRUPTIBLE), но предварительно добавляется в wait_queue. Когда происходит событие, wait_queue выполнит set_current_state(TASK_RUNNING) и нить снова "оживет".

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

>Может подскажите, как это делается или в каком направлении копать?

Для начала вам следует изучить примитивы синхронизации в Linux.

Бесконечный busy-wait цикл даже с schedule - это плохая затея. Если вы хотите подождать "чуть-чуть", то лучше использовать yield(), поскольку это заставит планировщик прогнать остальные задания, которые не спят. schedule() же может привести к тому, что планировщик убедится, что ваша нить не исчерпала своего времени, и снова передаст управление ей, т.е. вы загрузите не только своей нитью процессор, но и пробуксовывающим планировщиком, что плохо вдвойне. Имейте в виду, что в обоих случаях у вас нить будет висеть на планировщике, т.е. вы будете иметь 100% суммарной загрузки одного из процессоров.

Товарищ вот правильно написал, что если вы хотите все сделать цивилизованно, то вам нужно уснуть, т.е. снять с планировки свою нить, а потом разбудить, когда нужно. На низком уровне это set_task_state(INTERRUPTIBLE/UNINTERRUPTIBLE)/schedule с последующим wake_up(), а на высоком уровне - синхронизация на семафоре(down), мьютексе(mutex_lock), событии(wait_event) и др.

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

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

Спасибо.

>На низком уровне это set_task_state(INTERRUPTIBLE/UNINTERRUPTIBLE)/schedule
>с последующим wake_up(),

Не совсем понял где вызывается wake_up() ?

>исключается из выбора планировщиком >set_current_state(TASK_[UN]INTERRUPTIBLE), 
>но предварительно добавляется в wait_queue. 
 
Я это пытался сделать.
Но наверное не разобрался с wait_queue, как добавлять.
Наверно нужно в этом случае определить по какому событию оживлять ?

cпасибо, буду разбираться.

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

>Не совсем понял где вызывается wake_up() ?

Эээ... там, где вы хотите пробудить нить. Это зависит от того, что вы делаете.

Пример (на практике это происходит немного иначе, но для примера пойдет):

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

Если вы принципиально из-за архитектуры Linux не можете поставить триггер на событие (hook или свой обратчик или еще что), то, конечно, вам придется проверять наступление события через периодическую проверку, но обычно этого удается избежать. Если избежать не удалось, то вам в любом случае лучше это делать через определенные, пусть небольшие, интервалы времени (schedule_timeout), нежели в busy-wait цикле.

anonymous
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.