进程内核栈

进程内核栈


为什么有进程内核栈

  进程在创建的时候也可以理解为一个程序,或者在简单的理解也可以把进程理解为一个函数,只不过这个函数很大而已,这个进程也需要有一些函数调用,也需要有一些函数去标记一些信息,于是 便有了进程内核栈这个东西。

  简单理解,进程内核栈实际上就是为进程开辟一个栈帧空间。
  但是这个栈帧空间不是用户的栈帧空间,因为用户的栈帧空间时不安全的,所以内核会专门为它开辟一个空间,这个就是内核栈。

进程内核栈和进程描述符的关系

  • 进程描述符

  进程描述符就是进程结构体(struct task_struct),每一个进程在创建之后都会有一个进程结构体,用来记录进程的所有信息。这其中的有一个信息就是就是进程内核栈,用一个指针指示。void *stack;就是指向下面的内核栈结构体的“栈底”。

  • 内核栈结构体

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

其中的stack成员就是内核栈。

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

从这里可以看出内核栈空间和 thread_info是共用一块空间的。如果内核栈溢出, 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    *task;        /* 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 */
    #endif
        struct restart_block    restart_block;
    };

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

图文表示三者关系

内核栈—内核栈结构体(struct thread_info)—-进程描述符(struct task_struct)三者的关系入下图

这里写图片描述

内核栈的产生

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

### Linux 进程内核结构与使用 在 Linux 内核中,进程内核是为每个进程分配的一块内存区域,主要用于保存进程从用户态切换到内核态时的相关信息。内核的设计非常紧凑,通常大小固定为两页(在大多数架构上为 8KB),以减少内存消耗并提高效率[^2]。 #### 内核的基本结构 内核的布局是固定的,其主要用途是在中断或系统调用发生时保存上下文信息。以下是一些关键点: - **空间限制**:由于内核的空间有限,因此必须谨慎使用。如果溢出,可能会导致内核崩溃。 - **帧**:每次进入内核时,都会创建一个新的帧,用于保存寄存器值、函数参数和局部变量等信息。这些帧在进程返回用户态时会被清理。 - **初始化**:当创建一个新进程时,内核会为其分配内核,并初始化相关数据结构。例如,在 `kernel_thread` 函数中,通过设置 `struct pt_regs` 来初始化内核[^4]。 #### 内核的使用方法 1. **用户态到内核态的切换** 当进程从用户态切换到内核态时,内核会被激活,并保存当前的上下文信息,包括寄存器值和堆指针。这个过程由硬件自动完成,随后内核代码可以访问这些保存的信息[^3]。 2. **内核地址的获取** 内核的地址可以通过 `thread_info` 结构体找到。`thread_info` 是一个存储在进程底部的小型结构,其中包含指向 `task_struct` 的指针,从而能够方便地访问进程的其他信息[^5]。 3. **顶地址的设置** 在进程进入内核态时,内核顶地址会被设置为当前的堆指针寄存器值。这样,内核可以直接使用这块内存来保存临时数据。 #### 示例代码 以下是一个简单的示例,展示如何通过 `thread_info` 获取当前进程内核地址: ```c #include <linux/thread_info.h> struct thread_info *get_current_thread_info(void) { return (struct thread_info *)(current_stack_pointer & ~(THREAD_SIZE - 1)); } void print_kernel_stack_address(void) { struct thread_info *ti = get_current_thread_info(); pr_info("Kernel stack address: %p\n", ti); } ``` 上述代码中,`get_current_thread_info` 函数通过掩码操作计算出当前线程的 `thread_info` 地址,而 `print_kernel_stack_address` 则打印内核的起始地址[^5]。 ### 注意事项 - 内核的大小有限,因此应避免在内核上分配过大的局部数组或递归调用。 - 如果需要更大的空间,可以使用动态分配的方式(如 `kmalloc`)来代替分配。
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值