首页 > 系统相关 >Linux系统 —— 进程系列 - 进程的概念,PCB与PID和fork

Linux系统 —— 进程系列 - 进程的概念,PCB与PID和fork

时间:2024-12-08 10:28:34浏览次数:7  
标签:fork 操作系统 PID 内存 进程 struct

目录

1. 进程的基本概念与基本操作

1.1 总结:什么才是进程

2. 描述进程-PCB(process control block)

2.1  task_ struct

2.2 task_ struct内容分类

2.3 组织进程

3. PID 

获取当前进程PID - getpid

获取父进程PID - getppid

如何查看进程

1 进程的信息可以通过 /proc 系统⽂件夹查看

2 我们还可以使⽤top和ps这些⼯具来获取

4. fork - 创建子进程

4.1 为什么fork要给父进程返回子进程的pid, 给子进程返回0呢?

4.2 fork函数为什么会返回两次?

总结:


1. 进程的基本概念与基本操作

课本概念:程序的⼀个执⾏实例,正在执⾏的程序等

   
内核观点:担当分配系统资源(CPU时间,内存)的实体

但是,上面的内容对于我们初学者来说是非常难以理解的,所以我们可以画图来进行理解 

一个程序运行起来叫做进程,未运行起来就是一个二进制文件,存储在磁盘当中,程序运行之前要加载到内存,在操作上就是(./cmd) ,而真正意义当我们执行时cmd就会加载到内存当中去

  

 
这里我们是把一个程序加载到内存,有没有可能我们在操作系统里,在同一时刻我们可以把成百上千给程序加载到内存里呢?答案是一定的

  

而有一款软件是最开始就加载进来了,叫做操作系统

  

  

如上图,我们可以看到在内存中,有很多加载的程序,那么我们就有一个问题:这些程序在内存中的什么位置被加载?代码和数据是否已经被cpu已经执行完毕呢?有没有可能其中的程序需要暂停运行?有没有可能其中内存不够了,需要扩容呢?

  

所以在内存中有这么多加载的内存,它们都需要申请内存和加载内存,所以操作系统要对这些进程,这些代码进行管理,但是我们可以做管理吗?答案是不能,因为操作系统虽然知道里面有代码,但是操作系统并不知道是那个进程

  

但是我们有没有解决方法呢?答案是先描述,再组织

  

     

  

操作系统会在操作系统内部(内核)给每一个代码和数据构建一个struct结构体,然后每加载进来一个程序,操作系统就为该程序创建一个同类型的对象,然后把当前这个进程按照创造的对象填写好信息,填好之后就有了一个对应的节点,而这里每一个节点都有对应的指针可以指向对应的代码和数据

  

与此同时,我们对应的每个节点当中,它的指针还可以指向它的下一个节点,最终在操作系统内我们形成了一个程序列表,我们把这个程序列表叫做进程列表

  

1.1 总结:什么才是进程

  ​​​​​​


2. 描述进程-PCB(process control block)

其实我们上面已经见过PCB了,就是我们上面创建的结构体,在操作系统中所有表示进程的都叫PCB,而这个结构体叫做进程控制块

  

  

进程控制块里包含进程的所有属性,进程的所有属性都可以直接或者间接通过 task_ struct找到

    

进程信息被放在⼀个叫做进程控制块的数据结构中,可以理解为进程属性的集合

  

所以为什么一个进程加载的时候操作系统要给它创建一个对应的PCB即task_ struct对象呢?

      

因为操作系统要管理进程,所以要先描述,再组织,所以必须要有描述进程的task_ struct再组织管理成数据结构,操作系统就会转化成对应数据结构的增删查改

  

操作系统要对进程做管理,那么就必须要对进程的PCB做管理


2.1  task_ struct

Linux中进程控制块PCB-------task_struct结构体结构 - 童嫣 - 博客园icon-default.png?t=O83Ahttps://www.cnblogs.com/tongyan2/p/5544887.html

1. 在Linux中描述进程的结构体叫做task_struct

   

2. task_struct是Linux内核的⼀种数据结构,它会被装载到RAM(内存)⾥并且包含着进程的信息


2.2 task_ struct内容分类

   
1. 标示符: 描述本进程的唯⼀标⽰符,用来和其他进程进行区别

    
2. 状态: 任务状态,退出代码,退出信号等

    
3. 优先级: 相对于其他进程的优先级

   
4. 程序计数器: 程序中即将被执⾏的下⼀条指令的地址

   
5. 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针

    
6. 上下文数据: 进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器]

    
7. I/O状态信息: 包括显⽰的I/O请求,分配给进程的I∕O设备和被进程使⽤的⽂件列表

    
8. 记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等

    
9. 其他信息


2.3 组织进程

所有运⾏在系统⾥的进程都以task_struct链表的形式存在内核里

  

