模拟进程状态转换程序_物联网学习教程—Linux系统编程之进程介绍

本文深入探讨Linux系统中的进程概念,解释进程的生命周期、状态转换(就绪态、执行态、等待态)以及如何通过时间片实现多任务。还介绍了进程控制块、进程号及其相关操作函数,帮助读者理解Linux进程管理。
摘要由CSDN通过智能技术生成

Linux系统编程之进程介绍

一、进程概述

我们平时写的 C 语言代码,通过编译器编译,最终它会成为一个可执行程序,当这个可执行程序运行起来后(没有结束之前),它就成为了一个进程。

程序是存放在存储介质上的一个可执行文件,而进程是程序执行的过程。进程的状态是变化的,其包括进程的创建、调度和消亡。程序是静态的,进程是动态的。

51df28f25a6c420c7b221f5c08e17961.png

在 Linux 系统中,操作系统是通过进程去完成一个一个的任务,进程是管理事务的基本单元。进程拥有自己独立的处理环境(如:当前需要用到哪些环境变量,程序运行的目录在哪,当前是哪个用户在运行此程序等)和系统资源(如:处理器 CPU 占用率、存储器、I/O设备、数据、程序)。我们可以这么理解,公司相当于操作系统,部门相当于进程,公司通过部门来管理(系统通过进程管理),对于各个部门,每个部门有各自的资源,如人员、电脑设备、打印机等。

二、进程状态

我们现在的电脑基本上都是多任务,我们聊着 QQ 的时候,同时可以看着视频,这里相当于 QQ 和视频两个程序同时运行着(两个进程)。早期的时候,电脑的 CPU 是单核的(单核理论上只运行操作一个任务),那它是如何做到多任务的呢?这就涉及到进程的调度策略。现在给大家举这么一个例子,有 A,B,C 三个进程,在我们单 CPU 的情况下,每一个时刻只有一个进程在运行,如果 A 运行完,B 运行,B 运行完,C 运行,C 运行完,A 运行,而 CPU 的运算速度足够快,A 两次运行时间间隔足够短,从宏观上就我们就看到 A,B,C 好像同时运行,这就是实现单 CPU 运行多个任务的核心原理,通过时间片轮询调度策略实现多任务(更多详情,请看《Linux 进程调度浅析》)。

从上面的例子,我们可以得知,对于 A 进程而言,有时候在运行,有时候没有运行,两个状态不一样,所以,进程是有状态的,同时,状态是可以相互进行转换的,从执行的状态转换为不执行的状态,这里,我们可以把进程运行的整个生命周期简单划分为三种状态(实际上不指这三种状态):就绪态、执行态、等待态。

就绪态:

进程已经具备执行的一切条件,正在等待分配 CPU 的处理时间。

执行态:

该进程正在占用 CPU 运行。

等待态:

进程因不具备某些执行条件而暂时无法继续执行的状态。

这里需要注意,就绪态和等待态都是不执行,但它们是有区别的,就绪态是指满足条件,时间没到,等待态是不满足条件。

同样的,进程的这三种状态可以相互转换:

f5000c3e842e7e540b19f999219bbae3.png

执行态-->等待态:

正在执行的进程因等待某种事件发生而无法继续执行时,便从执行状态变成等待状态

等待态-->就绪态:

处于等待态的进程,若其等待的事件发生,于是进程由等待状态变成就绪态

就绪态-->执行态:

当就绪态的进程所等待的cpu时间片一到来,进程就会从就绪态变成执行态

执行态-->就绪态:

处于执行状态的进程在其执行过程中,因分配给它的一个时间片已用完而不得不让出cpu,于是进程从执行状态转变成就绪状态

为了让大家更加清晰地了解三种状态的转换,给大家举一个lh买火车票的例子。

lh匆忙地赶去火车站买火车票,太着急了,到了售票厅才发现忘记带身份证,这时候,就算 lh排队也没用,因为 lh不具备买票的条件(没带身份证),这时候的 lh属于等待态。

lh给它对象打电话,让她把身份证带过来,等会,身份证送到了,这时候,lh可以去排队买票了,只是时间到,lh就可以买票了,这时,lh属于就绪态。而这过程是由等待态转换到就绪态。

等了 10 分钟,终于到 lh了,lh开始买票,这时候, lh属于执行态。而这过程是由就绪态转换为执行态。

