数学家冯·诺依曼提出了计算机制造的三个基本原则,即采用二进制逻辑、程序存储执行以及计算机由五个部分组成(运算器、控制器、存储器、输入设备、输出设备),这套理论被称为冯·诺依曼体系结构。
冯·诺依曼最先提出程序存储的思想,并成功将其运用在计算机的设计之中。冯·诺伊曼体系结构是现代计算机的基础,现在大多计算机仍是冯·诺伊曼计算机的组织结构,因此冯·诺依曼又被称为“现代计算机之父”。
存储器:用来存放数据和程序
运算器:主要运行算数运算和逻辑运算,并将中间结果暂存到运算器中
控制器:主要用来控制和指挥程序和数据的输入运行,以及处理运算结果
输入设备:用来将人们熟悉的信息形式转换为机器能够识别的信息形式,常见的有键盘,鼠标等
输出设备:可以将机器运算结果转换为人们熟悉的信息形式,如打印机输出,显示器输出等
我们这里说的内存,主要是指主存。就是主板上插的内存条。它的读写速度比磁盘快了几十万倍。但是相对于CPU的速度依旧还是慢。那么主存和CPU之间,可以继续添加速度更快的过度层。所以intel i7的存储器层次结构是这样的。
关于冯诺依曼
OS是什么?
一款软件,专门针对软硬件资源进行管理工作的软件。
官方概念
内核(进程管理,内存管理,文件管理,驱动管理)
其他程序(例如函数库,shell程序等等)
管理不是直接管理,是对对象的数据处理。
操作系统是用C语言写的,所以一般描述一个对象的集合我们可以使用结构体。
操作系统的理念:对目标的管理,转换成对数据的管理!
在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
基本概念
表层概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
task_struct-PCB的一种
在Linux中描述进程的结构体叫做task_struct。
task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
操作系统并不是直接管理进程,而是通过管理PCB来管理进程,所以进程就得到了一个真正的描述:进程 = 程序文件内容(代码和文件相关内部数据)+ 相关的数据结构(task_struct,由OS自动创建)
在系统根目录下有一个proc文件,里面存放了进程的名字
这些数字就是进程的标识符(PID),如果知道进程的pid就可以通过这个查找该进程。
例如ls /proc/1
单独使用ps命令,会显示所有进程信息。
命令:ps aux
命令:ps axj
作用类似
进程执行时处理器的寄存器中的数据。
我们知道CPU的运行速度是相当快的,但每个进程也不可能很快停止,比如我们打开的应用程序QQ、微信可能一天都在运行,但是大多数计算机CPU只有一个,不可能一直运行这一个进程。
所以CPU规定每个进程单次运行的时间片(一个进程单次运行的最长时间),比如:10ms
这样在单CPU下,让用户感到多个进程同时在运行,本质是通过CPU的快熟切换完成的。
那么在切换的时候,下一次再运行不可能再从零开始,所以就需要记录上次运行结束的信息,CPU只有一套寄存器用于存放当前进程的运行数据,当进程单次运行结束时,需要保存当次运行的相关数据(来自寄存器),再次运行进程的时候,恢复上次运行的信息即可。
关于fork的返回值:
失败:返回值<0
成功:
- 给父进程返回子进程PID
- 给子进程返回0
那么fork()创建进程有什么特点吗?
子进程:
PCB(task_struct): 以父进程为模板初始化
代码和数据:继承父进程的代码和数据(父子共享,但如果发生修改就会发生“写时拷贝”)
使用fork函数创建子进程后就有了两个进程,这两个进程被操作系统调度的顺序是不确定的,这取决于操作系统调度算法的具体实现。
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。
下面的状态在kernel源代码里定义:
/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char * const task_state_array[] = {
"R (running)", /* 0 */
"S (sleeping)", /* 1 */
"D (disk sleep)", /* 2 */
"T (stopped)", /* 4 */
"t (tracing stop)", /* 8 */
"X (dead)", /* 16 */
"Z (zombie)", /* 32 */
};
并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列
里。每一时刻只有一个进程在使用CPU。
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠(interruptible sleep))。
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。
进程完成某种任务时,任务条件不具备,需要进程进行某种等待。
常见概念:
可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可
以通过发送 SIGCONT 信号让进程继续运行。
例如程序的调试
这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵死(尸)进程僵死进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。
所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态
僵尸进程危害
进程的退出状态必须被维持下去,因为他要告诉关心它的进程(父进程),你交给我的任务,我办的怎么样了。可父进程如果一直不读取,那子进程就一直处于Z状态。
维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存task_struct(PCB)中,换句话说,Z状态一直不退出,PCB一直都要维护。
那一个父进程创建了很多子进程,就是不回收,是不是就会造成内存资源的浪费。因为数据结构对象本身就要占用内存,想想C中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间!
会造成内存泄漏
父进程如果提前退出,那么子进程后退出,进入Z之后,那该如何处理呢?
父进程先退出,子进程就称之为“孤儿进程”孤儿进程被1号init进程(OS)领养,当然要有init进程回收喽。
基本概念
查看系统进程
在linux或者unix系统中,用ps –l命令
我们很容易注意到其中的几个重要信息,有下:
UID : 代表执行者的身份
PID : 代表这个进程的代号
PPID :代表这个进程是由哪个进程发展衍生而来的,亦即父进程的代号
PRI :代表这个进程可被执行的优先级,其值越小越早被执行
NI :代表这个进程的nice值,也叫优先级
PRI and NI
用top命令更改已存在进程的nice
top
进入top后按“r”–>输入进程PID–>输入nice值
此时的PRI = 原来的80+NI值8 = 88
NI的值只能在-20~19之间,而且每次修改是在最原始的基础修改的。
建议不要修改进程的优先级