在需要多任务的编程中,多数情况下都是使用的pthread,网上查找资料的话,也有很多使用fork/vfork的例子。
pthread是线程,fork是进程,这是没有疑问的。
但是有资料又说,linux不区分进程和线程,或者说根本就没有实现线程。
可但是又有资料明明写着,有linux有内核线程。。。,查看内核代码确有kernel_thread,
真是傻傻分不清楚。
那么,下面就试着理一理头绪,不一定正确,只代表了当前的认识。
1.kernel_thread
内核源码:
init/main.c
asmlinkage void __init start_kernel(void)-->
static noinline void __init_refok rest_init(void){
kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
cpu_idle();
}
rest_init中启动了两个内核线程kernel_init和kthreadd,
rest_init本身也是一个内核线程,最后死在cpu_idle中。
此后还会有很多地方
(待续。。。。。。)
在内核启动过程中会有很多地方通过kernel_thread创建内核线程(只是创建方法不像kernel_init和kthreadd这样显示调用)
大部分启动完成,干完自己的活就退出了。
这里rest_init本身是线程0, pid为0; kernel_init的pid为1,kthreadd的pid为2.
kernel_init最后会调用/sbin/init程序,成为第一个用户进程。
(待续。。。。。)
看一下kernel_thread的源码
/*
* Create a kernel thread.
*/
pid_t kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)
{
struct pt_regs regs;
memset(®s, 0, sizeof(regs));
regs.ARM_r4 = (unsigned long)arg;
regs.ARM_r5 = (unsigned long)fn;
regs.ARM_r6 = (unsigned long)kernel_thread_exit;
regs.ARM_r7 = SVC_MODE | PSR_ENDSTATE | PSR_ISETSTATE;
regs.ARM_pc = (unsigned long)kernel_thread_helper;
regs.ARM_cpsr = regs.ARM_r7 | PSR_I_BIT;
return do_fork(flags|CLONE_VM|CLONE_UNTRACED, 0, ®s, 0, NULL, NULL);
}
EXPORT_SYMBOL(kernel_thread);
也就是kernel_thread是通过do_fork实现的,当用户程序通过fork创建进程的时候,内核同样会进入到do_fork.
因此这就证明了,linux内核不区分线程和进程,他们是同一个东西,内核线程,也可以叫内核进程。
(待续。。。。)
kernel_init和kthreadd可以说是纯手工打造的,在内核启动的时候会创建很多内核线程,我们在写一个设备驱动的时候也可能需要创建一个线程
而这些地发不能用这种手工的方式,而是有更加通用的API。我们看一下kthreadd都做了什么
int kthreadd(void *unused)
{
struct task_struct *tsk = current;
/* Setup a clean context for our children to inherit. */
set_task_comm(tsk, "kthreadd");
printk("[%s-%d][%s] current->pid=%d,current->tgid=%d current->state=%d current->comm=%s\n",__FILE__,__LINE__,__func__,current->pid,current->tgid,current->state,current->comm);
ignore_signals(tsk);
set_cpus_allowed_ptr(tsk, cpu_all_mask);
set_mems_allowed(node_states[N_HIGH_MEMORY]);
current->flags |= PF_NOFREEZE | PF_FREEZER_NOSIG;
printk("[%s-%d][%s] current->pid=%d,current->tgid=%d current->state=%d\n",__FILE__,__LINE__,__func__,current->pid,current->tgid,current->state);
for (;;) {
printk("[%s-%d][%s] current->pid=%d,current->tgid=%d current->state=%d\n",__FILE__,__LINE__,__func__,current->pid,current->tgid,current->state);
set_current_state(TASK_INTERRUPTIBLE);
if (list_empty(&kthread_create_list)){
printk("[%s-%d][%s] call schedule\n",__FILE__,__LINE__,__func__);
schedule();
}
__set_current_state(TASK_RUNNING);
spin_lock(&kthread_create_lock);
while (!list_empty(&kthread_create_list)) { //检查list是否为空
struct kthread_create_info *create;
create = list_entry(kthread_create_list.next, //获取一个create请求
struct kthread_create_info, list);
list_del_init(&create->list);
spin_unlock(&kthread_create_lock);
printk("[%s-%d][%s] create->threadfn=0x%x\n",__FILE__,__LINE__,__func__,create->threadfn);
create_kthread(create); //创建内核线程
spin_lock(&kthread_create_lock);
}
spin_unlock(&kthread_create_lock);
}
return 0;
}
可以看到kthreadd就是查看kthread_create_list 如果不为空,则从列表取出一个create(请求),然后执行create_kthread(create);
去创建一个内核线程,可以想到一定有一个地发将这个create请求放到kthread_create_list上,它就是一个通用的创建内核线程的API,如下
struct task_struct *kthread_create(int (*threadfn)(void *data),
void *data,
const char namefmt[],
...)
{
struct kthread_create_info create;
create.threadfn = threadfn;
create.data = data;
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)) {
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);