怎么理解linux内核栈?

关于linux内核栈,一直很困惑,找不到详细资料。 1. linux内核栈是所有进程共享的吗,每个进程都有一个单独的内核栈? 2. 从内核模块编程的角…
关注者
289
被浏览
125,890

40 个回答

1. 读Linux内核以及相关的资料的时候,时刻要清醒地认识到它说的是内核态还是用户态的东西。

2. 一个用户态进程/线程在内核中都是用一个task_struct的实例描述的,这个有点类似设计模式里面的桥接模式(handle-body), 用户态看到的进程PID,线程TID都是handle, task_struct是body。

3. C语言书里面讲的堆、栈大部分都是用户态的概念,用户态的堆、栈对应用户进程虚拟地址空间里的一个区域,栈向下增长,堆用malloc分配,向上增长。

4. 用户空间的堆栈,在task_struct->mm->vm_area里面描述,都是属于进程虚拟地址空间的一个区域。

5.而内核态的栈在tsak_struct->stack里面描述,其底部是thread_info对象,thread_info可以用来快速获取task_struct对象。整个stack区域一般只有一个内存页(可配置),32位机器也就是4KB。

6. 所以说,一个进程的内核栈,也是进程私有的,只是在task_struct->stack里面获取。

7. 内核态没有进程堆的概念,用kmalloc()分配内存,实际上是Linux内核统一管理的,一般用slab分配器,也就是一个内存缓存池,管理所有可以kmalloc()分配的内存。所以从原理上看,在Linux内核态,kmalloc分配的所有的内存,都是可以被所有运行在Linux内核态的task访问到的。

1. Linux 内核中使用 `task_struct` 作为进程描述符,该结构定义在<linux/sched.h>文件中:

struct task_struct {
 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */
 void *stack;
 atomic_t usage;
 unsigned int flags; /* per process flags, defined below */
 unsigned int ptrace;
 int lock_depth; /* BKL lock depth */ 
 /* ...... */ 
}; 

可以发现 `task_struct` 中有一个 `stack` 成员,而 `stack` 正好用于保存内核栈地址。内核栈在进程创建时绑定在 `stack` 上。可以观察 `fork` 流程:Linux 通过 `clone()` 系统调用实现 `fork()`,然后由 `fork()` 去调用 `do_fork()`。定义在<kernel/fork.c>中的 `do_fork()` 负责完成进程创建的大部分工作,它通过调用 `copy_process()` 函数,然后让进程运行起来。`copy_process()` 完成了许多工作,这里重点看内核栈相关部分。`copy_process()` 调用 `dup_task_struct` 来创建内核栈、`thread_info` 和 `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 err; prepare_to_copy(orig);
 tsk = alloc_task_struct();
 if (!tsk) return NULL;
 ti = alloc_thread_info(tsk); 
 if (!ti) { 
  free_task_struct(tsk);
  return NULL; 
 err = arch_dup_task_struct(tsk, orig);
 if (err) goto out;
 tsk->stack = ti;
 err = prop_local_init_single(&tsk->dirties);
 if (err) goto out;
 setup_thread_stack(tsk, orig);
 stackend = end_of_stack(tsk);
 *stackend = STACK_END_MAGIC;
 /* for overflow detection */
 #ifdef CONFIG_CC_STACKPROTECTOR 
 tsk->stack_canary = get_random_int();
 #endif 
 /* One for us, one for whoever does the "release_task()" 
 (usually parent) */
 atomic_set(&tsk->usage,2);
 atomic_set(&tsk->fs_excl, 0);
 #ifdef CONFIG_BLK_DEV_IO_TRACE
 tsk->btrace_seq = 0;
 #endif 
 tsk->splice_pipe = NULL;
 account_kernel_stack(ti, 1);
 return tsk;
out:
 free_thread_info(ti);
 free_task_struct(tsk);
 return NULL; 
} 

其中重点是下面部分:

tsk = alloc_task_struct(); 
if (!tsk) return NULL; 
ti = alloc_thread_info(tsk); 
if (!ti) {