堆栈在linux内存中的使用
链接: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_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) {
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内存中的使用的更多相关文章
- Linux内存中的Cache真的能被回收么?
在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: [root@tencent64 ~]# free ...
- Linux 内存中的Cache,真的能被回收么?
您真的了解Linux的free命令么? 在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: 这里的默认显示单位是k ...
- Linux内存中的Cache真的能被回收吗? 【转】
在Linux系统中,我们经常用free命令来查看系统内存的使用状态.在一个RHEL6的系统上,free命令的显示内容大概是这样一个状态: [root@tencent64 ~]# free ...
- Linux内存中的 buffer 和 cache 到底是个什么东东?
Linux 中的 free 命令,会输出: total 总量 used 已使用 free 空闲 shared 共享内存 buffers cached 前面四项都比较好理解,一看我也就知道啥意思了.但 ...
- 【转载】Linux内存中buffer和 cached的比较
经常遇到一些刚接触Linux的新手会问内存占用怎么那么多? 在Linux中经常发现空闲内存很少,似乎所有的内存都被系统占用了,表面感觉是内存不够用了,其实不然.这是linux内存管理的一个优秀特性,在 ...
- Linux内存中Swap机制(转)
在做监控时,发现内存中有一项Swap space,不是很理解,这里查了一些资料: http://blog.sina.com.cn/s/blog_502d765f0100krph.html 在linux ...
- 释放linux内存中的cache缓存
echo 3 > /proc/sys/vm/drop_caches 记一次 经常用 exp 导出oracle全量数据库,发现linux内存一直在减小没有释放,即使 oracle重启也不行,只有 ...
- Cgroup - Linux 内存资源管理
Hi ,我是 Zorro .这是我的微博地址,我会不定期在这里更新文章,如果你有兴趣,可以来关注我呦. 另外,我的其他联系方式: Email: mini.jerry@gmail.com QQ: 300 ...
- Linux 内存Cache和Buffer理解
在 Linux 系统中,我们经常用 free 命令来查看系统内存的使用状态.在一个 RHEL6 的系统上,free 命令的显示内容大概是这样一个状态: [root@tencent64 ~]# fr ...
随机推荐
- jQuery.getJSON()方法小记
今天看了下jQquery中的getJSON()方法,做点小结: 原型: jQuery.getJSON( url [, data ] [, success(data, textStatus, jqXHR ...
- Linux(Centos6.5)下安装svn服务器,并通过http访问
linux安装svn其实很容易,个人觉得难就难在配置上,反复配置,琢磨,查找相关资料,总算是成功了.. 安装: 安装svn,一般情况下,选择yum方式安装还是比较简单的. ? 1 2 [root@mo ...
- phantom的使用
phantom页面加载 通过Phantomjs,一个网页可以被加载.分析和通过创建网页对象呈现,访问我的博客园地址:http://www.cnblogs.com/paulversion/p/83938 ...
- shell脚本中出现^M
在Windows中编辑的shell脚本,传到linux系统中,在末尾发现出现了很多^M字符 1.问题分析 在windows下使用notepad++写的脚本上传到Linux下,在使用vim编辑的时候我们 ...
- TP数据查询
[数据查询] select()是数据模型的一个指定方法,可以获得数据表的数据信息 返回一个二维数组信息,当前数据表的全部数据信息 $obj = D(); 创建对象 $obj -> select ...
- hoj 2543 (费用流 拆边)
http://acm.hit.edu.cn/hoj/problem/view?id=2543 1.将原图中的每条边(u, v)拆成两条:(u, v, Ci, 0), (u, v, ∞, Ei) 2.购 ...
- Springboot 1.5.x版本上读取自定义配置文件问题
原来的解决方案: 现在1.5.x以后取消了location地址 1.5以后解决方案:
- openvas-tutorial-for-beginners
https://jonathansblog.co.uk/openvas-tutorial-for-beginners
- 批处理 ECHO命令输出空行
众所周知,如果echo后面跟一个环境变量,但是该变量却为空时,相当于不加任何参数的echo,即输出当前echo是on还是off.很多文章或者教程给出的解决方案都是在echo后面加一个点号echo.,这 ...
- pycharm中导入自写模块时,模块下出现红线
问题描述: 在pycharm中导入自己写的模块时,得不到智能提示,并在模块名下出现下红线,但是代码可以执行,错误提示为下图所示: 原因:出现 以上情况,是因为文件目录设置的问题,pycharm中的最上 ...