目录
一、进程概念
(一)什么是进程
(二)描述进程-PCB
二、fork创建进程
(一)bash概念
(二)如何创建子进程
一、进程概念
(一)什么是进程
结论:进程=PCB(内核数据结构)+代码和数据
我们的代码经过编译链接生成可执行程序(代码和数据),这个可执行程序存放在磁盘中,要使其运行起来必须加载到内存中由CPU调度。
内存只有一个但可执行程序可能有多个,加载到内存遵循先描述再组织,内存收到可执行程序时会为每个程序自动创建一个struct pcb数据结构,把这个可执行程序的内部属性(变量、函数)描述起来,也就是获取其数据,在每个结构体内定义一个指针指向下一个可执行程序的结构体,这样就形成一个链表,对进程的管理就转换为对链表的增删查改。而struct pcb称为进程控制块!!
操作系统对进程管理方法:先描述再组织,进程=PCB(内核数据结构)+代码和数据,具体Linux下的进程控制块叫:struct task_struct;进程排队本质是让PCB节点排队。
(二)描述进程-PCB
思路:整个进程最主要的就是PCB,学习进程从了解PCB开始
上面讲到进程信息被放在一个叫进程控制块的数据结构中,接下来就来聊聊CPU中这个数据结构task_ struct的细节。
(1)task_ struct的组成
1. 标识符:进程的id,描述本进程的唯一标识符
2. 状态:描述进程目前处于那种状态(任务状态,退出代码,退出信号等)比如你有学习状态、游戏状态
3. 优先级:进程多了需要区分谁先被调度
4. 程序计数器:存放程序中即将被执⾏的下⼀条指令的地址。
5. 内存指针:包括程序代码和进程相关数据的指针
6. 上下文数据:进程执行时CPU寄存器中存储的临时数据
1.1 细节详解(4/5/6)
程序计数器、内存指针:
进程被CPU调度时,CPU内会产生程序计数器和内存指针。CPU其实是很笨的你给什么它就执行什么,这时CPU要执行进程就会问内存指针:我要从该进程的哪里开始执行啊?内存指针就会问程序计数器:CPU要执行了,告诉我要执行的代码地址;这时程序计数器就会把它存的指令地址给内存指针,然后自动加到下一条要执行指令地址,这样CPU就知道从哪里开始执行了。
1.2 上下文数据:
一个进程可能被长时间运行,运行时CPU内会产生寄存器(寄存器是硬件)这些寄存器存放当前进程的执行指令、变量、返回值、栈顶、栈底等。寄存器在CPU内只有一套,进程关心的永远是寄存器里面的内容,因为内容是进程自己的数据!!!称为进程的硬件上下文!!
进程在执行的时候是要被切换和调度的,当进程被切走后它会把自己的上下文数据保存在PCB带走,等再次被调度时再把数据放回去寄存器中,从上次没执行完的地方开始继续执行。
(2)查看进程
输入指令:/proc
补充知识:当前路径
如果用fopen打开一个不存在的文件夹,默认会在当前路径下新建。
什么是当前路径?
可执行程序二进制文件所在处就是当前路径!进程启动后PCB会自动记录下自己的cwd(当前路径),所以创建文件时,操作系统默认是在当前路径后加上要创建的文件名。
二、fork创建进程
思路:了解进程的id—>父进程id—>bash如何创建子进程—>通过fork—>fork介绍
(一)bash概念
每个进程都有自己唯一标识id,可以通过getpid()获取到进程id,进程在创建时也一定有自己的父进程id,通过getppid()获取到父进程id。getpid()和getppid()是两个函数接口,通过调用获取到task_ struct里面的id数据。
Linux系统中一个进程可以直接创建个子进程,子进程的task_ struct除了记录自己id外还要记录父进程id。所以:Linux系统增多进程是通过父进程创建子进程的方式让Linux系统中的进程变多。
我们在命令行启动我们程序时它的父进程全部是bash,每启动一个进程都是bash创建的子进程来执行我们的代码。
(二)如何创建子进程
在bash中创建子进程通过fork命令,也就是说子进程都是bash通过fork创建出来的。而bash是一个父进程,当它执行fork()时会自动分成两个分支!!!
以下是fork创建子进程代码:
从上图可以验证:bash通过fork创建出一个个子进程!!
也许大家会有疑问执行完fork有两个输出,两个返回值,一个函数为什么两个返回值?
可以去查一下fork的返回值,会发现fork成功后,父进程返回子进程id,子进程返回0。
(1)fork返回值为什么是这样的?
每子进程都由父进程创建,对与父进程来说它有多个孩子,要找其中一个不好找,所以返回子进程id父进程才能找到孩子,而每个孩子都唯一只有一个父进程getppid就行。
(2)一个函数为什么有两个返回值?
在Linux中创建一个新的子进程不是那么简单的,被创建的进程必须包含内核数据结构(task_ struct)和代码数据结构。fork创建的子进程默认共享父进程的代码和数据,子进程的task_ struct也以父进程为模版初始化,可能根据需要,继承下来后做部分改动成为自己新的task_ struct。所以当两个运行完之后各自返回也就有两个返回值了。
希望利己同时也利你,感谢观看!