程序14-26 linux/include/linux/sched.h


  1 #ifndef _SCHED_H

  2 #define _SCHED_H

  3

  4 #define HZ 100                           // 定义系统时钟滴答频率(1百赫兹,每个滴答10ms)

  5

  6 #define NR_TASKS        64               // 系统中同时最多任务(进程)数。

  7 #define TASK_SIZE       0x04000000       // 每个任务的长度(64MB)。

  8 #define LIBRARY_SIZE    0x00400000       // 动态加载库长度(4MB)。

  9

 10 #if (TASK_SIZE & 0x3fffff)

 11 #error "TASK_SIZE must be multiple of 4M"       // 任务长度必须是4MB的倍数。

 12 #endif

 13

 14 #if (LIBRARY_SIZE & 0x3fffff)

 15 #error "LIBRARY_SIZE must be a multiple of 4M"  // 库长度也必须是4MB的倍数。

 16 #endif

 17

 18 #if (LIBRARY_SIZE >= (TASK_SIZE/2))

 19 #error "LIBRARY_SIZE too damn big!"             // 加载库的长度不得大于任务长度的一半。

 20 #endif

 21

 22 #if (((TASK_SIZE>>16)*NR_TASKS) != 0x10000)

 23 #error "TASK_SIZE*NR_TASKS must be 4GB"         // 任务长度*任务总个数必须为4GB

 24 #endif

 25

    // 在进程逻辑地址空间中动态库被加载的位置(60MB处)。

 26 #define LIBRARY_OFFSET (TASK_SIZE - LIBRARY_SIZE)

 27

    // 下面宏CT_TO_SECS CT_TO_USECS用于把系统当前嘀嗒数转换成用秒值加微秒值表示。

 28 #define CT_TO_SECS(x)   ((x) / HZ)

 29 #define CT_TO_USECS(x)  (((x) % HZ) * 1000000/HZ)

 30

 31 #define FIRST_TASK task[0]             // 任务0比较特殊,所以特意给它单独定义一个符号。

 32 #define LAST_TASK task[NR_TASKS-1]     // 任务数组中的最后一项任务。

 33

 34 #include <linux/head.h>

 35 #include <linux/fs.h>

 36 #include <linux/mm.h>

 37 #include <sys/param.h>

 38 #include <sys/time.h>

 39 #include <sys/resource.h>

 40 #include <signal.h>

 41

 42 #if (NR_OPEN > 32)

 43 #error "Currently the close-on-exec-flags and select masks are in one long, max 32 files/proc"

 44 #endif

 45

    // 这里定义了进程运行时可能处的状态。

 46 #define TASK_RUNNING            0   // 进程正在运行或已准备就绪。

 47 #define TASK_INTERRUPTIBLE      1   // 进程处于可中断等待状态。

 48 #define TASK_UNINTERRUPTIBLE    2   // 进程处于不可中断等待状态,主要用于I/O操作等待。

 49 #define TASK_ZOMBIE             3   // 进程处于僵死状态,已经停止运行,但父进程还没发信号。

 50 #define TASK_STOPPED            4   // 进程已停止。

 51

 52 #ifndef NULL

 53 #define NULL ((void *) 0)           // 定义NULL为空指针。

 54 #endif

 55

    // 复制进程的页目录页表。Linus认为这是内核中最复杂的函数之一。( mm/memory.c, 105 )

 56 extern int copy_page_tables(unsigned long from, unsigned long to, long size);

    // 释放页表所指定的内存块及页表本身。( mm/memory.c, 150 )

 57 extern int free_page_tables(unsigned long from, unsigned long size);

 58

    // 调度程序的初始化函数。( kernel/sched.c, 385 )

 59 extern void sched_init(void);

    // 进程调度函数。( kernel/sched.c, 104 )

 60 extern void schedule(void);

    // 异常(陷阱)中断处理初始化函数,设置中断调用门并允许中断请求信号。( kernel/traps.c, 181 )

 61 extern void trap_init(void);

    // 显示内核出错信息,然后进入死循环。( kernel/panic.c, 16 )

 62 extern void panic(const char * str);

    // tty上写指定长度的字符串。( kernel/chr_drv/tty_io.c, 290 )

 63 extern int tty_write(unsigned minor,char * buf,int count);

 64

 65 typedef int (*fn_ptr)();                   // 定义函数指针类型。

 66

    // 下面是数学协处理器使用的结构,主要用于保存进程切换时i387的执行状态信息。

 67 struct i387_struct {

 68         long    cwd;            // 控制字(Control word)

 69         long    swd;            // 状态字(Status word)

 70         long    twd;            // 标记字(Tag word)

 71         long    fip;            // 协处理器代码指针。

 72         long    fcs;            // 协处理器代码段寄存器。

 73         long    foo;            // 内存操作数的偏移位置。

 74         long    fos;            // 内存操作数的段值。

 75         long    st_space[20];   /* 8*10 bytes for each FP-reg = 80 bytes */

 76 };                              /* 810字节的协处理器累加器。*/

 77

    // 任务状态段数据结构。

 78 struct tss_struct {

 79         long    back_link;      /* 16 high bits zero */

 80         long    esp0;

 81         long    ss0;            /* 16 high bits zero */

 82         long    esp1;

 83         long    ss1;            /* 16 high bits zero */

 84         long    esp2;

 85         long    ss2;            /* 16 high bits zero */

 86         long    cr3;

 87         long    eip;

 88         long    eflags;

 89         long    eax,ecx,edx,ebx;

 90         long    esp;

 91         long    ebp;

 92         long    esi;

 93         long    edi;

 94         long    es;             /* 16 high bits zero */

 95         long    cs;             /* 16 high bits zero */

 96         long    ss;             /* 16 high bits zero */

 97         long    ds;             /* 16 high bits zero */

 98         long    fs;             /* 16 high bits zero */

 99         long    gs;             /* 16 high bits zero */

100         long    ldt;            /* 16 high bits zero */

101         long    trace_bitmap;   /* bits: trace 0, bitmap 16-31 */

102         struct i387_struct i387;

103 };

