一、冯诺依曼体系结构
冯诺依曼由 输入设备、输出设备、运算器、控制器、存储器 五部分组成。
冯诺依曼的设计特点
二进制表示 所有数据(包括程序指令)均以二进制形式存储和运算,简化了硬件逻辑设计,提高了可靠性。
存储程序原理 程序与数据共同存储于同一存储器中,且程序可像数据一样被修改。
顺序执行机制 指令按线性顺序逐条执行,由程序计数器(PC)控制执行流程。
指令结构 每条指令由操作码(定义操作类型)和地址码(指定操作数位置)组成。
冯诺依曼体系结构的作用
引言: 外设 和 CPU 读取速度完全不一样,如果 外设 和 CPU 直接进行数据交互,就会导致 CPU 读取速度非常缓慢。但 冯诺依曼体系结构 就解决了这种问题,那它是怎么解决的呢?
二、操作系统(Operator System)
概念
操作系统(Operating System,简称OS) 是管理计算机硬件与软件资源的系统软件。它提供用户接口和资源分配,是计算机系统中最基本的系统软件,任何计算机系统都包含一个基本的程序集合,统称为 操作系统(OS) 。 简单来讲, 操作系统 包含 :
设计OS的目的

OS如何管理软、硬件
OS 是怎么管理软、硬件的呢?,简单6个字就可以总结:先描述,再组织
进程
上面讲了 OS 是怎么将软硬件资源管理起来的,那么对于进程也是再这样吗?答案是肯定的
概念
task_struct (描述进程)
task_struct 中的进程属性很多,我在这里描述其中的一部分: 3. 标识符:描述本进程的唯一标识符,用来区别其他进程 4. 状态:任务状态,退出代码,退出信号等 5. 优先级:用来描述进程被调度的先后的数字,数字小的优先级高,数字大的优先级低,和成绩排名有异曲同工之妙 6. 程序技术器(PC):表示程序中即将执行的下一条指令的地址 7. 内存指针:包括程序代码和进程相关数据的指针,还有和其他进程共享内存块的指针 8. 上下文数据:进程执行时处理器的寄存器中的数据,主要包括: CPU寄存器的值:包括程序计数器(PC)、指令寄存器、堆栈指针等。 内存映射信息:进程的虚拟内存空间布局。 进程状态:如运行状态、阻塞状态、就绪状态等。 I/O状态:与进程相关的输入输出设备的状态。 其他系统资源的状态:如文件描述符、信号状态等。 9. I / O状态信息:包含显示的 I / O 请求,分配给进程的 I / O 设备和被进程使用的文件链表 10. 记账信息:可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号的等。
组织进程
PCB 在内核中使用双链表进程链接起来的 
查看进程
通过 /proc 系统文件夹来查看 
通过 top 和 ps 这些用户工具来获取进程信息 top: top -p [pid]
ps: ps axj

通过系统调用获取进程标识符
- 获取进程id (PID):getpid()
- 父进程id (PID): getppid()
- 通过手册来查看相关系统调用:man 2 getpid

通过系统调用fork创建子进程
通过 fork 创建的子进程和父进程共享同一份代码,但数据是各自私有的(写时拷贝)(其中 pid_t 的类型其实是一个有符号整数类型,取了个别名而已,当 fork() 返回值为 0 时就表示子进程,当 fork() 返回值大于0时,表示的就为父进程。) 至于为什么数据各自私一份,有我们通过一个程序就可以看出来
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int val = 0;
pid_t id = fork();
if(id == 0)//表示子进程
{
while(1)
{
printf("我是子进程,pid: %d ,ppid: %d, val = %d\\n",getpid(),getppid(),val++);
sleep(1);
}
}
else//父进程
{
while(1)
{
printf("我是父进程,pid: %d ,ppid: %d, val = %d\\n",getpid(),getppid(),val);
sleep(1);
}
}
return 0;
}
程序运行结果:
可以看到父进程和子进程的 val 的值并不是一样的,我们就可以得出父子进程并不共享同一份数据的结论
进程状态
进程状态有很多种:
- R运行状态:并不意味着进程一定在运行中,它表明进程要么在运行中,要么在运行队列中(运行队列用于管理处于“就绪状态”(Ready State)的进程。这些进程已经准备好运行,但正在等待 CPU 时间片分配。当调度器选择一个进程运行时,它会从运行队列中选取一个进程,并将其状态从“就绪”变为“运行”。)
- S睡眠状态:以为着进程在等待事件完成(也叫做可中断睡眠) 注意: 以下有个场景可能让人误会为是 R 状态 ,但其实是 S 状态
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main()
{
int cnt = 0;
while(1)
{
printf("pid: %d,cnt = %d\\n",getpid(),cnt++);
}
return 0;
}
~
这里可能很多认为进程的状态是R状态,其实不然,这里的进程状态其实大部分都是S+(+表示是前台进程)状态,为什么呢?因为进程这里大部分时间都在做IO交互,IO的时间是很慢的,所有看不到R状态
表示每隔1秒查看对应名为code进程的信息:while :;do ps axj | head -1 && ps axj | grep code | grep -v grep;sleep 1 ; done

