0

0

对Linux的进程内核栈的认识

PHPz

PHPz

发布时间:2024-02-10 18:09:41

|

1440人浏览过

|

来源于良许Linux教程网

转载

当我重新阅读《ldd3》时,我发现了一个曾经忽略的句子:“内核具有非常小的栈,它可能只有像一个4096字节大小的页那样小。” 针对这句话,我简单地学习了一下进程的“内核栈”。

进程的“内核栈”是什么? 在每个进程的生命周期中,必然会通过系统调用陷入内核。在执行系统调用陷入内核后,这些内核代码所使用的栈并不是原先用户空间中的栈,而是一个内核空间的栈,也就是进程的“内核栈”。

例如,一个简单的字符驱动程序实现了open()方法。在这个驱动程序挂载后,应用程序通过glib库调用Linux的open()系统调用。当执行系统调用陷入内核后,处理器会转换为特权模式(具体的转换机制因处理器架构而异。例如,对于ARM,普通模式和用户模式的栈指针(SP)是不同的寄存器)。此时,使用的栈指针就是内核栈指针,它指向内核为每个进程分配的内核栈空间。

内核栈的作用 个人理解是:在陷入内核后,系统调用中也存在函数调用和自动变量,这些都需要栈支持。用户空间的栈显然不安全,因此需要内核栈的支持。此外,内核栈还用于保存一些系统调用前的应用层信息(例如,用户空间栈指针和系统调用参数)。

内核栈与进程结构体的关联 每个进程在创建时都会得到一个内核栈空间。内核栈和进程的对应关系是通过两个结构体中的指针成员来完成的: (1)struct task_struct 在学习Linux进程管理时,必须学习的结构体。它在内核中代表了一个进程,并记录了进程的所有状态信息。该结构体定义在Sched.h(include\linux)中。其中有一个成员void *stack,它指向下面的内核栈结构体的“栈底”。 在系统运行时,宏current获得的是当前进程的struct task_struct结构体。

(2)内核栈结构体union thread_union

1. union thread_union {
2.   struct thread_info thread_info;
3.   unsigned long stack[THREAD_SIZE/sizeof(long)];
4. };

其中struct thread_info是记录部分进程信息的结构体,其中包括了进程上下文信息:

1. /*
2.  \* low level task data that entry.S needs immediate access to.
3.  \* __switch_to() assumes cpu_context follows immediately after cpu_domain.
4.  */
5. struct thread_info {
6.   unsigned long    flags;    /* low level flags */
7.   int      preempt_count;  /* 0 => preemptable,  bug */
8.   mm_segment_t    addr_limit;  /* address limit */
9.   struct task_struct  *task;    /* main task structure */
10.   struct exec_domain  *exec_domain;  /* execution domain */
11.   __u32      cpu;    /* cpu */
12.   __u32      cpu_domain;  /* cpu domain */
13.   struct cpu_context_save  cpu_context;  /* cpu context */
14.   __u32      syscall;  /* syscall number */
15.   __u8      used_cp[16];  /* thread used copro */
16.   unsigned long    tp_value;
17.   struct crunch_state  crunchstate;
18.   union fp_state    fpstate __attribute__((aligned(8)));
19.   union vfp_state    vfpstate;
20. \#ifdef CONFIG_ARM_THUMBEE
21.   unsigned long    thumbee_state;  /* ThumbEE Handler Base register */
22. \#endif
23.   struct restart_block  restart_block;
24. };

关键是其中的task成员,指向的是所创建的进程的struct task_struct结构体

佳蓝在线销售系统(创业版) 佳蓝在线销售
佳蓝在线销售系统(创业版) 佳蓝在线销售

1、对ASP内核代码进行DLL封装,从而大大提高了用户的访问速度和安全性;2、采用后台生成HTML网页的格式,使程序访问速度得到进一步的提升;3、用户可发展下级会员并在下级购买商品时获得差额利润;4、全新模板选择功能;5、后台增加磁盘绑定功能;6、后台增加库存查询功能;7、后台增加财务统计功能;8、后台面值类型批量设定;9、后台财务曲线报表显示;10、完善订单功能;11、对所有传输的字符串进行安全

下载

而其中的stack成员就是内核栈。从这里可以看出内核栈空间和 thread_info是共用一块空间的。如果内核栈溢出, thread_info就会被摧毁,系统崩溃了~~~

内核栈—struct thread_info—-struct task_struct三者的关系入下图:

对Linux的进程内核栈的认识

内核栈的产生
在进程被创建的时候,fork族的系统调用中会分别为内核栈和struct task_struct分配空间,调用过程是:

fork族的系统调用—>do_fork—>copy_process—>dup_task_struct

在dup_task_struct函数中:

