一台计算机笼统来讲,可以分为四个部分:
1.输入设备:键盘、磁盘、网卡、显卡、话筒、摄像头等 2.输出设备:显示器、磁盘、网卡、显卡、音箱等 3.存储器(内存) 4.运算器&&控制器(芯片即CPU)
由上面的磁盘、网卡、显卡同属于输入设备和输出设备,需要知道输入和输出设备并不是完全独立的工作的!
这里着重解释存储器(内存):
为什么要有内存?先引入一个概念:距离CPU越近的存储设备,运行效率越高!
如果没有内存,那么当用户在外设输入数据时,CPU就只能直接从外设获取数据再进行处理。但是,外设处理数据的速度相对于CPU而言是非常慢的。而由“木桶原理”可知,
而所谓的IO(输入输出)是站在内存的角度而言的,把数据从输入设备存入内存的过程叫做input,把内存中的数据输出到输出设备的过程叫做output。 这里的IO是一个广义的概念,包括本地IO和网络IO。而区分两者的关键在于输入和输出设备(例如磁盘和网卡)。
关于冯诺依曼,必须强调几点:
•这里的存储器指的是内存
•不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
•外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
任何外设,在数据层面,基本先和内存打交道。而CPU在数据层面上,也先和内存打交道。
什么是操作系统?
答:OS是一款专门针对软硬件资源进行管理工作的软件。
补充:OS说白了就是存在磁盘上的软件,只有当它的软件数据和代码被加载到内存中开始工作时,才有意义。
为什么要有操作系统?
答:对下管理好软硬件资源;对上给用户提供稳定的、高效的、安全的运行环境。
操作系统是怎么进行管理的?
答:操作系统并不直接对计算机中的各种硬件进行决策管理,而是根据各种驱动程序中包含的各种硬件的数据进行分析,最终做出决策,再由驱动执行,从而达到管理效果的。(如图)
操作系统如何管理进程?
答:先描述,再组织。
先用描述进程的结构体----PCB(进程控制块)对进程的各种属性进行描述,再用某一种数据结构(红黑树、AVL树、哈希表等)对各种属性数据进行存储。操作系统直接对相应的数据结构中的节点进行管理即可。
(这也是PCB存在的原因)
这里主要是解释上面那张图中用户操作接口和system call这两层。
在一台计算机上,用户并不是直接和操作系统进行交流的,而是通过调用各种接口间接地进行交流的。
总所周知,Linux是用C语言实现的,那么这里的接口实际上也就是C函数,可以理解为接口就是函数的一个高大上的名字而已。
•在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。 •系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
进程是什么?
答:加载到内存中的程序就叫做进程(大多数书本上给出的定义)。
担当分配系统资源(CPU时间,内存)的实体。(内核观点)
这里补充一点:操作系统中是可以由多个进程的,而操作系统对进城实现管理的过程可以简称为先描述,再组织(前面已经解释过了)。每个进程在被创建时,操作系统都要为进程创建PCB(进程控制模块)来保存进程所有的属性。
PCB是什么?
答:进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。课本上称之为PCB(process control block),Linux操作系统下的PCB是task_struct。
进程和程序的区别?
答:程序本质是存放在磁盘中的可执行程序,即.exe文件,是一个文件。而进程则是将程序加载到内存当中,并且由操作系统为其生成一个描述自身性质的数据结构(PCB),两者共同组成“进程”。
再补充一个概念:
CPU不能同时运行多个进程,而我们平常看到的多个进程能同时运行只是一个假象,而CPU是通过短时间内频繁切换正在运行的进程来制造出这种假象的。实际上,每个进程对应的PCB会排列起来形成一个CPU队列,这些PCB就会循环往复的不停交替出现在CPU中,以此实现”多个进程同时运行“。
故有了PCB,所有的进程管理任务与进程对应的程序毫无关系!与进程对应的由内核(操作系统)创建的PCB强相关!
task_ struct内容分类:
标示符(PID): 描述本进程的唯一标示符,用来区别其他进程。 状态: 任务状态,退出代码,退出信号等。 优先级: 相对于其他进程的优先级。 程序计数器: 程序中即将被执行的下一条指令的地址。 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。 I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
这些东西下文中一一讲解。
首先,我们先在Linux中创建一个.c文件和Makefile文件:
内容如下:
接下来开始执行这个进程:
内容开始在屏幕上输出
接下来执行下面指令:
这就是查看进程的指令,而结束进程只需Ctrl+c即可。
标识符:
由上图可知,获取进程PID需要两个头文件,调用响相应函数,最后的返回值就是进程的PID。
对myproc.c稍作修改,内容如下:
执行结果如下:
而结束进程的方法不只有Ctrl+c这一种,还可以执行来结束进程。
而在上面图中,可以看到,一个进程还有自己的父进程,其父进程拥有对应的PPID,而获取PPID只需执行==getppid()==即可,如下:
退出码:在我们写C/C++程序时,main函数的最后总是要return 0,而这个0就是程序的退出码,不同的退出码代表的含义不同,这里不细说。
Linux中,查看进程退出码的指令为。(注意,这里查看的是上一个指令的退出码)
程序计数器:
这个概念其实跟汇编代码中的call指令有异曲同工之妙,这里就不细说了,感兴趣的伙伴可以浏览一下博主之前写的文章《不懂函数栈帧,你敢说你了解C语言吗?》
记账信息:
先补充一个概念:
操作系统中有一个调度模块,它负责比较均衡地为每一个进程分配CPU资源,以使得每一个进程都拥有相对均衡的运行时间。
而记账信息存储的就是每一个进程占用CPU的时间,以及每一次运行时运行的具体程度和产生的效果等信息。
上下文数据(很重要)
我们知道,当我们运行一段C代码时,里面会产生一些临时变量,而这些变量都会被保存在一个个的寄存器中,如下:
同样的,当进程运行时,也会产生一些临时变量,它们也是被保存在寄存器中的。
还需要知道的是,当有多个进程需要执行时,操作系统会为每一个进程设置运行时间片,即每一个进程单次运行时间是固定的,当一段代码需要很长时间才能被执行完时,就需要多次占用CPU。
可是,CPU的寄存器只有一套!当多个进程不断切换时,寄存器中的信息就会被覆盖,当一个进程下一次运行时,之前运行得到的数据不就丢了吗?
为了解决这个问题,操作系统规定,当一个进程运行时间达到规定时间,但并没有运行完毕的时候,进程本身在离开CPU之前,就会将寄存器中自己产生的数据保存起来,当下一次运行时,再将这些数据放进寄存器中,以便自己可以接着运行,而不是从头再来。
通过上下文,我们也可以非常直观地感受到,进程在CPU中是来回切换的!
下面介绍另一种查看进程的方法:
上图中的proc就是Linux中管理进程的目录,每一个进程运行时,都会在其中以自己的id为名字创建自己的目录。
来到这个进程的目录下,可以看到它所有的属性
其中cwd就是进程所在路径,默认是当前路径
而exe就是所谓的可执行程序。
用代码创建进程的方式:
可以看到,代码中只有一个printf语句,但运行结果却有两个。
接下来看一个更神奇的现象:
我们知道,在一段C代码运行的时候,是不可能同时执行已经配对的if else语句的,但是这里却可以。第三个现象:
可以看到,在一个while循环中打印出的两次Id并不相同,
这就表明,在fork之后,又创建了一个进程,两个进程分别在两个执行流中,因此可以分别执行if 和 else,也可以分别进入while循环!
如何理解fork创建进程?
1.在操作系统的角度,用命令行调用进程和自己在代码中用fork调用进程是没有区别的。 2.创建进程的本质就是操作系统中多了一套与进程相关的数据结构(task_struct)。并且默认情况下,创建的子进程或继承父进程的代码和数据。
前面提到,进程是由进程和进程相关的数据和代码组成的。那么创建了子进程之后,对数据会有什么处理呢?
答:进程的代码时共享的,是不可修改的。如果父子进程都没有要修改数据的操作,那么他们就共享一份数据。可如果有一方想要进行修改,操作系统为了保证进程的独立性,就会采用“写时拷贝”的方法,重新拷贝一份数据,这样两个进程就不会相互影响了。
创建子进程就是为了让它和父进程做同样的事情吗?
答:不是的,它们做的事是不同的。当fork创建子进程时,会分两种情况。创建失败时,返回值小于0;创建成功时,给父进程返回子进程的pid,给子进程返回0,如下:
所以,我们可以在一段程序中利用返回值不同,借助if else语句让父子进程做不一样的事
免责声明:本文系网络转载或改编,未找到原创作者,版权归原作者所有。如涉及版权,请联系删