Linux进程【补充】

news2025/1/20 1:35:07

文章目录

  • 进程概念
    • 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.coloradmin.cn/o/1469859.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

你真的了解@Async吗?

你真的了解Async吗? 使用场景: 开发中会碰到一些耗时较长或者不需要立即得到执行结果的逻辑,比如消息推送、商品同步等都可以使用异步方法,这时我们可以用到Async。但是直接使用 Async 会有风险,当我们没有指定线程池…

政安晨【示例演绎虚拟世界开发】(一):Cocos Creator 的 Hello World

政安晨的个人主页:政安晨 欢迎 👍点赞✍评论⭐收藏 收录专栏: AI虚拟世界大讲堂 希望政安晨的博客能够对您有所裨益,如有不足之处,欢迎在评论区提出指正。 前言 Cocos Creator是一款非常强大的游戏开发引擎,它有着优秀…

李宏毅2023机器学习作业1--homework1——python语法

# 定义list del_col del_col [0, 38, 39, 46, 51, 56, 57, 64, 69, 74, 75, 82, 87] # 删除raw_x_train中del_col的列,axis为1代表删除列 raw_x_train np.delete(raw_x_train, del_col, axis1) # numpy数组增删查改方法 # 定义列表get_col get_col [35, 36, 37,…

sql语法中所有的条件字段都可以用函数来加工

SELECTt_test.age FROMt_testINNER JOIN t_test_copy1 ON SUBSTR( t_test.age, 1, 4 ) t_test_copy1.idd

加速Python代码的秘密武器,探索Cython的秘密

首先和大家明确一下这个Cython单词的读法,这个单词Cython以前我也不知道怎么读,老后面要用到这个包的时候,老是不清楚读法,才去搜了下,这个单词是读"赛森",就是前面的cy是读"赛"&#…

C语言每日一题(61)盛最多水的容器

题目链接 力扣 11 盛最多水的容器 题目描述 给定一个长度为 n 的整数数组 height 。有 n 条垂线,第 i 条线的两个端点是 (i, 0) 和 (i, height[i]) 。 找出其中的两条线,使得它们与 x 轴共同构成的容器可以容纳最多的水。 返回容器可以储存的最大水…

IDEA安装配置以及安装配置Maven

IEDA官方下载地址,有专业版(收费,破解),社区版(免费) 下载 IntelliJ IDEA – 领先的 Java 和 Kotlin IDE 安装配置Maven 1.解压apache-maven-3.6.3-bin.zip,安装maven到D盘softwar…

高校隔离宿舍管理系统

高校隔离宿舍管理系统 获取源码——》公主号:计算机专业毕设大全

堆排序、快速排序和归并排序

堆排序、快速排序和归并排序是所有排序中最重要的三个排序,也是难度最大的三个排序;所以本文单独拿这三个排序来讲解 目录 一、堆排序 1.建堆 2.堆排序 二、快速排序 1.思想解析 2.Hoare版找基准 3.挖坑法找基准 4.快速排序的优化 5.快速排序非…

Java中PDF文件传输有哪些方法?

专栏集锦,大佬们可以收藏以备不时之需: Spring Cloud 专栏:http://t.csdnimg.cn/WDmJ9 Python 专栏:http://t.csdnimg.cn/hMwPR Redis 专栏:http://t.csdnimg.cn/Qq0Xc TensorFlow 专栏:http://t.csdni…

Type-C连接器笔记

一、Type-C的介绍 Type-C是一种全新的USB接口形式,由USB Implementers Forum(USB-IF)制定,并在2014年获得苹果、谷歌、英特尔、微软等厂商支持后开始普及。它是一种通用串行总线(USB)的硬件接口规范&#x…

python学习26

前言:相信看到这篇文章的小伙伴都或多或少有一些编程基础,懂得一些linux的基本命令了吧,本篇文章将带领大家服务器如何部署一个使用django框架开发的一个网站进行云服务器端的部署。 文章使用到的的工具 Python:一种编程语言&…

软考37-上午题-【数据库】-数据模型、数据库的三级模式和二级映像

一、考情简介 上午题:6分——6道选择题 下午题:15分——一道分析题 E-R图、关系模式:下午考试必考!!!(编制也要考!) 二、数据模型 数据模型是对现实世界数据特征的抽象…

信息抽取(UIE):使用自然语言处理技术提升证券投资决策效率

一、引言 在当今快速变化的证券市场中,信息的价值不言而喻。作为一名资深项目经理,我曾领导一个关键项目,旨在通过先进的信息抽取技术,从海量的文本数据中提取关键事件,如企业并购、新产品发布以及政策环境的变动。这些…

[SUCTF 2019]EasySQL1 题目分析与详解

一、题目介绍 1、题目来源: BUUCTF网站,网址:https://buuoj.cn/challenges 2、题目描述: 通过以上信息,拿到flag。 二、解题思路 首先打开靶机,尝试输入1查看回显,回显如图所示:…

细说python3中sort和sorted

sort和sorted的区别 虽然python3中sort()和sorted()都可以用来排序,但二者有以下两个最主要的区别: sort只能应用在列表list上,而sorted可以对所有可迭代的对象进行排序的操作 sort方法会在原list上直接进行排序,不会创建新的l…

Vue3(pinia) 整合 SpringWebsocket链接url动态传参

前言: 👏作者简介:我是笑霸final,一名热爱技术的在校学生。 📝个人主页:个人主页1 || 笑霸final的主页2 📕系列专栏:java专栏 📧如果文章知识点有错误的地方,…

【Vuforia+Unity】AR06-空间环境识别功能(AreaTargets)

Vuforia原理:把被识别的物体转成图、立体图、柱形图,3D模型、环境模型,然后模型生成Vuforia数据库-导入Unity-参考模型位置开始摆放数字内容,然后参考模型自动隐藏-发布APP-识别生活中实物-数字内容叠加上去! 不论你是否曾有过相关经验,只要跟随本文的步骤,你就可以成功…

04|事务的隔离级别

数据库的事务隔离越严格,并发副作用越小,但付出的代价也就越大,因为事务隔离实质上就是 使事务在一定程度上“串行化”进行,这显然与“并发”是矛盾的。 同时,不同的应用对读一致性和事务隔离程度的要求也是不同的,比如许多应用对“不可重复 读"和“幻读”并不敏感,可能更…

c++入门学习⑧——模板

目录 前言 基本介绍 什么是模板? 作用 特点 分类 函数模板 语法 使用方式 注意事项 函数模板和普通函数区别 普通函数和函数模板的调用规则 局限性 类模板 语法 类模板的成员函数创建时机 类模板实例化对象 类模板实例化对象做函数参数 类模板成…