1. 背景 引入jump label的背景,参考Linux: Jump label实现简析 在内核代码中,有很多分支判断条件,它们在绝大多数情形下,都是不成立的。尽管我们已经加上unlikely修饰来进行优化,但是,读取 condition 仍然要访问内存,仍然需要用到cache;另外,也会CPU分支预测失败。虽然少数这样的代码影响不大,但当这样的条件判断代码(如内核中大量的tracepoint)增多的时候,将对cache会造成很大压力,所有这些代码导致的cache miss,以及CPU分支预测失败,所造成的性能损失,就变得可观起来。因此,内核需要一种方案,来解决这样的问题。这个解决方案,就是本文描述的 Jump label。...
使用drm mm管理虚拟内存和物理内存
深度分析系统调用从用户程序到内核的流程
1. 简介 ARM64中断入口相关的代码主要在arch/arm64/kernel/entry.S,启动过程中,会将中断向量表vectors的起始虚拟地址写入到VBAR_EL1。 当发生中断或者异常时,硬件会保存一些寄存器,然后就是软件的工作: 进入中断后,根据中断原因,跳转到对应的中断处理函数,这部分是汇编实现。 在中断处理函数中,先通过kernel_entry将寄存器压栈,然后将栈空间记录的struct pt_regs传递给C语言的对应中断处理函数。 完成中断处理后,根据中断发生的异常等级,决定是调用ret_to_user还是ret_to_kernel退出中断,这两个hasn’t都是对汇编代码kernel_exit的封装。...
1. kernel_clone kernel_clone是内核创建进程/线程的核心函数,如下功能都是通过调用kernel_clone实现的。 kernel_thread:创建内核进程 user_mode_thread:创建1号进程,1号进程的回调函数先是内核态的kernel_init,之后通过execve切换到用户进程 fork和clone系统调用:创建用户进程/线程 kernel_clone主要流程是copy_process函数实现task_struct结构的创建和初始化,而后通过wake_up_new_task将新进程/线程加入运行队列,并唤醒。详细流程见源码注释。 1/// kernel/sched/core.c 2/* 3...
1. memblock 1.1. 结构体 memblock相关的全局变量是编译时静态初始化的。 1/// mm/memblock.c 2 3/// INIT_MEMBLOCK_MEMORY_REGIONS和INIT_MEMBLOCK_RESERVED_REGIONS见arch/arm64/include/asm/memory.h 4static struct memblock_region memblock_memory_init_regions[INIT_MEMBLOCK_MEMORY_REGIONS] __initdata_memblock; 5static struct memblock_region...
1. 基本概念 Linux内核中没有使用单独的数据结构来描述进程和线程,而是将它们统一起来,使用task_struct结构体来描述,这就是“Linux不区分进程和线程”的来源。 内核态: 所有线程都由kthreadd创建,运行在内核态,由独立的task_sturct描述。内核中不区分进程和线程,为了方便区分,这里统称为内核线程 用户态: 单线程进程:仅有一个线程,也就是主线程,内核中由一个task_struct描述,进程id就是task_struct.pid。 多线程进程:包含多个线程,每个线程都有自己的task_struct,有几个线程(包含主线程)就会有几个task_struct,所有task_struct...
1. 总览 这里以ARM64为例。 在内核的起始阶段,还没有进程和线程的概念,在开启MMU之后,__primary_switched的第一步就是将init_task的地址写到sp_el0,这个时候就可以用get_current()或者curent获取到0号线程的task_struct了。在0号线程的上下文,完成调度器的相关初始化之后,创建1号和2号线程,然后开启调度器,init_task自此进入idle状态。 这里看几个概念: 0号线程:对应init_task结构体,名字为swapper,在多处理器中,名称为swapper/<id>,负责内核的一些基础的初始化工作。 1号线程:由0号线程创建,最初的函数为kernel_init,运...
1. 1号进程的内核态 1号进程首先运行在内核态,其函数是kernel_init,这里简单介绍一下其工作: 非bootcpu相关初始化 页分配器初始化的收尾工作 驱动子系统和设备树初始化 调用__initcall指定的初始化函数 挂载根文件系统 是否__init指定的内存 执行用户态init程序 2. 1号进程内核态到用户态的切换 kernel_init最后通过kernel_execve函数执行用户程序,完成内核态到用户态的切换。 这里以elf格式的init程序为例简单说明一下kernel_execve的工作流程:kernel_execve会申请新的mm_struct,记录init程序的代码段和数据段,将当前进程内核栈中记录的pc...
1. 2号线程kthreadd Linux内核规定,所有的线程必须由已存在的线程创建出来,也就是要求所有的task_struct都需要在已有的task_struct上复制出来。Linux可以通过kernel_thread来创建内核线程,这个函数会复制当前线程的task_struct。如果任由各个子系统或驱动自己调用kernel_thread来创建线程,那在创建内核线程时current所指向的task_struct是不确定的。为了解决这一问题,Linux内核将所有内核线程的创建交给固定的线程来做,这个线程就是2号线程kthreadd。 2. 内核线程创建的接口 内核为开发者提供了两个创建内核线程的宏kthread_create...