您好,欢迎访问三七文档
当前位置:首页 > 商业/管理/HR > 管理学资料 > 第二版linux操作系统原理与应用chp3
第三章进程进程介绍进程控制块进程的组织方式进程调度进程的创建与进程相关的系统调用及其应用与调度相关的系统调用及应用进程介绍-程序和进程进程介绍-进程层次结构initABCDE进程介绍-进程状态运行态阻塞态就绪态进程介绍-进程示例#includesys/types.h/*提供类型pid_t的定义,在PC机上与int型相同*/#includeunistd.h/*提供系统调用的定义*/main(){pid_tpid;/*此时仅有一个进程*/printf(“PIDbeforefork():%d\n”,(int)getpid());pid=fork();/*此时已经有两个进程在同时运行*/if(pid0)printf(errorinfork!);elseif(pid==0)printf(Iamthechildprocess,myprocessIDis%d\n,getpid());elseprintf(Iamtheparentprocess,myprocessIDis%d\n,getpid());}进程介绍-进程示例编译并运行这个程序:$gccfork_test.c-ofork_test$./fork_testPIDbeforefork():1991Iamtheparentprocess,myprocessIDis1991Iamthechildprocess,myprocessIDis1992再运行一遍,输出结果可能不同。读者考虑一下为什么?进程控制块对进程进行全面描述的数据结构Linux中把对进程的描述结构叫做task_struct:structtask_struct{……};传统上,这样的数据结构被叫做进程控制块PCB(processcontrolblaock)PCB是进程存在和运行的唯一标志进程控制块-信息分类状态信息-描述进程动态的变化。链接信息-描述进程的父/子关系。各种标识符-用简单数字对进程进行标识。进程间通信信息-描述多个进程在同一任务上协作工作。时间和定时器信息-描述进程在生存周期内使用CPU时间的统计、计费等信息。调度信息-描述进程优先级、调度策略等信息。文件系统信息-对进程使用文件情况进行记录。虚拟内存信息-描述每个进程拥有的地址空间。处理器环境信息-描述进程的执行环境(处理器的寄存器及堆栈等)进程控制块-Linux进程状态及转换fork()TASK_RUNNING就绪TASK_INTERRUPTIBLE浅度睡眠TASK_UNINTERRUPTIBLE深度睡眠TASK_STOPPED暂停TASK_ZOMBIE僵死占有CPU执行do_exit()schedule()ptrace()schedule()时间片耗尽等待资源到位sleep_on()schedule()等待资源到位interruptible_sleep_on()schedule()资源到位wake_up_interruptible()或收到信号wake_up()资源到位wake_up()收到信号SIGCONTwake_up()进程控制块-进程标识符每个进程都有一个唯一的标识符,内核通过这个标识符来识别不同的进程。进程标识符PID也是内核提供给用户程序的接口,用户程序通过PID对进程发号施令。PID是32位的无符号整数,它被顺序编号每个进程都属于某个用户组。task_struct结构中定义有用户标识符UID(UserIdentifier)和组标识符GID(GroupIdentifier)这两种标识符用于系统的安全控制系统通过这两种标识符控制进程对系统中文件和设备的访问。进程控制块-进程之间的亲属关系父进程兄进程进程P弟进程指向父进程指向兄进程指向子进程指向弟进程进程控制块-部分内容的描述上面通过对进程状态、标识符及亲属关系的描述,我们可以把这些域描述如下:structtask_struct{volatilelongstate;/*进程状态*/intpid,uid,gid;/*一些标识符*/structtask_struct*real_parent;/*真正创建当前进程的进程*/structtask_struct*parent;/*相当于养父*/structlist_headchildren;/*子进程链表*/structlist_headsibling;/*兄弟进程链表*/structtask_struct*group_leader;/*线程组的头进程*/…};进程控制块-如何存放为了节省空间,Linux把内核栈和一个紧挨近PCB的小数据结构thread_info放在一起,占用8KB的内存区,如下图所示:PCB和内核栈的存放进程控制块-如何存放C语言使用下列的联合结构表示这样一个混合结构:unionthread_union{structthread_infothread_info;unsignedlongstack[THREAD_SIZE/sizeof(long)];/*大小一般是8KB,但也可以配置为4KB*/};linux调用alloc_task_struct()函数分配8KB的thread_union内存区,调用free_task_struct()函数释放它。unionthread_info{structtask_struct*task;structexec_domain*exec_domain;……};thread_info表示和硬件关系更密切的数据。x86上,thread_info的定义如下:进程控制块-如何存放把PCB与内核栈放在一起具有以下好处:(1)内核可以方便而快速地找到PCB,用伪代码描述如下:p=(structtask_struct*)STACK_POINTER&0xffffe000(2)避免在创建进程时动态分配额外的内存在Linux中,为了表示当前正在运行的进程,定义了一个current宏,这个宏本质上等价current_thread_info()-task,可以把它看作全局变量来用,例如current-pid返回正在执行的进程的标识符进程的组织方式-进程链表在task_struct中定义如下:structtask_struct{...structlist_headtasks;charcomm[TASK_COMM_LEN];/*可执行程序的名字...};进程的组织方式-进程链表自己编写一个内核模块,打印进程的PID和进程名,模块中主要函数的代码如下:staticintprint_pid(void){structtask_struct*task,*p;structlist_head*pos;intcount=0;printk(HelloWorldenterbegin:\n);task=&init_task;list_for_each(pos,&task-tasks)/*关键*/{p=list_entry(pos,structtask_struct,tasks);count++;printk(%d---%s\n,p-pid,p-comm);}printk(thenumberofprocessis:%d\n,count);return0;}进程的组织方式-哈希表哈希函数#definepid_hashfn(x)\((((x)8)^(x))&(PIDHASH_SZ-1))图为地址法处理冲突时的哈希表假定哈希表义为:structtask_struct*pidhash[PIDHASH_SZ];对给定的PID,如何快速找到对应进程?进程的组织方式-可运行队列把可运行状态的进程组成一个双向循环链表,也叫可运行队列(runqueue)在task_struct结构中定义了两个指针。structtask_struct{...structlist_headrun_list;...};init_task起链表头的作用在调度程序运行过程中,允许队列中加入新出现的可运行态进程,新出现的可运行态进程插入到队尾进程的组织方式-等待队列等待队列表示一组睡眠的进程在include/linux/wait.h中,对等待队列的定义如下:struct__wait_queue{unsignedintflags;#defineWQ_FLAG_EXCLUSIVE0x01void*private;wait_queue_func_tfunc;structlist_headtask_list;/*最主要的域*/};typedefstruct__wait_queuewait_queue_t;func域指向唤醒函数,用于把等待队列中的进程唤醒:typedefint(*wait_queue_func_t)(wait_queue_t*wait,unsignedmode,intflags,void*key);进程的组织方式-等待队列等待队列头struct__wait_queue_head{spinlock_tlock;structlist_headtask_list;};typedefstruct__wait_queue_headwait_queue_head_t;为什么要使用lock自旋锁呢?等待队列及队列头形成的双链表等待队列的操作声明初并始化等待队列头name:#define__WAIT_QUEUE_HEAD_INITIALIZER(name){\.lock=__SPIN_LOCK_UNLOCKED(name.lock),\.task_list={&(name).task_list,&(name).task_list}}#defineDECLARE_WAIT_QUEUE_HEAD(name)\wait_queue_head_tname=__WAIT_QUEUE_HEAD_INITIALIZER(name)初始化等待队列中的一个元素,则调用如下函数:staticinlinevoidinit_waitqueue_entry(wait_queue_t*q,structtask_struct*p){q-flags=0;q-private=p;q-func=default_wake_function;}等待队列的操作如何让正在运行的进程等待某一特定事件?voidsleep_on(wait_queue_head_t*wq){wait_queue_twait;init_waitqueue_entry(&wait,current);current-state=TASK_UNINTERRUPTIBLE;add_wait_queue(wq,&wait);/*wq指向当前队列的头*/schedule();remove_wait_queue(wq,&wait);}如果要让等待的进程唤醒,就调用唤醒函数wake_up(),它让待唤醒的进程进入TASK_RUNNING状态。voidwake_up(wait_queue_head_t*q){structlist_head*tmp;wait_queue_t*curr;list_for_each(tmp,&q-task_list){curr=list_entry(tmp,wait_queue_t,task_list);if(curr-func(curr,TASK_INTERRUPTIBLE|TASK_UNINTERRUPTIBLE,0,NULL)&&curr-flags)break;}}进程调度-调度算法考虑的因素公平:保证每个进程得到合理的CPU时间。高效:使CPU保持忙碌状态,即总是有进程在CPU上运行。响应时间:使交互用户的响应时间尽可能短。周转时间:使批处理用户等待输出的时间尽可能短。吞吐量:使单位时间内处理的进程数量尽可能多。进程调度-调度算法时间片轮转调度算法系统使每个进程依次地按时间片轮流地执行优先权调度算法非抢占式优先权算法抢占式优先权调度算法多级反馈队列调度优先权高的进程先运行给定的时间片,相同优先权的进程轮流运行给定的时间片实
本文标题:第二版linux操作系统原理与应用chp3
链接地址:https://www.777doc.com/doc-2125348 .html