2015-04-30 2 views
2

Главный вопрос: как мы можем дождаться завершения потока в ядре Linux? Я видел несколько сообщений, которые касаются надлежащего способа обработки потоков в ядре Linux, но я не уверен, как мы можем дождаться завершения одного потока в основном потоке (предположим, что нам нужно, чтобы поток [3] выполнялся, затем продолжал):Как присоединиться к потоку в ядре Linux?

#include <linux/kernel.h> 
#include <linux/string.h> 
#include <linux/errno.h> 
#include <linux/sched.h> 
#include <linux/kthread.h> 
#include <linux/slab.h> 

void *func(void *arg) { 
    // doing something 
    return NULL; 
} 

int init_module(void) { 
    struct task_struct* thread[5]; 
    int i; 
    for(i=0; i<5; i++) { 
     thread[i] = kthread_run(func, (void*) arg, "Creating thread"); 
    } 
    return 0; 
} 

void cleanup_module(void) { 
    printk("cleaning up!\n"); 
} 
+0

Пространство пользователя или kthread? –

ответ

4

AFAIK нет эквивалента pthread_join() в ядре. Кроме того, я чувствую, что ваш шаблон (начальный набор потоков и ожидание только для одного из них) на самом деле не распространен в ядре. При этом в ядре есть несколько механизмов синхронизации, которые могут быть использованы для достижения вашей цели.

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

семафоры

Вы можете создать заблокированный семафор, а затем вызвать down в основном потоке. Это заставит его спать. Затем вы получите up этот семафор внутри вашей нити непосредственно перед выходом. Что-то вроде:

struct semaphore sem; 

int func(void *arg) { 
    struct semaphore *sem = (struct semaphore*)arg; // you could use global instead 

    // do something 

    up(sem); 
    return 0; 
} 

int init_module(void) { 
    // some initialization 
    init_MUTEX_LOCKED(&sem); 
    kthread_run(&func, (void*) &sem, "Creating thread"); 
    down(&sem); // this will block until thread runs up() 
} 

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

Пополнение

Вы можете объявить пополнения с помощью:

struct completion comp; 
init_completion(&comp); 

или:

DECLARE_COMPLETION(comp); 

Затем вы можете использовать wait_for_completion(&comp); вместо down() ждать в главном потоке и complete(&comp); вместо up() в ваш поток.

Вот полный пример:

DECLARE_COMPLETION(comp); 
struct my_data { 
    int id; 
    struct completion *comp; 
}; 

int func(void *arg) { 
    struct my_data *data = (struct my_data*)arg; 
    // doing something 

    if (data->id == 3) 
     complete(data->comp); 

    return 0; 
} 

int init_module(void) { 
    struct my_data *data[] = kmalloc(sizeof(struct my_data)*N, GFP_KERNEL); 

    // some initialization 
    for (int i=0; i<N; i++) { 
     data[i]->comp = &comp; 
     data[i]->id = i; 
     kthread_run(func, (void*) data[i], "my_thread%d", i); 
    } 
    wait_for_completion(&comp); // this will block until some thread runs complete() 
} 

Несколько потоков

Я не понимаю, почему вы бы начать 5 одинаковых потоков и только хотите ждать 3-го один, но, конечно, вы можете отправить разные данных в каждый поток, с полем, описывающим его id, а затем вызывать up или complete, только если этот идентификатор равен 3. Это показано в примере завершения. Есть и другие способы сделать это, это всего лишь один из них.

Слово предостережения

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

+0

Как вы связываете «завершение структуры» с одним потоком, если существует цепочка потоков? @ krzysztof – Farhad

+0

@Farhad: Если вы хотите только ждать одного потока, вам не обязательно это делать. Вы можете передать его всем потокам и игнорировать его во всех, кроме одного потока. Только этот вызов будет называться 'complete'. –

+0

, если это общая функция потока: 'void * func (void * arg) { struct complete comp; init_completion (&comp); // делать что-то возврата NULL; } ' , как можно вызвать' wait_for_competion (& аккомпанемента) 'в главном потоке, и это затрагивает только один поток – Farhad

3

kthread_stop() - это путь ядра для завершения потока до конца.

Помимо ожидания, kthread_stop() также устанавливает should_stop флага для ждали нити и просыпается его, если это необходимо. Это полезно для потоков, которые повторяют некоторые действия бесконечно.

Что касается задач с одним выстрелом, обычно проще использовать для них работы вместо kthreads.

РЕДАКТИРОВАТЬ: Примечание: kthread_stop() можно назвать только тогда, когда KTHREAD (task_struct) структура не освобождается.

функция потока Либо должна возвращать только после того, как он нашел kthread_should_stop() возвращает истину или get_task_struct() должна быть вызвана перед началом резьбы (и put_task_struct() должна вызываться после kthread_stop()).

+0

Итак, где же различие между 'join' и' exit' в реализации потока ядра? – Farhad

+0

'join' выполняется по * другому * потоку,' exit' выполняется самой нитью *. * must_stop * flag на самом деле является ** подсказкой ** к потоку: «другой поток ждет меня, чтобы завершить». Это зависит от потока, чтобы проверить этот флаг и отреагировать, если он установлен. Невозможно «уничтожить» поток ядра. – Tsyvarev

+0

@ Цыварев: Когда вы написали 'kthread_stop()' разбудит поток. Это очень важно, и именно поэтому я не буду использовать его в качестве общей замены «присоединиться». Ну, по крайней мере, при использовании 'kthread_run'. Тем не менее, это может быть лучше подходит для 'kthread_create'. –

Смежные вопросы