我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系
关于冯诺依曼:这里的存储器指的是内存;不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备);外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取,所有设备都只能和内存打交道。
课本概念:程序的一个执行实例,正在执行的程序等。
内核观点:担当分配系统资源(cpu时间,内存)的实体。
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
在Linux中描述进程的结构体叫做task_struct
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息
标示符(pid): 描述本进程的唯一标示符,用来区别其他进程。
状态: 任务状态,退出代码,退出信号等。
优先级: 相对于其他进程的优先级。
程序计数器: 程序中即将被执行的下一条指令的地址。
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
上下文数据: 进程执行时处理器的寄存器中的数据。
I/ O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
其他信息
PID就是各进程的身份标识,程序一运行系统就会自动分配给进程一个独一无二的PID。进程中止后PID被系统回收,可能会被继续分配给新运行的程序
通过getpid/getppid获取进程标识符
#include
#include
#include
int main()
{
printf("pid: %d\n", getpid()); // 获取当前进程pid
printf("ppid: %d\n", getppid());// 获取父进程pid
return 0;
}
通过系统调用创建进程-fork
#include
#include
#include
int main()
{
pid_t ret = fork(); // 父子谁先运行是不确定的
//使用fork创建子进程时fork会有一个返回值,父进程调用fork返回子进程的pid>0,
//子进程调用fork,返回0,所有我们可以使用判断使父子进程各自执行各自需要执行的代码
if(ret < 0) // 创建失败
{
perror("fork");
return 1;
}
else if(ret == 0)
{ //child
printf("I am child : %d!, ret: %d\n", getpid(), ret);
}
else
{ //father
printf("I am father : %d!, ret: %d\n", getpid(), ret);
}
sleep(1);
return 0;
}
注意:
1.默认情况下,子进程会继承父进程的代码和数据。
2.内核数据结构task_struct也会以父进程为模板,初始化子进程的task_struct
3.fork之后,子进程和父进程代码是共享的!
4.默认情况下,数据也是“共享”的,但是如果需要修改,会 采用写时拷贝。进程具有独立性!
fork之后,执行流会变成2个,谁先运行由调度器决定,fork之后的代码共享,通常我们通过if和elseif来执行执行分流
这里的+号表示进程是在前台运行,可通过kill指令或者ctrl + c关闭进程,如果没有+号,表示后台进程,需使用kill指令关闭进程
一般的操作系统的书中,或者学校中所讲的进程状态,一般是下图所示的五状态(或三状态)
阻塞: 进程因为等待某种条件就绪,而导致的一种不推进的状态 --进程卡住了–阻塞一定是在等待某种资源
为什么会阻塞? 进程要通过等待的方式,等具体的资源被别人使用完成后,再自己使用,等待某种资源就绪的过程
挂起: 由于系统内存大小有限,为了能够使更多的进程加载到内存中,操作系统会将一些阻塞的需要等待io的进程的代码和数组暂时存放外存,节省内存的大小,等到阻塞的进程io申请完成后再将这个进程的代码和数据重新放到内存的执行队列中。
linux可大致分为以下几个状态
R运行状态(running) : 并不意味着进程一定在运行中(占有CPU),它表明进程要么是在运行中要么在运行队列里
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)
D磁盘休眠状态(Disk sleep): 有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
X死亡状态(dead): 这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
Z僵尸状态(zombie): 当进程退出并且父进程,没有读取到子进程退出的返回代码时就会产生僵死进程僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
处于僵尸状态的进程就叫僵尸进程,进程的退出状态必须被维持下去,因为他要告诉父进程自己的任务完成的怎么样,可父进程如果一直不读取,那子进程就一直处于Z状态。
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说, Z状态一直不退出, PCB一直都要维护。
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数据结构对象本身就要占用内存。
僵尸进程会造成内存泄漏。
父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程”
孤儿进程被1号进程领养。
cpu资源分配的先后顺序,就是指进程的优先权(priority)。
优先权高的进程有优先执行权利。配置进程优先权对多任务环境的linux很有用,可以改善系统性能。
还可以把进程运行到指定的CPU上,这样一来,把不重要的进程安排到某个CPU,可以大大改善系统整体性能
在linux或者unix系统中,用ps –l命令则会类似输出以下几个内容
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI (priority):代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值
PRI,即进程的优先级,此值越小进程的优先级别越高
NI,就是nice值,其表示进程可被执行的优先级的修正数值
PRI值越小越快被执行,那么加入nice值后,将会使得PRI变为: PRI(new)=PRI(old)+nice
当nice值为负值的时候,那么该程序将会优先级值将变小,即其优先级会变高,则其越快被执行所以,调整进程优先级
在Linux下,就是调整进程nice值nice其取值范围是-20至19,一共40个级别
top
进入top后按“r”–>输入进程PID–>输入nice值