1. static struct task_struct *dup_task_struct(struct task_struct *orig)
2. {
3.   struct task_struct *tsk;
4.   struct thread_info *ti;
5.   unsigned long *stackend;
6.  
7.   int err;
8.  
9.   prepare_to_copy(orig);
10.  
11.   **tsk = alloc_task_struct();**
12.   if (!tsk)
13. ​    return NULL;
14.  
15.   **ti = alloc_thread_info(tsk);**
16.   if (!ti) {
17. ​    free_task_struct(tsk);
18. ​    return NULL;
19.   }
20.  
21.    err = arch_dup_task_struct(tsk, orig);
22.   if (err)
23. ​    goto out;
24.  
25.   **tsk->stack = ti;**
26.  
27.   err = prop_local_init_single(&tsk->dirties);
28.   if (err)
29. ​    goto out;
30.  
31.   **setup_thread_stack(tsk, orig);**
32. ......

 

其中alloc_task_struct使用内核的slab分配器去为所要创建的进程分配struct task_struct的空间
而alloc_thread_info使用内核的伙伴系统去为所要创建的进程分配内核栈(union thread_union )空间

注意:
后面的tsk->stack = ti;语句,这就是关联了struct task_struct和内核栈
而在setup_thread_stack(tsk, orig);中,关联了内核栈和struct task_struct:

1. static inline void setup_thread_stack(struct task_struct *p, struct task_struct *org)
2. {
3.   *task_thread_info(p) = *task_thread_info(org);
4.   task_thread_info(p)->task = p;
5. }

内核栈的大小
由于是每一个进程都分配一个内核栈空间,所以不可能分配很大。这个大小是构架相关的,一般以页为单位。其实也就是上面我们看到的THREAD_SIZE,这个值一般为4K或者8K。对于ARM构架,这个定义在Thread_info.h (arch\arm\include\asm),

1. \#define THREAD_SIZE_ORDER  1
2. \#define THREAD_SIZE   8192
3. \#define THREAD_START_SP   (THREAD_SIZE - 8)

所以ARM的内核栈是8KB

在(内核)驱动编程时需要注意的问题:
由于栈空间的限制,在编写的驱动(特别是被系统调用使用的底层函数)中要注意避免对栈空间消耗较大的代码,比如递归算法、局部自动变量定义的大小等等

相关专题

更多
golang结构体相关大全
golang结构体相关大全

本专题整合了golang结构体相关大全,想了解更多内容,请阅读专题下面的文章。

194

2025.06.09

golang结构体方法
golang结构体方法

本专题整合了golang结构体相关内容,请阅读专题下面的文章了解更多。

186

2025.07.04

c语言union的用法
c语言union的用法

c语言union的用法是一种特殊的数据类型,它允许在相同的内存位置存储不同的数据类型,union的使用可以帮助我们节省内存空间,并且可以方便地在不同的数据类型之间进行转换。使用union时需要注意对应的成员是有效的,并且只能同时访问一个成员。本专题为大家提供union相关的文章、下载、课程内容,供大家免费下载体验。

122

2023.09.27

javascriptvoid(o)怎么解决
javascriptvoid(o)怎么解决

javascriptvoid(o)的解决办法:1、检查语法错误;2、确保正确的执行环境;3、检查其他代码的冲突;4、使用事件委托;5、使用其他绑定方式;6、检查外部资源等等。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

173

2023.11.23

java中void的含义
java中void的含义

本专题整合了Java中void的相关内容,阅读专题下面的文章了解更多详细内容。

93

2025.11.27

堆和栈的区别
堆和栈的区别

堆和栈的区别:1、内存分配方式不同;2、大小不同;3、数据访问方式不同;4、数据的生命周期。本专题为大家提供堆和栈的区别的相关的文章、下载、课程内容,供大家免费下载体验。

373

2023.07.18

堆和栈区别
堆和栈区别

堆(Heap)和栈(Stack)是计算机中两种常见的内存分配机制。它们在内存管理的方式、分配方式以及使用场景上有很大的区别。本文将详细介绍堆和栈的特点、区别以及各自的使用场景。php中文网给大家带来了相关的教程以及文章欢迎大家前来学习阅读。

564

2023.08.10

页面置换算法
页面置换算法

页面置换算法是操作系统中用来决定在内存中哪些页面应该被换出以便为新的页面提供空间的算法。本专题为大家提供页面置换算法的相关文章,大家可以免费体验。

389

2023.08.14

php源码安装教程大全
php源码安装教程大全

本专题整合了php源码安装教程,阅读专题下面的文章了解更多详细内容。

74

2025.12.31

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
PostgreSQL 教程
PostgreSQL 教程

共48课时 | 6.4万人学习

Git 教程
Git 教程

共21课时 | 2.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号 技术交流群
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn | 湘ICP备2023035733号