Linux kernel Process Management 2.1(amd64)——Creation and Switch

来源:互联网 发布:java 读取上传txt文件 编辑:程序博客网 时间:2024/06/05 23:39

进程切换部份有很大部份是机器依赖的,这里转载的一篇是基于amd 64位的。x86请见Linux kernel Process Management 2.2(x86)
进程在OS中是一个非常关键的抽象概念。
在OS中虚拟CPU称为执行线程,简称为线程。
用于创建和管理多执行线程的实用工具通常包含在一个pthread库。因为该库中接口是按照POSIX标准定义的,所以以p开头。

在UNIX Os中,单线程进程和多线程进程模型如下:


在linux中,单线程任务和多线程任务组模型如下:

在linux Os中,用“任务”替代“进程”,而没有“进程”这个对象。
用数据结构task_struct来描述任务,任务就相当于UNIX OS中的进程。 每一个任务都有任务地址空间(相当于UNIX OS中的进程地址空间),但一个任务中只有一个线程。通过“任务组”这个概念来实现多线程任务(相当于UNIX中的多线程进程)。

可以这样简单地说:“Linux的任务是UNIX单线程进程的对等体”。

用于描述任务的数据结构task_struct,是一个信息量非常大的数据结构。但是并不是每一个线程都会有完整的task_struct成员,而只是保留了需要的成员变量值。在多线程的任务组中,每个线程都有一个task_struct数据结构来描述线程所在的任务。但是所有的线程都共享所在任务组的资源和相关信息,所以这些副本是一种浪费。实际上,并不是这么糟糕,大多数任务的成员变量是一些单独的对象,共享这些对象的线程,仅仅保存了对它的引用。

在linux操作系统中,定义了一个指向当前任务的指针current
在单处理器中,任何时刻只有一个任务在执行,current指针指向的任务在执行,current是一个全局变量。
在多处理器中,在同一时刻可以有多个任务在执行,那么在OS中可以看到的每个CPU上(也就是“执行线程”)有一个current指针,并且都是局部变量。
由于current使用地过于频繁,OS都把current申明为寄存器变量。在IA64平台下,通用寄存器r13用来保存current指针。
/*
* In kernel mode, thread pointer (r13) is used to point to the current task
* structure.
*/
#define _IA64_REG_TP     1037   /* R13 */
#define current   ((struct task_struct *) ia64_getreg(_IA64_REG_TP))

创建任务
在linux Os中创建任务(也就是创建进程和线程,只不过在Linux中没有进程的概念了,用任务替换了进程的概念,并且任务都是单线程的,多线程的任务称为任务组)根据不同的体系结构不同。我们在此之讨论在IA64结构下的实现办法。
在linux Os中没有提供用于创建原始线程的函数,因为除了系统启动的初始线程外(即PID为0的线程),任何一个线程都是从原有的线程上复制过来的而产生的。
通过copy_thread函数创建新的线程。
int copy_thread (int nr, unsigned long clone_flags,
         unsigned long user_stack_base, unsigned long user_stack_size,
         struct task_struct *p, struct pt_regs *regs)
这个函数在linux中封装成copy_process函数(用于创建任务),再一次被封装成函数do_fork(创建一个任务)和函数fork_idle(创建空闲任务或者说是空闲进程、空闲线程)
,函数do_fork再一次被封装成系统调用sys_fork。

内核创建新的任务步骤:
1、为新任务分配内存:在内核内存空间分配一块连续的内存用于保存task_struct、thread_struct(和平台相关,一般几个字节到大于1KB不等)、内核堆栈。
2、初始化任务结构(task_struct),但还没有初始化thread_struct。
3、初始化thread_struct
4、完成初始化task_struct中剩余的与平台无关的部分
5、将新创建的任务添加到运行队列中,这就可以运行了

task_struct分成两个部分:平台无关的部分和平台特定部分(线程结构)。
在创建任务过程中涉及到几个非常重要的数据结构:pt_regs、switch_stack、thread_struct等

