用户进程,用户线程,内核线程

来源:互联网 发布:爆破手机号软件 编辑:程序博客网 时间:2024/05/20 00:35

详细:http://blog.163.com/zhe_wang_2009/blog/static/17228212120123971418489/

总的示意图如下:

用户进程,用户线程,内核线程 - wanny - wanny


Location:arch/x86/kernel/process.c(linux 2.6.38)

238 int sys_fork(struct pt_regs *regs) 239 {240 return do_fork(SIGCHLD, regs->sp, regs, 0, NULL, NULL);241 }

Location:arch/x86/kernel/process.c(linux 2.6.38)

253 int sys_vfork(struct pt_regs *regs) 254 {255 return do_fork(CLONE_VFORK | CLONE_VM | SIGCHLD, regs->sp, regs, 0,256 NULL, NULL);257 }

Location:arch/x86/kernel/process.c(linux 2.6.38)

259 long260 sys_clone(unsigned long clone_flags, unsigned long newsp,261 void __user *parent_tid, void __user *child_tid, struct pt_regs * regs)262 {263 if (!newsp)264 newsp = regs->sp;265 return do_fork(clone_flags, newsp, regs, 0, parent_tid, child_tid);266 }

Location:kernel/fork.c

1399 long do_fork(unsigned long clone_flags, 1400 unsigned long stack_start,1401 struct pt_regs *regs,1402 unsigned long stack_size,1403 int __user *parent_tidptr,1404 int __user *child_tidptr)1405 {1406 struct task_struct *p;1407 int trace = 0;1408 long nr;

 进程,线程,内核线程在内核中都是通过do_fork()完成创建的,那do_fork()是如何体现其功能的多样性的呢?

 主要发挥作用的是do_fork()的第一个参数,clone_flags

下面介绍几个常见的clone_flags的取值:

CLONE_VM :子进程共享父进程内存描述符和所有的页表
CLONE_FS :子进程共享父进程所在文件系统的根目录和当前工作目录
CLONE_FILES :子进程共享父进程打开的文件
CLONE_SIGHAND :子进程共享父进程的信号处理程序、阻塞信号和挂起的信号,
                               使用该标志必须同时设置 CLONE_VM 标志

fork() :由于 do_fork() 中 clone_flags 参数除了子进程结束时返
回给父进程的 SIGCHLD 信号外并无其他特性标志,因此由 fork()
创建的进程不会共享父进程的任何资源。通常子进程会完全复制父
进程的资源,也就是说父子进程相对独立

do_fork()中的参数:
 clone_flags :进程各种特性的标志。低字节指定子进程结
束时发送给父进程的信号代码,一般为 SIGCHLD 信号,剩
余三个字节是若干个标志或运算的结果

 regs :指向通用寄存器值的指针,当进程从用户态切换到
内核态时通用寄存器中的值会被保存到内核态堆栈中

vfork() : do_fork() 中的 clone_flags 使用了 CLONE_VFORK 和 CLONE_VM 两个标志
 CLONE_VFORK 标志使得子进程先于父进程执行,父进程会阻塞到子进程结束或执行新的程序
CLONE_VM 标志使得子进程共享父进程的内存地址空间(父进程的页表项除外)

clone() :与前两个函数不同, clone 通常用于创建轻量级进程.
使用 clone 函数时候通常是自定义的传入 flags 标志,一般
flags 的取值为 CLONE_VM|CLONE_FS|CLONE_FILES|
CLONE_SIGHAND
由上述标志可以看到,轻量级进程通常共享父进程的内存地址
空间、父进程所在文件系统的根目录以及工作目录信息、父进
程当前打开的文件以及父进程所拥有的信号处理函数

通过 pthread_create() 创建的线程最终在内核中是
通过 clone() 完成创建的,而 clone() 最终调用
do_fork 函数

关于kernel_thread()函数(location::arch/x86/kernel/process.c(linux 2.6.38))

278 int kernel_thread(int (*fn)(void *), void *arg, unsigned long flags)279 {280 struct pt_regs regs;281 282 memset(&regs, 0, sizeof(regs));283 284 regs.si = (unsigned long) fn;285 regs.di = (unsigned long) arg;286 287 #ifdef CONFIG_X86_32288 regs.ds = __USER_DS;289 regs.es = __USER_DS;290 regs.fs = __KERNEL_PERCPU;291 regs.gs = __KERNEL_STACK_CANARY;292 #else293 regs.ss = __KERNEL_DS;294 #endif295 296 regs.orig_ax = -1;297 regs.ip = (unsigned long) kernel_thread_helper;298 regs.cs = __KERNEL_CS | get_kernel_rpl();299 regs.flags = X86_EFLAGS_IF | 0x2;300 301 /* Ok, create the new process.. */302 return do_fork(flags | CLONE_VM | CLONE_UNTRACED, 0, &regs, 0, NULL, NULL);303 }304 EXPORT_SYMBOL(kernel_thread);

从上面的组合的 flag 可以看出,新的内核线程至少会共享父内核
线程的内存地址空间。
原创粉丝点击