1.冯诺依曼体系结构
我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系
这里要注意的是:存储器并不是硬盘,而是指内存。
运算器和控制器统称为中央处理器,也就是我们所说的cpu;
如上图所示,CPU只和内存打交道,输入输出设备也是只和内存打交道,CPU和输入输出设备没有直接的交流
常见的输入输出设备有哪些呢?
-
输入设备:键盘,网卡,硬盘,话筒,摄像头
-
输出设备:显示器,音响,网卡,硬盘
ps:像网卡,硬盘这种硬件是既可以做输入设备又可以做输出设备。他们在不同的情景下扮演不同的角色。以硬盘为例,当你把一个文件写到硬盘上时,硬盘就是一个输出设备。而当你将硬盘上的文件发送给其他人的时候,此时你的硬盘就是一个输入设备
2.操作系统
2.1概念
任何计算机都有一个基本的程序集合,称为操作系统(OS).操作系统是一款进行软硬件资源管理的软件
笼统的理解,操作系统包括:
- 内核(进程管理,内存管理,文件管理,驱动管理)
- 其他程序(如函数库,shell程序等)
2.2为什么要设计操作系统呢?
- 与硬件交互,管理所有的软硬件资源
- 为用户程序提供一个良好的执行环境
根据上图,我们要思考下面几个问题
-
为什么要有驱动层?
这是为了让操作系统和硬件解耦。如果没有驱动层,那么在写操作系统的时候就要考虑兼容底层硬件,可是硬件是要更新换代的,如果每一次硬件的更新都需要重写操作系统,这代价就太大了。所以我(操作系统)索性不管了,驱动由硬件厂商负责,你们写驱动的时候要兼容我,那么这样就大大降低了成本
-
为什么有系统调用接口的存在?
这是操作系统为了保护自身的安全,不会完全对用户开放,只留一部分接口供用户调用。Linux是c语言编写的,其实系统调用接口就是一个个的函数调用
3.如何理解管理?
我们说操作系统是一款进行软硬件资源管理的软件,那么这个”管理“该如何理解呢?
我们先来谈谈驱动管理,下面我们以学生,辅导员,校长为例来理解。
这里面,操作系统就对应着校长,驱动程序就对应着辅导员,学生就对应着硬件资源
下面咱们在理解一下进程管理
4.task_struct内容分类
-
标示符: 描述本进程的唯一标示符,用来区别其他进程。
这里的pid就是本进程的标示符,而ppid是当前进程的父进程的标示符
-
状态: 任务状态,退出代码,退出信号等。关于状态,咱么在下面再细说
-
优先级: 相对于其他进程的优先级。
优先级就是谁先,谁后拿到某种资源。因为CPU的资源是有限的,进程是可以有多个的,所有进程直接必须要有优先级
-
程序计数器: 程序中即将被执行的下一条指令的地址。
在CPU中有一个寄存器eip,里面存的是下一条指令的地址
所以,所谓的函数跳转,分支判断,循环等,都是通过修改eip完成的
每个运行的进程,都有自己的“时间片”,一旦时间片到了,操作系统就会把当前PCB拿下去,换下一个PCB,
-
内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
-
上下文数据: 进程执行时处理器的寄存器中的数据。
CPU内部只有一套寄存器,计算机需要将内存数据移动到CPU的寄存器中,形成当前进程的上下文数据
当一个时间片到了,进程开始切换,可是当前进程还没有完成,那么此时CPU中的寄存器中当前进程的上下文数据为了避免丢失,要把上下文数据放回当前进程的PCB,以便于进程恢复
-
I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
-
记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
-
其他信息
5.fork创建子进程
fork()函数,可以创建子进程。在fork()函数之前的代码,父进程执行,fork()之后的代码,父子进程都可以执行。
fork()之后,就有了两个进程,那么这两个进程谁先被调度呢?
- 这个是不确定的,是由OS的调度算法决定的。
fork()函数会有两次返回值,给父进程返回子进程的pid,给子进程返回0;
#include <stdio.h>
2 #include <unistd.h>
3 #include <sys/types.h>
4 int main()
5 {
6 printf("I am running ....\n");
7 int id = fork();
8 if(id == 0)
9 {
10 //child
11 while(1)
12 {
13 printf("I am child,pid: %d, ppid: %d\n",getpid() ,getppid());
14 sleep(1);
15 }
16 }
17 else if(id > 0)
18 {
19 //father
20 while(1)
21 {
22 printf("I am father, pid: %d, ppid: %d\n", getpi d(), getppid());
23 sleep(2);
24 }
else
27 {
28 //do nothing;
29 }
30 return 0;
31 }
由此可见,父子进程是同时进行的,分别执行不同的代码
6.进程状态
为了弄明白正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时候也叫做任务)。进程的状态信息保存在task_struct里面。
-
R运行状态(running): 并不意味着进程一定在运行中,它表明进程要么是在运行中要么在运行队列里。
一个CPU是可以同时存在多个R进程的,R状态,不代表正在运行,代表可被调度。
系统中的所有R进程,会放在一个队列里面。
-
S睡眠状态(sleeping): 意味着进程在等待事件完成(这里的睡眠有时候也叫做可中断睡眠)。浅度睡眠,随时可以被唤醒,也可以被杀掉
-
D磁盘休眠状态(Disk sleep)有时候也叫不可中断睡眠状态(uninterruptible sleep),在这个状态的进程通常会等待IO的结束。深度睡眠,表示该进程不会被杀掉,即使你是OS
-
T停止状态(stopped): 可以通过发送 SIGSTOP 信号给进程来停止(T)进程。这个被暂停的进程可以通过发送 SIGCONT 信号让进程继续运行。
-
X死亡状态(dead):这个状态只是一个返回状态,你不会在任务列表里看到这个状态。
-
Z僵尸状态(zombie):僵死状态(Zombies)是一个比较特殊的状态。当进程退出并且父进程没有读取到子进程退出的返回代码时就会产生僵尸进程
僵尸进程会以终止状态保持在进程表中,并且会一直在等待父进程读取退出状态代码。所以,只要子进程退出,父进程还在运行,但父进程没有读取子进程状态,子进程进入Z状态。为什么要有僵尸状态?
进程被创建是为了完成某种工作,当任务完成时,调用方需要知道任务被完成的怎么样。进程退出,在系统层面,并不是立即释放曾经申请的资源,而是要暂存一段时间供父进程读取。
-
孤儿进程:父进程已经退出了,而子进程还在运行,这时候子进程就是孤儿进程,当子进程也退出的时候,就变成僵尸进程,可是又没有父进程来回收他,那么就会导致“内存泄漏”,这是绝对不允许的,所以一旦产生孤儿进程,立即被操作系统(1号进程)领养
务被完成的怎么样。进程退出,在系统层面,并不是立即释放曾经申请的资源,而是要暂存一段时间供父进程读取。
- 孤儿进程:父进程已经退出了,而子进程还在运行,这时候子进程就是孤儿进程,当子进程也退出的时候,就变成僵尸进程,可是又没有父进程来回收他,那么就会导致“内存泄漏”,这是绝对不允许的,所以一旦产生孤儿进程,立即被操作系统(1号进程)领养