在linux内核中,任务和进程是同一概念,进程的四要素总结如下:
(1)有一段程序可执行,不一定被某进程所专有,可与其他进程共有;
(2)进程有专有的系统堆栈空间;
(3)进程控制块即task_struct结构体,登记在内核中,使得进程可成为一个基本单位接受内核调度;同时它还记录着进程所占有的各项资源;
(4)除上述的专有的系统堆栈空间,还要有独立的存储空间,意味着有专有的用户空间;注意,进程只能改变自身的系统堆栈空间,是不能改变系统空间(不独立);如用于虚存管理的mm_struct以及它下属的vm_area,页目录项和页面表,它们从属于task_struct的资源;
如果缺失了条件4,那么就是线程;完全没有用户空间,称为内核线程;共享用户空间,称为用户线程;请不要与有些系统(比如JVM)在用户空间的同一进程实现了的线程的相混淆;linux内核已经提供了对线程的支持;linux并不严格区分线程和进程的概念;Linux系统运行初期时的第一个进程是初始化阶段创造出来的,后面的进程或线程都是通过已存在的进程创建出来的;比如fork和clone;
Intel在i386系统结构设计中考虑到了进程的管理和调度,并从硬件上支持任务间的调度,就是“任务状态段”TSS,它用来记入一个任务的关键的状态信息(104字节),包括:
(1)任务切换前夕的各通用寄存器的内容以及段寄存器(ES,CS,SS,DS,FS,ES)的内容,EFLAGS,地址寄存器EIP的内容;
(2)指向前一个任务的TSS结构的段选择码,当前任务执行IRET指令时,就返回到这个段选择器所指的任务;
(3)LDT段选择码,CR3任务的页面目录,0,1,2,三级的堆栈指针,SS0,SS1,SS2,ESP0,ESP1,ESP2;
(4)程序跟踪的Debug标志位T;
(5)其他的附加细腻系,包括非内核和非用户空间能够执行的外设驱动程序,即I/O权限的位图;vm86模式下的中断重定向位图;
CPU还增设了一个任务寄存器TR,指向当前任务的TSS;相应的增加了一条指令LTR,对TR寄存器进行装入操作;在IDT表中,除以前的中断门,陷阱门,调用门外,还定义了一种任务门;任务门中包含一个TSS段选择码,当CPU因中断而穿过一个任务门时,会将任务门中的段选择码自动装入TR,使TR指向新的TSS,并完成任务的切换;CPU还可通过JMP或CALL来通过调用门将目标段实际指向GDT表中的一个TSS描述项,就会引起一次任务切换;
然而Linux实际上是不采用i386CPU硬件提供的任务切换机制;但是要设置TR和TSS(走过场),linux内核也不使用任务门,也不实用JMP和call内容;内核只在初始化阶段设置TR,使它指向一个TSS(全局使用),从此以后不改变TR的内容,即每一个CPU在初始化以后的全部运行过程中永远各自使用同一个TSS;同时内核切换也不依靠TSS保存每一个进程切换时的寄存器副本,而是将这些寄存器的副本保存到各个进程自己的系统堆栈中(用这个来跟新TSS中的值,而Intel的意图是有多个轮流的TSS);结构体数组init_tss[NR_CPUS],是系统中CPU的个数,每一个TSS的内容都是相同的;
(1)每一个进程都有要素(2),(3)即系统空间堆栈和一个task_struct;他们紧密联系,在物理空间也是连在一起的,分配两个连续的物理页面共8KB,其中task_struct为1KB左右,系统空间堆栈是7KB,不能动态扩展,因此不能使用太多和太大的局部变量;THREAD_SIZE定义为2个页面,表示每一个内核线程这两项基本资源所占的物理存储空间的大小,通过alloc_task_struct()(请求了两个页的数据);
(2)在get_current()中通过当前指针寄存器ESP和0xfffffe00与就可得到当前进程的task_struct的起始地址(and需4个CPU时钟),这个效率很高,比放在全局变量里要高(先and然后还要mov总共需6个CPU时钟);其实宏操作GET_CURRENT的汇编实现也是一样的;
(3)在task_struct结构体中,state表示进程当前的运行状态,可取值TASK_RUNNING(表示一个进程已就绪,但不一定在执行),TASK_INTERRUPTABLE(可被信号打断,浅度睡眠,interrupt_sleep_on()和interrupt_wake_up())以及TASK_UNINTERRUPTABLE(深度睡眠,不受信号打断,使用sleep_on()和wake_up()),其中深度睡眠一般用于临界区和关键性的部位,而可中断的睡眠就是通用的了,可用于阻塞性系统调用中,如按键等待时,长时间不按时,其他进程可通过信号将它杀时,TASK_ZOMBIE(进程已死,但户口未被注销),TASK_STOPPED(调试使用的,可挂起,收到SIGCONT信号又可恢复继续执行);
(4)flags反映进程的状态信息,但不是运行状态的,如警告信息,刚被创建,正在关机,fork但没exec,使用了超级权限,dumped core,被一个信号杀死了,正在分配内存,被父亲在mm_release中wake up;除了state,flags还有收到信号但尚未处理的sigpending,调度相关counter,调度相关need_sched,虚存空间上限add_limit,个性Personality(各个不同类UNIX操作系统下,应用范围的使用范围);应用程序版本差异exe_domian;应用程序文件格式binfmt,进程号pid,进程组session(pgrp,session,leader),优先级别prioity和实时优先级别rt_priority,调度政策policy,进程组session(parent_exec_id,self_exec_id);文件操作权限有关(uid,euid,suid,gid,egid,sgid,fsgid);权限(cap_effective,cap_inheritable,cap_permitted);user进程所属 的用户,各种资源的使用数量受限制rlim(栈大小,数据大小,地址大小,进程的个数等);
(5)进程所占有和使用的资源,如mm,active_mm,fs,files,tty,real_timer,times,it_real_value等
(6)per_cpu_utime[]和per_cpu_stime[]表示各个进程在不同CPU上运行于用户空间和系统空间上的累计时间;
(7)页面异常统计字数min_fit,maj_fit,以及换入换出次数nswap等;
(8)当一个进程do_exit()时,将该进程的有关统计信息要合并到父进程中,每项统计信息都有相应的统计信息,如min_fit对应cmin_fit,times对应于tms_utime和tms_cutime等;
(9)表示进程之间的关系,p_ooptr和p_pptr指向父进程的task_struct,p_cptr指向最年轻的子进程,p_ysptr指向其哥哥,p_osptr指向弟弟;从而形成了一个子进程链;
(10)给定一个进程号查找某个task_struct ,为查找时的效率,以计算结果为下标在杂凑表中找到一个队列,在顺着队列找到特定的进程;task_struct *pidhash[PIDHASH_SZ],PIDHASH_SZ为1024,由于每一个指针4个字节,所以整个杂凑表正好占一个物理页面;task_struct通过pidhash_next和pidhash_pprev链入杂凑表中的某一个队列,统一个队列中所有进程的pid都具有相同的杂凑值;
(11)内核需要对每一个进程做工作时,为方便,组成一个线性队列,第一个进程就为init_task,它是所有进程的总根,是起点;task_struct通过next_task和prev_task将两个指针链入这个线性队列中;
(12)每一个进程必然处于上述的三个队列中,知道进程消亡时才会从这3个队列中移除,所以这三个队列都是静态的;
(13)在运行过程中,一个进程还可以动态的链接(list_head run_list)进可执行队列中接受系统调度;睡眠时还要离开该队列,被唤醒时又链入,调度可能改变一个进程在队列中的位置;