-
D磁盘休眠状态:有时候叫做不可中断状态(不会响应信号),在这个状态等待IO的结束
-
T停止状态:可以通过发送 SIGSTOP 信号来给进程停止 (T)进程。这个被暂停的进程可以通过发送 SIGCOUT 信号让进程继续执行

-
X死亡状态:这是一个返回状态,你不会在任务列表中看到这个状态,因为这是一个瞬时状态,一下就消失了
-
Z僵死状态:是一个比较特殊的状态。当进程退出并且父进程没有读到子进程返回的退出码时,子进程就会除以一个僵死状态。僵死状态会以终止状态保持在进程表中,并且会一直等待父进程读取退出状态代码(通过 kill -9 pid 杀死子进程就可以看到子进程处于僵死状态)

僵死状态危害
没创建一个子进程就需要一个 PCB ,如果父进程创建了很多子进程,而没有去回收就会造成内存资源的浪费
孤儿进程
父进程如果提前退出,子进程就会变成孤儿进程,孤儿进程就会被1号 init 也就是操作系统启动后第一个运行的用户空间程序 
进程优先级
基本概念
-
cpu资源分配的先后顺序,就是进程的优先权。
-
优先级高的进程有优先执行权利。

-
UID : 代表执⾏者的⾝份
-
PID : 代表这个进程的代号
-
PPID :代表这个进程是由哪个进程发展衍⽣⽽来的,亦即⽗进程的代号
-
普通优先级:100 ~ 139
-
实时优先级:0 ~ 99
-
PRI :代表这个进程可被执⾏的优先级,其值越⼩越早被执⾏
-
NI :代表这个进程的nice值(修正优先级数值的)
其中 PRI(new) = PRI(采用默认值80) + nice
更改进程nice值
通过更改进程 nice 来改变进程优先级 用 top 命令来改变进程 nice 值:
进程竞争、独立、并行、并发
进程切换
在多任务操作系统中,多个进程会共享有限的 CPU 资源。为了实现高效的资源利用和良好的用户体验,操作系统需要在这些进程之间切换 CPU 的控制权。这种切换通常发生在以下几种情况:

Linux2.6内核进程O(1)调度队列
LInux源码查看网站 Linux源码官方网站
Linux2.6内核调度队列

活动队列(array[0])
- 时间片还没有结束的所有的进程都会按照优先级放在该队列
- nr_active:总共有多少个运行的进程
- queue[140]:一个元素就是一个进程队列,相同优先级的进程按照 FIFO(先进先出)的规则排队调度,所以数组下标就是优先级
- bitmap[5]:一共140个优先级,用位图就可以提高非空队列的效率,一个整形占32个比特位,数组大小为5,就有5 * 32 = 160 个bit位来表示队列是否为空,大大提高了查找效率

过期队列
- 过期队列上的进程都是时间片耗尽之后的进程
- 活动队列上的进程都被处理完毕以后,就会对过期队列的进程进行时间片重新计算
active 和 expired 指针
- active指针永远指向活动队列
- expired指针永远指向过期队列
- 活动队列中的进程进程会越来越少,过期队列上的进程会越来越多,因为进程时间片到期就会进入过期队列,但在合适的时间交换 active 指针和 expired 指针的内容,即 swap(&active,&expired) ,活动队列就又有了一批新的活动进程


