内核代码中和用户栈相关的几个片段
来源:互联网 发布:深圳单片机培训 编辑:程序博客网 时间:2024/06/05 11:00
今天突然脑子里面几处知识点大串联,貌似是迸发出了些许思维的火花,以前一直感觉模模糊糊的有关用户栈的大小限制、栈是怎么自动扩展等问题,通过对照以前看过的内核代码的一些关键片段,似乎清晰了起来。机不可失,趁还没迷糊之前拿笔记下来。
问题1 Linux下用户程序的默认栈大小是多少?如何查看,又如何更改默认值(上层指令和底层实现)?
在bash中输入“ulimit -s ",就可以查看用户程序的默认栈大小,在我的机器上的结果是(单位为KB)
修改默认栈大小也很容易
注意,以上的修改只对该bash之后产生成的子进程具有影响,不具备持久性。
知道了方法,那就来看看有时如何在内核中体现的吧。首先要清楚系统中的每个进程都有一组相关的资源限制,指定了能获取和使用的系统资源的上限,以避免用户过度使用系统资源。
这样的一组限制在kernel中是通过元素为rlimit结构的数组来实现的,数组的每个元素对应一种资源限制。
其中rlim_curr表示的是资源限制的当前值(注意不要误解为是资源当前使用量),而rlim_max表示的是资源限制的最大值。前者是软限制,可以由用户自行修改,只要不超过rlim_max;后者是硬限制,只有管理员才有权限对rlim_max进行更改。
每个进程的资源限制数组存放在current->signal->rlim字段中,并且资源限制会被子进程自动继承。
这样就容易理解前面的bash命令了,实质就是读和更新current->signal->rlim[RLIMIT_STACK].rlim_curr的值。
2。栈空间溢出
如果在函数内部声明了尺寸巨大的数组,很容易会出现栈溢出的错误,即程序运行时提示:"segment error.core dumped"。内核是如何检测到栈溢出的错误呢?
概括的说,这是通过对缺页异常的处理来完成的。假设我们定义如下一个函数
其中的数组生命通常会被编译器翻译为“ sub 0x800000 %exp",即通过更改ESP来为局部变量分配空间。而对于这里的函数foo,由于这里局部数组的大小和默认栈大小相同(8192KB),因此ESP的值会变为非法,确切的说,在执行完“ sub 0x800000 %exp"这条语句后,ESP所指向的内存地址不在进程的任何一个合法内存线性区的范围之内;当程序运行到printf()时,会用到ESP进行寻址,这时候会出现缺页异常,而异常处理程序会根据导致异常发生的不同场景采取不同的处理;对于这个foo函数,缺页异常处理函数会判定属于访问非法地址,因此向进程发送SIGSEGV信号,导致进程终止执行。
3.栈的自动扩展
事实上,当一个程序开始运行时(fork+exec),为其用户栈分配的VMA并不是对应着全部栈空间的,而只是一小部分;随着函数调用的一层层叠加,栈会自动扩展,自动增大栈的当前大小。这也是在缺页异常处理中完成的。
与前一种情况的区别在于,只有在导致缺页异常的那个线性地址并不比当前esp低很多的情况下,才会调用expand_stack()这个函数来试图扩展栈空间,而该函数内部调用acct_stack_growth() 来检测是否栈空间已经达到currnent->signal->rlim[RLIMIT_STACK].rlim_curr规定的上限,如果没有,那么就扩展栈空间,否则同样发送SIGSEGV信号,终止进程。
问题1 Linux下用户程序的默认栈大小是多少?如何查看,又如何更改默认值(上层指令和底层实现)?
在bash中输入“ulimit -s ",就可以查看用户程序的默认栈大小,在我的机器上的结果是(单位为KB)
whodare@whodare:~$ ulimit -s
8192
8192
修改默认栈大小也很容易
whodare@whodare:~$ ulimit -s 16384
whodare@whodare:~$ ulimit -s
16384
whodare@whodare:~$ ulimit -s
16384
注意,以上的修改只对该bash之后产生成的子进程具有影响,不具备持久性。
知道了方法,那就来看看有时如何在内核中体现的吧。首先要清楚系统中的每个进程都有一组相关的资源限制,指定了能获取和使用的系统资源的上限,以避免用户过度使用系统资源。
这样的一组限制在kernel中是通过元素为rlimit结构的数组来实现的,数组的每个元素对应一种资源限制。
struct rlimit
{
unsigned long rlim_curr;
unsigned long rlim_max;
}
{
unsigned long rlim_curr;
unsigned long rlim_max;
}
其中rlim_curr表示的是资源限制的当前值(注意不要误解为是资源当前使用量),而rlim_max表示的是资源限制的最大值。前者是软限制,可以由用户自行修改,只要不超过rlim_max;后者是硬限制,只有管理员才有权限对rlim_max进行更改。
每个进程的资源限制数组存放在current->signal->rlim字段中,并且资源限制会被子进程自动继承。
这样就容易理解前面的bash命令了,实质就是读和更新current->signal->rlim[RLIMIT_STACK].rlim_curr的值。
2。栈空间溢出
如果在函数内部声明了尺寸巨大的数组,很容易会出现栈溢出的错误,即程序运行时提示:"segment error.core dumped"。内核是如何检测到栈溢出的错误呢?
概括的说,这是通过对缺页异常的处理来完成的。假设我们定义如下一个函数
#define SIZE 0x800000
int foo ( )
{
char array[SIZE];
printf("foo");
return 0;
}
int foo ( )
{
char array[SIZE];
printf("foo");
return 0;
}
其中的数组生命通常会被编译器翻译为“ sub 0x800000 %exp",即通过更改ESP来为局部变量分配空间。而对于这里的函数foo,由于这里局部数组的大小和默认栈大小相同(8192KB),因此ESP的值会变为非法,确切的说,在执行完“ sub 0x800000 %exp"这条语句后,ESP所指向的内存地址不在进程的任何一个合法内存线性区的范围之内;当程序运行到printf()时,会用到ESP进行寻址,这时候会出现缺页异常,而异常处理程序会根据导致异常发生的不同场景采取不同的处理;对于这个foo函数,缺页异常处理函数会判定属于访问非法地址,因此向进程发送SIGSEGV信号,导致进程终止执行。
3.栈的自动扩展
事实上,当一个程序开始运行时(fork+exec),为其用户栈分配的VMA并不是对应着全部栈空间的,而只是一小部分;随着函数调用的一层层叠加,栈会自动扩展,自动增大栈的当前大小。这也是在缺页异常处理中完成的。
与前一种情况的区别在于,只有在导致缺页异常的那个线性地址并不比当前esp低很多的情况下,才会调用expand_stack()这个函数来试图扩展栈空间,而该函数内部调用acct_stack_growth() 来检测是否栈空间已经达到currnent->signal->rlim[RLIMIT_STACK].rlim_curr规定的上限,如果没有,那么就扩展栈空间,否则同样发送SIGSEGV信号,终止进程。
- 内核代码中和用户栈相关的几个片段
- Android常用的几个系统参数相关代码片段
- Ubuntu中和用户、用户组相关的操作
- Android相关的代码片段
- JSP中和代码相关的语法元素:
- 需求分析过程中和用户沟通的几个心得
- 几个有用的JavaScript/jQuery代码片段
- Android编程几个有用的代码片段
- linux时区的几个代码片段
- 几个项目中用到的代码片段
- 内核配置中和U盘相关的若干选项
- Linux2.6内核--VFS层中和进程相关的数据结构
- Linux2.6内核--VFS层中和进程相关的数据结构
- 分享几个非常有用的PHP代码片段
- 分享几个非常有用的PHP代码片段
- (转载)推荐几个非常实用的JQuery代码片段
- 总结几个实用的js/jq代码片段
- 与Pocket Outlook相关的一些代码片段(C#)
- 今天买了个本本
- 如何获取msn的好友列表
- 用存储过程实现分页显示
- xml和dataset
- Get your free wiki now!
- 内核代码中和用户栈相关的几个片段
- 三层交换技术 的简单解释
- vb中textbox刷新无闪烁的方法
- FIREBIRD浅历
- 程序員如何創業?
- Flex 常用语句
- E文积累_20080221_this is not how it looks
- 在LINUX下安装postgres数据库
- 在VB.NET中应用SQLDMO