请教一个linux进程linux内核网络协议栈栈的问题

下次自动登录
现在的位置:
& 综合 & 正文
请问内核的堆和栈的起始地址是在哪里设置的、有多大?(一个关于内核进程堆栈的讨论,不错)
(newbie)07-02-07 10:14
请问内核的堆和栈的起始地址是在哪里设置的、有多大?
请问各位兄弟:
我看内核的链接脚本和System.map中,没有发现内核的栈的设置地址(而在像Bootloader中的链接脚本里面一般会指定栈的地址,在初始化的时候就把SP设置到这个地址)。 而在内核中没有发现在哪里设置的栈指针。
另外,我还想问,内核态的内存预留的堆和栈各有多大?在哪里设置的?
多谢各位!
--------------------开心工作,快乐生活
文章选项:
(addict)07-02-07 15:20
Re: 请问内核的堆和栈的起始地址是在哪里设置的、有多大? []
我个人理解,不一定正确。而且我的理解经常错误,呵呵。
内核中的栈和堆与应用程序的栈和堆,概念上相同,但是在实现和使用上不太一样。
linux下应用程序的栈和堆都是基于虚拟内存的地址空间的概念的,就是对应内核中的vma的list。在应用程序中,栈和堆的起始地址是有约定的。
但是内核本身没有另外一个更高层的程序来管理内核的地址空间,所以内核空间的栈的组织,和应用程序有所不同。内核的栈如果溢出时,不能像应用程序那样产生一个page fault,然后分配新的地址空间,继续使用。内核的栈如果溢出,没有人知道,除非溢出的地方影响了运行,让程序crash。
而内核本身更没有堆,堆在我理解是针对应用程序的,堆内的线性地址空间靠libc中的malloc和free来管理。堆的空间的扩大靠brk()系统调用。而内核的内存使用,get_free_pages来实现的(虽然上层封装了kmalloc和vmalloc),这个基本上是自己管理自己,由于Linux内核还不支持自己的swapping,所以内核如果找不到可用的页框了,也就没办法了。
最后,Linux下应用程序的堆起始地址,一般就是在bss后面开始。也许需要某个单位的地址对齐,也许中间还有一些堆的管理数据结构,但是大致就是这个位置。Linux下栈的起始,有两种方式:1,从3G往下某一个地址开始,往下生长,直到和堆碰上;2,在应用程序线性地址的低端,即文本段之前的低端地址空间的某一个位置开始,然后往下生长(这种情况下,栈的起始位置和文本段的起始位置中间会有一些空隙,主要是用来放置参数用的)。
而内核中,就比较随便了。首先内核中分配数据的可用页框区域,没有固定的起始地址。get_free_pages最初能获得哪个页框,完全取决于buddy系统的初始化,以及bootmem中内存分配的布局。这个和堆有点点不一样。 而内核的栈也很随便,完全可以get_free_pages获得一块连续的物理页框们,然后用它做内核栈——没有人会知道它是否溢出。除非在内核编译时加上栈溢出检查的配置,但是这样会频繁的检查是否栈溢出,速度慢得紧。
上面是我的主要的理解,可能具体的编码实现在linux中会有所差异。希望没有错误,呵呵。因为我理解有限,文字中的错误是经常和正常的。
--------------------
文章选项:
(newbie)07-02-07 16:14
Re: 请问内核的堆和栈的起始地址是在哪里设置的、有多大? []
Coly,非常感谢你,多谢指点,也不必这么谦虚 :) 你写的东西对我很有帮助,多谢。
上面你说了,内核的栈起始是在BSS段的开始,那么结束位置呢? 我们知道BSS段是内存的低地址,内核线程的栈是否也是降栈呢?(从上面某个位置到BSS段),如果是降栈的话,那么最上面的地址是怎么确定的?(也就是我想问的预分配的栈是多大?)。 NOTE. 当然,如果使用增栈的话,就不会存在我问的问题了(栈从BSS段向上,到哪里是未知的)。
另外,我说的内核的堆就是用Kmalloc管理的那块内存,它的范围是多少呢?
请各位大侠们赐教。
--------------
我这个问题的引出是这样的,在单板上传给内核参数的内存大小是40M(mem=40M),然后在系统起来以后,看到的内存总大小却是36.7M,那剩下的内存去哪里了?
从System.map中可以看出来,有3.16M是作为内存的代码段数据段BSS段,及初始化数据段等。 那么就是说剩下的就是内核的栈和堆了,它们是预先分配大小的话?如果是预先分配大小的那大小是多大?
还有另外一个问题,在System.map中可以看出,有一些初始化的代码与数据段,这些东西使用完后,会不会被重新利用起来呢?
上面算是我的问题的引论吧?
再次感谢Coly :)
--------------------开心工作,快乐生活
文章选项:
(newbie)07-02-07 16:34
Re: 请问内核的堆和栈的起始地址是在哪里设置的、有多大? []
补充: 上面所说的,看到的内存的总大小是指用cat /proc/meminfo中看出的。
--------------------开心工作,快乐生活
文章选项:
(addict)07-02-07 16:38
Re: 请问内核的堆和栈的起始地址是在哪里设置的、有多大? []
1,“上面你说了,内核的栈起始是在BSS段的开始,那么结束位置呢? ” 我没有说过这样的话,从BSS段开始的,就是BSS。应用程序在BSS后面的某一个位置开始是他的堆区域。堆区域的起始地址和BSS的结尾中间很可能会有一个空隙,这段区域中很可能是堆的管理数据。 2,我说过内核的栈的分配是比较随意的(我的理解),没有强制要求要在内核的某个地址空间位置上。确定栈的顶端很容易,譬如 ptr = (unsigned char *)get_free_pages(GFP_KERNEL, 0); stack_base = ptr + PAGE_SIZE; 就这样就可以确定内核栈顶端了。我在写自己的mlxos的时候,就是这么做的。Linux 2.4的idle线程似乎是一个预置的数组,但是思路都差不多。兄弟,别把你理解的我的话,说成是我说的话,在某些场合,我会被你害死的。 3,另外再说一次,由于内核自己不会通过brk来扩展自己的地址空间(在内核初始化的时候,内核的地址空间都和物理内存映射好了),所以在内核中没有应用程序中的堆的概念。所以我认为“我说的内核的堆就是用Kmalloc管理的那块内存,它的范围是多少呢? ”这个问题没有意义,因为内核的堆这个东西在我看来是一个不准确的概念。
关于最初问题的引出,我认为得看内核的内存初始化,以及显示数据的计算办法。我对嵌入式不熟悉,但是仅仅数字比数字是不行的。最好看看代码是如何根据你的40M得出36.7M的。
另外,兄弟我有一个建议,就是在做结论之前,最好能先验证一下自己的结论。不然这种风格真的会死人的,不是害死别人,就是把自己搞了。
希望我的语气没有严厉(我没有那个意思,只是看到你将自己的理解说成是我的话,后背有点凉)。其实我也是菜鸟,在论坛上跟大家互相请教的。一起进步,一起进步
--------------------
文章选项:
(addict)07-02-07 16:46
Re: 请问内核的堆和栈的起始地址是在哪里设置的、有多大? [<a href="http://www.linuxforum.net/forum/showthreaded.php?Cat=&Board=linuxK&Number=639136&page=89&view=collapse
【上篇】【下篇】分析Linux内核创建一个新进程的过程
攥写人:杨光 &学号:
(&*原创作品转载请注明出处*)
( 学习课程:《Linux内核分析》MOOC课程http://mooc.study.163.com/course/USTC-100
知识点及分析:
***操作系统内核三大功能:
进程管理,内存管理,文件系统
最核心的是进程管理
pid是系统区别进程的编号
***linux进程的状态和操作系统原理的描述进程状态有所不同,比如就绪状态和运行状态都是TASK_RUNNING。(这个表示它是可运行的,但是实际上有没有在运行取决于它是否占有CPU)
***Linux通过复制父进程来创建一个新进程
& & & fork函数,具体的过程:
复制一个PCB&&task_struct
要给新进程分配一个新的内核堆栈
ti = alloc_thread_info_node(tsk, node)
要修改复制过来的进程数据,比如pid、进程链表等,见copy_process内部。
*childregs = *current_pt_regs();
系统调用内核处理函数sys_fork,sys_vfrok,sys_clone,其实最终执行的都是do_fork
do_fork里有:
 &copy_process
      dup_task_struct 之后初始化子进程***创建的新进程是从哪里开始执行的--》ret_from_fork
*childregs = *current_pt_regs();
p-&thread.sp = (unsigned long)
ip指向的是ret_from_fork,所以是从这里开始执行的。
实验要求:
阅读理解task_struct数据结构;
分析fork函数对应的内核处理过程,理解创建一个新进程如何创建和修改task_struct数据结构;
使用gdb跟踪分析一个fork系统调用内核处理函数 ,验证您对Linux系统创建一个新进程的理解,推荐在环境下完成实验。
特别关注新进程是从哪里开始执行的?为什么从哪里能顺利执行下去?即执行起点与内核堆栈如何保证一致。
实验过程:
删除原来的menu,并clone新的menu,用test_fork.c覆盖test.c
&make rootfs之后新的内核启动,测试fork功能
&使用-s -S冷冻内核,准备调试
&加载符号表,配置端口
根据断点,进行跟踪,得到结果
阅读(...) 评论()博客访问: 79001
博文数量: 115
博客积分: 0
博客等级: 民兵
技术积分: 10
注册时间:
路漫漫其修远兮,吾将上下而求索!
分类: LINUX 10:01:27
原文地址: 作者:
& & 在重游《LDD3》的时候,又发现了一个当年被我忽略的一句话:“内核具有非常小的栈,它可能只和一个4096字节大小的页那样小”&针对这句话,我简单地学习了一下进程的“内核栈”
什么是进程的“内核栈”?& & 在每一个进程的生命周期中,必然会通过到系统调用陷入内核。在执行系统调用陷入内核之后,这些内核代码所使用的栈并不是原先用户空间中的栈,而是一个内核空间的栈,这个称作进程的“内核栈”。& & &比如,有一个简单的字符驱动实现了open方法。在这个驱动挂载后,应用程序对那个驱动所对应的设备节点执行open操作,这个应用程序的open其实就通过glib库调用了Linux的open系统调用,执行系统调用陷入内核后,处理器转换为了特权模式(具体的转换机制因构架而异,对于ARM来说普通模式和用户模式的的栈针(SP)是不同的寄存器),此时使用的栈指针就是内核栈指针,他指向内核为每个进程分配的内核栈空间。
内核栈的作用& & &我个人的理解是:在陷入内核后,系统调用中也是存在函数调用和自动变量,这些都需要栈支持。用户空间的栈显然不安全,需要内核栈的支持。此外,内核栈同时用于保存一些系统调用前的应用层信息(如用户空间栈指针、系统调用参数)。
内核栈与进程结构体的关联& & 每个进程在创建的时候都会得到一个内核栈空间,内核栈和进程的对应关系是通过2个结构体中的指针成员来完成的:
(1)struct task_struct& & 在学习Linux进程管理肯定要学的结构体,在内核中代表了一个进程,其中记录的进程的所有状态信息,定义在Sched.h (include\linux)。& & 其中有一个成员:void *就是指向下面的内核栈结构体的“栈底”。& & 在系统运行的时候,宏current获得的就是当前进程的struct task_struct结构体。
(2)内核栈结构体union thread_union
union thread_union {
&&&&struct thread_info thread_info;
&&&&unsigned long stack[THREAD_SIZE/sizeof(long)];
&其中struct thread_info是记录部分进程信息的结构体,其中包括了进程上下文信息:
&* low level task data that entry.S needs immediate access to.
&* __switch_to() assumes cpu_context follows immediately after cpu_domain.
struct thread_info {
&&&&unsigned long&&&&&&&&flags;&&&&&&&&/* low level flags */
&&&&int&&&&&&&&&&&&preempt_count;&&&&/* 0 => preemptable, <0 => bug */
&&&&mm_segment_t&&&&&&&&addr_limit;&&&&/* address limit */
&&&&struct task_struct&&&&*&&&&&&&&/* main task structure */
&&&&struct exec_domain&&&&*exec_domain;&&&&/* execution domain */
&&&&__u32&&&&&&&&&&&&cpu;&&&&&&&&/* cpu */
&&&&__u32&&&&&&&&&&&&cpu_domain;&&&&/* cpu domain */
&&&&struct cpu_context_save&&&&cpu_context;&&&&/* cpu context */
&&&&__u32&&&&&&&&&&&&syscall;&&&&/* syscall number */
&&&&__u8&&&&&&&&&&&&used_cp[16];&&&&/* thread used copro */
&&&&unsigned long&&&&&&&&tp_value;
&&&&struct crunch_state&&&&crunchstate;
&&&&union fp_state&&&&&&&&fpstate __attribute__((aligned(8)));
&&&&union vfp_state&&&&&&&&vfpstate;
#ifdef CONFIG_ARM_THUMBEE
&&&&unsigned long&&&&&&&&thumbee_state;&&&&/* ThumbEE Handler Base register */
&&&&struct restart_block&&&&restart_block;
& &关键是其中的task成员,指向的是所创建的进程的struct task_struct结构体
& & 而其中的stack成员就是内核栈。从这里可以看出内核栈空间和 thread_info是共用一块空间的。如果内核栈溢出, thread_info就会被摧毁,系统崩溃了~~~
内核栈---struct thread_info----struct task_struct三者的关系入下图:
&内核栈的产生& & 在进程被创建的时候,fork族的系统调用中会分别为内核栈和struct task_struct分配空间,调用过程是:fork族的系统调用--->do_fork--->copy_process--->dup_task_struct
在dup_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 =
&&&&err = prop_local_init_single(&tsk->dirties);
&&&&if (err)
&&&&&&&&goto out;
&&&&setup_thread_stack(tsk, orig);
其中alloc_task_struct使用内核的slab分配器去为所要创建的进程分配struct task_struct的空间
而alloc_thread_info使用内核的伙伴系统去为所要创建的进程分配内核栈(union thread_union )空间
后面的tsk->stack =语句,这就是关联了struct task_struct和内核栈
而在setup_thread_stack(tsk, orig);中,关联了内核栈和struct task_struct:
static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
&&&&*task_thread_info(p) = *task_thread_info(org);
&&&&task_thread_info(p)->task = p;
内核栈的大小& & 由于是每一个进程都分配一个内核栈空间,所以不可能分配很大。这个大小是构架相关的,一般以页为单位。其实也就是上面我们看到的THREAD_SIZE,这个值一般为4K或者8K。对于ARM构架,这个定义在Thread_info.h (arch\arm\include\asm),
#define THREAD_SIZE_ORDER&&&&1
#define THREAD_SIZE&&&& 8192
#define THREAD_START_SP&&&& (THREAD_SIZE - 8)
所以ARM的内核栈是8KB
在(内核)驱动编程时需要注意的问题:& & 由于栈空间的限制,在编写的驱动(特别是被系统调用使用的底层函数)中要注意避免对栈空间消耗较大的代码,比如递归算法、局部自动变量定义的大小等等
更多关于内核栈的资料请参考:
阅读(470) | 评论(0) | 转发(0) |
给主人留下些什么吧!~~
请登录后评论。怎么理解linux内核栈? - 知乎113被浏览<strong class="NumberBoard-itemValue" title="分享邀请回答3311 条评论分享收藏感谢收起42 条评论分享收藏感谢收起博客分类:
有两种方法:
第一种:pstack 进程ID
第二种,使用gdb 然后attach 进程ID,然后再使用命令 thread apply all bt
两种方法都可以列出进程所有的线程的当前的调用栈。
不过,使用gdb的方法,还可以查看某些信息,例如局部变量,指针等。
不过,如果只看调用栈的话,pstack还是很方便的。
CodeDestiny
浏览: 23983 次
来自: 成都
太神奇了,但个人认为会带来代码的阅读困难
(window.slotbydup=window.slotbydup || []).push({
id: '4773203',
container: s,
size: '200,200',
display: 'inlay-fix'

我要回帖

更多关于 linux 进程内核栈 的文章

 

随机推荐