Linux C : 进程管理实验:创建进程、上下文切换
进程可以看成程序的执行过程,可以展示在当前时刻的执行状态。它是程序在一个数据集合上的一次动态执行的过程。这个数据集合通常包含存放可执行代码的代码段,存放初始化全局变量和初始化静态局部变量的数据段、用于调试的符号表、未初始化的全局变量和未初始化的静态局部变量的bss段,存放对象数据和临时变量以及数据层次结构的堆栈,系统资源等
进程有多种状态,例如创建,就绪,执行,等待,终止 等。
为了实验简单,暂时只设置如下的进程状态:空闲状态,就绪,睡眠,僵尸状态。其中,僵尸状态值的是进程已经执行结束,但是还没释放资源的进程。而对于单CPU的系统来说,获得CPU使用权的进程状态就是执行态,进程执行结束就应该释放资源。
目录
一、结构体定义
二、上下文切换
三、优先级队列
四、进程操作
五、编译和执行
一、结构体定义
在 type.h中 定义进程的结构体:
为了尽量和实际操作系统中保持一致,要把栈 kstack 放在结构体的最后面,栈底到栈顶,就是从高地址到低地址,从kstack[SSIZE -1 ] 到 kstack [0] 。而saved_sp就是这个栈的栈顶指针。
//
// Created by Administrator on 2021/6/7.
//#ifndef PROCESSMANAGEMENT_TYPE_H
#define PROCESSMANAGEMENT_TYPE_H#define NPROC 9
#define SSIZE 1024
#define proc PROCenum ProcStatus{FREE=0,READY,SLEEP};typedef struct Process{struct Process * next; //同优先级的下一个同状态进程int *saved_sp ; //栈顶指针int pid; //idint ppid; //父进程int status; //状态 Free|ready| etcint priority; //优先级int event ; //造成睡眠的事件idint exitCode ; //进程退出值struct Process *child; //first child PROC pointerstruct Process *slbling; //兄弟节点struct Process *perent; //父节点int kstack[SSIZE]; //栈空间}PROC;#endif //PROCESSMANAGEMENT_TYPE_H
二、上下文切换
上下文切换,指的是正在执行的进程因为某些原因需要让出CPU使用权,需要保留现有的状态,等到重新获得CPU使用权的时候,恢复刚刚暂停执行时的状态继续执行。scheduler 方法在后续中会讲到,它的功能是把当前执行的进程running弄到就绪队列中,再从就绪队列中搞一个进程来执行。
在tswitch.s 汇编文件中写入下述代码。其中
movl running,%ebx
movl %esp,4(%ebx)
这段代码把 running指针的值放入%ebx,而 4(%ebx)则是running地址再往高地址偏移4个字节,对应再程序中的变量是 Process 中的 *saved_sp变量。所以这两句代码就是为了 saved_sp= %esp
当running换成新进程后,再 Resume 代码块,先 %esp = saved_sp,然后再按顺序出栈,把之前的状态进行恢复。执行到ret时,弹出PC寄存器的代码地址继续执行。
.globl running,scheduler,tswitch
tswitch:
SAVE: # on entry: stack top = retPCpushl %eax # save CPU registers on stackpushl %ebxpushl %ecxpushl %edxpushl %ebppushl %esipushl %edipushflmovl running,%ebx # ebx -> running PROCmovl %esp,4(%ebx) # running PROC.saved_sp = espFIND: call scheduler # pick a new running PROCRESUME: movl running,%ebx # ebx -> (new) running PROCmovl 4(%ebx),%esp # esp = (new) running PROC.saved_sppopfl # restore saved registers from stackpopl %edipopl %esipopl %ebppopl %edxpopl %ecxpopl %ebxpopl %eaxret # return by retPC on top of stack # stack contents = |retPC|eax|ebx|ecx|edx|ebp|esi|edi|eflag|
# -1 -2 -3 -4 -5 -6 -7 -8 -9
三、优先级队列
队列采用链表形式进行组织,对进程列表按照优先级进行入队、出队、打印操作
#include "queue.h"/*** 把进程指针P 放进优先级指针数组中* @param queue* @param p* @return*/
static int enqueue(PROC **queue ,PROC * p){PROC *q = *queue;if (q==NULL || p->priority > q->priority){//如果队列为空或者P的优先级要比队列头来得高,那么直接放进队列头*queue = p;p->next = q;}else{//否则定位到对应的优先级序列while(q->next!=NULL && p->priority <= q->next->priority){q = q->next;}p->next = q->next;q->next = p;}
}static PROC* dequeue(PROC ** queue){PROC *p= *queue;if(p!=NULL){*queue = (*queue)->next;}return p;
}static int printList (const char * name ,PROC * p){printf("%s =",name);while(p){printf("[%d %d ]->",p->pid,p->priority);p=p->next;}printf("NULL\n");return 0;
}
四、进程操作
1)定义执行、空闲、就绪、睡眠状态进程队列 ,本次示例最多有 NPROC=9 个进程
2)CPU使用权切换 scheduler,把一个正在执行的换进就绪队列中,再从就绪队列换出一个进程。
3)上下文切换 do_switch, 其中,tswitch的定义在 tswitch.s文件中。该方法实现,保存寄存器状态值,恢复一个就绪态的进程,并恢复该进程暂停之前的状态
4)创建进程 do_kfork ,从空闲队列中抽出一个进程并初始化信息,加入到就绪队列中。
在初始化过程中,kstack[SSIZE-1] 赋成和 body的函数地址,save_sp指针指向kstack[SSIZE -9]。意味着,如果在tswtich.s 执行 movl .save_sp ,%esp 后再pop 8个寄存器后,esp寄存器对应的内存单元就是kstack[SSIZE-1] 即body的函数地址,再ret一下,就会执行body函数。
5)进程退出 do_exit , 把进程重新加入到空闲队列中。
6)系统初始化 init ,假设在该系统中 proc [0] 即 P0 的进程优先级最低,在空闲列表中的进程都是P0进程的子进程 ,子进程的优先级都为1 ,P0进程的优先级为0。
7) main 函数:初始化进程P0,并创建一个子进程。调用tswitch 切换到子进程中,如果子进程都结束退出,那么P0才有机会重新获取CPU执行权。P0结束,程序结束
#include
#include "type.h"int kfork(), kexit(), tswitch(), printList(), enqueue() ;PROC proc[NPROC], *running, *freeList, *readyQueue, *sleepList;int do_switch()
{printf("proc %d switching task\n", running->pid);tswitch();printf("proc %d resuming\n", running->pid);
}int do_kfork()
{int child = kfork();if (child < 0)printf("kfork failed\n");else{printf("proc %d kforked a child = %d\n", running->pid, child); printList("readyQueue", readyQueue);}return child;
}int kexit(){running->status = FREE;running->priority = 0;// ASSIGNMENT 3: add YOUR CODE to delete running PROC from parent's child listenqueue(&freeList, running); // enter running into freeListprintList("freeList", freeList); // show freeListtswitch();
}int do_exit()
{if (running->pid==1){printf("P1 never dies\n");return -1;}kexit(); // journey of no return
}int body()
{int c, CR;printf("proc %d starts from body()\n", running->pid);while(1){printf("***************************************\n");printf("proc %d running: Parent = %d\n", running->pid, running->ppid);// ASSIGNMENT 3: add YOUR CODE to show child listprintf("input a char [f|s|q] : ");c = getchar(); CR=getchar(); switch(c){case 'f': do_kfork(); break;case 's': do_switch(); break;case 'q': do_exit(); break;}}
}/*******************************************************kfork() creates a child porc; returns child pid.When scheduled to run, child PROC resumes to body();
********************************************************/
int kfork()
{PROC *p;int i;/*** get a proc from freeList for child proc: ***/p = dequeue(&freeList);if (!p){printf("no more proc\n");return(-1);}/* initialize the new proc and its stack */p->status = READY;p->priority = 1; // for ALL PROCs except P0p->ppid = running->pid;// -1 -2 -3 -4 -5 -6 -7 -8 -9// kstack contains: |retPC|eax|ebx|ecx|edx|ebp|esi|edi|eflag|for (i=1; i<10; i++)p->kstack[SSIZE - i] = 0;p->kstack[SSIZE-1] = (int)body;p->saved_sp = &(p->kstack[SSIZE - 9]); /**************** ASSIGNMENT 3 ********************add YOUR code to implement the PROC tree as a BINARY treeenter_child(running, p);
****************************************************/enqueue(&readyQueue, p);return p->pid;
}int init()
{int i;for (i = 0; i < NPROC; i++){proc[i].pid = i; proc[i].status = FREE;proc[i].priority = 0;proc[i].next = (PROC *)&proc[(i+1)];}proc[NPROC-1].next = 0;freeList = &proc[0]; readyQueue = 0;sleepList = 0;// create P0 as the initial running processrunning = dequeue(&freeList);running->status = READY;running->priority = 0; running->child = 0; running->sibling = 0; running->parent = running;printf("init complete: P0 running\n"); printList("freeList", freeList);
}/*************** main() ***************/
int main()
{printf("\nWelcome to 360 Multitasking System\n");init();kfork(); printf("P0: switch task\n");tswitch();printf("All dead. Happy ending\n");
}/*********** scheduler *************/
int scheduler()
{ printf("proc %d in scheduler()\n", running->pid);if (running->status == READY)enqueue(&readyQueue, running);printList("readyQueue", readyQueue);running = dequeue(&readyQueue);printf("next running = %d\n", running->pid);
}
五、编译和执行
编译上述文件:命令为
gcc -m32 t.c tswtich.s queue.c
再执行 ./a.out 如下结果:

参考书本《Systems Programming in Unix/Linux》 或 译文《Unix/Linux 系统编程》
本文来自互联网用户投稿,文章观点仅代表作者本人,不代表本站立场,不承担相关法律责任。如若转载,请注明出处。 如若内容造成侵权/违法违规/事实不符,请点击【内容举报】进行投诉反馈!
