complete_all 简介
complete_all
是 Linux 内核中用于线程同步的一种机制,属于 Completion API 的一部分。它的主要功能是唤醒等待在特定 struct completion
对象上的所有线程,并允许这些线程继续执行。以下是对 complete_all
的详细讲解:
功能
complete_all
唤醒等待队列中所有的线程,而不是像complete
那样只唤醒一个线程。- 它会将
struct completion
中的done
字段设置为一个高值(通常为UINT_MAX
),确保当前和未来的所有等待者都可以继续执行。
函数定义
void complete_all(struct completion *c);
- 参数:
c
: 指向一个struct completion
对象的指针。
- 作用: 唤醒所有在该
completion
上等待的线程。
使用场景
当多个线程需要等待某个事件完成时,调用 complete_all
可以同时唤醒所有等待线程。例如:
- 线程 A 需要在某个条件满足后通知多个线程继续执行。
- 用于广播式通知,确保所有依赖该事件的线程都能被唤醒。
实现原理
-
数据结构:
struct completion
是 Completion API 的核心结构,包含以下字段:struct completion { unsigned int done; // 表示完成状态 wait_queue_head_t wait; // 等待队列 };
done
字段表示完成状态,wait
是等待队列,用于存储等待该事件的线程。
-
工作机制:
- 当调用
complete_all
时,它会将done
设置为一个高值(如UINT_MAX
),并唤醒等待队列中的所有线程。 - 这些被唤醒的线程会检查完成状态并继续执行。
- 当调用
-
注意事项:
- 如果需要重复使用同一个
completion
对象,必须在调用complete_all
后使用reinit_completion()
对其重新初始化。 - 多次调用
complete_all
是错误操作,因为它会破坏同步逻辑。
- 如果需要重复使用同一个
示例代码
以下代码展示了如何使用 complete_all
:
#include <linux/completion.h>
#include <linux/kthread.h>
#include <linux/module.h>
static struct task_struct *thread1, *thread2;
static struct completion my_completion;
static int thread_func(void *data) {
printk("Thread %s waiting for completion...\n", current->comm);
wait_for_completion(&my_completion);
printk("Thread %s resumed after completion!\n", current->comm);
return 0;
}
static int __init my_module_init(void) {
init_completion(&my_completion);
thread1 = kthread_run(thread_func, NULL, "thread1");
thread2 = kthread_run(thread_func, NULL, "thread2");
msleep(1000); // 模拟一些操作
printk("Signaling all threads to continue...\n");
complete_all(&my_completion);
return 0;
}
static void __exit my_module_exit(void) {
if (thread1)
kthread_stop(thread1);
if (thread2)
kthread_stop(thread2);
}
module_init(my_module_init);
module_exit(my_module_exit);
MODULE_LICENSE("GPL");
说明
- 初始化了一个
completion
对象。 - 两个内核线程分别调用了
wait_for_completion()
,进入等待状态。 - 主线程调用了
complete_all()
,唤醒了所有等待的线程。
注意事项
- 如果只需要唤醒一个线程,应使用
complete()
而非complete_all()
。 - 在中断上下文或原子上下文中调用是安全的,因为内部使用了自旋锁来保护数据结构。
- 使用后若需重复利用同一对象,必须重新初始化,例如:
reinit_completion(&my_completion);
总结
- 特点: 轻量级、简单、高效,用于多线程间同步。
- 适用场景: 多个线程需要同时被通知某一事件完成时。
- 局限性: 调用后需重新初始化才能重复使用。
Citations:
[1] https://docs.kernel.org/translations/zh_CN/scheduler/completion.html
[2] https://developer.aliyun.com/article/375278
[3] https://www.cnblogs.com/zongfanstudy/p/13438211.html
[4] https://www.cnblogs.com/zhuyp1015/archive/2012/06/13/2548458.html
[5] https://stxinu.blogspot.com/2018/07/completion.html
[6] https://blog.csdn.net/weixin_29031161/article/details/117274948
[7] https://blog.csdn.net/tiantao2012/article/details/78810797
[8] http://www.deansys.com/doc/ldd3/ch05s04.html