** 🌈个人主页:**秦jh__https://blog.csdn.net/qinjh_?spm=1010.2135.3001.5343
🔥 系列专栏:https://blog.csdn.net/qinjh_/category_12625432.html
前言
💬 hello! 各位铁子们大家好哇。
** 今日更新了Linux的进程内容**
🎉 欢迎大家关注🔍点赞👍收藏⭐️留言📝
冯诺依曼体系结构
- 输入设备:键盘、鼠标、话筒、磁盘、声卡...
- 输出设备:显示器、声卡、磁盘 ....
- 中央处理器(CPU):运算器和控制器
- 存储器:内存
存储器可以预先把输入设备的数据放入到存储器中,预先加入的数据可以预先进行计算,计算好的数据也可以先放到存储器中,等到输出设备需要时,再输出。这样就提高了效率。如果没有存储器,输入设备直接到cpu再到输出设备的话,由于输入、输出设备的速度很慢,效率就很低,尽管cpu的速度很快(木桶效应)。
结论:
- cpu不和外设直接打交道,cpu只和内存打交道。
- 外设(输入和输出)的数据,不是直接给cpu的,而是要先放入内存中。
操作系统
概念
任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。
操作系统是进行软硬件资源管理的软件。
广义的认识:操作系统=内核+外壳周边程序
狭义的认识:只是操作系统的内核。
设计OS的目的
- 与硬件交互,管理所有的软硬件资源
- 为用户程序(应用程序)提供一个良好的执行环境
结构示意图(不完整)
每一种硬件都有对应的驱动层,驱动层是一套软件。驱动层向操作系统提供通信接口,使OS能通过驱动程序访问硬件,对其进行管理。有了驱动层,就不怕更换硬件时,由于硬件的厂商不同,导致OS无法管理了。
理解"管理"
举例:
学校里面有校长、辅导员、学生。校长是管理者,学生是被管理者。校长不会直接与学生接触,校长管理学生只需要拿到学生的数据即可。校长关心的是学习相关的和基本信息,不关注你喜欢吃什么。校长关心的是学生,而不是某个人。学校人多,管理起来难,通过列表格的形式,管理学生的数据。这就相当于c语言里的结构体类型。为了让这些信息都连接起来,就需要通过链表或者其他高效的数据结构链接。当需要找数学成绩最好的学生时,通过查找数学成绩,找到某个结构体对象,就可以知道这个学生的全部基本信息。
任何管理都是:先描述,再组织。
比如:C++和STL,C++的封装就是先描述,STL就是组织。
总结:
计算机管理硬件:
- 描述起来,用struct结构体
- 组织起来,用链表或其他高效的数据结构
用户不能直接访问硬件,硬件由OS管理着。而用户又不能直接访问OS,因为OS里面有重要的数据,所以OS就必须提供一个系统调用接口,用户通过系统调用方式使用操作系统。
打个比方:在银行,客户通过柜台窗口进行存取钱服务,而不是到了银行直接进去银行的系统自行修改数据。
系统调用和库函数概念
- 在开发角度,操作系统对外会表现为一个整体,但是会暴露自己的部分接口,供上层开发使用,这部分由操作系统提供的接口,叫做系统调用。
- 系统调用在使用上,功能比较基础,对用户的要求相对也比较高,所以,有心的开发者可以对部分系统调用进行适度封装,从而形成库,有了库,就很有利于更上层用户或者开发者进行二次开发。
打个比方:来银行办事的人可能是大爷,大爷不知道流程,就会有老人通道进行辅助。这个通道就相当于库,入门的程序员就可以通过库来进行系统调用。
不同的操作系统的系统调用可能会不同,但有了库,即使底层的代码不同,都可以通过相同的函数名调用,比如C语言和C++的标准库,都可以用printf打印。
进程
可执行程序没被运行前,是存在磁盘中的。运行后,程序的二进制代码和数据就会加载到内存中。由于进程可以同时有很多个,就有哪些进程的优先级高等问题,所以在内存中的代码和数据不是进程。OS需要管理很多进程,所以就会有结构体PCB进行管理。每一个进程都有一个PCB。进程=PCB+自己的代码和数据。
基本概念
- 课本概念:程序的一个执行实例,正在执行的程序等
- 内核观点:担当分配系统资源(CPU时间,内存)的实体。
描述进程-PCB
- 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
- 课本上称之为PCB(process control block),Linux操作系统下的PCB是: task_struct
task_struct-PCB的一种
- 在Linux中描述进程的结构体叫做task_struct。
- task_struct是Linux内核的一种数据结构,它会被装载到RAM(内存)里并且包含着进程的信息。
举例:每个人的简历到了hr的手上,每个人是一个实体,而简历就相当于 task_struct 。OS看的是task_struct,而不会直接看代码和数据。
进程=内核task_struct 结构体+程序的代码和数据。
调度运行进程,本质就是让进程控制块task_struct进行排队。
进程状态查看
**ps aux / ps axj 命令 **
如果想查找某个进程的状态,可以使用grep。
可这样就看不到开头第一行的说明了。所以还可以使用&&进行连接,表示同时执行该指令。
&&左右两边执行完再进行合并。
还可以通过 **/proc **系统文件夹查看:
我们运行程序后,通过/proc系统文件夹查看,存在该pid命名的文件夹,里面存的就是文件的属性信息。
exe是可执行程序,也就是说我们刚刚的进程是在这个路径下的process加载形成的。
如果我们把这个可执行程序干掉了,如下图
我们发现程序还在运行,如下图:
原因:因为程序已经运行了,我们删掉的是磁盘的可执行程序。而内存中的还在。
上方中的cwd,指的是进程的当前工作路径。
每个进程在启动的时候,会记录自己当前在哪个路径下启动。
我们把进程跑起来后,它会在当前的工作路径下生成log.txt文件。
如果我们想改变进程的工作路径,需要使用chdir
我们将进程跑起来,
发现工作路径确实被更改了。
创建的log.txt文件也在该路径中。
task_ struct内容分类
- 标示符: 描述本进程的唯一标示符,用来区别其他进程。
- 状态: 任务状态,退出代码,退出信号等。
- 优先级: 相对于其他进程的优先级。
- 程序计数器: 程序中即将被执行的下一条指令的地址。
- 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
- 上下文数据: 进程执行时处理器的寄存器中的数据[休学例子,要加图CPU,寄存器]。
- I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
- 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
- 其他信息
通过系统调用获取进程标示符
PID就是进程的唯一标识符。系统有提供接口来给我们查看。我们通过man getpid 指令查看
使用getpid()时需要包<unistd.h> 和 <sys/types.h> 两个头文件。pid_t 就是无符号整形。
getppid()就是获取当前进程的父进程的id。
上图中,左右两边PID对应得上。
由上图可知,进程每次启动,对应的pid都不一样,这是正常的。但是为什么父进程的ppid都是一样的?
可以看到,bash就是父进程。还记得之前讲过的,shell是媒婆,bash是王婆。bash会创建很多个子进程。
终止进程
ctrl+c是在用户层面上终止进程。
**kill -9 pid **可以用来直接杀掉进程。
通过系统调用创建进程-fork初识
通过**man fork **指令来认识fork。fork的作用就是创建一个子进程。
父子进程代码共享,数据各自开辟空间,私有一份(采用写时拷贝)
while :; do ps axj | head -1 && ps axj | grep myprocess|grep -v grep;sleep 1;done
通过上方指令,我们可以不停打出进程的状态。运行后如下图:
按照代码的逻辑,后面多出了一个子进程。fork之后,父子代码共享。所以打印了两次hello world。
fork返回值
fork会放回两个值,如果创建成功就会返回子进程的pid给父进程,并且返回0给子进程。如果创建失败,就会返回-1。
fork 之后通常要用 if 进行分流 ,如下图:
fork有两个返回值,其实是在fork函数内部创建子进程的时候,子进程已经创建完成了,才到return ,这时候就就是父子进程各自return了。
父子进程具有独立性,互不影响。
创建多个子进程
组织进程
可以在内核源代码里找到它。所有运行在系统里的进程都以task_struct链表的形式存在内核里。
版权归原作者 秦jh_ 所有, 如有侵权,请联系我们删除。