如何在Linux内核中加入线程?
主要问题是:我们如何等待Linux内核中的线程完成?我看到一些帖子关注Linux内核中处理线程的正确方法,但我不确定如何等待主线程中的单个线程完成(假设我们需要完成线程[3],然后继续):如何在Linux内核中加入线程?,linux,multithreading,linux-kernel,Linux,Multithreading,Linux Kernel,主要问题是:我们如何等待Linux内核中的线程完成?我看到一些帖子关注Linux内核中处理线程的正确方法,但我不确定如何等待主线程中的单个线程完成(假设我们需要完成线程[3],然后继续): #包括 #包括 #包括 #包括 #包括 #包括 void*func(void*arg){ //做某事 返回NULL; } int init_模块(void){ struct task_struct*线程[5]; int i; 对于(i=0;iAFAIK),内核中没有与之等价的pthread_join()。此外
#包括
#包括
#包括
#包括
#包括
#包括
void*func(void*arg){
//做某事
返回NULL;
}
int init_模块(void){
struct task_struct*线程[5];
int i;
对于(i=0;iAFAIK),内核中没有与之等价的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);
或:
然后您可以在主线程中使用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 = ∁
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()
}
申报完成(comp);
结构我的_数据{
int-id;
结构完成*comp;
};
int func(void*arg){
struct my_data*data=(struct my_data*)arg;
//做某事
如果(数据->id==3)
完成(数据->comp);
返回0;
}
int init_模块(void){
struct my_data*data[]=kmalloc(sizeof(struct my_data)*N,GFP_内核);
//一些初始化
对于(int i=0;icomp=&comp;
数据[i]->id=i;
kthread_run(func,(void*)数据[i],“我的线程%d”,i);
}
等待完成(&comp);//这将阻塞,直到某些线程运行完成()
}
多线程
我真的不明白为什么你会启动5个相同的线程,只想等待第三个线程,但当然你可以向每个线程发送不同的数据,并用一个字段描述它的id,然后调用up
或complete
,只要这个id等于3。这在完成示例中显示。还有其他方法可以做到这一点,我这只是其中之一
警告
在使用任何一种机制之前,请阅读更多关于这些机制的信息。这里有一些我没有写的重要细节。这些示例也经过了简化,没有经过测试,它们在这里只是为了展示整体思路。kthread\u stop()是内核等待线程结束的方法
除了等待之外,kthread_stop()还为等待的线程设置should_stop标志,并在需要时将其唤醒。这对于无限重复某些操作的线程非常有用
对于单发任务,通常使用works而不是KThread更简单
编辑:
注意:只有在未释放kthread(task_struct)结构时,才能调用kthread_stop()
任何一个线程函数都应该仅在找到kthread后返回,或者应该在kthread\u stop()返回true,或者应该在启动线程之前调用get\u task\u struct()(而put\u task\u struct()应该在kthread\u stop()之后调用).如果有一堆线程,如何将结构完成
与单个线程关联?@krzysztof@Farhad:如果您只想等待一个线程,则不必这样做。您可以将其传递给所有线程,并在除一个线程外的所有线程中忽略它。只有此线程将调用complete
。如果这是常规线程函数操作:void*func(void*arg){struct completion comp;init_completion(&comp);//doing something return NULL;}
如何调用wait_for_completion(&comp)
在主线程中,它只影响一个线程?@Farhad:这样做是错误的。你应该完全按照信号量示例中描述的那样做-你在主线程中声明完成,并将其传递给线程函数。我添加了完整的多线程和完成示例。@KrzysztofAdamski:这些同步机制都不可用确保线程函数结束是很有必要的。这是因为信号操作是在该函数内部执行的,而不是在该函数之后。因此,即使在包含函数代码的模块被卸载之后,该函数也可能被执行。这将导致崩溃。那么join
和exit在内核线程实现中?join
由另一个线程执行,exit
由线程本身执行。should\u stop标志实际上是对线程的提示:“其他线程等待我终止”。如果设置了该标志,则由线程检查该标志并作出反应。无法杀死内核线程。@Tsyvarev:正如您所写的kthread\u stop()
将唤醒线程。这非常重要,也正是我不将其用作常规“连接”的原因替换。至少在使用kthread\u run
时是这样。不过,它可能更适合kthread\u create
。@KrzysztofAdamski:由于大多数等待实际上都是事件等待,所以从kthread\u stop()
错误唤醒不是问题。从另一方面来说,kthread\u stop()
是唯一的方法
DECLARE_COMPLETION(comp);
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 = ∁
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()
}