pt_regs结构:
这个结构封装了需要在内核入口中保存的最少的状态信息。比如说每一次的系统调用、中断、陷阱、故障时,pt_regs结构中保存了最少的状态信息。该结构中主要保存了必要的scratch类型的寄存器。(在现代IA64架构中还有3类寄存器:scratch寄存器、保持寄存器、专用寄存器)。在每一次的系统调用、中断、陷阱、故障发生时,依次会发生下列事件:
1、在内核堆栈上为pt_regs结构分配内存
2、在pt_regs结构中保存scratch寄存器
3、调用了适当的内核处理器(执行系统调用内部处理、中断处理程序等)
4、从pt_regs中恢复scratch寄存器
5、从内核堆栈中释放pt_regs占用的内存
应该保持pt_regs尽可能的小,可以提高性能。
在IA64平台的Linux中pt_regs定义如下:
struct pt_regs {
     /* The following registers are saved by SAVE_MIN: */
     unsigned long b6;     /* scratch */
     unsigned long b7;     /* scratch */

     unsigned long ar_csd;           /* used by cmp8xchg16 (scratch) */
     unsigned long ar_ssd;           /* reserved for future use (scratch) */

     unsigned long r8;     /* scratch (return value register 0) */
     unsigned long r9;     /* scratch (return value register 1) */
     unsigned long r10;       /* scratch (return value register 2) */
     unsigned long r11;       /* scratch (return value register 3) */

     unsigned long cr_ipsr;       /* interrupted task's psr */
     unsigned long cr_iip;       /* interrupted task's instruction pointer */
     /*
     * interrupted task's function state; if bit 63 is cleared, it
     * contains syscall's ar.pfs.pfm:
     */
     unsigned long cr_ifs;

     unsigned long ar_unat;       /* interrupted task's NaT register (preserved) */
     unsigned long ar_pfs;       /* prev function state   */
     unsigned long ar_rsc;       /* RSE configuration */
     /* The following two are valid only if cr_ipsr.cpl > 0 || ti->flags & _TIF_MCA_INIT */
     unsigned long ar_rnat;       /* RSE NaT */
     unsigned long ar_bspstore;   /* RSE bspstore */

     unsigned long pr;     /* 64 predicate registers (1 bit each) */
     unsigned long b0;     /* return pointer (bp) */
     unsigned long loadrs;       /* size of dirty partition << 16 */

     unsigned long r1;     /* the gp pointer */
     unsigned long r12;       /* interrupted task's memory stack pointer */
     unsigned long r13;       /* thread pointer */

     unsigned long ar_fpsr;       /* floating point status (preserved) */
     unsigned long r15;       /* scratch */

     /* The remaining registers are NOT saved for system calls.   */

     unsigned long r14;       /* scratch */
     unsigned long r2;     /* scratch */
     unsigned long r3;     /* scratch */

     /* The following registers are saved by SAVE_REST: */
     unsigned long r16;       /* scratch */
     unsigned long r17;       /* scratch */
     unsigned long r18;       /* scratch */
     unsigned long r19;       /* scratch */
     unsigned long r20;       /* scratch */
     unsigned long r21;       /* scratch */
     unsigned long r22;       /* scratch */
     unsigned long r23;       /* scratch */
     unsigned long r24;       /* scratch */
     unsigned long r25;       /* scratch */
     unsigned long r26;       /* scratch */
     unsigned long r27;       /* scratch */
     unsigned long r28;       /* scratch */
     unsigned long r29;       /* scratch */
     unsigned long r30;       /* scratch */
     unsigned long r31;       /* scratch */

     unsigned long ar_ccv;       /* compare/exchange value (scratch) */

     /*
     * Floating point registers that the kernel considers scratch:
     */
     struct ia64_fpreg f6;       /* scratch */
     struct ia64_fpreg f7;       /* scratch */
     struct ia64_fpreg f8;       /* scratch */
     struct ia64_fpreg f9;       /* scratch */
     struct ia64_fpreg f10;       /* scratch */
     struct ia64_fpreg f11;       /* scratch */
};
switch_stack结构:
该结构用在内核将执行一个线程切换到另一个线程之时,该结构主要保存了保持寄存器。pt_regs和switch_stack结合起来,一起封装了每个线程正确运行所需的最低限度的机器状态。这种机器状态称为高度管理状态(eagerly managed state),与松散管理状态(lazily managed state)相对。
简单地说switch_stack保存了任务切换的上下文,主要保存了保持寄存器。 (即内核态堆栈)
在IA64架构的linux中,switch_stack定义如下:
struct switch_stack {
unsigned long caller_unat;   /* user NaT collection register (preserved) */
     unsigned long ar_fpsr;       /* floating-point status register */

     struct ia64_fpreg f2;       /* preserved */
     struct ia64_fpreg f3;       /* preserved */
     struct ia64_fpreg f4;       /* preserved */
     struct ia64_fpreg f5;       /* preserved */

