Linux进程【补充】

文章目录

  • 进程概念
    • task_struct
  • 进程创建
    • fork
    • vfork
    • 写时拷贝
  • 进程状态
    • 僵尸进程
    • 孤儿进程
    • 守护进程
  • 进程地址空间
    • 是什么
    • 为什么
    • 怎么做


进程概念

进程是一个程序的执行实例或者是担当系统资源分配的实体。当一个程序运行时,被从硬盘加载到内存中,操作系统为每个程序运行定义了描述信息,通过这个描述信息来控制和管理程序的运行,因此对于操作系统来说,pcb就是进程,而Linux的pcb是task_struct。所有运行在系统里的进程都以task_struct链表的形式存在内核里。

在这里插入图片描述

进程 = 内核关于进程的数据结构 + 当前进程的代码 + 数据

task_struct

task_struct是Linux内核中用于表示进程(或线程)的数据结构。在Linux内核源代码中,task_struct结构体定义了进程的各种属性和状态信息,包括进程的标识、调度信息、内存管理信息等。

  1. 进程标识符(PID): 每个进程在系统中都有一个唯一的PID,用于区分和标识不同的进程。

  2. 进程状态: 进程可能处于运行态、就绪态、睡眠态、停止态等不同的状态,task_struct中会记录当前进程的状态。

  3. 进程调度信息: 包括进程的优先级、调度策略、调度器相关的信息等。

  4. 进程描述符: 描述进程的各种属性和信息,如进程名称、用户标识、组标识等。

  5. 进程的父子关系: 指向父进程和子进程的指针,用于建立进程之间的父子关系链表。

  6. 地址空间信息: 包括进程的虚拟地址空间、页表信息等。

  7. 文件描述符表: 记录进程打开的文件以及文件的状态信息。

  8. 信号处理器: 记录进程注册的信号处理函数以及当前信号的状态。

  9. 调度器相关信息: 与进程调度相关的一些附加信息,如调度实体、调度策略等。

  10. 内存管理信息: 包括进程在内存中的分配情况、内存页的状态等。

进程创建

fork

fork()函数用于创建进程。当一个进程(父进程)调用fork()函数时,它创建了一个新的进程(子进程)。这个新的子进程几乎是父进程的完整副本:它继承了父进程的地址空间、进程环境、打开的文件描述符等资源。不过,父进程和子进程有不同的进程ID,并且它们的内存是分开的。

当调用fork()时,操作系统做了以下几步:

  1. 分配新的进程ID(PID)给子进程。
  2. 复制父进程的地址空间到子进程,包括代码段、数据段和堆栈。
  3. 复制父进程的进程控制块(PCB),包括文件描述符、信号处理方式等。
  4. 为子进程分配独立的内存空间。

fork()函数的返回值:

  • 对于父进程fork()返回新创建子进程的进程ID(PID>0)。
  • 对于子进程fork()返回0。
  • 错误情况:如果出现错误,fork()返回-1,并设置errno以指示错误原因。

注意事项

  • fork()之后,父子进程各自独立执行,但是由于地址空间的复制,它们会从fork()调用的下一条指令开始执行。
  • 父子进程间共享文件描述符,如果不希望共享,需要在fork()之后,exec()之前调用close()
  • fork()可能会因为系统资源不足而失败。
  • 创建进程后,父进程通常会调用wait()waitpid()等待子进程结束,避免僵尸进程的产生。

vfork

vfork()用于创建一个新进程,但是与fork()有所不同。vfork()是"虚拟fork"的缩写,它与fork()类似,但是设计用于创建新进程的方式不同。

vfork()fork()的区别主要在于 fork()操作可能会导致内存的复制。而在vfork()中,子进程共享父进程的地址空间。子进程会暂时阻塞父进程的运行,直到调用exec()exit()为止。由于子进程与父进程共享内存空间,所以对内存的修改会影响父进程,因此子进程通常需要立即调用exec()来替换自己的内存空间,或者直接调用exit()来结束自己的执行。

