Liunx进程
这篇博客,我们介绍进程的一些基础概念
首先,在提到进程之前,我打算从“管理”这个词入手,再接着引入进程的概念。
冯诺依曼体系结构
首先,大家可以想象一下,我们从键盘输入数据,接着显示器上显示出数据,是个怎样的实现过程呢?它并不是一步到位的,而是经过了下面冯诺依曼体系结构从而做到的,先上图。
在这上面,输入设备有我们常说的键盘,鼠标,摄像头,话筒,等等,输出设备有显示器等等。中央处理器也就是我们常说的CPU,它包含了运算器和控制器,存储器就是我们的内存部分。
我们从键盘上输入数据,到显示器上显示数据。实际上是输入设备键盘先将数据和代码给到存储器,CPU对数据进行读取后,再给到存储器,由存储器再给到输出设备显示器上显示出来。
数据要在计算机体系结构中进行流动,流动过程中,对数据进行加工处理。从一个设备到另一个设备的过程本质是一种拷贝,那么数据设备间拷贝的效率,影响计算机整机基本效率。
接着说说冯诺依曼体系结构的优势之处:
之所以数据要通过内存进行传输,是因为输入设备和输出设备的运行效率实际会很低,通过经手内存,CPU再经过快速的计算,可以提高传输的效率。
在该体系中规定,数据不直接与CPU打交道,而是先和内存进行打交道。也就是说输入和输出设备的数据一定经内存再到CPU的,而不是直接到达CPU。
操作系统
接着浅浅聊聊操作系统,并通过操作系统我们要引进“管理”的概念。
但我们先区分一下:
操作系统和CPU的区别
CPU和操作系统是密切相关的。①操作系统运行在硬件系统之上,通过CPU指令集与硬件进行通信和控制。②操作系统为应用程序提供硬件抽象层和标准接口,使得应用程序可以方便地运行。③操作系统也负责调度进程、分配内存、处理中断等任务。④操作系统有最高的特权级别,可以控制应用程序的权限和对CPU的访问。⑤CPU执行操作系统和应用程序的指令,完成各种计算和处理。
简单的说,CPU要负责执行操作系统所发出的指令。那么对于上面操作系统功能的描述,看完接下来对于操作系统的描述后会更加清晰。
操作系统的实现原理
操作系统包括操作系统的内核+操作系统的外壳周边程序,是一个进行软硬件资源“管理”的软件。
这里借用一张图给大家进行说明:
操作系统对下面向硬件,对上面向用户。
这里注意两个点就好:
①操作系统向下对硬件的管理是通过驱动层访问到硬件的。
因为操作系统只有一个,但硬件会有很多个,很多种。 在更新硬件或操作系统时,为了使得两者之间的兼容性,也就是不造成更新其一,得两者都实现更新的情况。我们有了驱动层进行隔离。通过驱动层对硬件管理起来,每个硬件在驱动层都有一个接口,通过这个接口对接操作系统。这样就把两者都给分开了!
②操作系统向上对用户通过系统调用接口对接用户,用户不允许直接访问到操作系统。
因为用户如果直接访问操作系统的话,万一对操作系统进行了数据修改,将造成不可挽回的损失。所以有了系统调用接口,用户要实现的指令,通过这个接口连接到操作系统进行实现。
为什么要有操作系统?
操作系统的本质是向下对软硬件进行管理,向上为用户提供安全、高效、稳定的运行环境。
管理的本质
在操作系统中所谓的内存管理,进程管理等等。“管理”的概念引进:管理的本质是先描述再组织
例如,在学校校长对学生进行管理,实际上不会时常见到学生的人,对学生管理的手段是通过获取到学生的一些基本信息进行管理,学号,姓名,电话等等。对学生的成绩进行管理,也是去获取到学生成绩的数据。 通过先描述(得到数据),再实现组织(进行学生管理)。
有了管理的理念,我们就可以引进进程了。
进程
概念:进程就是程序的一个执行实例,也就是正在执行的程序,它担当的角色是分配系统资源的实体。
进程不止于可执行程序的代码和数据,它还包括一个PCB的概念。
进程 = PCB + 自己的代码和数据
PCB是进程控制块,是操作系统管理控制进程运行的信息集合。操作系统用 PCB 来描述进程的基本情况以及运行变化的过程。
接着上图,看看进程PCB的抽象图
上面说过,硬盘通过内存再到CPU。 再这里的PCB方便大家理解,大家可以去联想上面说的“管理”的概念。在这里,为了对内存中的这些数据进行管理,PCB相当于获取到这些数据的一些属性,从而便于了对这些数据的管理。
每个进程都有对应的一个PCB!
PCB中有一个类似于 struct PCB* next 的指针这也说明了我们对进程的管理就可以通过PCB来进行对应的管理。进一步理解,我们对进程的管理就可以理解成对PCB这个链表进行增删查改。
在Linux中,我们把这个结构体取名为task_struct
进程的动态运行
进程的动态运行,本质是通过调度运行进程,让进行控制块进行排队。
pid和ppid
假设有一个可执行程序myprocess
我们执行 ./myprocess 实际上就是启动运行了这个进程。
对于每一个进程都要有一个唯一的标识符,这个标识符就是进程pid。
pid 是pid_t类型的,也就是unsigned int 无符号整数。
如果我们想要获取pid,我们可以使用getpid()的方式。因为我们不能直接访问操作系统,所以我们得通过系统调用接口,也就是使用getpid()的方式获取pid。
一个进程除了pid,还有ppid,也就是父进程的pid值。那么我们使用的接口就是getppid()获取ppid。
每次启动时,对应的pid都不一样,这属于正常的情况!
父进程的代码和数据是从磁盘中加载出来的。子进程默认继承父进程的代码和数据。
可是有父进程可以直接执行了,为啥还要创建子进程呢?
有了子进程,父子进程就可以执行不同的操作。
1 #include<stdio.h>
2 #include <sys/types.h>
3 #include <unistd.h>
4
5 int main()
6 {
7 printf("i am a father process,pid:%d,ppid:%d\n",getpid(),getppid());
8 sleep(5);
9 pid_t id = fork(); //这里我们使用fork()函数来实现父子进程执行不同的操作
10 if(id == 0)
11 {
12 printf("i am a child process,pid:%d,ppid:%d,我返回的值是:%d\n",getpid(),getppid