     struct ia64_fpreg f12;       /* scratch, but untouched by kernel */
     struct ia64_fpreg f13;       /* scratch, but untouched by kernel */
     struct ia64_fpreg f14;       /* scratch, but untouched by kernel */
     struct ia64_fpreg f15;       /* scratch, but untouched by kernel */
     struct ia64_fpreg f16;       /* preserved */
     struct ia64_fpreg f17;       /* preserved */
     struct ia64_fpreg f18;       /* preserved */
     struct ia64_fpreg f19;       /* preserved */
     struct ia64_fpreg f20;       /* preserved */
     struct ia64_fpreg f21;       /* preserved */
     struct ia64_fpreg f22;       /* preserved */
     struct ia64_fpreg f23;       /* preserved */
     struct ia64_fpreg f24;       /* preserved */
     struct ia64_fpreg f25;       /* preserved */
     struct ia64_fpreg f26;       /* preserved */
     struct ia64_fpreg f27;       /* preserved */
     struct ia64_fpreg f28;       /* preserved */
     struct ia64_fpreg f29;       /* preserved */
     struct ia64_fpreg f30;       /* preserved */
     struct ia64_fpreg f31;       /* preserved */

     unsigned long r4;     /* preserved */
     unsigned long r5;     /* preserved */
     unsigned long r6;     /* preserved */
     unsigned long r7;     /* preserved */

     unsigned long b0;     /* so we can force a direct return in copy_thread */
     unsigned long b1;
     unsigned long b2;
     unsigned long b3;
     unsigned long b4;
     unsigned long b5;

     unsigned long ar_pfs;       /* previous function state */
     unsigned long ar_lc;     /* loop counter (preserved) */
     unsigned long ar_unat;       /* NaT bits for r4-r7 */
     unsigned long ar_rnat;       /* RSE NaT collection register */
     unsigned long ar_bspstore;   /* RSE dirty base (preserved) */
     unsigned long pr;     /* 64 predicate registers (1 bit each) */
};
thread_struct结构:
该结构封装了松散管理状态,主要封装了内核堆栈指针ksp,ksp指向swicth_stack。松散管理状态,并不是每次上下文切换时都要切换松散管理状态,往往只在确实需要新的状态时才切换松散管理状态。切换松散管理状态比切换高度管理状态慢很多,所以尽量不切换松散管理状态,以提高性能。
struct thread_struct {
     __u32 flags;         /* various thread flags (see IA64_THREAD_*) */
     /* writing on_ustack is performance-critical, so it's worth spending 8 bits on it... */
     __u8 on_ustack;           /* executing on user-stacks? */
     __u8 pad[3];
     __u64 ksp;         /* kernel stack pointer */
     __u64 map_base;           /* base address for get_unmapped_area() */
     __u64 task_size;     /* limit for task size */
     __u64 rbs_bot;           /* the base address for the RBS */
     int last_fph_cpu;     /* CPU that may hold the contents of f32-f127 */

#ifdef CONFIG_IA32_SUPPORT
     __u64 eflag;         /* IA32 EFLAGS reg */
     __u64 fsr;         /* IA32 floating pt status reg */
     __u64 fcr;         /* IA32 floating pt control reg */
     __u64 fir;         /* IA32 fp except. instr. reg */
     __u64 fdr;         /* IA32 fp except. data reg */
     __u64 old_k1;         /* old value of ar.k1 */
     __u64 old_iob;           /* old IOBase value */
     struct ia64_partial_page_list *ppl; /* partial page list for 4K page size issue */
         /* cached TLS descriptors. */
     struct desc_struct tls_array[GDT_ENTRY_TLS_ENTRIES];

# define INIT_THREAD_IA32   .eflag =   0,         /
               .fsr =     0,         /
               .fcr =     0x17800000037fULL,   /
               .fir =     0,         /
               .fdr =     0,         /
               .old_k1 =   0,         /
               .old_iob = 0,         /
               .ppl =     NULL,
#else
# define INIT_THREAD_IA32
#endif /* CONFIG_IA32_SUPPORT */
#ifdef CONFIG_PERFMON
     void *pfm_context;             /* pointer to detailed PMU context */
     unsigned long pfm_needs_checking;     /* when >0, pending perfmon work on kernel exit */
# define INIT_THREAD_PM     .pfm_context =       NULL,     /
               .pfm_needs_checking =     0UL,
#else
# define INIT_THREAD_PM
#endif
     __u64 dbr[IA64_NUM_DBG_REGS];
     __u64 ibr[IA64_NUM_DBG_REGS];
     struct ia64_fpreg fph[96];   /* saved/loaded on demand */
};