注意:

  • vfork()适用于需要在子进程中立即调用exec()来执行新程序的场景,因为子进程共享父进程的内存空间,所以对于子进程而言,任何内存修改都会影响到父进程,这样可以减少内存的复制和性能开销。

  • 一般来说,如果没有立即调用exec()exit(),而是在子进程中执行其他操作,可能会导致父进程和子进程的行为变得不可预测,因为它们共享相同的内存空间。

写时拷贝

写时拷贝(Copy-On-Write,简称COW)是一种用于资源管理的优化策略,广泛应用于操作系统中,尤其是在进程创建(如fork()系统调用)时。在没有写时拷贝技术之前,fork()调用会直接复制父进程的整个地址空间到子进程,这个过程不仅耗时,还会消耗大量的系统资源。写时拷贝技术的引入,显著优化了这一过程。

在使用写时拷贝技术时,fork()调用并不立即复制父进程的物理内存页面到子进程。相反,操作系统使父进程和子进程共享同一物理内存页面,页面的权限被设置为只读。如果父进程或子进程尝试写入这些共享页面,操作系统会捕捉到这一写操作尝试,并仅为执行写操作的进程复制被写的页面,在必要时才会进行数据复制。这种延迟复制的策略极大地提高了系统的效率和性能。

优点:

  • 减少不必要的数据复制
  • 提高效率和性能
  • 优化内存使用
  • 简化编程模型

进程状态

在Linux系统中,进程的状态是通过进程控制块中的状态字段来表示的。主要包括以下几种:

  • 运行(R, Running or Runnable): 这个状态意味着进程要么正在CPU上运行,要么在等待队列中准备运行。换句话说,这些进程是准备执行的。
  • 中断睡眠(S, Interruptible Sleep): 进程因等待某个条件而睡眠,如等待输入/输出操作完成。在这个状态下,进程会睡眠直到等待的事件发生,但可以被信号(signal)唤醒。
  • 不可中断睡眠(D, Uninterruptible Sleep): 这也是一种睡眠状态,但进程不能通过信号唤醒。这通常发生在进程等待某些硬件操作完成时,比如磁盘I/O。
  • 暂停(T, Stopped or Traced): 进程已经被停止,通常是因为接收到了停止信号。这个状态的进程可以通过接收SIGCONT信号重新进入运行状态。
  • 僵尸(Z, Zombie): 进程已经结束,但其父进程还没有通过调用wait()来回收其资源。在这个状态下,进程释放了除进程控制块之外的所有资源,等待父进程读取其退出状态。
  • 死亡(X, Dead): 这个状态比较少见,表示进程已经被完全销毁,但在某些情况下,可能会在进程列表中短暂看到。

在这里插入图片描述

僵尸进程

僵尸进程产生的主要原因是父进程没有及时处理子进程的退出状态。当一个子进程终止时,内核会向父进程发送一个信号,告知子进程已经退出,并且等待父进程调用 wait()waitpid() 来获取子进程的退出状态。如果父进程没有处理这个退出状态,子进程就会变成僵尸进程。

僵尸进程可能产生的一些原因包括:

  1. 父进程没有正确地调用 wait()waitpid() 函数来等待子进程的退出状态。 父进程可能忙于处理其他任务,或者没有正确处理子进程退出的情况,导致子进程变成僵尸进程。

  2. 父进程被阻塞,无法处理子进程的退出状态。 如果父进程正在执行一些阻塞操作,例如等待 I/O 完成,那么它可能无法及时处理子进程的退出状态。

僵尸进程的存在可能会对系统造成一些危害:

  1. 资源浪费: 僵尸进程在系统进程表中占用资源,虽然不再执行任何代码,但仍然占用一些系统资源,例如进程标识符(PID)等。

  2. 影响系统稳定性: 如果系统中存在大量的僵尸进程,可能会消耗系统的资源,并且可能影响系统的性能和稳定性。

  3. 影响进程管理: 如果系统中存在过多的僵尸进程,会使得进程管理变得困难,特别是在需要监控和管理系统进程的情况下。