而在买票的过程中,lh的对象打电话给他,让 lh也帮她买一张火车票,但是, lh没有她对象的身份证,接着,lh继续等他对象送身份证,这时候,lh由执行态转换为等待态。

假如是这么一种情况,lh买火车票是给公司的同事买的(需要买 100 多张票),在买着票的过程中(执行态),后面还有很多人在排队,后面排队的人肯定不爽,这时售票员就说,20分钟后,如果你还没处理完,请你到后面排队。结果,lh花了 20 分钟还是没有处理完,于是,乖乖地到后面重新排队,这时候,lh由执行态转换为就绪态。

三、进程控制块

对于操作系统而言,它需要控制很多进程,同时,每个进程都有不同的状态,系统如何知道 A 执行完到 B 执行而不是 C?系统如何协调控制进程呢?

当我们运行一个程序使它成为一个进程时,系统会开辟一段内存空间存放与此进程相关的数据信息,而这个数据信息是通过结构体( task_struct,ubuntu12.04中打开 /usr/src/linux-headers-3.2.0-23/include/linux/sched.h 可以找到 task_struct 的定义 )来存放,我们把这个存放进程相关数据信息的结构体称为进程控制块。

66da0465e00918f1647635f4deb188ef.png
bca775e592a9de0ba5fa2c858f2e912f.png

操作系统就是通过这个进程控制块来操作控制进程。更多详情,请看《 Linux 进程管理》。

进程控制块是操作系统中最重要的记录型数据结构。进程控制块记录了用于描述进程进展情况及控制进程运行所需的全部信息,它是进程存在的唯一标志。进程控制块里有很多信息,其中比较重要的是进程号,至于其他的一些信息我们不在这详细讨论。

四、进程号

每个进程都由一个进程号来标识,其类型为 pid_t(无符号整型),进程号的范围:0~32767。进程号总是唯一的,但进程号可以重用。当一个进程终止后,其进程号就可以再次使用。

faf82586443e82d10c5a6ed8cb0e3c75.png

系统允许一个进程创建新进程,新进程即为子进程,子进程还可以创建新的子进程,形成进程树结构模型。整个 Linux 系统的所有进程也是一个树形结构。树根是系统自动构造的,即在内核态下执行的 0 号进程,它是所有进程的祖先。进程号为 0 的进程通常是调度进程,常被称为交换进程( swapper )。由 0 号进程创建 1 号进程(内核态),1 号负责执行内核的部分初始化工作及进行系统配置,并创建若干个用于高速缓存和虚拟主存管理的内核线程。随后,1 号进程调用 execve() 运行可执行程序 init,并演变成用户态 1 号进程,即 init 进程。

所以,在 Linux 下面所有的进程都由 init 进程直接或者间接创建。

接下来,再给大家介绍三个不同的进程号。

进程号(PID):

标识进程的一个非负整型数。

父进程号(PPID):

任何进程( 除 init 进程)都是由另一个进程创建,该进程称为被创建进程的父进程,对应的进程号称为父进程号(PPID)。如,A 进程创建了 B 进程,A 的进程号就是 B 进程的父进程号。

进程组号(PGID):

进程组是一个或多个进程的集合。他们之间相互关联,进程组可以接收同一终端的各种信号,关联的进程有一个进程组号(PGID) 。这个过程有点类似于 QQ 群,组相当于 QQ 群,各个进程相当于各个好友,把各个好友都拉入这个 QQ 群里,主要是方便管理,特别是通知某些事时,只要在群里吼一声,所有人都收到,简单粗暴。但是,这个进程组号和 QQ 群号是有点区别的,默认的情况下,当前的进程号会当做当前的进程组号。

五、进程号操作函数

Linux 操作系统提供了三个获得进程号的函数 getpid()、getppid()、getpgid()。

所需头文件:

#include

#include

pid_t getpid(void);

功能:

获取本进程号(PID)

参数:无

返回值:本进程号

pid_t getppid(void);

功能:获取调用此函数的进程的父进程号(PPID)

参数:无

返回值:

pid_t getpgid(pid_tpid);

功能:获取进程组号(PGID)

参数:pid:进程号

返回值:参数为 0 时返回当前进程组号,否则返回参数指定的进程的进程组号

示例代码如下:

#include

#include

#include

int main(int argc, char *argv[])

