博客
关于我
强烈建议你试试无所不能的chatGPT,快点击我
基于2.6内核的Init_task进程之一
阅读量:2383 次
发布时间:2019-05-10

本文共 11626 字,大约阅读时间需要 38 分钟。

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,%ecxmovl %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,%ecxmovl (%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。

转载地址:http://jafab.baihongyu.com/

你可能感兴趣的文章
centos7 硬盘性能测试
查看>>
cgroup使用--cpu资源限制
查看>>
cgroup使用--memory资源限制
查看>>
Redis 单机环境搭建
查看>>
elasticsearch 单机环境搭建
查看>>
spark 独立模式部署
查看>>
Redis 基础命令 --- String篇
查看>>
Redis 基础命令 --- Hash篇
查看>>
Redis 基础命令 --- List篇
查看>>
Redis 基础命令 --- Set篇
查看>>
Redis数据库篇 -- 生存时间
查看>>
面向对象设计基本原则
查看>>
Redis数据库篇 -- 事务
查看>>
hadoop 完全分布式环境搭建
查看>>
HDFS 回收站
查看>>
hadoop 完全分布式HA高可用集群(手工切换)搭建
查看>>
hadoop 完全分布式HA高可用集群(自动切换)搭建
查看>>
Hbase shell常见命令
查看>>
看看这同一句sql,scan index占用的资源大了很多!!
查看>>
couldn't set locale correctly报错解决
查看>>