linux 线程的内核栈是独立的还是共享父进程的?

时间:2022-11-15 18:42:56

需要考证

考证结果:

其内核栈是独立的

 206 static struct task_struct *dup_task_struct(struct task_struct *orig)
207 {
208 struct task_struct *tsk;
209 struct thread_info *ti;
210 int err;
211
212 prepare_to_copy(orig);
213
214 tsk = alloc_task_struct();
215 if (!tsk)
216 return NULL;
217
218 ti = alloc_thread_info(tsk);
219 if (!ti) {
220 free_task_struct(tsk);
221 return NULL;
222 }
223
224 err = arch_dup_task_struct(tsk, orig);
225 if (err)
226 goto out;
227
228 tsk->stack = ti;
229
230 err = prop_local_init_single(&tsk->dirties);
231 if (err)
232 goto out;
233
234 setup_thread_stack(tsk, orig);
。。。。。。。、 98 #ifndef __HAVE_ARCH_THREAD_INFO_ALLOCATOR
99 static inline struct thread_info *alloc_thread_info(struct task_struct *tsk)
100 {
101 #ifdef CONFIG_DEBUG_STACK_USAGE
102 gfp_t mask = GFP_KERNEL | __GFP_ZERO;
103 #else
104 gfp_t mask = GFP_KERNEL;
105 #endif
106 return (struct thread_info *)__get_free_pages(mask, THREAD_SIZE_ORDER);
107 }
108

。。。。。。
Breakpoint 2, __get_free_pages (gfp_mask=208, order=1) at mm/page_alloc.c:1670
1670 page = alloc_pages(gfp_mask, order);
(cskygdb) bt
#0 __get_free_pages (gfp_mask=208, order=1) at mm/page_alloc.c:1670
#1 0x9000f5d6 in alloc_thread_info (clone_flags=4001536, stack_start=7, regs=0x0, stack_size=715825152, child_tidptr=0x0, pid=0x0, trace=0)
at kernel/fork.c:106
#2 dup_task_struct (clone_flags=4001536, stack_start=7, regs=0x0, stack_size=715825152, child_tidptr=0x0, pid=0x0, trace=0)
at kernel/fork.c:218
#3 copy_process (clone_flags=4001536, stack_start=7, regs=0x0, stack_size=715825152, child_tidptr=0x0, pid=0x0, trace=0)
at kernel/fork.c:922
#4 0x90010a7e in do_fork (clone_flags=4001536, stack_start=715921168, regs=0x90847fb8, stack_size=0, parent_tidptr=0x2aac1be8,
child_tidptr=0x2aac1be8) at kernel/fork.c:1348
#5 0x90005c4e in csky_clone (clone_flags=<value optimized out>, newsp=<value optimized out>, parent_tidptr=<value optimized out>,
child_tidptr=0x90005c4e, tls_val=<value optimized out>, regs=<value optimized out>) at arch/csky/kernel/process.c:237
#6 0x900009d2 in system_call ()

网上的介绍:

http://www.360doc.com/content/13/0527/00/12499915_288433220.shtml

linux 线程有自己独立的内核栈吗?
首先,我们知道所有线程共享主线程的虚拟地址空间(current->mm指向同一个地址),且都有自己的用户态堆栈(共享父进程的地址空间,再在里面分配自己的独立栈,默认2M)。这是毫无疑问的,但还有一点我没搞明白,内核栈是共享还是独立的?
猜测:独立的。理由:要不然内核栈对应的thread_info中的tast_struct没有办法与每个线程对应起来,因为现在已经有多个task_struct了,但保存内核栈的thread_info(其实是thread_union联合体)中只能保存一个task_struct。所以理论上分析,虽然可以共享地址空间,但每个线程还是需要一个单独的内核栈的。看代码:
分析创建线程最终肯定会走到内核函数do_fork()中来的,所以从此函数看起。
do_fork()->copy_process()->dup_task_struct()
fork.c中dup_task_struct()的实现:
static struct task_struct *dup_task_struct(struct task_struct *orig)
{
struct task_struct *tsk;
struct thread_info *ti;
unsigned long *stackend;
int node = tsk_fork_get_node(orig);
int err; tsk = alloc_task_struct_node(node);
if (!tsk)
return NULL; ti = alloc_thread_info_node(tsk, node);/*就是这里,果然分配内核栈了*/
if (!ti)
goto free_tsk; err = arch_dup_task_struct(tsk, orig);/*这里分配task_struct结构*/
if (err)
goto free_ti; tsk->stack = ti;
...
}

线程在Linux中的实现

3.3   线程在Linux中的实现

线程机制是现代编程技术中常用的一种抽象。该机制提供了在同一程序内共享内存地址空间运行的一组线程。这些线程还可以共享打开的文件和其他资源。线 程机制支持并发程序设计技术(concurrent programming),在多处理器系统上,它也能保证真正的并行处理(parallelism)。

Linux实现线程的机制非常独特。从内核的角度来说,它并没有线程这个概念。Linux把所有的线程都当作进程来实现。内核并没有准备特别的调度 算法或是定义特别的数据结构来表征线程。相反,线程仅仅被视为一个与其他进程共享某些资源的进程。每个线程都拥有惟一隶属于自己的 task_struct,所以在内核中,它看起来就像是一个普通的进程(只是该进程和其他一些进程共享某些资源,如地址空间)。

上述线程机制的实现与Microsoft Windows或是Sun Solaris等操作系统的实现差异非常大。这些系统都在内核中提供了专门支持线程的机制(这些系统常常把线程称作轻量级进程,lightweight process)。“轻量级进程”这种叫法本身就概括了Linux在此处与其他系统的差异。在其他的系统中,相较于重量级的进程,线程被抽象成一种耗费较 少资源,运行迅速的执行单元。而对于Linux来说,它只是一种进程间共享资源的手段(Linux的进程本身就够轻了)。举个例子来说,假如我们有一个包 含四个线程的进程,在提供专门线程支持的系统中,通常会有一个包含指向四个不同线程的指针的进程描述符。该描述符负责描述像地址空间、打开的文件这样的共 享资源。线程本身再去描述它独占的资源。相反,Linux仅仅创建四个进程并分配四个普通的task_sturct结构。建立这四个进程时指定它们共享某 些资源就行了。

线程的创建和普通进程的创建类似,只不过在调用clone()的时候需要传递一些参数标志来指明需要共享的资源:

  1. clone(CLONE_VM | CLONE_FS | CLONE_FILES | CLONE_SIGHAND, 0);

上面的代码产生的结果和调用fork()差不多,只是父子俩共享地址空间、文件系统资源、文件描述符和信号处理程序。换个说法就是,新建的进程和它的父进程就是流行的所谓线程。

对比一下,一个普通的fork()的实现是:

  1. clone(SIGCHLD, 0);

而vfork()的实现是:

  1. clone(CLONE_VFORK | CLONE_VM | SIGCHLD, 0);

传递给clone()的参数标志决定了新创建进程的行为方式和父子进程之间共享的资源种类。表3-1列举了这些clone()用到的参数标志以及它们的作用,这些是在<linux/sched.h>中定义的。