当前位置:Linux教程 - Linux - 读核日记(三)

读核日记(三)

作者:sunmoon
在linux 中每一个进程都由task_struct 数据结构来定义. task_struct就是我们通常所说的PCB.她是对进程控制的唯一手段也是最有效的手段. 当我们调用fork() 时, 系统会为我们产生一个task_struct结构.然后从父进程,那里继承一些数据, 并把新的进程插入到进程树中, 以待进行进程管理.因此了解task_struct的结构对于我们理解任务调度(在linux 中任务和进程是同一概念)的关键.在进行剖析task_struct的定义之前. 我们先按照我们的理论推一下它的结构. 1, 进程状态 ,将纪录进程在等待,运行,或死锁 2, 调度信息, 由哪个调度函数调度,怎样调度等 3, 进程的通讯状况 4,因为要插入进程树,必须有联系父子兄弟的指针, 当然是task_struct型 5,时间信息, 比如计算好执行的时间, 以便cpu 分配 6,标号 ,决定改进程归属 7,可以读写打开的一些文件信息 8, 进程上下文和内核上下文 9,处理器上下文 10,内存信息 因为每一个PCB都是这样的, 只有这些结构, 才能满足一个进程的所有要求.打开/include/linux/sched.h 找到task_struct 的定义 struct task_struct { /* these are hardcoded - don''t touch */ 这里是一些硬件设置对程序原来说是透明的. 其中state 说明了该进程是否可以执行,还是可中断等信息. Flage 是进程号, 在调用 fork() 时给出,addr_limit 是区分内核进程与普通进程在内存存放的位置不同 volatile long state; /* -1 unrunnable, 0 runnable, >0 stopped */ unsigned long flags; /* per process flags, defined below */ int sigpending; mm_segment_t addr_limit; /* thread address space: 0-0xBFFFFFFF for user-thead 0-0xFFFFFFFF for kernel-thread */ struct exec_domain *exec_domain; long need_resched;
/* various fields */ count 是 计数器 priorrity 是优先级 long counter; long priority; cycles_t avg_slice; /* SMP and runqueue state */ 为多处理机定义的变量. int has_cpu; int processor; int last_processor; int lock_depth; /* Lock depth. We can context switch in and out of holding a syscall kernel lock... */ 为了在进程树中排序, 定义的父子,兄弟指针 struct task_struct *next_task, *prev_task; struct tas74k_struct *next_run, *prev_run;
/* task state */ 定义可 task 运行的状态, 以及信号 struct linux_binfmt *binfmt; int exit_code, exit_signal; int pdeath_signal; /* The signal sent when the parent dies */ /* 定义可进程的用户号,用户组以及进程组*/ unsigned long personality; int dumpable:1; int did_exec:1; pid_t pid; pid_t pgrp; pid_t tty_old_pgrp; pid_t session; /* boolean value for session group leader */ 是不是进程组的头文件 int leader; /* * pointers to (original) parent process, youngest child, younger sibling, * older sibling, respectively. (p->father can be replaced with * p->p_pptr->pid) */ 父子进程的一些指针 struct task_struct *p_opptr, *p_pptr, *p_cptr, *p_ysptr, *p_osptr;
/* PID hash table linkage. */ 在调度中用的一些hash 表 struct task_struct *pidhash_next; struct task_struct **pidhash_pprev;
/* Pointer to task[] array linkage. */ struct task_struct **tarray_ptr;
struct wait_queue *wait_chldexit; /* for wait4() 等待队列 */ struct semaphore *vfork_sem; /* for vfork() */ unsigned long policy, rt_priority; unsigned long it_real_value, it_prof_value, it_virt_value; 进程的性质因为实时进程与普通进程的调度算法不一样所以应有变量区分 下面是进程的一些时间信息 unsigned long it_real_incr, it_prof_incr, it_virt_incr; struct timer_list real_timer; struct tms times; unsigned long start_time; long per_cpu_utime[NR_CPUS], per_cpu_stime[NR_CPUS];定义了时间片的大小 /* mm fault and swap info: this can arguably be seen as either mm-specific or thread-specific */ 内存信息 unsigned long min_flt, maj_flt, nswap, cmin_flt, cmaj_flt, cnswap; int swappable:1; /* process credentials */ uid_t uid,euid,suid,fsuid; gid_t gid,egid,sgid,fsgid; int ngroups; gid_t groups[NGROUPS]; kernel_cap_t cap_effective, cap_inheritable, cap_permitted; struct user_struct *user; 以下英文注释很清楚 /* limits */ struct rlimit rlim[RLIM_NLIMITS]; unsigned short used_math; char comm[16]; /* file system info */ int link_count; struct tty_struct *tty; /* NULL if no tty */ /* ipc stuff */
struct sem_undo *semundo; struct sem_queue *semsleeping; /* tss for this task */ struct thread_struct tss; /* filesystem information */ struct fs_struct *fs; /* open file information */ struct files_struct *files; /* memory management info */ struct mm_struct *mm;
/* signal handlers */ spinlock_t sigmask_lock; /* Protects signal and blocked */ struct signal_struct *sig; sigset_t signal, blocked; struct signal_queue *sigqueue, **sigqueue_tail; unsigned long sas_ss_sp; size_t sas_ss_size; }; 在分析完 这个结构之后, 还有很多问题要想, 也许不能读 但框架要搞好.需要向的问题有以下几个 1,在task_struct 中用的常量在那里定义呢, 如最大进程个数, 最多支持的cpu 个数,等等 2,在调用fork() 时, 系统是分配一块内存 会是这样么 malloc(1,sizeof(struct task_struck)) 拷贝一些变量,还是和服进程公用一部分内存.malloc 函数怎么实现(在内存管理那一部分,但此处我认为不能不想) 3,.对于线程来说, 又如何实现呢? 4, 调度策略函数 schedul() 有几种形势, 时间片轮转, 抢占式,优先级抢占式, 多级反馈制.除了时间片轮转外都要对进程树进行遍历,(对于实时进程的fifo机制不用)linux 是怎样保证了高效呢?如果把最大线成数修改, 效率会不会降低 5, 进程通讯用到的管道,信号结构如何 待续..