为了避免僵尸进程的产生和危害,父进程应该在子进程终止后及时调用 wait()waitpid() 函数来等待子进程的退出状态,并确保对子进程进行适当的处理。这样可以及时释放子进程所占用的系统资源,并保持系统的稳定性和可靠性。

孤儿进程

孤儿进程是指其父进程先于它结束,由 init 进程(PID 为 1)接管的进程。在 Unix/Linux 系统中,每个进程都有一个父进程,当父进程退出或终止时,内核会将孤儿进程的父进程设置为 init 进程。

孤儿进程的产生可以由以下几种情况引起:

  1. 当一个父进程创建了一个子进程后,如果父进程先于子进程退出,并且子进程还在运行,那么子进程就会成为孤儿进程。
  2. 如果一个进程是由 init 进程直接启动的,而 init 进程不会退出,那么这个进程本身就是一个孤儿进程。

孤儿进程并不会被立即回收,而是会继续运行,直到它自己退出或被系统进程管理器接管。当孤儿进程退出时,它的资源会被释放,但是其进程号(PID)会一直保留在系统中,直到父进程调用 wait()waitpid() 来获取退出状态。

孤儿进程的存在并不会对系统造成太大的影响,但是如果大量孤儿进程堆积在系统中,会占用系统资源。

守护进程

守护进程(Daemon Process)是在后台运行的一种特殊类型的进程,通常在系统引导时启动,并且在系统关闭时终止。它们在操作系统启动时启动,通常不与任何控制终端关联。

守护进程通常用于执行系统服务或后台任务,它们独立于用户会话并在后台默默地执行工作。守护进程的特点包括:

  1. 没有控制终端: 守护进程通常不会与任何控制终端(如终端或控制台)关联,因此它们不接收或发送任何与终端相关的输入或输出。

  2. 独立于用户会话: 守护进程不受用户登录或注销的影响,它们在系统启动时启动,在系统关闭时终止。

  3. 后台运行: 守护进程在后台默默地执行任务,通常不会产生交互式的用户界面。

  4. 执行系统服务或后台任务: 守护进程通常用于执行系统服务,如网络服务、日志服务、定时任务等,或者执行后台任务,如定期备份、监控等。

  5. 通常以超级用户权限运行: 一些守护进程需要特殊的权限才能执行其任务,因此它们通常以超级用户(root)的身份运行。

编写守护进程的关键是将进程脱离控制终端,并且使之成为后台进程。通常涉及到以下几个步骤:

  1. 调用 fork() 创建子进程,然后父进程退出,使得子进程成为孤儿进程。
  2. 调用 setsid() 函数创建新的会话,并使进程成为会话组的首领,脱离控制终端。
  3. 改变当前工作目录,防止守护进程占用某个挂载点而导致其无法卸载。
  4. 关闭不需要的文件描述符,防止其在后续操作中产生问题。
  5. 执行守护进程的核心任务。

进程地址空间

是什么

Linux进程地址空间是操作系统为每个正在运行的进程分配的虚拟内存空间。它是进程所能访问的全部内存区域的抽象表示,包含了进程运行时所需的代码、数据、堆、栈等内容。

在这里插入图片描述

Linux进程地址空间通常被划分为以下几个主要部分:

  1. 代码段(Text Segment): 代码段存放着可执行程序的指令代码。在Linux中,这部分内存通常是只读的,以防止程序意外修改自身的指令内容。

  2. 数据段(Data Segment): 数据段存放着已初始化的全局变量和静态变量的内存空间。这些变量在程序开始时就已经分配了内存空间,并且可以在程序的整个生命周期中使用。

  3. 堆(Heap): 堆是动态分配内存的区域,程序可以在运行时通过调用 malloc()calloc()realloc() 等函数从堆中分配内存。堆的大小在程序运行过程中是动态变化的,取决于程序的动态内存分配和释放操作。

  4. 栈(Stack): 栈用于存储函数调用期间的局部变量、函数参数、返回地址等信息。每次函数调用时,都会在栈上分配一段内存空间,函数返回时则释放这段空间。栈的大小在程序启动时就确定了,并且通常比堆小得多。

  5. 内存映射区域(Memory-mapped Regions): 这部分内存用于存储程序加载的动态链接库、共享库、内存映射文件等内容。它们被映射到进程的地址空间中,使得程序可以直接访问这些内容。

  6. 其他: 除了以上主要的部分外,Linux进程地址空间还可能包括一些其他区域,例如进程环境变量、命令行参数、进程控制块等。

