目录
一、冯诺依曼体系结构
二、操作系统
2.1什么是操作系统?
2.2为什么需要操作系统?
2.3怎么管理?
2.4总结
2.5系统调用
三、进程的理解
3.1进程的基本概念
3.2对进程的描述
3.3对进程的组织
3.4task_struct内容的分类
3.5Linux下查看进程
四、进程的标识符
4.1通过系统调用获取进程标示符
4.2创建进程的方式
4.3fork()理解
4.4bash父进程的理解
一、冯诺依曼体系结构
- 冯诺依曼体系结构规定,计算机是由多个独立的硬件单元用总线连接起来的,包括输入单元、输出单元、存储器(内存)、中央处理器(运算器、控制器)
- 由于存储是分级的,速度越快离CPU越近,但容量越小造价越高,由于数据从外设到CPU的运行速度差别太大,这就要求了CPU访问任何外设数据都必须通过内存
- 数据从外设加载到内存中,再放到CPU中运行,运行结果由CPU放到内存中,再由内存刷新到外设,这样可以缓解CPU访问外设数据的代差
- 任何设备都只能和内存打交道
- 冯诺依曼体系还规定一个程序要运行起来必须加载到内存中运行
- 采用冯诺依曼体系结构的好处是,可以用较小的造价打造一台性价比高的计算机
二、操作系统
2.1什么是操作系统?
操作系统是一款做软硬件资源管理的“软件”
2.2为什么需要操作系统?
操作系统通过对下管理好软硬件资源(手段),对上提供一个良好(安全、稳定、高效)的运行环境
2.3怎么管理?
“先描述再组织”
- 操作系统对数据的管理,遵循六个字“先描述再组织”
- 管理的本质是对数据进行管理
- 操作系统通过驱动层序来获得对应底层的软硬件资源
- 如何描述?定义一个结构体类型,用来描述该数据的全部属性信息,被管理的每一份数据都会填充对应的属性值,创建出结构体对象
- 如何组织?再将每一个结构体对象用链表的形式组织起来,对数据的管理就变成了对链表的增删查改
- 在操作系统中,一定存在着大量的数据结构,操作系统对任何对象的管理,实际上就转化为了对某种数据结构的增删查改
2.4总结
- 描述起来,用struct结构体
-
组织起来,用链表或其他更高效的数据结构
2.5系统调用
- 操作系统中存在着大量的数据,操作系统为了保证数据的安全,又为了能够给用户提供获取数据的服务,操作系统以接口的形式为用户提供调用的入口,让用户能够获取操作系统内部的数据
- 该接口是操作系统内部用c语言实现的函数调用,也叫做系统调用,任何访问操作系统的行为,都只能通过系统调用来完成
-
库函数和系统调用是上下层调用和被调用的关系,可以通过对系统调用接口的封装来形成库,有了库,就可以让上层用户或者开发者进行二次开发
三、进程的理解
3.1进程的基本概念
- 粗浅理解:一个被加载到内存中的程序,或一个正在运行的程序,叫作进程(任务)
- Linux内核中,进程=内核pcb数据结构对象+代码和数据(可执行文件)
3.2对进程的描述
- 在操作系统中,不仅仅只有一个进程在运行,可能有多个进程在运行,这就需要操作系统对进程进行管理
- 我们上面已经提到了,在操作系统中对任何对象进行管理,都要遵循“先描述,再组织”,操作系统对进程的管理也是如此
- 操作系统对进程的管理,就变成了对链表的增删查改
- 在操作系统中,进程在被加载到内存中形成真正的进程前,要先创建描述进程属性的结构体,进程的全部属性信息都被放在一个叫做进程控制块的数据结构中(pcb),每一个进程都会有对象的pcb数据结构对象
- Linux操作系统下的pcb是struct task_struct
- Linux下用task_struct结构体来描述进程,task_struct是Linux下的一种结构体,他会被装载到内存中并且包含着进程的全部信息(进程属性的集合)
3.3对进程的组织
- 采用某种数据结构对每一个pcb对象进行组织
- Linux下采用双向链表来组织进程task_struct对象
3.4task_struct内容的分类
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 进程上下文数据:进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
3.5Linux下查看进程
- 使用top或者ps用户级工具来获取
- 其中ps(Process Status)查看的是进程信息的一个快照,显示的我们执行ps这个命令时进程的信息,top显示的是进程的动态信息,使用这个命令会看到进程信息的动态变化。
如:ps -ajx | head -1 && ps -ajx | grep myprocess | grep -v grep
head -1 来显示进程的属性标头,用grep来过滤我们所要查看到进程信息
-
进程的信息可以通过 ls /proc系统文件来查看
如:查看我们当前父进程的信息,可以查看 ls /proc/17558 -al
exe:指向当前进程的可执行文件的完整路径
cwd:当前进程所处在的工作目录(当进程被运行起来时就会保存)
四、进程的标识符
4.1通过系统调用获取进程标示符
-
获取进程ID( getpid() )
-
获取父进程ID( getppid() )
4.2创建进程的方式
-
运行我们的程序/指令 -- 指令级别
-
fork()创建子进程 -- 代码级别
- fork()返回值:创建子进程成功,给子进程返回0,给父进程返回子进程的pid;创建失败返回-1
- 给父进程返回子进程的pid是为了区分不同的子进程,方便对子进程进行管理和控制;只需要给子进程返回0来标识子进程创建成功即可
4.3fork()理解
- 问题1:为什么需要返回两个不同的返回值?返回不同的返回值,是为了区分不同的执行流,再通过if语句可以让父子进程执行不同的代码块
- 问题2:一个函数时如何做到返回两次的?回答这个问题前,要先明白fork()内部具体做了什么?子进程的创建是在fork()内执行的,每一个进程的创建都要在操作系统中有一个对应的pcb数据结构对象,由fork()函数创建出来的子进程也不例外。子进程的pcb数据结构对象被创建出来了,但是子进程没有自己的代码和数据,所以子进程必须和父进程共享一份代码!!但是由于每个进程具有独立性,相互之间不得干扰,所以子进程还必须通过写时拷贝获得自己的一份私有数据
- 这样就可以回答第二个问题了。fork()由于在return前子进程已经被创建出来了,return这条执行语句属于父子进程共享的,父进程会执行一次,子进程也会执行一次,因而fork()就返回了两次
- 问题3:一个pid_t类型的变量怎么会有两个不同的值?前面已经说到了,父子进程虽然代码共享,但是数据是不共享的!!return的时候其实就是在向返回值写入数据!!由于子进程的数据是写时拷贝产生的,并不和父进程共享一份,所以一个变量会有两个不同的值!
- 问题4:总结fork()的实现?fork()会按照父进程创建一个子进程,子进程和父进程在代码上是共享的,在数据上子进程通过写时拷贝和和父进程各自拥有一份私有数据。由于代码共享,就会导致fork()的return被执行两次,因而就会有两个返回值;return的时候实际上就是在向返回值进行数据的写入,由于数据的写时拷贝,就可以实现子进程和父进程的返回值是不相同的,因而一个pid_t类型的变量就会有两个不同的值
- fork()之后的父进程和子进程,谁先运行是由调度器决定的,不同的调度算法运行顺序不一样
4.4bash父进程的理解
- 所有在命令行提示符后输入的指令都是在bash(父进程)上创建的子进程
- 有多少个终端就会有多少个不同的bash pid