大家好我是沐曦希💕
文章目录
- 一、冯诺依曼体系结构
- 二、操作系统OS
- 三、系统调用和库函数概念
- 四、进程
- 1.概念
- 2.描述进程-PCB
- 3.查看进程
- 4.查看系统调用
- 5.查看进程调用
- 6. 通过系统调用创建进程-fork初识
一、冯诺依曼体系结构
我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系。
计算机包含输入设备、输出设备、存储器、中央处理器(运算器+控制器)
至目前,我们所认识的计算机,都是有一个个的硬件组件组成。
输入设备:键盘、磁盘、网卡、鼠标、摄像头等
输出设备:显示器、磁盘、网卡、各种打印机等
存储器:内存(掉电易失)只能作为临时存储
运算器&&控制器:CPU
磁盘属于外存,具有永久性存储能力。
CPU作为计算机的核心,负责计算,速度最快,寄存器次之,内存在次之,而外设是较慢的。
但是CPU只能被动接受别人的指令和别人的数据,那么CPU必须先认识别人的指令(每个CPU被制作都有自己的指令集),才能执行别人的指令,起到计算别人数据的目的。
上面说到CPU的速度很快,而外设的速度很慢,那么CPU直接从外设拿数据效率就降低了,所以数据是从外设加载到内存中的,然后CPU再从内存中读取数据,CPU处理完的数据再返回给内存(产生了缓存),数据再从内存中加载到外设,这大大提高的数据的处理。
所以CPU再读取和写入时候,在数据层面只和内存打交道,为了整体效率。
内存从外设读取和写入数据的过程成为I/O过程(input/output)
对于冯诺依曼体系,我们应该注意:
这里的存储器指的是内存
在数据层面不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
CPU不和外设直接打交道,和内存直接打交道。外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
所有外设有数据需要载入,只能载入到内存中,内存写出也一定是写到外设中
简单来说,就是所有设备都只能直接和内存打交道,提高整机效率
所以程序的运行必须要加载到内存,CPU执行的代码访问数据,只能从内存中读取(这也是体系结构规定的)。
对冯诺依曼的理解,不能停留在概念上,要深入到对软件数据流理解上,请解释,从你登录上qq开始和某位朋友聊天开始,数据的流动过程。 从你打开窗口,开始给他发消息,到他的到消息之后的数据流动过程。如果是在qq上发送文件呢?
- 发送消息 --> 接受消息 的数据流动:A 的键盘 --> A的内存 --> A的CPU (加密) --> A的网卡 --> B的网卡 --> B的内存 --> B的CPU (解密) --> B的显示器;
- 发送文件 --> 接受文件 的数据流动:A 的磁盘 --> A的内存 --> A的CPU (加密) --> A的网卡 --> B的网卡 --> B的内存 --> B的CPU (解密) --> B的磁盘;
二、操作系统OS
- 是什么
操作系统是一个进行软硬件资源管理的软件,操作系统包含了进程管理,文件管理,内存管理,驱动管理。
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
内核(进程管理,内存管理,文件管理,驱动管理)
其他程序(例如函数库, shell程序等等)
- 为什么
操作系统通过合理的管理软硬件资源(手段),为用户提供良好的,稳定的,高效的,安全的执行环境(目的)。
在整个计算机软硬件架构中,操作系统的定位是: 一款纯正的“搞管理”的软件
- 怎么办
- 管理者不需要与被管理者直接交互,依旧能够很好的将被管理者管理起来
比如在学校学生是被管理者,校长是管理者。管理者不需要和被管理者直接交互,依旧能够把被管理者对象管理起来。
- 管理的本质是对数据进行管理
对于计算机来说,各种硬件对应的驱动就是所谓的执行者,比如网卡有网卡驱动,磁盘有磁盘驱动;操作系统从这些驱动获取硬件数据,然后通过对硬件的数据进行管理实现对硬件的管理;
- 管理的方法是先描述,再组织
可以利用面向对象的思想将学生的数据描述成一个结构体或者一个类,然后每个学生都对应着由这个类/结构体实例化出的一个对象;最后,我们可以使用某一种数据结构 (比如顺序表、链表) 将这些类对象组织起来,形成一个学生信息管理系统;此时,我们对学生数据的管理就转变成了对某一数据结构的增删查改,大大提高了管理的效率。
所有的管理,本质是对数据做管理,管理的方法是先描述在组织
计算机管理硬件
1.描述起来,用struct结构体或者类
2.组织起来,用链表或其他高效的数据结构
总结:
管理的本质是对数据进行管理;
管理的方法是先描述,再组织;
三、系统调用和库函数概念
在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
系统提供调用接口原因:操作系统不相信任何用户 – 操作系统不确定我们是否会对各种软硬件进行违法操作。但是操作系统又必须给上层用户提供各种服务,于是 给用户提供系统调用的接口,即当用户有访问软硬件的需求时,直接调用操作系统提供的接口,然后由操作系统来帮助用户完成对应的工作;这样即满足了用户的需求,又保护了软硬件资源。
四、进程
1.概念
课本概念:程序的一个执行实例,正在执行的程序等
内核观点:担当分配系统资源(CPU时间,内存)的实体。
也有书籍是一个运行起来(加载到内存)的程序时进程。
进程和程序相比进程具有动态属性。
而程序的本质计算文件,存储在磁盘中。
2.描述进程-PCB
当大量的程序加载到内存中,,操作系统用进行管理,会对程序进行先描述,在组织。
而描述进程-PCB
//进程控制块
struct task_struct3
{
//该进程的所有属性
struct task_struct* next;
//该进程对应的代码和属性的地址
}
组织:对进程管理变成了进程对应的PCB进行相关的的管理。而对进程管理转化成了对链表的增删查。
struct task_struct* p1 = malloc(struct task_struct);
p1->..=xx;
p1->addr = 代码和数据的地址;
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
- 课本上称之为PCB(process control block), Linux操作系统下的PCB是: task_struct
先描述,再组织工作 : struct task_struct内核结构体 --> 内核对象task_struct 对象 --> 将该结构体,代码和数据关联起来。
所以 进程 = 内核数据结构(task_struct) + 进程对应的磁盘代码
- 程序和进程
程序的本质是放在磁盘上的可执行文件(.exe文件),就是一个文件,根据冯诺依曼体系,软件运行要加载到内存中,而进程则是将程序加载到内存当中,并且由操作系统进行管理,生成一个描述自身性质的数据结构(PCB),由内核数据结构和进程对应的磁盘代码两者共同组成“进程”
- task_struct
task_struct-PCB的一种,在Linux中描述进程的结构体叫做task_struct。task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息 。
可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。
- task_ struct内容分类
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/ O状态信息: 包括显示的I/O请求,分配给进程的I/ O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
3.查看进程
首先创建一个死循环文件,方便查看进程:
make一下,生成可执行程序,开始执行:
接下来可以查看进程:
进程的信息可以通过 /proc 系统文件夹查看
如:要获取PID为1的进程信息,你需要查看 /proc/1 这个文件夹
大多数进程信息同样可以使用top和ps这些用户级工具来获取
ps ajx | grep "myproc"
ps ajx | head -1
ps ajx | head -1 && ps ajx | grep "myproc"
同时,我们可以杀掉(kill)进程,当然ctrl+c也可以直接结束掉:
kill -9 进程PID
进程在调度运行的时候,进程就具有动态属性。
进程在运行的时候本质是在读取进程内部的代码,内部在执行,从启动到终止中间可能会有一段很长的时间,这个进程就具备了动态属性。
查看进程:
ls /proc/
4.查看系统调用
- getpid()
man 2 getpid
如果指令无法实现,则要下载:
sudo yum -y install man-pages
获取进程PID需要两个头文件,调用响相应函数,最后的返回值就是进程的PID
对myproc.c进行修改:
运行并查看进程:
此外,还有另一种方法查看进程:
在Linux中proc是内存级目录
ls /proc/
数字开头就是进程的pid,一个进程也可以当做文件来看待
ls /proc/进程PID -d
如果程序被删除了,那么进程还会跑吗?
可以看到进程依然可以跑。
5.查看进程调用
- getppid()
获得父进程ID
man 2 getppid
- 创建文件
touch myproc.c
tpuch Makefile
- 运行停掉
可以看到每次进程运行的时候,子进程的ID是变的,父进程的ID不变。 - 查看父进程
ps axj | head -1 && ps axj | grep 父进程PPID
命令行上启动的进程,一般它的父进程没有特殊情况的话,都是bash
以子进程方式运行,遇到问题只与子进程有关,与父进程无关。
6. 通过系统调用创建进程-fork初识
- fork
make fork
- fork第一种用法
运行
fork是一个函数,用于创建子进程,函数执行前只有一个进程,函数执行后有两个进程:父进程和子进程,即打印两条。
即前一个进程的子进程是后一个进程的父进程。
- fork第二种用法
fork()返回类型是 pid_t
同一个变量id,在后续不被修改的情况下,竟然有不同的内容。
(所以对于结果我们很好理解,返回成功时,会给父进程返回子进程的pid,子进程会返回0)。但是,这里居然一个变量id居然会有两个返回值。我们根据这个返回结果写代码:
我们可以看到,两个while循环都执行起来了,这也证明了实际上参与while循环的有两个进程!并且它们是父子进程,即父子关系。
这也说明了,在fork之后,又创建了一个进程,两个进程分别在两个执行流中,因此可以分别执行if 和 else if,也可以分别进入while循环!
总结一句话,fork()之后,会有父进程+子进程两个进程在执行后续代码,fork()后续的代码,被父子进程共享,通过返回值不同,让父子进程执行共享代码的一部分,这就是并发式编程。