Linux进程地址空间的管理由操作系统内核负责,它通过虚拟内存管理来管理和分配进程的地址空间,以及实现内存保护和地址映射等功能。每个进程都拥有自己独立的地址空间,使得多个进程之间的内存访问彼此隔离,提高了系统的安全性和稳定性。

为什么

进程地址空间在操作系统中起着至关重要的作用,没有它会导致诸多问题:

  1. 内存隔离: 进程地址空间使得每个进程拥有独立的内存空间,进程之间的内存互相隔离。这种隔离确保了进程的安全性和稳定性,防止了进程之间的相互干扰和不受控制的内存访问。

  2. 内存保护: 进程地址空间允许操作系统实现内存保护机制,通过设置页面权限和地址空间布局,防止进程对其他进程的内存区域进行非法访问或修改。这有助于防止恶意软件攻击和进程崩溃。

  3. 资源管理: 进程地址空间使得操作系统可以更好地管理系统资源,包括内存、CPU 时间和文件描述符等。操作系统可以根据进程的需要动态调整地址空间的大小和布局,以优化系统性能和资源利用率。

  4. 动态内存分配: 进程地址空间允许程序在运行时动态地分配和释放内存,从而实现灵活的内存管理。程序可以根据需要动态调整堆和栈的大小,以适应不同的运行环境和内存需求。

  5. 共享内存和通信: 进程地址空间为进程间通信提供了基础,允许进程通过共享内存等机制进行数据交换和通信。这种通信方式在多进程和多线程编程中非常常见,可以提高程序的效率和并发性。

怎么做

当源代码经过编译、链接等处理后,生成了 ELF(Executable and Linkable Format)格式的可执行文件。当用户执行一个 ELF 格式的可执行文件时,操作系统会创建一个新的进程,并为其分配独立的进程地址空间。进程地址空间是操作系统为进程分配的虚拟内存空间,用于存放程序的代码、数据、堆、栈等内容。

ELF 文件是一种标准的可执行文件格式,在 Unix、Linux 和类 Unix 系统中广泛使用。它包含了程序的代码、数据、符号表、重定位表等信息。

ELF 格式可执行文件在进程地址空间中的运行过程如下:

  • 当用户执行一个 ELF 可执行文件时,操作系统会加载该文件到内存中,然后将其内容映射到新创建的进程地址空间中。
  • ELF 文件的代码段被映射到进程的代码段,数据段被映射到进程的数据段,全局变量和静态变量等被放置在数据段中。
  • 堆和栈被初始化并分配给进程,堆用于动态内存分配,栈用于函数调用和局部变量存储。
  • 进程的地址空间中还可能包括共享库、内存映射文件等内容,它们也被映射到进程的地址空间中。

由上就可以知道操作系统运行程序的步骤大概如下:

  1. 加载 ELF 文件:

    • 操作系统的程序加载器(如 Linux 上的 ld-linux.so)首先解析 ELF 文件头,确定程序入口点(entry point),代码(.text)段、数据(.data 和 .bss)段等必要信息。
    • 加载器将 ELF 文件中的各个段映射到进程的虚拟地址空间中。代码段通常是只读的,数据段可以是可写的。
  2. 地址空间映射:

    • ELF 文件中定义的虚拟地址在此步骤中被映射到进程的虚拟地址空间。这涉及到将程序的代码和数据加载到内存中的适当位置。
    • 对于动态链接的应用程序,加载器还负责解析和加载所有需要的共享库(.so 文件),并将它们映射到进程的地址空间中。
  3. 重定位:

    • ELF 文件可能需要重定位,以便调整代码和数据中的某些引用,使它们指向正确的地址。对于动态链接的应用程序,这也包括解析符号引用,将它们绑定到实际的内存地址。
  4. 初始化:

    • 在开始执行程序之前,操作系统或运行时环境可能需要执行一些初始化操作,例如初始化运行库、设置堆、处理环境变量和命令行参数等。
  5. 执行:

    • 一切准备就绪后,控制权被转移给程序的入口点(通常是一个名为 main 的函数)。此时,程序开始执行其代码。
    • 程序运行过程中,可以进行函数调用、内存分配、输入输出操作等。
  6. 终止:

    • 程序执行完毕后(例如 main 函数返回),或者调用退出(如 exit)函数,程序开始终止过程。
    • 在终止过程中,操作系统负责清理资源,关闭打开的文件描述符,释放内存等,最终结束进程。