104

    // 下面是任务(进程)数据结构,或称为进程描述符。

    // long state                    任务的运行状态(-1不可运行,0可运行(就绪)>0已停止)。

    // long counter                  任务运行时间计数(递减)(滴答数),运行时间片。

    // long priority                 优先数。任务开始运行时counter=priority,越大运行越长。

    // long signal                   信号位图,每个比特位代表一种信号,信号值=位偏移值+1

    // struct sigaction sigaction[32]   信号执行属性结构,对应信号将要执行的操作和标志信息。

    // long blocked                  进程信号屏蔽码(对应信号位图)。

    // -------------------

    // int exit_code                 任务执行停止的退出码,其父进程会取。

    // unsigned long start_code      代码段地址。

    // unsigned long end_code        代码长度(字节数)。

    // unsigned long end_data        代码长度 + 数据长度(字节数)。

    // unsigned long brk             总长度(字节数)。

    // unsigned long start_stack     堆栈段地址。

    // long pid                      进程标识号(进程号)

    // long pgrp                     进程组号。

    // long session                  会话号。

    // long leader                   会话首领。

    // int groups[NGROUPS]           进程所属组号。一个进程可属于多个组。

    // task_struct *p_pptr           指向父进程的指针。

    // task_struct *p_cptr           指向最新子进程的指针。

    // task_struct *p_ysptr          指向比自己后创建的相邻进程的指针。

    // task_struct *p_osptr          指向比自己早创建的相邻进程的指针。

    // unsigned short uid            用户标识号(用户id)。

    // unsigned short euid           有效用户id

    // unsigned short suid           保存的用户id

    // unsigned short gid            组标识号(组id)。

    // unsigned short egid           有效组id

    // unsigned short sgid           保存的组id

    // long timeout                  内核定时超时值。

    // long alarm                    报警定时值(滴答数)。

    // long utime                    用户态运行时间(滴答数)。

    // long stime                    系统态运行时间(滴答数)。

    // long cutime                   子进程用户态运行时间。

    // long cstime                   子进程系统态运行时间。

    // long start_time               进程开始运行时刻。

    // struct rlimit rlim[RLIM_NLIMITS]  进程资源使用统计数组。

    // unsigned int flags;           各进程的标志,在下面第149行开始定义(还未使用)。

    // unsigned short used_math      标志:是否使用了协处理器。

    // ------------------------

    // int tty                       进程使用tty终端的子设备号。-1表示没有使用。

    // unsigned short umask          文件创建属性屏蔽位。

    // struct m_inode * pwd          当前工作目录i节点结构指针。

    // struct m_inode * root         根目录i节点结构指针。

    // struct m_inode * executable   执行文件i节点结构指针。

    // struct m_inode * library      被加载库文件i节点结构指针。

    // unsigned long close_on_exec   执行时关闭文件句柄位图标志。(参见include/fcntl.h

    // struct file * filp[NR_OPEN]   文件结构指针表,最多32项。表项号即是文件描述符的值。

    // struct desc_struct ldt[3]     局部描述符表。0-空,1-代码段cs2-数据和堆栈段ds&ss

    // struct tss_struct tss         进程的任务状态段信息结构。

    // ======================================

