基于2.6内核的Init_task进程之一

本文深入探讨了UL Linux Kernel中的进程管理机制,详细解析了进程描述符、线程描述符、内核态堆栈的数据结构,以及它们在进程生命周期中的作用。特别关注了thread_info结构与进程内核堆栈的关联,以及0号进程的创建过程。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

1. 环境

Kernel Version: 2.6.11
Architecture: i386

英文名称中文名称数据结构
Process Descriptor进程描述符struct task
-线程描述符struct thread_info
-进程内核态堆栈unsigned long stack
--union thread_union
函数名作用
CURRENT根据ESP得到struct task地址
current_thread_info()根据ESP得到struct thread_info地址

为了文章的名词的可读和唯一性,使用数据结构作为名词。

2. 概要

在ULK3中,英文原文:
Processes are dynamic entities whose lifetimes range from a few milliseconds to months. . Thus, kernel must be able to handle many processes at the same time, and process descriptors are stored in dynamic memory rather than rather than in the memory area permanently assigned to the kernel. For each process, Linux packs two different data structures in a single per-process memory area: a small data structure linked to the process descriptor, namely the thread_info structure, and the Kernel Mode process stack.

因为进程是动态分配的,所以内核(kernel)必须能够同时处理全部进程;因为进程是动态分配,则进程描述符(process descriptor)也应该动态存储。
对于每个进程,内核会为每个进程把两个数据结构封装在一起: thread_info和stack。

  • thread_info是和process descriptor相关联。
  • stack是对应进程内核态的堆栈。

主要讲:

  1. thread_info和stack
  2. 第0个进程的堆栈

弄清楚两件事:
4. 进程相关的堆栈知识
5. 内核idle进程的堆栈
6. 内核idle进程的创建

3. union thread_union

3.1 定义

在include/linux/sched.h中,thread_union定义如下:

 806 union thread_union {                                                                                                                                                                                                                              
 807         struct thread_info thread_info;                                         
 808         unsigned long stack[THREAD_SIZE/sizeof(long)];                          
 809 };

用联合体thread_union,大小是TREAD_SIZE个字节;(0-51)个字节用作thread_info和 (52-8191)个字节用于stack,在使用stack时候,应该不能踩踏到thread_info的成员值。

thread_union中的struct thread_info定义在文件include/asm-i386/thread_info中:

 28 struct thread_info {                                                                                                                                                                                                                                                      
 29         struct task_struct      *task;          /* main task structure */       
 30         struct exec_domain      *exec_domain;   /* execution domain */          
 31         unsigned long           flags;          /* low level flags */           
 32         unsigned long           status;         /* thread-synchronous flags */  
 33         __u32                   cpu;            /* current CPU */               
 34         __s32                   preempt_count; /* 0 => preemptable, <0 => BUG */
 35                                                                                                          
 36                                                                                                          
 37         mm_segment_t            addr_limit;     /* thread address space:        
 38                                                    0-0xBFFFFFFF for user-thead  
 39                                                    0-0xFFFFFFFF for kernel-thread
 40                                                 */                                                       
 41         struct restart_block    restart_block;                                                           
 42                                                                                                          
 43         unsigned long           previous_esp;   /* ESP of the previous stack in case
 44                                                    of nested (IRQ) stacks       
 45                                                 */                                                       
 46         __u8                    supervisor_stack[0];                                                     
 47 }; 

3.2 示意图

union thread_union和struct task之间的关系示意图如下:
123
Figure 1 Storing the thread_info struct and the process kernel stack in two page frames
说明:
1)esp指向内核堆栈的栈顶,堆栈生长的方向是从高地址到低地址。
2)进程间切换,esp赋值,后续研究。
3)研究某进程的esp值得到 struct thread_info地址和从某进程esp得到struct task地址?

3.3 current_thread_info

current_thread_info()是根据ESP得到struct thread_info地址。
从图1中,可以看出,只要把esp的低12位和0与上,就能得到structure thread_info地址,是由函数current_thread_info()函数完成的,等价于汇编语言:

movl $0xffffe000,%ecx /* or 0xfffff000 for 4KB stacks */
andl %esp,%ecx
movl %ecx,p

2.4 CURRENT

CURRENT是根据ESP得到struct task地址。

先看下thread_info结构体,在include/asm-i386/thread_info.h中

 28 struct thread_info {                                                            
 29         struct task_struct      *task;          /* main task structure */       
 30         struct exec_domain      *exec_domain;   /* execution domain */          
 31         unsigned long           flags;          /* low level flags */           
 32         unsigned long           status;         /* thread-synchronous flags */  
 33         __u32                   cpu;            /* current CPU */               
 34         __s32                   preempt_count; /* 0 => preemptable, <0 => BUG */
 35                                                                                 
 36                                                                                 
 37         mm_segment_t            addr_limit;     /* thread address space:        
 38                                                    0-0xBFFFFFFF for user-thead  
 39                                                    0-0xFFFFFFFF for kernel-thread
 40                                                 */                              
 41         struct restart_block    restart_block;                                  
 42                                                                                 
 43         unsigned long           previous_esp;   /* ESP of the previous stack in case
 44                                                    of nested (IRQ) stacks       
 45                                                 */                              
 46         __u8                    supervisor_stack[0];                            
 47 };  

