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://xiahunao.cn/news/2807844.html

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

相关文章

Canvas学习笔记02:canvas的路径扫盲,附代码案例

hello,我是贝格前端工场,最近在学习canvas,分享一些canvas的一些知识点笔记,本期分享canvas的路径知识,欢迎老铁们一同学习,欢迎关注,如有前端项目可以私信贝格。 一、什么是canvas路径 Canvas…

大模型 Advanced-RAG(高级检索增强生成):从理论到 LlamaIndex 实战!

最近关于检索增强生成进行了调查,总结了三种最近发展的范式: Naive RAG(简单RAG)Advanced RAG(高级RAG)Modular RAG(模块化RAG) 本文首先讨论这些技术,接着分享如何使…

数字电路 第二章—第二节(半导体二极管、三极管和MOS管的开关特性)

一、理想开关的开关特性 1、静态特性 (1)断开时,无论在多大范围内变化,其等效电阻,通过其中的电流。 (2)闭合时,无论流过其中的电流在多大范围内变化,其等效电阻&…

Spark大数据分析与实战笔记(第三章 Spark RDD 弹性分布式数据集-05)

文章目录 每日一句正能量第3章 Spark RDD弹性分布式数据集章节概要3.7 Spark的任务调度3.7.1 DAG的概念3.7.2 RDD在Spark中的运行流程 总结 每日一句正能量 成功的速度一定要超过父母老去的速度,努力吧。做事不必与俗同,亦不与俗异;做事不必令…

Mysql运维篇(六) 部署MHA--一主二从部署

MAH架构图 一、上传MySQL软件 1、同步MySQL软件 [rootmysql01 ~]# ls -lrt total 626112 -rw-r--r--. 1 root root 641127384 Jan 30 15:13 mysql-5.7.20-linux-glibc2.12-x86_64.tar.gz -rw-------. 1 root root 1697 Feb 3 12:09 anaconda-ks.cfg -rw-r--r--. 1 root…

[SpringDataMongodb开发游戏服务器实战]

背景: xdb其实足够完美了,现在回想一下,觉得有点复杂,我们不应该绑定语言,最好有自己的架构思路。 七号堡垒作为成功的商业项目,告诉我:其实数据是多读少写的,有修改的时候直接改库也…

推荐一个 Obsidian 的 ChatGPT 插件

源码地址:https://github.com/nhaouari/obsidian-textgenerator-plugin Text Generator 是目前我使用过的最好的 Obsidian 中的 ChatGPT 功能插件。它旨在智能生成内容,以便轻松记笔记。它不仅可以在 Obsidian 中直接使用 ChatGPT,还提供了优…

Python staticmethod函数

Python是一种功能强大且灵活的编程语言,具有许多特性和功能,其中之一就是staticmethod函数。staticmethod函数是Python中用于定义静态方法的一种特殊装饰器。在本文中,将深入探讨staticmethod函数的用法、优势以及与其他方法类型的比较。 什…

【力扣 - 将有序数组转化为二叉搜索树】

题目描述 给你一个整数数组 nums ,其中元素已经按 升序 排列,请你将其转换为一棵 高度平衡 二叉搜索树。 高度平衡 二叉树是一棵满足「每个节点的左右两个子树的高度差的绝对值不超过 1 」的二叉树。 题解 前言 二叉搜索树的中序遍历是升序序列&a…

黑马JavaWeb开发跟学(一)Web前端开发HTML、CSS基础

黑马JavaWeb开发一.Web前端开发HTML、CSS基础 引子、Web开发介绍传统路线本课程全新路线本课程适用人群课程收获一、什么是web开发二、网站的工作流程三、网站的开发模式四、网站的开发技术 前端开发基础一、前端开发二、HTML & CSS2.1 HTML快速入门2.1.1 操作第一步第二步…

Arduino中安装ESP32网络抽风无法下载 暴力解决办法 python

不知道什么仙人设计的arduino连接网络部分,死活下不下来。(真的沙口,第一次看到这么抽风的下载口) 操作 给爷惹火了我踏马解析json选zip直接全部下下来 把这个大家的开发板管理地址下下来跟后面python放在同一目录下&#xff0c…

FDTD算法总结

计算电磁学(Computational Electromagnetics, CEM)是通过数值计算来研究电磁场的交叉学科。 数值求解电磁学问题的方法可以分成频域(Frequency Doamin, FD)、时域(Time Domain, TD)等两类。 频域法基于时谐微分,通过对多个采样值的傅里叶逆变换得到所需的脉冲响应…

构建高效教学平台系统:关键要素与最佳实践

随着在线教育的迅速发展,教学平台系统成为了教育行业不可或缺的一部分。本文将总结构建高效教学平台系统的关键要素,并介绍最佳实践,以帮助教育机构和企业打造具有竞争力的教学平台系统。 引言: 随着信息技术的不断进步和普及&…

神经网络系列---分类度量

文章目录 分类度量混淆矩阵(Confusion Matrix):二分类问题二分类代码多分类问题多分类宏平均法:多分类代码多分类微平均法: 准确率(Accuracy):精确率(Precision)&#xf…

K8s安全一

Kubernetes是一个开源的,用于编排云平台中多个主机上的容器化的应用,目标是让部署容器化的应用能简单并且高效的使用, 提供了应用部署,规划,更新,维护的一种机制。其核心的特点就是能够自主的管理容器来保证云平台中的…

值得推荐收藏的5款顶级免费数据恢复软件!

今天分享5个超级简单又适合电脑小白的恢复删除的文件的恢复方法! 在我们的日常生活中,偶尔会因为误删除或者清空回收站等原因导致数据丢失。对于电脑小白来说,这或许是一个非常棘手的问题。但是,不用太担心,今天我为大…

【C++那些事儿】C++入门 | 命名空间 | 缺省参数 | 引用 | 内联函数 | auto关键字 | 范围for循环 | nullptr

📷 江池俊: 个人主页 🔥个人专栏: ✅数据结构冒险记 ✅C那些事儿 🌅 有航道的人,再渺小也不会迷途。 文章目录 前言1. C关键字(C98)2. 命名空间2.1 命名空间定义2.2 命名空间使用 3. C输入&输出4. 缺…

模型上下文长度达到10000000,又一批创业者完蛋了?

没有疑问,Gemini 1.5 Pro的隆重推出被Sora抢了风头。 社交平台X上OpenAI介绍Sora的第一条动态,现在已经被浏览了超过9000万次,而关于Gemini 1.5 Pro热度最高的一条,来自谷歌首席科学家Jeff Dean,区区123万人。 或许J…

【设计模式】策略模式及函数式编程的替代

本文介绍策略模式以及使用函数式编程替代简单的策略模式。 策略模式 在策略模式(Strategy Pattern)中一个类的行为或其算法可以在运行时更改。这种类型的设计模式属于行为型模式。 在策略模式定义了一系列算法或策略,并将每个算法封装在独立…

Jenkins解决Host key verification failed (2)

Jenkins解决Host key verification failed 分析原因情况 一、用OpenSSH的人都知ssh会把你每个你访问过计算机的公钥(public key)都记录在~/.ssh/known_hosts。当下次访问相同计算机时,OpenSSH会核对公钥。如果公钥不同,OpenSSH会发出警告,避免…