整个过程是由操作系统的内核、程序加载器、运行时库共同协作完成的。这确保了程序能够被正确地加载、执行,并在执行完毕后,相关资源能够被妥善地释放。

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/408881.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

python 线程笔记一 (概念+示例代码)

1. 线程的概念 线程,可简单理解为是程序执行的一条分支,也是程序执行流的最小单元。线程是被系统独立调度和分派的基本单位,线程自己不拥有系统资源,只拥有一点儿在运行中必不可少的资源,但它可与同属于一个进程的其它…

第10届蓝桥杯Scratch图形化编程 选拔赛初级组编程题1:小猫走城堡

准备工作: 导入育最库中的“Castle 3 小猫从坐标点(-165,-93)出发向城堡走去。随着位置的移动,角色大小逐渐变小,最后在城堡前消失。注意1.角色大小在逐渐变化,运行结束再次点击绿旗,程序应还能再次扶行。2.角色应该是…

云原生之API网关Traefik

1. 前言 说到web服务的开源网关,我首先想到的是Nginx,最早使用的就是它,现在都还在使用它。系统上线了Docker Swarm集群之后,不继续使用Nginx直接做Docker服务的网关,是因为Nginx毕竟比Docker Swarm出现的早&#xff0…

YOLOv7基础 | 第2种方式:简化网络结构之yolov7.yaml(由104层简化为30层)

前言:Hello大家好,我是小哥谈。通过下载YOLOv7源码可知,原始的yolov7.yaml文件是拆开写的,比较混乱,也不好理解,并且为后续改进增添了很多困难。基于此种情况,笔者就给大家介绍一种将yolov7.yaml文件简化的方法,将104层简化为30层,并且参数量和计算量和原来是一致的,…

✅鉴权—cookie、session、token、jwt、单点登录

基于 HTTP 的前端鉴权背景cookie 为什么是最方便的存储方案,有哪些操作 cookie 的方式session 方案是如何实现的,存在哪些问题token 是如何实现的,如何进行编码和防篡改?jwt 是做什么的?refresh token 的实现和意义ses…

pythonJax小记(五):python: 使用Jax深度图像(正交投影和透视投影之间的转换)(持续更新,评论区可以补充)

python: 使用Jax深度图像(正交投影和透视投影之间的转换) 前言问题描述1. 透视投影2. 正交投影 直接上代码解释1. compute_projection_parameters 函数a. 参数解释b. 函数计算 2. ortho_to_persp 函数a. 计算投影参数:b. 生成像素坐标网格&am…

protobuf简单使用(二)

介绍 上一节中,我们介绍了protobuf,简单来说,它是一种消息数据格式,其作用类似于json,但是比json的使用效率要高。 除此以外,我们介绍了protobuf的简单使用,也就是如何可以像使用json一样&…

AI游戏初创公司“奇酷网络”,获得500万元天使投资

发布 | 大力财经 2024年新春伊始的2月25日,国内首家“AI游戏”应用公司“奇酷网络”正式对外宣布:以3,000万元人民币的估值,成功获得500万元人民币的融资;资金来源于一名百度高层和一位知名的天使投资人。 据悉,“奇…

进程 2月24日学习笔记