{

pid_t pid, ppid, pgid;

pid = getpid();

printf("pid = %d

流程图,代码,截图三、程序源代码: #include"stdlib.h" #include"stdio.h" #include"string.h" /********** globle structure and viable ******/ struct PCB { int P_Id; //PCB的ID号 char P_Name[10]; //PCB的名称 char P_State[10]; //PCB状态 int P_Runtime; //PCB的所需要的运行时间 int P_Requiry; //PCB所需要的资源要求 struct PCB * next ; //PCB块的下一个指针 } ; struct PCB * Create_state; //创建状态 struct PCB * Run_state; //运行状态 struct PCB * Ready_state; //就绪状态 struct PCB * Block_state; //阻塞状态 struct PCB * Exit_state; //退出状态 int signal4=0; //标示进程4的完成状态 int signal5=0; //标示进程5的完成状态 void InsertQueue(struct PCB **head,struct PCB *node) /* insert node function */ { struct PCB * p,*q; node->next=NULL; if(*head==NULL) //如果队列为空 { *head=node; } Else //队列不空 { p=*head; q=p->next; while(q!=NULL) //找到最后的元素位置 { p=q; q=q->next; } p->next=node; //将节点插入队列 } } void DeleteQueue(struct PCB **head,struct PCB *node) //撤销进程,从队列中删除元素 { struct PCB *p,*q; q=*head; if(*head==NULL||node==NULL) //如果队列为空,返回 return ; if(*head==node) //如果要删除的元素是队首元素 { *head=(*head)->next; return; } Else //如果不是队列的首元素 { while(q->next!=p&&q->next!=NULL) q=q->next; q=p->next; p->next=NULL; } } void Display_Process(struct PCB * node) //打印进程状态的元素函数 { printf("\n\nthis process Id is : %d \n",node->P_Id); printf("this process name is : %s \n",node->P_Name); printf("this process state is : on %s \n ",node->P_State); printf("this process Runtime is : %d \n",node->P_Runtime); if(node->P_Requiry) printf("this process resource is ready \n"); else printf("this process resource is not ready ! \n"); } void DispatchToBlock(struct PCB *node) // /* dispatch to block function*/ { //调度到阻塞状态的函数 //struct PCB *p=(struct PCB *)malloc(sizeof(struct PCB)); if(!node->P_Requiry) //如果所需要的资源没有满足则,调度到阻塞状态 { strcpy(node->P_State,"block"); InsertQueue(&Block_state,node); //插入到阻塞队列 Display_Process(node); } } void DispatchToReady(struct PCB *node) // dispatch to ready state { //调度到就绪状态的函数 if(node->P_Requiry) //如果所需的资源满足,则调度 { strcpy(node->P_State,"Ready"); InsertQueue(&Ready_state,node); Display_Process(node); } } void DispatchBlockToReady() //dispatch the process to readyqueue { //从阻塞状态调度到就绪状态函数 struct PCB*p,*q; q=Block_state; while(q!=NULL) //如果阻塞状态队列不空 { p=q; q=q->next; if(signal4&&p->P_Id==4) //如果所需要的资源满足 { DeleteQueue(&Block_state,p); strcpy(p->P_State,"ready"); InsertQueue(&Ready_state,p); printf("process4 will be in the state of ready!\n"); Display_Process(p); } if(signal5&&p->P_Id==5) { DeleteQueue(&Block_state,p); strcpy(p->P_State,"ready"); InsertQueue(&Ready_state,p); printf("process5 will be in the state of ready!\n"); Display_Process(p); } } } void Create_Process() //创建进程函数 { int i; struct PCB *p; char name[10]; strcpy(name,"process"); for(i=1;iP_Id=i; name[7]=i+'0'; name[8]='\0'; strcpy(p->P_Name,name); strcpy(p->P_State,"create"); p->P_Runtime=1; //所需要的时间片为1 p->P_Requiry=0; Display_Process(p); sleep(4); printf(" \n process%d will be in the state of Block, waiting the resource ready \n\n",i); DispatchToBlock(p); //同时调度到阻塞队列 } for(i=3;iP_Id=i; name[7]=i+'0'; name[8]='\0'; strcpy(p->P_Name,name); strcpy(p->P_State,"create"); p->P_Requiry=1; if(i==6) //在这里个进程6所需要的时间片为2 p->P_Runtime=2; else p->P_Runtime=1; Display_Process(p); sleep(4); printf(" \n process%d will be in the state of Ready, waiting to run \n\n",i); DispatchToReady(p); } } void display(struct PCB **head) //打印各个状态队列里进程数目 { struct PCB *p,*q; p=*head; while(p!=NULL) { sleep(2); //printf("\n\n///////////////////////////////////\n"); printf("\n\nthis process Id is : %d \n",p->P_Id); printf("this process name is : %s \n",p->P_Name); printf("this process state is : on %s \n ",p->P_State); printf("this process Runtime is : %d \n",p->P_Runtime); if(p->P_Requiry) printf("this process resource is ready \n"); else printf("this process resource is not ready ! \n"); p=p->next; } } void Process_Run() //进程运行函数 { struct PCB *p,*q; p=Ready_state; q=p; while(p!=NULL) //就绪队列不空则继续执行 { if(p->P_RuntimeP_State,"running"); Display_Process(p); p->P_Runtime=p->P_Runtime-1; sleep(4); if(p->P_Runtime>0) //没有完成,则进入就绪队列 { printf("this process is not finished,will be dispatch to the ready queue!!\n"); DeleteQueue(&Ready_state,p); strcpy(p->P_State,"ready"); InsertQueue(&Ready_state,p); Display_Process(p); } Else //执行完成,则跳出,并发送相应的信息 { printf("\n\nProcess%d is finished and will be in the state of exit!\n\n",p->P_Id); if(p->P_Id==4) signal4=1; if(p->P_Id==5) signal5=1; } if(signal4||signal5) DispatchBlockToReady(); //如果资源满足,则将进程调度到就绪队列 q=q->next; p=q; } if(q==NULL) printf("\nthere is no process ready!\n STOP Machine!!!\n"); } int main(int argc,char * argv[]) //主函数 { int i; char c='c'; //界面 printf("\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\\ \n"); printf("...................................Ding Hai bo\n"); printf("......Press s to start the process.......\n"); scanf("%c",&c); while(1) { if(c=='s')break; scanf("%c",&c); } Create_Process(); //调用创建进程函数 printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); printf("\n>>>>>>> Display the Ready queue >>>>>>>>>>>>>>>\n"); sleep(5); display(&Ready_state); ////////////////显示就绪队列里的进程 printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n"); printf("\n>>>>>>>> Display the Block queue >>>>>>>>>>>>\n"); sleep(5); //显示阻塞队列函数 display(&Block_state); ///////////////////// printf("\n>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>>\n\n\n"); printf("\n>>>>>>>> Now the process start to run >>>>>>>>>>>\n"); sleep(5); Process_Run(); //调用进程运行函数 } 都有
1.目的: 自行编制模拟程序,通过形象化的状态显示,深入理解进程的概念、进程之间的状态转换及其所带来的PCB内容 、组织的变化,理解进程与其PCB间的一一对应关系。 2. 内容及要求: 1) 设计并实现一个模拟进程状态转换及其相应PCB内容、组织结构变化的程序。 2) 独立编写、调试程序进程的数目、进程的状态模型(三状态、五状态、七状态或其它)以及PCB的组织形式可自行选择。 3) 合理设计与进程PCB相对应的数据结构。PCB的内容要涵盖进程的基本信息、控制信息、资源需求及现场信息。 4) 设计出可视性较好的界面,应能反映出进程状态的变化引起的对应PCB内容、组织结构的变化。 5) 代码书写要规范,要适当地加入注释。 6) 认真进行预习,完成预习报告。 7) 实验完成后,要认真总结,完成实验报告。 3.使用的数据结构及说明: 在本实验中,主要用到的数据结构是PCB的结构,其中PCB的数据结构如下: struct PCB { int P_Id; //PCB的ID号 char P_Name[10]; //PCB的名称 char P_State[10]; //PCB状态 int P_Runtime; //PCB的所需要的运行时间 int P_Requiry; //PCB所需要的资源要求 struct PCB * next ; //PCB块的下一个指针 } ; 其中,P_Id,和P_Name用来标示一个进程,而P_State用来标示进程的五种状态:Create_state,Ready_state,Block_state,Run_state,Exit_state。P_Runtime标示要完成一个进程所需要的时间。P_Requiry标示一个进程的执行所需要的其他条件,当其他的条件满足,则P_Requiry置1,否则置0。Struct PCB * next 用来指向同一队列中的下一个PCB块。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值