创建内核线程比较常用的接口有:kthread_create, kthread_run,这是kernel.h里面的两个宏,原型如下:
#define kthread_create(threadfn, data, namefmt, arg...) \
kthread_create_on_node(threadfn, data, -1, namefmt, ##arg)
参数有四个:
threadfn---创建的线程需要执行的任务函数,类型是 int (*thread)(void *data)
data---传送给任务函数的参数
namefmt,arg---创建的内核线程的名字,也就是通过ps 看到的名字
这仅仅是创建一个线程实例,启动它还是需要wake_up_process来唤醒并运行它。
#define kthread_run(threadfn, data, namefmt, ...) \
({ \
struct task_struct *__k \
= kthread_create(threadfn, data, namefmt, ## __VA_ARGS__); \
if (!IS_ERR(__k)) \
wake_up_process(__k); \
__k; \
})
kthread_create的自启动版本,其实就是多了一个wake_up_process。
kthread_create其实是调用的函数:kthread_create_on_node,这是一个节点情缘性的函数,创建的节点将在指定的numa节点上分配内存,
这里需要注意的是内存分配在指定的numa节点上,但是cpu调度不是限定在此节点上的cpu上的。
这个在numa系统中是非常有用的,在用户态可以使用类似的numactl的功能将特定的进程绑定到对应的numa节点上。对于内核线程同样有这种需求,
例如数据库的后台日志线程就有可能使用这种方式创建的。
函数原型如下:
struct task_struct *kthread_create_on_node(int (*threadfn)(void *data),
void *data, int node,
const char namefmt[],
...)
{
struct kthread_create_info create;
create.threadfn = threadfn;
create.data = data;
create.node = node;
init_completion(&create.done);
spin_lock(&kthread_create_lock);
list_add_tail(&create.list, &kthread_create_list);
spin_unlock(&kthread_create_lock);
wake_up_process(kthreadd_task);
wait_for_completion(&create.done);
if (!IS_ERR(create.result)) {
static const struct sched_param param = { .sched_priority = 0 };
va_list args;
va_start(args, namefmt);
vsnprintf(create.result->comm, sizeof(create.result->comm),
namefmt, args);
va_end(args);
/*
* root may have changed our (kthreadd's) priority or CPU mask.
* The kernel thread should not inherit these properties.
*/
/*这个是主动地去干预调度策略*/
sched_setscheduler_nocheck(create.result, SCHED_NORMAL, ¶m);
set_cpus_allowed_ptr(create.result, cpu_all_mask);
}
return create.result;
}
这个函数是EXPORT_SYMBOL(kthread_create_on_node);在模块里面是可以使用的。
此外还有一个就是在指定的cpu上创建内核线程的函数:kthread_create_on_cpu,原型如下:
struct task_struct *kthread_create_on_cpu(int (*threadfn)(void *data),
void *data, unsigned int cpu,
const char *namefmt)
{
struct task_struct *p;
/*调用kthread_create_on_node在cpu所属的节点上创建线程*/
p = kthread_create_on_node(threadfn, data, cpu_to_node(cpu), namefmt,
cpu);
if (IS_ERR(p))
return p;
set_bit(KTHREAD_IS_PER_CPU, &to_kthread(p)->flags);
/*设置线程的cpu*/
to_kthread(p)->cpu = cpu;
/* Park the thread to get it out of TASK_UNINTERRUPTIBLE state */
/*设置线程到park状态,这样以后才能使用wake_up_on唤醒它*/
kthread_park(p);
return p;
}
此函数是内部函数,也就是说在模块内部是无法使用的。
此外还有一些非常有用的系统函数:
void kthread_bind(struct task_struct *p, unsigned int cpu)
{
__kthread_bind(p, cpu, TASK_UNINTERRUPTIBLE);
}
EXPORT_SYMBOL(kthread_bind);
绑定一个新创建的线程到指定的cpu上,这个跟kthread_create_on_cpu有点功能相似。
int kthread_park(struct task_struct *k)
{
struct kthread *kthread = to_live_kthread(k);
int ret = -ENOSYS;
if (kthread) {
if (!test_bit(KTHREAD_IS_PARKED, &kthread->flags)) {
set_bit(KTHREAD_SHOULD_PARK, &kthread->flags);
if (k != current) {
wake_up_process(k);
wait_for_completion(&kthread->parked);
}
}
ret = 0;
}
return ret;
}
切换线程到park状态。
int kthread_stop(struct task_struct *k)
{
struct kthread *kthread;
int ret;
trace_sched_kthread_stop(k);
get_task_struct(k);
kthread = to_live_kthread(k);
if (kthread) {
set_bit(KTHREAD_SHOULD_STOP, &kthread->flags);
__kthread_unpark(k, kthread);
wake_up_process(k);
wait_for_completion(&kthread->exited);
}
ret = k->exit_code;
put_task_struct(k);
trace_sched_kthread_stop_ret(ret);
return ret;
}
EXPORT_SYMBOL(kthread_stop);
停止一个使用kthread_create创建的线程。
阅读(3993) | 评论(0) | 转发(0) |