在linux内核中, 最基本的组织进程task_struct的方式, 是采用双向链表进行组织的


3. PID 

获取当前进程PID - getpid

   

进程创建的时候, 里面都有一个自己的PID,我们如何在一个程序运行时获取这个程序的PID呢?

如图所示,操作系统里面上层是系统调用, 下层是内存缓冲区, 这个时候内存中已经缓存了两个进程,PCB对象里面含有PID,我们可以使用ps axj和管道来获取进程的PID

  

但是我们都知道,操作系统不相信我们用户, 所以我们就不能直接访问PCB(task_struct)也就是里面的PID, 状态等, 想要获取这些字段就必须使用系统调用接口

  

如果我们想要获取当前进程的PID的系统调用接口的话,我们就要使用getpid(), 这个函数在哪个进程里被调用, 就会返回哪个进程的PID

   

注意:pid是一个整形, 下面是我们自己定义的一个获取系统调用接口的程序

当前进程为: 


获取父进程PID - getppid

对上面的概念进行试验之后, 我们再来看一下父进程, 也就是PPID 

我们发现,父进程ppid都是不改变的,而pid每次都是变化的,这是为什么呢?

  

我们联想到王婆的例子

  

  

王婆不想给自己的牌子砸了,所以就找了些实习生去解决,不需要王婆亲自去解决了,派实习生去解决,所以我们的-bash就是王婆(父进程)

  

1.运行一个进程时,系统会自动创建bash进程

   
2.命令行再执行所有的程序或者指令时,它所对应的进程,所对应的父进程就是bash本身。我们自己执行的程序或者指令都是bash进程的子进程

   
3.执行出问题的时候,只会是子进程出问题,不会影响bash进程

   
4.我们启动xshell时候,系统自动生成bash进程,显示命令行

如何查看进程

1 进程的信息可以通过 /proc 系统⽂件夹查看

   
如:要获取PID为1的进程信息,你需要查看 /proc/1 这个⽂件夹

proc文件夹里面的目录都是临时文件,当进程开始就会创建一个以这个进程的pid作为名字的文件夹,进程结束的时候就会删除这个文件夹  

我们发现: 这些进程都是目录, 并且这些目录的名字都是数字 

我们可以通过proc文件来查看这个进程更详细的信息

 


2 我们还可以使⽤top和ps这些⼯具来获取

 ps:

top:

在命令行中,执行命令/执行程序,本质是bash的进程,创建子进程,来执行我们的代码


4. fork - 创建子进程

fork是一个系统调用,fork没有参数,有两个返回值

   
父子进程代码共享,数据各⾃开辟空间,私有⼀份(采用写时拷贝)

  

fork函数的本质就是是一个系统调用

上面那张图意思就是说如果fork函数成功了, 那么给父进程返回子进程的pid, 0返回给子进程。 如果失败了, 就返回 -1 给父进程。 并且没有子进程被创建

   

也就是说, fork有两个返回值, 并且这两个返回值的类型都是pid_t, 也就是有符号整形

 运行结果:

我们可以看到每一秒打印一条父进程, 打印一条子进程,这说明父进程和子进程是同时进行的,并且id > 0,   和 id == 0同时成立, 如果在其他的代码中, 这两种情况不可能同时存在,但是在调用的fork下就可以

  

所以在我们fork之后所有的代码都是共享的,只不过父进程认为自己>0,子进程认为自己=0,所以父进程会进入第三个,子进程会进入第二个,所以我们就可以做到父子执行不同的代码块

  


到了这里,我们有几个问题


4.1 为什么fork要给父进程返回子进程的pid, 给子进程返回0呢?

在我们的操作系统里,我们的父进程比上子进程是1:N的,简单来说就是任何一个父进程可以有一个或者多个子进程

   

所以我们在创建子进程时,一定要把子进程的pid返回给父进程,因为父进程需要通过返回的不同的pid来区分不同的子进程,而子进程不需要获得父进程的pid,因为子进程已经能够获得getppid了,所以子进程只需要表明自己成功建立就可以了

  


4.2 fork函数为什么会返回两次?

在这个问题之前我们先讨论另一个问题:一个函数执行到return时,函数的核心功能做完了吗?答案是核心功能已经完成了

  

fork函数的本质就是是一个系统调用

 

如图所示:fork函数创建子进程后, 函数后面的代码就会被子进程和父进程所共享

    

当fork函数里面创建好子进程后(绿色方框部分) 子进程就被创建出来了, 然后执行流就变成了两个(可以使用if或者else或者else if来进行分流), 一个子进程的执行流, 一个父进程的执行流

   

也就是说, 这个时候的return语句, 其实就是由两个执行流会执行它,因为return也是语句,所以会被执行两次,而这两个执行流都会返回一个值,所以这就是为什么fork会有两个返回值, 并且返回值给一个给子进程, 一个给父进程的原因


总结:

  

