堆栈在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 ...
随机推荐
- C++11写算法之插入排序
插入排序,是指将从1 –> size-1的数一个个插入到前面已经排序好的数组中. 时间复杂度:O(n^2) , O(nlgn) (lgn指使用二分查找插入点位置) 空间复杂度:O(1) // ...
- OpenCV学习笔记八:opencv_photo模块
一,简介: 该库用于数码照片的处理,处于发展中,目前只包含如下算法: //! restores the damaged image areas using one of the available i ...
- es6 async与await实战
在使用js的时候,我们经常会遇到一个问题,就是我们需要等待请求返回再做下一步处理,之前的处理方式是通过ajax的success或者callback之类的方法,不过一层一层真的恶心,而且只是针对单个页面 ...
- shell脚本中echo显示内容带颜色显示
格式如下 : echo -e "\033[41;36m something here \033[0m" 其中41的位置代表底色, 36的位置是代表字的颜色 注: 1.字背景颜色和文 ...
- Ninject学习笔记<三>
ASP.NET MVC学前篇之Ninject的初步了解 1.介绍 废话几句,Ninject是一种轻量级的.基础.NET的一个开源IoC框架,在对于MVC框架的学习中会用到IoC框架的,因为这种IoC开 ...
- EasyNVR摄像机无插件直播流媒体服务器前端构建之输入框样式的调整
EasyNVR授权方式分为软件的授权和硬件授权两种方式,软件授权需要在软件输入永久邀请码可以激化永久授权 起初我们的界面设计是为了满足功能的需求就是 ,用户可以输入激活码提交,完成永久授权. 在实际的 ...
- SQL查临时表没有返回数据集
问题描述:在SQL中可以查询到数据,返回不到页面上. 解决办法: set nocount on create table #list [转] 每次我们在使用查询分析器调试SQL语句的时候,通常会看到一 ...
- mock数据(模拟后台数据)
mock数据(模拟后台数据) - Emily恩 - 博客园 https://www.cnblogs.com/enboke/p/vue.html Mock.js http://mockjs.com/ 前 ...
- 江卓尔与比特币增发,谣言or先知-千氪
最近,很多圈内人都在讨论比特币是否应该增发,但等等,比特币真的会增发吗?到底是真有增发计划还是某些人别有用心地在散布谣言? 那么消息是从哪里出来的?北京时间 2 月 10 日晚,莱比特矿池创始人江卓尔 ...
- 【AWS】AWS云计算赋能数字化转型专题研讨会圆满落幕
大会精彩回顾:查看原文 大会使用的PPT下载地址:点击下载