链接:https://www.zhihu.com/question/57013926/answer/151506606

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_infotask_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) {
free_task_struct(tsk);
return NULL;
}
err = arch_dup_task_struct(tsk, orig);
if (err) goto out;
tsk->stack = ti;

这里可以看到内核栈的创建过程。可能会疑惑为何 stack 指向了 thread_info,那是因为在2.6以前的内核中,各个进程的 task_struct 存放在内核栈的尾端,这样做是为了在寄存器较少的体系结构中直接使用栈指针加偏移就可以算出它的位置。2.6以后使用slab分配器动态分配 task_struct ,所以只需要在栈顶创建一个 thread_info 记录 task_struct 的地址。所以这里回答了第一个问题, 每个进程都有一个单独的内核栈。

2.从内核模块编程的角度看(不涉及用户态进程),内核栈该怎么理解?和用户进程进行系统调用使用的栈空间有什么不同?

每个进程运行时都持有上下文,用于保证并行性。为了保证内核和用户态隔离,陷入内核不影响用户态,所以使用了不同的栈。内核栈只是对内核态上下文中的栈的称谓。为了方便管理用户程序,限制用户程序权限,所以区分了内核态和用户态。内核态中拥有高特权级,能够执行io等特权指令,而用户态程序想要执行特权级指令则必须陷入内核态。从用户程序角度来看,内核更类似与库文件的存在。内核通过虚拟地址访问权限来限制用户程序访问内存地址,比如内核空间的代码和数据不应该被用户程序访问到。因此内核运行时使用的栈不应该能被用户态代码访问到,否则用户态代码完全可以通过构造特定的数据控制内核(参考ret2libc)。因此,用户态使用的栈空间和内核栈并无本质区别,它们均处于同一块页表映射中,内核栈处于高特权级访问限制的虚拟地址中,防止用户态代码访问内核数据。

3.怎么理解linux内核栈空间只有4KB或8KB,linux内核编程中的堆(heap)和栈(stack)有什么区别?

内核中的资源是非常宝贵的,而一个比较大的栈空间多数时间是浪费了。那为何不设计小一点,然后保证内核调用层次低、局部变量小,做到不溢出?而内核编程中的堆和栈并非通常写程序时所说的堆和栈有严格的区分。

    最高内存地址
+-------------------+
| 堆栈段 |
| | |
| | |
| | |
| v |
| |
| |
| | | |
| ^ |
| | |
| | |
| | |
| 堆 |
|-------------------|
| BSS段 |
| |
| |
| |
| 数据段 |
|-------------------|
| 代码段 |
+-------------------+

内核中的堆和栈没有严格的地址区分,只是程序角度的不同解释而已。

堆栈在linux内存中的使用的更多相关文章

  1. Linux内存中的Cache真的能被回收么?

    在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: [root@tencent64 ~]# free       ...

  2. Linux 内存中的Cache,真的能被回收么?

    您真的了解Linux的free命令么? 在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: 这里的默认显示单位是k ...

  3. Linux内存中的Cache真的能被回收吗? 【转】

    在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: [root@tencent64 ~]# free        ...

  4. Linux内存中的 buffer 和 cache 到底是个什么东东?

    Linux 中的 free 命令,会输出: total 总量 used  已使用 free 空闲 shared 共享内存 buffers cached 前面四项都比较好理解,一看我也就知道啥意思了.但 ...

  5. 【转载】Linux内存中buffer和 cached的比较

    经常遇到一些刚接触Linux的新手会问内存占用怎么那么多? 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是linux内存管理的一个优秀特性,在 ...

  6. Linux内存中Swap机制(转)

    在做监控时,发现内存中有一项Swap space,不是很理解,这里查了一些资料: http://blog.sina.com.cn/s/blog_502d765f0100krph.html 在linux ...

  7. 释放linux内存中的cache缓存

    echo 3 > /proc/sys/vm/drop_caches 记一次 经常用  exp 导出oracle全量数据库,发现linux内存一直在减小没有释放,即使 oracle重启也不行,只有 ...

  8. Cgroup - Linux 内存资源管理

    Hi ,我是 Zorro .这是我的微博地址,我会不定期在这里更新文章,如果你有兴趣,可以来关注我呦. 另外,我的其他联系方式: Email: mini.jerry@gmail.com QQ: 300 ...

  9. Linux 内存Cache和Buffer理解

    在 Linux 系统中,我们经常用 free 命令来查看系统内存的使用状态.在一个 RHEL6 的系统上,free 命令的显示内容大概是这样一个状态:   [root@tencent64 ~]# fr ...

随机推荐

  1. Linux命令之split

    split用来将大文件分割成小文件.有时文件越来越大,传送这些文件时,首先将其分割可能更容易. 使用vi或其他工具诸如sort时,如果文件对于工作缓冲区太大,也会存在一些问题. 因此有时没有选择余地, ...

  2. PHP中MVC的编程思想浅谈

    我相信这样的文章已经被写烂了,但是我今天还是愿意冒着风险把自己的经验与大家分享一下.纯属原创,我也没什么可保留,希望对新手有帮助,有说的什么不对的地方,欢迎大家伙吐槽. 什么是MVC? 简单的说就是将 ...

  3. 深入了解UIAutomation 的API

    有关UiAUiAutomation的API对象的文件名称. 1.UIAutomation中的对象都是以UIA#####开头的出现的.eg:UIAButton 2.有关Logger对象负责日志的输出 U ...

  4. 免安装mysql配置

    1.下载压缩包:去官网下载免安装的MySQL的压缩包http://dev.mysql.com/downloads/mysql/根据机器选择64位或者32位: 2.解压到相应目录.我解压到了D:\Pro ...

  5. 1052 最大M子段和(DP)

    1052 最大M子段和 基准时间限制:2 秒 空间限制:131072 KB 分值: 80 难度:5级算法题 N个整数组成的序列a[1],a[2],a[3],…,a[n],将这N个数划分为互不相交的M个 ...

  6. 记录-Hibernate+servlet实现简单的增、删、查、改

    由于需要对Hibernate作个了解,所以写了个简单的实现 以上是大概目录 1.新建Hibernate.cfg.xml配置文件 <?xml version='1.0' encoding='UTF ...

  7. ehcarts之toolbox,工具栏

    toolbox 工具栏.内置有导出图片,数据视图,动态类型切换,数据区域缩放,重置五个工具. feature各工具配置项.具体显示功能 1.saveAsImage 保存为图片. 2.restore 还 ...

  8. HTML页面布局

    接下来的下面代码,只是给了一个大的前端编写布局,如果你已经是牛人了,就当没看到,如果是一些初学者,不妨拿去用用,里面也写了一些常用的css样式,现在虽然有很多牛逼的前段框架,用起来也非常得心应手,但是 ...

  9. 基于Cpython的 GIL(Global Interpreter Lock)

    一 介绍 定义: In CPython, the global interpreter lock, or GIL, is a mutex that prevents multiple native t ...

  10. myBatis 课纲

    myBatis 课纲 第一章 MyBatis 架构.主要构件及相互关系 使用 MyBatis 构建项目 基本的增删改查映射文件方式(特殊符号处理),使用接口方式实现 结果集映射: 单个对象映射到Has ...