目录
Linux---进程学习---1
1.冯诺依曼体系结构
冯·诺依曼提出了电子计算机系统制造的三个基本原则,即采用二进制逻辑、程序存储执行以及电子计算机系统由五个部分组成(运算器、控制器、存储器、输入设备、输出设备,其中运算器与控制器又共同组成为中央处理器CPU),这套理论被称为冯·诺依曼体系结构。
我们常见的计算机,如笔记本。我们不常见的计算机,如服务器,大部分都遵守冯诺依曼体系
截至目前,我们所认识的计算机,都是有一个个的硬件组件组成
输入单元:包括键盘, 鼠标,扫描仪, 写板等
中央处理器(CPU):含有运算器和控制器等
输出单元:显示器,打印机等
关于冯诺依曼,必须注意几点
存储器指的是内存!,一定要注意!
磁盘和硬盘既可以是输入设备也可以是输出设备
不考虑缓存情况,这里的CPU能且只能对内存进行读写,不能访问外设(输入或输出设备)
外设(输入或输出设备)要输入或者输出数据,也只能写入内存或者从内存中读取。
总结一句话,所有设备都只能直接和内存打交道
**对冯诺依曼的理解,不能停留在概念上,要深入到对软件数据流理解上。比如:从你登录上qq开始和某位朋友聊天开始,数据的流动过程。**从你打开窗口,开始给他发消息,到他的到消息之后的数据流动过程。如果是在qq上发送文件呢?
那发文件呢?
其实也是一样的,只不过输入设备不在是键盘,而是磁盘 ,因为文件是存储在本地的磁盘中的,将文件从磁盘传输到内存,经过cpu处理之后,传输给网卡和显示器等输出设备,网卡通过网络将文件数据传输到另外一个用户的输入设备---网卡上,然后再加载到内存中,经过cpu的处理之后,传输给输出设备显示器。[此时用户一般可以选择下载到本地,即将文件从内存中传输到输出设备---磁盘上]
2.操作系统
2.1操作系统的概念
操作系统的概念:
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:
内核(进程管理,内存管理,文件管理,驱动管理)
其他程序(例如函数库,shell程序等等)
操作系统的本质就是一个对软硬件资源进行管理的软件
2.2操作系统的目的
其目的就是:
- 通过对软硬件资源的合理的管理(手段),给用户创造一个良好的(稳定的,高效的,安全的)运行环境(目的)
2.3如何理解管理
操作系统是管理者,那谁是被管理者------其实是软硬件资源,下面是一个图,方便理解。
对于操作系统是如何对软硬件进行管理的理解图,有一个例子可以方便理解:
就像校长就是学生的管理者,校长不需要去接触每个具体的学生,但是仍然能够对学生进行管理,就是通过拿到学生的所有具体的数据,以数据的变化为依据,进行对学生进行管理的重大决策。
经过上图的理解,可以总结出------管理的本质就是对数据继续管理
那管理者是如何不断的拿到被管理者的数据,并根据数据的变化来下达相应的决策,对被管理者进行管理的呢?
这中间就还需要引出一个执行者的概念。
如果是在学校里,执行者可以是辅导员,可以是班长等其他位置,这里就当做辅导员。
如果是在操作系统中,若被管理者是硬件,那么执行者就是驱动
管理者(OS/校长)具体来说是如何管理被管理者的呢?
所有的管理本质的逻辑都是------先描述、再组织【也就是面向对象思维】
将被管理者的数据描述成一个类,然后再将类的对象用数据结构组织起来【将一个个类的对象用某个数据结构存储起来】,通过这个数据结构的一些算法【比如找到增删查改】来对数据进行管理
总结
计算机管理硬件
描述起来,用struct结构体
组织起来,用链表或其他高效的数据结构
2.4计算机软硬件体系的理解
此时有一个问题:**操作系统是不相信用户的,它不会将自己底层的进程管理、文件管理等、以及底层管理的数据开放给用户进行访问。**不然用户如果操作错误,就会导致操作系统崩溃。但是操作系统又需要给用户提供各种服务。不然用户怎么知道自己的硬件是什么情况呢?自己的文件什么情况等。
因此操作系统给用户提供了许许多多的接口来给用户使用。比如用户想往磁盘中存储一些数据,用户是不能自己往磁盘输入数据的,只能将这个申请提交给操作系统的接口,经过操作系统的判断之后,才会往磁盘输入数据。
就像我们不能直接拿钱去将钱存到银行的仓库中,只能交由银行的人员来完成这个操作一样
上面提到的这个操作系统的接口一般叫做系统调用
如果是Linux系统的系统调用,其实就是调用c式的接口、因为Linux系统是由C写的,也就是说系统调用其实就是OS提供的一个C语音编写的函数的调用
但是如果让用户直接去使用系统调用的话,也是不太好的,因为这要求用户对操作系统有一定的了解,不然是无法使用的、因此就会有一些上层的软件,比如指令操作(shell)和编程操作(c/c++)、来帮我们使用系统调用
下面是举例:
在shell中,我们输入ls或者touch等指令,其实都是通过调用shell的接口,这个接口和系统调用会有关系,因此最终还是在调用系统接口来完成对硬件的访问。比如ls就是将这个文件的内容全部输入,那不就需要读取磁盘内的空间并输出到显示器吗,touch指令也需要在磁盘中创建一个空间用于存放新建文件。这需要向磁盘空间输入数据,最终也是系统调用完成的工作、
在写c/c++代码的时候,printf和cout是调用库函数,库函数内部会有系统调用。最终仍然是通过系统调用实现的向显示器(硬件)输出
对于上面描述的计算机软硬件体系可以用一个图片来总结:
这里最底层的硬件需要了解冯诺依曼体系结构、
驱动程序是执行者,它执行操作系统的管理决策,并将硬件的数据变动交给操作系统,便于操作系统管理、
操作系统会将驱动程序先描述成一个类,并用如链表的数据结构将其组织起来、方便做出一个个对外界提供服务的系统接口、也就是系统调用
而在我们写代码或者输入指令的时候,涉及对硬件的访问,都是通过库函数来帮助我们使用系统调用,进而访问硬件、
2.5系统调用和库函数的概念
在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
3.进程
3.1进程是什么
进程------一个被加载到内存的程序
内核观点:担当分配系统资源(CPU时间,内存)的实体。
3.2管理进程
首先程序是一个文件,因此,程序想要被运行,根据冯诺依曼体系结构,存储程序的文件必须要被加载到内存中。然后再被cpu计算,运行程序。但是很多时候,被加载到内存上的程序非常多、
这个时候被加载到内存上的程序非常多,此时就会有很多问题,先运行那个程序?程序的在内存的位置如何分配?那些程序执行过?那些程序没有执行过?有些执行完的程序要销毁释放,有些程序需要多执行几次等等问题。
因此操作系统为了更好的管理这些被加载到内存的大量程序文件,就将加载到内存的程序称作进程,然后管理这些进程。如何管理呢?
前面说了,管理的逻辑就是先描述、再组织。
因此操作系统对进程的管理自然也需要先描述、再组织
3.2.1描述进程-PCB
要相对进程进行管理,需要先将进程描述起来。如何描述呢?
在描述的时候,需要将进程其描述成一个类或者结构体。这个类和结构体中存放着进程的所有属性。
操作系统会用PCB来描述进程
进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
进程控制块称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
其实在Linux下的进程控制块(PCB)就是一个结构体,这个结构体放着进程的所有属性【比如进程的id,进程的优先级,进程的状态,进程的地址、以及各自属性等等】
要注意:这里所说的进程的属性,都是操作系统自己添加的,原本被加载到内存的可执行代码文件是没有这些进程属性的,它只有自己写的代码和数据逻辑
因此每一个可执行程序被加载到内存的时候,操作系统都会将其作为进程并将该进程的所有属性放到PCB中。
将进程描述成PCB之后,就要将PCB组织起来,方便管理
3.2.2组织进程
当内存中有多个进程的时候,就会存在多个进程控制块(PCB),操作系统就会将其用数据结构联系起来,将多个进程控制块用链表之类的数据结构联系起来。这样就实现了组织
这样组织起来了之后,如果cpu需要找到优先级最高的进程来执行,就只需要遍历进程控制块(PCB)的链表,找到优先级最高的进程,并执行就可以了。
如果进程执行完毕需要删除释放,只需要遍历PCB的链表,找到状态为死亡的进程,然后将其代码从内存释放出去,然后删除对应的PCB即可。
3.2.3总结
操作系统对进程管理,不是对进程本身进行管理,而是对进程控制块(PCB)进行管理、
如果是用链表将PCB组织起来,那么对进程管理就转化成了对链表的增删查
内核数据结构 用数据结构将内核结构体(task_struct)组织起来 -> 内核结构体
struct task_struct
-> 内核对象task_struct
的对象->将该结构和你的代码和数据关联起来 ->先描述,在组织的工作
进程 = 内核数据结构(task_struct) + 进程对应的磁盘代码
linux内核的部分PCB(task_struct)的源代码:
这个结构体有300多行。比我在linux写的代码都多了
3.3查看进程
- 大多数进程信息可以使用top和ps这些用户级工具来获取
下面是个例子,当ProCessOn代码正在执行的时候,可以输入ps ajx | grep 'ProCessOn'
来抓取这个进程是否存在
如果想让抓取的进程的信息带上标题可以输入ps ajx | head -1 && ps ajx | grep "ProCessOn"
- 进程的信息可以通过 /proc 系统文件夹查看
输入ls /proc
如果要获取PID为1的进程信息,需要查看 /proc/1 这个文件夹。
注意:
通过/proc来查看进程,这是一个内存级别的目录,可以显示当前正在运行的所有进程的pid,如果一个新的进程被创建,pid号是24641,那么在/proc目录也会出现一个24641的目录,可以进行查看,里面会有该进程的属性信息、
4.与进程有关的系统调用
getpid()
这个是c语言自带的接口,可以实现系统调用,拿到当前进程的pid
pid_t其实就是一个整形类型,按照整形使用即可
这里我写了个死循环程序
执行结果如下:
在另外一个窗口抓取进程
如果想要杀死进程,就输入kill -9 进程pid
即可
getppid()
这个的作用就是获取父进程的pid
当我们运行多几次程序我们就会发现,进程本身是会变的,因为每次被加载到内存,操作系统都会为该进程重新分配pid。
但是父进程是不会变得,我们抓取一下这个父进程pid就知道了
可以看到父进程本身是bash,也就是shell本身
如果将该进程kill掉,那么就会直接退出这个命令行解释器。
- 在命令行上运行的所有程序,其都是子进程,父进程一般情况下都是bash
这样有个好处,子进程有问题不会影响到父进程,父进程可以安全的运行下去
现在又有了一个新的问题,子进程是如何创建出来的,又是如何运行起来的呢?
等到学习进程控制就会明白了。