如何进入内核态

进入内核态的方式

中断,包括软中断和硬中断,其中软中断包括系统调用(int 80)和各种异常(segmentfatel 信号11),硬中断包括网卡收包,usb插入等等。

内核栈

  • 每个进程独有一个内核栈
  • 各cpu架构有不同的异常栈,中断栈,有的是使用的是被打断的程序的内核栈,有的有独有的空间

内核栈结构体

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

其中thread_info保存在进程的重要信息,最重要的是指针task,其中包含了进程的所有信息。
该结构体的大小就表示了整个内核栈的大小,即THREAD_SIZE,一般都是4k或者8k。所以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;
};

在这里插入图片描述

内核栈的产生

static struct task_struct *dup_task_struct(struct task_struct *orig)
{
    struct task_struct *tsk;
    struct thread_info *ti;
    unsigned long *stackend;

    int err;

    prepare_to_copy(orig);

    tsk = alloc_task_struct();
    if (!tsk)
        return NULL;

    ti = alloc_thread_info(tsk);
    if (!ti) {
        free_task_struct(tsk);
        return NULL;
    }

     err = arch_dup_task_struct(tsk, orig);
    if (err)
        goto out;

    tsk->stack = ti;

    err = prop_local_init_single(&tsk->dirties);
    if (err)
        goto out;

    setup_thread_stack(tsk, orig);
......
  • alloc_task_struct申请task结构体
  • alloc_thread_info申请thread_info结构体
  • tsk->stack = ti; task关联thread_info
  • setup_thread_stack(tsk, orig);thread_info关联task

通过系统调用分析如何进入内核态

  • 应用程序调用系统调用,最终都是syscall,syscall实际上是发出一个int 80的软中断,并给出系统调用号
  • 内核响应80中断,调入syscall中断处理函数,
  • 然后调用GET_THREAD_INFO(%ebp),获取thread_info结构体,就拿到了内核栈空间
  • 此时已经进入内核栈,保存应用程序栈空间,返回时恢复使用
  • 后续根据系统调用号进入不同的处理函数进行处理

研究该问题的原因

文末再描述下研究内核态的原因,原因是想在应用程序级别实现锁范围内的代码执行时不会发生线程切换,于是找到了自旋锁spinlock,spinlock陷入内核态上锁时的确是防止软中断的,即不会发生线程切换,因为线程切换就是软中断,但是上完锁回到用户态空间时,用户态的特权级是3,并没有关闭软中断,做不到锁内代码不切换线程。


探花原创


版权声明:本文为weixin_45490038原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接和本声明。