105 struct task_struct {

106 /* these are hardcoded - don't touch */

107         long state;     /* -1 unrunnable, 0 runnable, >0 stopped */

108         long counter;

109         long priority;

110         long signal;

111         struct sigaction sigaction[32];

112         long blocked;   /* bitmap of masked signals */

113 /* various fields */

114         int exit_code;

115         unsigned long start_code,end_code,end_data,brk,start_stack;

116         long pid,pgrp,session,leader;

117         int     groups[NGROUPS];

118         /*

119          * pointers to parent process, youngest child, younger sibling,

120          * older sibling, respectively.  (p->father can be replaced with

121          * p->p_pptr->pid)

122          */

123         struct task_struct      *p_pptr, *p_cptr, *p_ysptr, *p_osptr;

124         unsigned short uid,euid,suid;

125         unsigned short gid,egid,sgid;

126         unsigned long timeout,alarm;

127         long utime,stime,cutime,cstime,start_time;

128         struct rlimit rlim[RLIM_NLIMITS];

129         unsigned int flags;     /* per process flags, defined below */

130         unsigned short used_math;

131 /* file system info */

132         int tty;                /* -1 if no tty, so it must be signed */

133         unsigned short umask;

134         struct m_inode * pwd;

135         struct m_inode * root;

136         struct m_inode * executable;

137         struct m_inode * library;

138         unsigned long close_on_exec;

139         struct file * filp[NR_OPEN];

140 /* ldt for this task 0 - zero 1 - cs 2 - ds&ss */

141         struct desc_struct ldt[3];

142 /* tss for this task */

143         struct tss_struct tss;

144 };

145

146 /*

147  * Per process flags

148  */

    /* 每个进程的标志 */  /* 打印对齐警告信息。还未实现,仅用于486 */

149 #define PF_ALIGNWARN    0x00000001      /* Print alignment warning msgs */

150                                         /* Not implemented yet, only for 486*/

151

152 /*

153  *  INIT_TASK is used to set up the first task table, touch at

154  * your own risk!. Base=0, limit=0x9ffff (=640kB)

155  */

    /*

     * INIT_TASK用于设置第1个任务表,若想修改,责任自负J

     * 基址Base = 0,段长limit = 0x9ffff=640kB)。

     */

    // 对应上面任务结构的第1个任务的信息。

156 #define INIT_TASK \