1. 进程具有独立性,简单来说就是一个进程挂掉了并不会影响其他进程,哪怕是父进程挂掉了也不会影响子进程

  

如果子进程和父进程公用一个数据块, 当子进程改变数据的时候, 父进程也会改变数据,所以 不能让父进程和子进程共享一份数据

   

对于子进程来说数据是独立的,所以当创建子进程的时候要拷贝一份父进程的数据独立出来

  

这个时候父进程崩溃或者子进程崩溃都不会影响对方

 


未完待续~

标签:fork,操作系统,PID,内存,进程,struct
From: https://blog.csdn.net/hedhjd/article/details/144299919

相关文章

  • CEF 渲染进程与主进程的消息传递与事件管理
    在开发基于CEF(ChromiumEmbeddedFramework)的应用时,如何高效地处理渲染进程和主进程之间的消息传递与事件管理是至关重要的。由于CEF本身采用了多进程架构,浏览器的渲染进程、主进程以及其他可能的进程(如扩展进程、插件进程等)需要进行频繁的数据交换与通信。这一过程中,合......
  • Forkify学习笔记
    MVC模式之Controller与View如何通信?用到发布-订阅者模式订阅者(Subscriber):想要响应事件的代码发布者(Publisher):知道何时触发事件的代码View中监听事件发生,真正的事件处理要在Controller里面。现在的问题是:事件处理函数controlRecipe()在Controller当中,用于渲染菜谱;addHan......
  • 线程和进程(juc)
    线程一:概念辨析1:线程与进程进程:1:程序由指令和数据组成,指令要执行,数据要读写,就需要将指令加载给cpu,把数据加载到内存,同时程序运行时还会使用磁盘,网络等资源。进程就是负责管理内存,加载指令,管理io的;2:当一个程序运行时就会将程序的相关代码加载到内存中,这就开启了一个进程......
  • 多线程和多进程的区别与相同
    一:进程的定义 进程是操作系统分配资源的基本单位,每个进程之间的资源互不相通,不进行资源共享(除非使用管道或者其他共享资源的手段),每个进程都有独立的PCB(操作系统用于管理进程的数据结构,包含进程的基本信息,如进程ID、状态、优先级、程序计数器、寄存器集合、进程的内存管理信......
  • gudegg/yunSpider:百度云网盘爬虫
    项目简介yunSpider是一个用于百度云网盘的爬虫项目。它可以获取百度云网盘中的用户订阅、粉丝、分享等信息。项目由Go语言编写,其中Go占比90.1%,TSQL占比9.9%。项目安装与使用安装方面,需要先安装go并设置gopath,然后将项目克隆到gopath目录并安装依赖,如gogetgithub.com/go-sql......
  • 地址空间布局随机化(ASLR,Address Space Layout Randomization) 是一种重要的安全技术,旨
    地址空间布局随机化(ASLR,AddressSpaceLayoutRandomization)是一种重要的安全技术,旨在通过随机化程序和系统进程在内存中的加载位置,从而增加攻击者成功利用漏洞的难度。ASLR是防止许多类型的内存攻击(如缓冲区溢出、ROP(ReturnOrientedProgramming)攻击等)的有效手段。ASLR的工......
  • AIGC项目中的【模板进程】方案的设计实践
    1项目介绍1.1项目背景简单一句话:模板进程是流程的子流程;往往用于比较复杂的aigc项目流程中。由于一个模板有多个流程,一个运营人员可以操作多个流程,也可创建多个流程。在模板推荐时,就会导致不知道是哪次流程。1.2项目目标为了区分模板中流程,就需要增加进程的概念(子流程),为了......
  • vs2012 cmake dll工程 调试dll launch.vs.json 附加到进程
    在VisualStudio中,当你有一个DLL项目并且想要附加调试这个DLL时,你需要指定宿主应用程序(在这个例子中是bt.exe),因为DLL本身不是独立可执行的。以下是如何配置launch.vs.json文件以便附加到bt.exe并调试limit-ml-model.dll的步骤:确定宿主应用程序(bt.exe)的路径:你需要知道bt.exe的......
  • 【多进程】进程:multiprocessing
    Python使用multiprocessing实现多进程_python多进程multiprocessing-CSDN博客使用场景在计算密集型场景下使用进程来实现,因为进程跳过了全局解释器锁(GIL:确保了任何时候只有一个Python线程在执行Python字节码)实现了对CPU多核的利用,实现真正的并行运行。而且进程是计算机下的应用......
  • 【多进程】multiprocessing.Manager
    multiprocessing.Manager() 是Python multiprocessing 模块中的一个功能,它提供了一个服务器进程,该进程可以创建和管理跨多个Python进程共享的对象。这个管理器使得你可以创建像列表(list)、字典(dict)、锁(Lock)、事件(Event)等可以被多个进程安全访问和修改的对象。使用场景共享数......