进程是操作系统调度的实体,对进程所拥有的资源的描述称为进程控制块(PCB,ProcessControlBlock)。在task_struct描述的进程内核中,一个进程由task_struct结构描述,称为进程描述符(processdescriptor),它存储了支持一个进程正常运行的所有信息。task_struct结构体内容太多,这里只列出部分成员变量。有兴趣的读者可以去源码中的include/linux/sched.h头文件查看。structtask_struct{#ifdefCONFIG_THREAD_INFO_IN_TASK/**对于headersup(seecurrent_thread_info())的原因,这个*必须是task_struct的第一个元素。*/structthread_infothread_info;#endifvolatilelongstate;void*stack;......structmm_struct_t*mm;pid;…...structtask_struct*parent;...charcomm[TASK_COMM_LEN];...structfiles_struct*files;...structsignal_struct*signal;}task_struct中的主要信息类别:1.Identifier:描述本进程的唯一标识符pid,用于区分其他进程。2、状态:任务状态、退出代码、退出信号等3、优先级:相对于其他进程的优先级4、程序计数器:程序中下一条要执行的指令的地址5、内存指针:包括程序代码和指向进程相关数据的指针,以及指向与其他进程共享的内存块的指针6.上下文数据:进程执行时处理器寄存器中的数据7.I/O状态信息:包括显示的I/O请求,allocated进程使用的进程I/O设备和文件列表8.Accountinginformation:可能包括processortime的总和,使用的clocks的总和,时间限制,帐号等structthread_infothread_info:关于进程的信息scheduledforexecutionvolatilelongstate:-1未运行,=0为运行状态,>0为停止状态。下面是几个重要的进程状态以及它们之间的转换过程。void*stack:指向内核栈的指针,内核通过dup_task_struct为每个进程分配内核栈空间,并记录在这里。structmm_struct*mm:与进程地址空间相关的信息。pid_tpid:进程标识符charcomm[TASK_COMM_LEN]:进程名structfiles_struct*files:打开文件表structsignal_struct*signal:信号处理相关的task_struct,thread_info和内核栈sp之间的关系再看thread_info结构体:structthread_info{unsignedlongflags;/*lowlevelflags*/mm_segment_taddr_limit;/*addresslimit*/#ifdefCONFIG_ARM64_SW_TTBR0_PANu64ttbr0;/*savedTTBR0_EL1*/#endifunion{u64preempt_count;/*0=>可抢占,<0=>bug*/struct{#ifdefCONFIG_CPU_BIG_ENDIANuched32;需要32;需要u32need_resched;#endif}preempt;};#ifdefCONFIG_SHADOW_CALL_STACKvoid*scs_base;void*scs_sp;#endif};接着再来看下内核栈的定义:unionthread_union{#ifndefCONFIG_ARCH_TASK_STRUCT_ON_STACKstructtask_structtask;#endif#ifndefCONFIG_THREAD_INFO_IN_TASKstructthread_infothread_info;#endifunsignedlongstack[THREAD_SIZE/sizeof(long)];};当启用CONFIG_THREAD_INFO_IN_TASK配置时,thread_union结构中只存在任务成员。内核启动时会通过head.S中的__primary_switched初始化内核栈:SYM_FUNC_START_LOCAL(__primary_switched)adrpx4,init_thread_unionaddsp,x4,#THREAD_SIZEadr_lx5,init_taskmsrsp_el0,x5//保存thread_info将init_thread_union的地址保存到x4ShiftTHREAD_SIZE栈大小,用于初始化sp。将init_task进程描述符的地址赋值给x5,保存到sp_el0。下面再看下init_thread_union和init_task的定义:#include/linux/sched/task.hexternunionthread_unioninit_thread_union;#init/init_task.cstructtask_structinit_task__aligned(L1_CACHE_BYTES_INTASKINFIG)={#ifREADSKINDEFCONFIG.thread_info=INIT_THREAD_INFO(init_task),.stack_refcount=REFCOUNT_INIT(1),#endif....};因此,这三者之间的关系可以用下图来描述:当前进程的内核中如何通过current宏获取当前进程对应的structtask_sturct结构体,我们使用current,结合上面介绍的内容,看具体实现。static__always_inlinestructtask_struct*get_current(void){unsignedlongsp_el0;asm("mrs%0,sp_el0":"=r"(sp_el0));return(structtask_struct*)sp_el0;}#definecurrentget_current()代码比较简单,可以看出通过读取用户空间栈指针寄存器sp_el0的值,然后将这个值转换成一个task_struct结构来获取当前进程。(init_task存放在sp_el0,是thread_info的地址,thread_info在task_sturct的开头,以便找到当前进程。)