157 /* state etc */ { 0,15,15, \      // state, counter, priority

158 /* signals */   0,{{},},0, \      // signal, sigaction[32], blocked

159 /* ec,brk... */ 0,0,0,0,0,0, \  // exit_code,start_code,end_code,end_data,brk,start_stack

160 /* pid etc.. */ 0,0,0,0, \        // pid, pgrp, session, leader

161 /* suppl grps*/ {NOGROUP,}, \     // groups[]

162 /* proc links*/ &init_task.task,0,0,0, \    // p_pptr, p_cptr, p_ysptr, p_osptr

163 /* uid etc */   0,0,0,0,0,0, \    // uid, euid, suid, gid, egid, sgid

164 /* timeout */   0,0,0,0,0,0,0, \  // alarm,utime,stime,cutime,cstime,start_time,used_math

165 /* rlimits */   { {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff},  \

166                   {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}, \

167                   {0x7fffffff, 0x7fffffff}, {0x7fffffff, 0x7fffffff}}, \

168 /* flags */     0, \            // flags

169 /* math */      0, \            // used_math, tty,umask,pwd,root,executable,close_on_exec

170 /* fs info */   -1,0022,NULL,NULL,NULL,NULL,0, \

171 /* filp */      {NULL,}, \      // filp[20]

172         { \                     // ldt[3]

173                 {0,0}, \

174 /* ldt */       {0x9f,0xc0fa00}, \  // 代码长640K,基址0x0G=1D=1DPL=3P=1 TYPE=0xa

175                 {0x9f,0xc0f200}, \  // 数据长640K,基址0x0G=1D=1DPL=3P=1 TYPE=0x2

176         }, \

177 /*tss*/ {0,PAGE_SIZE+(long)&init_task,0x10,0,0,0,0,(long)&pg_dir,\       // tss

178          0,0,0,0,0,0,0,0, \

179          0,0,0x17,0x17,0x17,0x17,0x17,0x17, \

180          _LDT(0),0x80000000, \

181                 {} \

182         }, \

183 }

184

185 extern struct task_struct *task[NR_TASKS];      // 任务指针数组。

186 extern struct task_struct *last_task_used_math; // 上一个使用过协处理器的进程。

187 extern struct task_struct *current;             // 当前运行进程结构指针变量。

188 extern unsigned long volatile jiffies;          // 从开机开始算起的滴答数(10ms/滴答)。

189 extern unsigned long startup_time;              // 开机时间。从1970:0:0:0开始计时的秒数。

190 extern int jiffies_offset;                      // 用于累计需要调整的时间嘀嗒数。

191

192 #define CURRENT_TIME (startup_time+(jiffies+jiffies_offset)/HZ)  // 当前时间(秒数)。

193

    // 添加定时器函数(定时时间jiffies滴答数,定时到时调用函数*fn())。( kernel/sched.c )

194 extern void add_timer(long jiffies, void (*fn)(void));

    // 不可中断的等待睡眠。( kernel/sched.c )

195 extern void sleep_on(struct task_struct ** p);

    // 可中断的等待睡眠。( kernel/sched.c )

196 extern void interruptible_sleep_on(struct task_struct ** p);

    // 明确唤醒睡眠的进程。( kernel/sched.c )

197 extern void wake_up(struct task_struct ** p);

    // 检查当前进程是否在指定的用户组grp中。

198 extern int in_group_p(gid_t grp);

199

200 /*

201  * Entry into gdt where to find first TSS. 0-nul, 1-cs, 2-ds, 3-syscall

202  * 4-TSS0, 5-LDT0, 6-TSS1 etc ...

203  */

    /*

     * 寻找第1TSS在全局表中的入口。0-没有用nul1-代码段cs2-数据段ds3-系统段syscall

     * 4-任务状态段TSS05-局部表LTD06-任务状态段TSS1,等。

     */

    // 从该英文注释可以猜想到,Linus当时曾想把系统调用的代码专门放在GDT表中第4个独立的段中。

    // 但后来并没有那样做,于是就一直把GDT表中第4个描述符项(上面syscall项)闲置在一旁。

    // 下面定义宏:全局表中第1个任务状态段(TSS)描述符的选择符索引号。

204 #define FIRST_TSS_ENTRY 4

    // 全局表中第1个局部描述符表(LDT)描述符的选择符索引号。

205 #define FIRST_LDT_ENTRY (FIRST_TSS_ENTRY+1)

    // 宏定义,计算在全局表中第n个任务的TSS段描述符的选择符值(偏移量)。

    // 因每个描述符占8字节,因此FIRST_TSS_ENTRY<<3 表示该描述符在GDT表中的起始偏移位置。

    // 因为每个任务使用1TSS1LDT描述符,共占用16字节,因此需要 n<<4 来表示对应

    // TSS起始位置。该宏得到的值正好也是该TSS的选择符值。