在任务创建后,会分配一大块内存给task_struct结构来维护。这块内存具体使用如下图:

在IA64架构下,Linux 中定义每次分配给任务的地址空间是IA64_STK_OFFSET,如下:
#define IA64_STK_OFFSET         ((1 << KERNEL_STACK_SIZE_ORDER)*PAGE_SIZE)
             #if defined(CONFIG_IA64_PAGE_SIZE_4KB)
             # define KERNEL_STACK_SIZE_ORDER     3
             #elif defined(CONFIG_IA64_PAGE_SIZE_8KB)
             # define KERNEL_STACK_SIZE_ORDER     2
             #elif defined(CONFIG_IA64_PAGE_SIZE_16KB)
             # define KERNEL_STACK_SIZE_ORDER     1
             #else
             # define KERNEL_STACK_SIZE_ORDER     0
             #endif
通过以上语句定义IA64_STK_OFFSET,决定分配内存的大小。就是说,如果系统配置每个页面的大小为4KB的情况下,那么IA64_STK_OFFSET就是8*4KB=32KB;
如果PAGE_SIZE=8KB,那么IA64_STK_OFFSET就是4*8KB=32KB;
如果PAGE_SIZE=16KB,那么IA64_STK_OFFSET就是2*16KB=32KB;
如果PAGE_SIZE=64KB,那么IA64_STK_OFFSET就是1*64KB=64KB;

在上图中还有一个变量IA64_RBS_BASE,该变量用来描述什么?看linux是如何实现的就知道了,用中文描述,我还真不知道。
#define IA64_RBS_OFFSET         ((IA64_TASK_SIZE + IA64_THREAD_INFO_SIZE + 31) & ~31)

       DEFINE(IA64_TASK_SIZE, sizeof (struct task_struct));
       DEFINE(IA64_THREAD_INFO_SIZE, sizeof (struct thread_info));
原创粉丝点击
热门问题 老师的惩罚 人脸识别 我在镇武司摸鱼那些年 重生之率土为王 我在大康的咸鱼生活 盘龙之生命进化 天生仙种 凡人之先天五行 春回大明朝 姑娘不必设防,我是瞎子 大学档案在自己手里放多年怎么办 档案在自己手里10年了怎么办 快递地址区域划分写错了怎么办 怀孕八个月肛门长个肉疙瘩怎么办呢 客户签了合同不要货了怎么办 不喂母乳宝宝只认识保姆了怎么办? 开创者比赛后没有买的鸽子怎么办 买回来小鸽子开家我敢了怎么办 小鸽子出壳后母鸽就不孵了怎么办 狗狗在笼子里一直叫怎么办 养了一年母鸽子不配对怎么办 血管翳云翳斑翳长满眼睛怎么办 鸽子训放50公里突然下雨了怎么办 安卓手机反应慢又卡怎么办 tcl安卓系统电视反应慢怎么办 狼人杀警长竞选一直平票怎么办 去驾校投诉教练被教练报复怎么办 哈尔滨机场大巴六点的飞机怎么办 山东德州恩城镇的农民怎么办养老险 我的世界联机平台房间进不去怎么办 验车时间过了3天怎么办 在携程订的机票和酒店不能退怎么办 千牛工作台中旺旺登录失败怎么办 掌通家园换手机号了忘了更改怎么办 qq注销了后绑定的全民k歌怎么办 小米账号绑定的手机号注销了怎么办 微信账号注销后绑定的手机号怎么办 微信账号不想用了注销不了怎么办 苹果手机掉了捡到不还怎么办 在工作中把和别人结下梁子了怎么办 在阿里巴巴上买的货物没发货怎么办 做了下颌角一边脸反复肿怎么办 玩广东11选5输50万怎么办 玩广东11选5输了十几万怎么办 新办公楼装修好就要搬进去怎么办 oppo手机上的黄页删了怎么办 小米不小心把手机联系人删了怎么办 58同城小区名输不了怎么办 e路发注册直接有积分怎么办 大众速腾的不锈钢圈被碰花了怎么办 公司卡着生育险不给怎么办