进程(一)--- 进程概念
程序和进程
什么是程序?
- 程序就是一个被动的实体(需要被用户指定执行),就是一个存储在磁盘上的文件包含了一系列的指令(被称作可执行文件)。
什么是进程?
当可执行文件被加载进内存后,程序就变成了进程。这个可执行文件包含了指令,当加载进内存后,cpu就可以执行这些指令,直到所有指令执行完成。
进程是一个活动的实体,这个实体中包含了程序计数器用来指明下一条需要执行的指令
程序计数器(pc--programer counter):是一个cpu中的寄存器,里面存放了下一条要执行指令的内存地址,通常,cpu取完一条指令之后会将PC寄存器的值加 "1",用来计算下一条要执行指令的地址。
当将一个程序代码加载进内存,并将第一条指令地址写入pc寄存器之后,进程就创建完成了。
进程再内存中的分布
当进程被加载进内存后,程序会将内分成几块区域----> 代码段、数据段,堆,栈
int global = 100; //全局变量
void f(int x,int y){ //
int* p = malloc(100);
return;
}
void g(int a){ //
f(a,a+1);
return;
}
int main(){
static int i = 10; //静态变量
g(i);
return 0;
}
-
以上代码编译后会变成二进制指令,这些指令会放在
text
区域,这个区域是只读区域。 -
这个代码的入口是
main
函数, 先将主函数的返回地址写入栈,发现主函数中有staic 变量i和全局变量global
,则将这两个变量存入data
区域 -
调用
g()
函数,将a
放入stack
上,接着将g()
函数的返回值(在main
函数内的地址,方便g()
函数返回的时候继续执行main
函数接下来的代码)也压入栈 -
g()
函数会调用f()
函数,因此同理,将x,y,p
三个局部变量放入栈中,接着将f的返回值放入栈中 -
p
变量被动态分配了一块内存,因此将p
变量放入到heap
上,同时分配100个字节 -
f函数执行完返回:将
f()
函数的返回地址读取出来,由于函数要返回了,因此需要将f函数中的局部变量地址清除,以及f的返回值也清除,接着执行g()
函数 -
g()
函数返回,取出g()
函数的返回地址,定位到main
函数中的g(i)那段代码,同时清除g()
函数返回地址和g()
函数内部局部变量 -
main
函数返回,清空堆内存(由于没有free p变量,p变量要等到函数退出才会销毁)
并发的进程
并发与并行
再同一个时间发生或者存在的两个或多个事件被称为并发(concurrency)。
再同一个时间同时运行的两个或多个事件被称为并行(parallel)
当再单核机器上,同时创建多个程序,那就是并发,这两个程序同时存在,但是再同一个时间只有一个进程再运行,如果在2核cpu上,执行两个进程,两个进程分别由不同的cpu来执行,那么这种就叫做并行,因为他们再同一时间都在运行。
cpu上的并发
cpu再执行程序的时候会被划分成一个个时间片,由操作系统来调度再这个时间片上由哪个进程来由cpu执行。当一个进程因为执行的时间到了而被另外一个进程顶替的占有cpu,这个过程叫“进程切换”
其中实线代表的是进程占有cpu,虚线代表的是进程没有占有cpu
进程状态
根据上面并发的知识知道,进程并不是一直都在运行的,而且会由操作系统进行调度,有时执行,有时并没有执行,因此需要给进程标记上他当前的状态,好让操作系统可以来调度管理进程。
三种基本状态
进程总共有五种状态,其中两种是创建和终止,运行中的进程一般只在以下三个状态切换
- 就绪态(ready):进程的代码再cpu上运行
- 运行态(running):进程具备运行条件,等待分配cpu
- 等待态(waiting):进程再等待某些时间的发生(比如IO操作结束或者一个信号),不具备运行条件==
通过上面图可以看出:
- running----> ready: 运行中的进程时间片用完或者有高优先级的进程到达会进入就绪态
- running----> waiting: 运行中的进程再遇到IO或者事件阻塞(比如将字符串输出到显示器的时候,由于这些操作很慢,进程就会被切换成等待)
- ready-----> running: 当操作系统开始调度分配进程的时候,进程就有就绪态转换为运行态
- waiting----->ready :当遇到IO或者某些事件完成