可以看出和Process Descriptor相关联的task,在thread_info结构中的偏移量是0;通过current_thread_info()得到thread_info地址,然后在这个地址存储的值就是tasck成员的值,这样就能得到struct task地址。

内核使用CURRENT宏,等价于下面汇编:

movl $0xffffe000,%ecx /* or 0xfffff000 for 4KB stacks */
andl %esp,%ecx
movl (%ecx),p

说明:ecx是thread_info地址,ecx指向地址的存储的值也就是(%ecx)就是struct task地址。

3.内核0号进程

0号进程是静态分配,主要涉及到两方面:
1) 进程描述符分配
2) 内核堆栈分配
3) 进程描述符和内核堆栈关联
4) 把内核空间堆栈描述加载到内存
5) 把ESP指向对应的栈空间
6) 跳转到进程的代码段

3.1 进程描述符分配

第0号进程描述符变量init_task,在文件arch/i386/kernel/init_task.c中:

 37 struct task_struct init_task = INIT_TASK(init_task);                            
 38                                                                                 
 39 EXPORT_SYMBOL(init_task);  

宏INIT_TASK的定义在文件include/linux/init_task.h中:

 69 #define INIT_TASK(tsk)  \                                                       
 70 {                                                                       \       
 71         .state          = 0,                                            \       
 72         .thread_info    = &init_thread_info,                            \       
 73         .usage          = ATOMIC_INIT(2),                               \       
 74         .flags          = 0,                                            \       
 75         .lock_depth     = -1,                                           \       
 76         .prio           = MAX_PRIO-20,                                  \       
 77         .static_prio    = MAX_PRIO-20,                                  \       
 78         .policy         = SCHED_NORMAL,                                 \       
 79         .cpus_allowed   = CPU_MASK_ALL,                                 \       
 80         .mm             = NULL,                                         \      
 ......................................................................... 
101         .comm           = "swapper",                                    \

从comm字段看出,进程0叫swapper。
从thread_info字段看出,进程的内核空间堆栈指向init_thread_info。

3.2 内核空间堆栈创建

内核空间堆栈变量名叫init_thread_info,在文件include/asm-i386/thread_info.h定义。

83 #define init_thread_info        (init_thread_union.thread_info) 

变量init_thread_info是init_thread_union联合体成员threa_info。

init_thread_union变量的section “.data.init_task”。

 28 union thread_union init_thread_union                                            
 29         __attribute__((__section__(".data.init_task"))) =                       
 30                 { INIT_THREAD_INFO(init_task) }; 
objdump -h vmlinux

在这里插入图片描述
可以看出这个段大小是0x2000,是8K,也就是union thread_union大小是8K。

在System.map中可以看出:
在这里插入图片描述
这个.data.init_task在arch/i386/kernel/vmlinux.lds.S中

 53   _edata = .;                   /* End of data section */                       
 54                                                                                 
 55   . = ALIGN(THREAD_SIZE);       /* init_task */                                                                                                                                                                                                                           
 56   .data.init_task : { *(.data.init_task) }                                      
 57                                                    

init_thread_union变量被编译进.data.init_task的section,大小是THTEAD_SIZE=8192=8K。

3.3 关联

在thread_info中

 70 #define INIT_THREAD_INFO(tsk)                   \                               
 71 {                                               \                               
 72         .task           = &tsk,                 \                               
 73         .exec_domain    = &default_exec_domain, \                               
 74         .flags          = 0,                    \                               
 75         .cpu            = 0,                    \                               
 76         .preempt_count  = 1,                    \                               
 77         .addr_limit     = KERNEL_DS,            \                               
 78         .restart_block = {                      \                                                                                                                                                                                                                         
 79                 .fn = do_no_restart_syscall,    \                               
 80         },                                      \                               
 81 } 

第72行可以,看出struct thread_info中的task指向进程描述符。

宏INIT_TASK的定义在文件include/linux/init_task.h中:

 69 #define INIT_TASK(tsk)  \                                                       
 70 {                                                                       \       
 71         .state          = 0,                                            \       
 72         .thread_info    = &init_thread_info,                            \       
101         .comm           = "swapper",                                    \

可以看出,进程描述符struct task的thread_info字段指向init_thread_info字段。

实现了进程堆栈描述符和进程描述符的关联。

3.4 内核堆栈加载与跳转

.data.init_task的section已经被编译进vmlinux中.
在arch/i386/kernel/head.S

425 ENTRY(stack_start)                                                                                                                             
426         .long init_thread_union+THREAD_SIZE                                     
427         .long __BOOT_DS 

193         /* Set up the stack pointer */                                          
194         lss stack_start,%esp   

326 #endif /* CONFIG_SMP */                                                         
327         call start_kernel

stack_start是init_thread_union+THREAD_SIZE,然后赋值给esp,如下面示意图,栈是空的,ESP处于栈顶位置。
在这里插入图片描述

执行进程代码段

在arch/i386/kernel/head.S中

326 #endif /* CONFIG_SMP */                                                         
327         call start_kernel

0号进程,就是从head.S的汇编代码到start_kernel。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值