本文共 11626 字,大约阅读时间需要 38 分钟。
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地址 |
为了文章的名词的可读和唯一性,使用数据结构作为名词。
在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。主要讲:
弄清楚两件事:
4. 进程相关的堆栈知识 5. 内核idle进程的堆栈 6. 内核idle进程的创建在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 };
union thread_union和struct task之间的关系示意图如下:
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,%ecxmovl %ecx,p
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,%ecxmovl (%ecx),p
说明:ecx是thread_info地址,ecx指向地址的存储的值也就是(%ecx)就是struct task地址。
0号进程是静态分配,主要涉及到两方面:
1) 进程描述符分配 2) 内核堆栈分配 3) 进程描述符和内核堆栈关联 4) 把内核空间堆栈描述加载到内存 5) 把ESP指向对应的栈空间 6) 跳转到进程的代码段第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。内核空间堆栈变量名叫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
在System.map中可以看出:
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。
在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字段。
实现了进程堆栈描述符和进程描述符的关联。
.data.init_task的section已经被编译进vmlinux中.
在arch/i386/kernel/head.S425 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。
转载地址:http://jafab.baihongyu.com/