206 #define _TSS(n) ((((unsigned long) n)<<4)+(FIRST_TSS_ENTRY<<3))

    // 宏定义,计算在全局表中第n个任务的LDT段描述符的选择符值(偏移量)。

207 #define _LDT(n) ((((unsigned long) n)<<4)+(FIRST_LDT_ENTRY<<3))

    // 宏定义,把第n个任务的TSS段选择符加载到任务寄存器TR中。

208 #define ltr(n) __asm__("ltr %%ax"::"a" (_TSS(n)))

    // 宏定义,把第n个任务的LDT段选择符加载到局部描述符表寄存器LDTR中。

209 #define lldt(n) __asm__("lldt %%ax"::"a" (_LDT(n)))

    // 取当前运行任务的任务号(是任务数组中的索引值,与进程号pid不同)。

    // 返回:n - 当前任务号。用于( kernel/traps.c )

210 #define str(n) \

211 __asm__("str %%ax\n\t" \            // 将任务寄存器中TSS段的选择符复制到ax中。

212         "subl %2,%%eax\n\t" \       // (eax - FIRST_TSS_ENTRY*8)èeax

213         "shrl $4,%%eax" \           // (eax/16)èeax = 当前任务号。

214         :"=a" (n) \

215         :"a" (0),"i" (FIRST_TSS_ENTRY<<3))

216 /*

217  *      switch_to(n) should switch tasks to task nr n, first

218  * checking that n isn't the current task, in which case it does nothing.

219  * This also clears the TS-flag if the task we switched to has used

220  * tha math co-processor latest.

221  */

    /*

     * switch_to(n)将切换当前任务到任务nr,即n。首先检测任务n不是当前任务,

     * 如果是则什么也不做退出。如果我们切换到的任务最近(上次运行)使用过数学

     * 协处理器的话,则还需复位控制寄存器cr0中的TS标志。

     */

    // 跳转到一个任务的TSS段选择符组成的地址处会造成CPU进行任务切换操作。

    // 输入:%0 - 指向__tmp               %1 - 指向__tmp.b处,用于存放新TSS的选择符;

    //       dx - 新任务nTSS段选择符;  ecx - 新任务n的任务结构指针task[n]

    // 其中临时数据结构__tmp用于组建177行远跳转(far jump)指令的操作数。该操作数由4字节

    // 偏移地址和2字节的段选择符组成。因此__tmpa的值是32位偏移值,而b的低2字节是新

    // TSS段的选择符(高2字节不用)。跳转到TSS段选择符会造成任务切换到该TSS对应的进程。

    // 对于造成任务切换的长跳转,a值无用。177行上的内存间接跳转指令使用6字节操作数作为跳

    // 转目的地的长指针,其格式为:jmp 16位段选择符:32位偏移值。但在内存中操作数的表示顺

    // 序与这里正好相反。任务切换回来之后,在判断原任务上次执行是否使用过协处理器时,是通过

    // 将原任务指针与保存在last_task_used_math变量中的上次使用过协处理器任务指针进行比较而

    // 作出的,参见文件kernel/sched.c中有关math_state_restore()函数的说明。

222 #define switch_to(n) {\

223 struct {long a,b;} __tmp; \

224 __asm__("cmpl %%ecx,_current\n\t" \     // 任务n是当前任务吗?(current ==task[n]?)

225         "je 1f\n\t" \                   // 是,则什么都不做,退出。

226         "movw %%dx,%1\n\t" \            // 将新任务TSS16位选择符存入__tmp.b中。

227         "xchgl %%ecx,_current\n\t" \    // current = task[n]ecx = 被切换出的任务。

228         "ljmp %0\n\t" \                 // 执行长跳转至*&__tmp,造成任务切换。

                                            // 在任务切换回来后才会继续执行下面的语句。

229         "cmpl %%ecx,_last_task_used_math\n\t" \     // 原任务上次使用过协处理器吗?

230         "jne 1f\n\t" \                  // 没有则跳转,退出。

231         "clts\n" \                      // 原任务上次使用过协处理器,则清cr0中的任务

232         "1:" \                          // 切换标志TS

233         ::"m" (*&__tmp.a),"m" (*&__tmp.b), \

234         "d" (_TSS(n)),"c" ((long) task[n])); \