1.进程: 程序:存放在外存中的一段数据组成的文件 进程:是一个程序动态执行的过程,包括进程的创建、进程的调度、进程的消亡 2.进程相关命令: 1.top 动态查看当前系统中的所有进程信息(根据CPU占用率排序) PID:唯一识…

namecheap域名如何购买?通过支付宝(详细图文教程)

引言 在完成博客搭建的时候,我们可能需要一个好的域名,自己看起来才会舒服点,同时也可以通过google或者百度等方式搜索到自己博客。 经过实验发现,一个好的后缀名会增强google和百度的搜索seo,增加自己博客的流量。 …

平头哥IP核C906的JTAG调试器DIY教程(一)

背景 最近买了一块基于平头哥C906核,SOC为全志的D1s,的核心板,手工焊接在白嫖的底板上(此处感谢百问网老师的友情支持,手动狗头)。在焊接完成后,进行点亮跑程序的时候,发现没有优雅…

C++ //练习 8.13 重写本节的电话号码程序,从一个命名文件而非cin读取数据。

C Primer(第5版) 练习 8.13 练习 8.13 重写本节的电话号码程序,从一个命名文件而非cin读取数据。 环境:Linux Ubuntu(云服务器) 工具:vim 代码块 /***************************************…

关于设备连接有人云的使用及modbus rtu协议,服务器端TCP调试设置

有人云调试 调试过程问题1. 关于modbus rtu协议,实质上有三种modbus基本原理modbus 格式2. 关于modbus crc16通信校验3. 关于在ubuntu阿里云服务器端,监听网络数据之调试mNetAssist4. 使用有人FAE传给的设置软件问题???之前的一个项目,再拿出来回顾下。 调试过程 先 要在有…

web安全学习笔记【16】——信息打点(6)

信息打点-语言框架&开发组件&FastJson&Shiro&Log4j&SpringBoot等[1] #知识点: 1、业务资产-应用类型分类 2、Web单域名获取-接口查询 3、Web子域名获取-解析枚举 4、Web架构资产-平台指纹识别 ------------------------------------ 1、开源-C…

特征选择|一种提升预测模型性能的方法(原理及其优化实现,Matlab)

文章来源于我的个人公众号:KAU的云实验台,主要更新智能优化算法的原理、应用、改进 如今,生成的数据集呈指数级增长,这将产生具有大量特征和样本的数据集,而显然,某些特征是不相关/冗余的,它们…

奇异递归模板模式应用6-类模板enable_shared_from_this

异步编程中存在一种场景,需要在类中将该类的对象注册到某个回调类或函数中,不能简单地将this传递给回调类中,很可能因为回调时该对象不存在而导致野指针访问(也有可能在析构函数解注册时被回调,造成对象不完整&#xf…

【C语言基础】:操作符详解(一)

文章目录 操作符详解1. 操作符的分类2. 二进制和进制转换2.1 什么是二进制、八进制、十进制、十六进制2.1.1 二进制和进制转换2.1.2 二进制转十进制2.2.3 二进制转八进制2.2.4 二进制转十六进制 3. 源码、反码、补码4. 移位操作符4.1 左移操作符4.2 右移操作符 5. 位操作符&…

IT廉连看——C语言——函数

IT廉连看——C语言——函数 一、函数是什么? 数学中我们常见到函数的概念。但是你了解C语言中的函数吗? 维基百科中对函数的定义:子程序 在计算机科学中,子程序(英语:Subroutine, procedure, function, …

【Java】java异常处理机制(实验五)

目录 一、实验目的 二、实验内容 三、实验小结 一、实验目的 1、理解java的异常处理机制 2、掌握try catch结构和thow和thows关键字的用法 二、实验内容 1、编写一个程序,输入某个班某门课程成绩,统计及格人数、不及格人数及课程平均分。设计一个异…

H12-821_59

59.R1、R2、R3、R4运行IS-IS,它们接口的DIS Priority如图所示,假如设备同时启动,则()被选举为D1S.(请填写设备名称、例如R1) 答案:R4 注释: IS-IS中DIS的选举支持抢占。 假设题目说R4最后启动,问谁被选举为DIS,答案仍然是R4。