linux 启动线程命令,Linux 线程分析

用户态线程

用户空间利用系统调用clone来创建线程,在glibc中文件夹nptl/sysdeps/pthreadcreatethread.c中的函数create_thread中指明了flags:

...

int clone_flags = (CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGNAL |CLONE_SETTLS | CLONE_PARENT_SETTID | CLONE_CHILD_CLEARTID | CLONE_SYSVEM)

...

调用创建线程的函数:

ARCH_CLONE(fct,STACK_VARIABLES_ARGS,clone_flags,pd,&pd->tid,TLS_VALUE,&pd->tid);

而ARCH_CLONE定义为:

ifndef ARCH_CLONE

#define ARCH_CLONE __clone    //调用clone函数

#endif

而clone函数的参数:

[glibc/include/sched.h]

extern int __clone(int (*__fn) (void *__arg),void *__child_stack,int __flags,void *__arg,...)

就是这些参数去调用do_fork函数来创建了一个线程

CLONE_VM:共享虚拟内存

CLONE_FS:共享文件命名空间,文件系统信息,以及pwd

CLONE_FILES:共享文件描述符表

CLONE_SIGNAL:POSIX信号量,具体不详,内核中没有该设置,()

CLONE_SETTLS:设置线程局部存储区域(类似pthread_setspecific,pthread_getspecific函数调用,不过该函数在用户层封装之后实现,而set/get_thread_area才是真正的系统调用)

CLONE_PARENT_SETTID:在clone

系统调用中从用户空间复制创建线程的pid:

if(clone_flags & CLONE_PARENT_SETTID)

put_user(nr,parent_tidptr);//从用户空间将parent_tidptr复制到内核态变量nr中

在nptl库中等价于调用CLONE_CHILD_SETTID,因为调用的实参都相同,从nptl注释中看出它的开销很大,因此通过CLONE_PARENT_SETTID来实现。

CLONE_CHILD_SETTID:将内核态的p->set_child_tid指向用户态的child_tidptr:

p->set_child_tid = (clone_flags &CLONE_CHILD_SETTID) ?child_tidptr:NULL;

当新进程执行前,又将当前进程的pid复制到该域中.

[kernel/schedule.c]

asmlinkage void schedule_tail(struct task_struct *prev)

{

...

if(current->set_child_tid)

put_user(task_pid_vnr(current),current->set_child_tid);

...

}

CLONE_CHILD_CLEARTID:在copy_process函数中,有同样的效果,指向child_tidptr:

p->clear_child_tid = (clone_flags & CLONE_CHILD_CLEARTID) ? child_tidptr:NULL;

但是该作用域用在当进程终止时,回收传递给用户空间的tid:

[kernel/fork.c]

void mm_release(struct task_struct *tsk,struct mm_struct *mm)

{

//线程引用计数大于1,线程共享内存模式

if(tsk->clear_child_tid && !(tsk->flags & PF_SIGNALED) && atomic_read(&mm->users)>1)

{

u32 __user *tidptr = tsk->clear_child_tid;

tsk->clear_child_tid = NULL;

put_user(0,tidptr);

//唤醒等待线程退出事件的线程

sys_futex(tidptr,FUTEX_WAKE,1,NULL,NULL,0);

}

}

上面的标志就是创建线程的所有标志了,CLONE_CHILD_SETTID,CLONE_PARENT_SETTID在线程创建时使用,而CLONE_CHLID_CLEARTID在线程退出时采用。

内核态线程

内核态线程通常指的是内核级守护线程,主要执行下面的任务:1.周期性与块驱动同步修改的内存页,如将脏数据回写至磁盘。

2.将内存页写至交换分区

3.实现日志文件系统的事务机制

主要分为两种类型的执行方法:

1.线程一旦启动,就进行等待直到内核要求其执行特定的任务

2.一旦创建就周期性检查各种资源,监视系统资源使用情况,超过限制时就需要采取行动,如pdflush.

创建内核线程函数如下:

[arch/x86/kernel/process_32.c]

int kernel_thread(int (*fn)(void *),void *arg,unsigned long flags)

{

struct pt_regs regs;

memset(&regs,0,sizeof(struct pt_regs));

/*构造一个符合do_fork系统调用的参数**/

regs.ebx = (unsigned long)fn;

regs.edx = (unsigned long)arg;

regs.xds = __USER_DS;

regs.xes = __USER_DS;

regs.xfs = __KERNEL_PERCPU;

regs.orig_eax = -1;

regs.eip = (unsigned long) kernel_thread_helper;

regs.xcs = __KERNEL_CS|get_kernel_rpl();

regs.flags = X86_EFLAGS_IF |X86_EFLAGS_SF|X86_EFLAGS_PF|0x2;

return do_fork(flags,CLONE_VM|CLONE_UNTRACED,0,&regs,0,NULL,NULL);

}

由于内核线程运行在内核态中,所有没有用户栈,而且也只能访问内核的部分虚拟内存。

在定义进程时有两个域:

struct task_struct {

...

struct mm_struct *mm,*active_mm;

...

}

Linux系统中虚拟内存分为两部分:低端内存为用户程序,而高端则留给内核,当用户程序进入内核态时,用户态的内存区域指针保存在mm中,内核态程序没有用户级程序,所有mm则为空,但是由于执行系统调用之后进入内核态,或者执行内核态进程时,为了清楚知道当前用户态的进程,active_mm就指向了当前的用户空间进程,为了防止被内核态访问时,用户空间被释放,系统增加一个计数器:mm_users,而僵尸进程也只有在计数为0时才会被释放。处于进程上下文时两个指针是一样的。内核线程能够使用两种方式来实现,通过传递一个函数指针给kernel_thread,然后调用daemonize进入守护模式:

1.如果有用户态资源,则释放用户态的所有资源,如内存上下区域,文件描述符等。

2.damonize阻塞接收所有的信号

3.将父进程设置为init

还可以通过下面的函数来创建内核线程:

[kernel/kthread.c]

struct task_struct *kthread_create(int (*threadfn)(void *data),

void *data,

const char namefmt[],

....)

该函数创建一个名称为namefmt的内核级线程,通过调用wake_up_process来启动它,运行函数threadfn(data),如果没有调用函数kthread_stop或kthread_should_stop,则调用do_exit来退出。

类似地,还可以调用kthread_run,kthread_create_on_cpu来创建线程。

可以通过调用ps

-fax来查看内核线程:

2 ? S 0:00 [kthreadd]

3 ? S 0:00 \_ [migration/0]

4 ? S 0:00 \_ [ksoftirqd/0]

5 ? S 0:00 \_ [watchdog/0]

6 ? S 0:00 \_ [migration/1]

7 ? S 0:00 \_ [ksoftirqd/1]

8 ? S 0:00 \_ [watchdog/1]

9 ? S 0:09 \_ [events/0]

10 ? S 0:10 \_ [events/1]

[]表示内核线程/0表明该内核线程限制运行在0号cpu之上。

参考资料

professional linux kernel architecture

linux-kernel-v.2.26.4源码

glibc2.9源码

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值