235 }

236

    // 页面地址对准。(在内核代码中没有任何地方引用!!

237 #define PAGE_ALIGN(n) (((n)+0xfff)&0xfffff000)

238

    // 设置位于地址addr处描述符中的各基地址字段(基地址是base)

    // %0 - 地址addr偏移2%1 - 地址addr偏移4%2 - 地址addr偏移7edx - 基地址base

239 #define _set_base(addr,base) \

240 __asm__("movw %%dx,%0\n\t" \           // 基址base16(15-0)è[addr+2]

241         "rorl $16,%%edx\n\t" \         // edx中基址高16(31-16)èdx

242         "movb %%dl,%1\n\t" \           // 基址高16位中的低8(23-16)è[addr+4]

243         "movb %%dh,%2" \               // 基址高16位中的高8(31-24)è[addr+7]

244         ::"m" (*((addr)+2)), \

245           "m" (*((addr)+4)), \

246           "m" (*((addr)+7)), \

247           "d" (base) \

248         :"dx")                 // 告诉gcc编译器edx寄存器中的值已被嵌入汇编程序改变了。

249

    // 设置位于地址addr处描述符中的段限长字段(段长是limit)

    // %0 - 地址addr%1 - 地址addr偏移6处;edx - 段长值limit

250 #define _set_limit(addr,limit) \

251 __asm__("movw %%dx,%0\n\t" \           // 段长limit16(15-0)è[addr]

252         "rorl $16,%%edx\n\t" \         // edx中的段长高4(19-16)èdl

253         "movb %1,%%dh\n\t" \           // 取原[addr+6]字节èdh,其中高4位是些标志。

254         "andb $0xf0,%%dh\n\t" \        // dh的低4(将存放段长的位19-16)

255         "orb %%dh,%%dl\n\t" \          // 将原高4位标志和段长的高4(19-16)合成1字节,

256         "movb %%dl,%1" \               // 并放会[addr+6]处。

257         ::"m" (*(addr)), \

258           "m" (*((addr)+6)), \

259           "d" (limit) \

260         :"dx")

261

    // 设置局部描述符表中ldt描述符的基地址字段。

262 #define set_base(ldt,base) _set_base( ((char *)&(ldt)) , base )

    // 设置局部描述符表中ldt描述符的段长字段。

263 #define set_limit(ldt,limit) _set_limit( ((char *)&(ldt)) , (limit-1)>>12 )

264

    // 从地址addr处描述符中取段基地址。功能与_set_base()正好相反。

    // edx - 存放基地址(__base)%1 - 地址addr偏移2%2 - 地址addr偏移4%3 - addr偏移7

265 #define _get_base(addr) ({\

266 unsigned long __base; \

267 __asm__("movb %3,%%dh\n\t" \           // [addr+7]处基址高16位的高8(31-24)èdh

268         "movb %2,%%dl\n\t" \           // [addr+4]处基址高16位的低8(23-16)èdl

269         "shll $16,%%edx\n\t" \         // 基地址高16位移到edx中高16位处。

270         "movw %1,%%dx" \               // [addr+2]处基址低16(15-0)èdx

271         :"=d" (__base) \               // 从而edx中含有32位的段基地址。

272         :"m" (*((addr)+2)), \

273          "m" (*((addr)+4)), \

274          "m" (*((addr)+7))); \

275 __base;})

276

    // 取局部描述符表中ldt所指段描述符中的基地址。

277 #define get_base(ldt) _get_base( ((char *)&(ldt)) )

278

    // 取段选择符segment指定的描述符中的段限长值。

    // 指令lslLoad Segment Limit缩写。它从指定段描述符中取出分散的限长比特位拼成完整的

    // 段限长值放入指定寄存器中。所得的段限长是实际字节数减1,因此这里还需要加1后才返回。

    // %0 - 存放段长值(字节数)%1 - 段选择符segment

279 #define get_limit(segment) ({ \

280 unsigned long __limit; \

281 __asm__("lsll %1,%0\n\tincl %0":"=r" (__limit):"r" (segment)); \

282 __limit;})

283

284 #endif

285