深入浅出:进程管理的艺术

简介: 进程可以被视为一个执行中的程序实例,它包含了程序代码、数据集以及一个描述其状态和控制信息的进程控制块(Process Control Block, PCB)。操作系统通过PCB来跟踪和控制进程的执行状态,包括进程的ID、内存映射、打开的文件、状态信息等。

进程的定义

进程可以被视为一个执行中的程序实例,它包含了程序代码、数据集以及一个描述其状态和控制信息的进程控制块(Process Control Block, PCB)。操作系统通过PCB来跟踪和控制进程的执行状态,包括进程的ID、内存映射、打开的文件、状态信息等。

进程的特征

  1. 动态性:进程是程序的一次执行过程,有创建、执行和消亡的生命周期。
  2. 并发性:多个进程可以并发执行,共享处理器和其他系统资源。
  3. 独立性:每个进程都有自己独立的内存空间和系统资源,相互之间不受影响。
  4. 异步性:进程的执行是异步的,即进程的执行速度不可预知,受到多种因素的影响。

进程的状态

进程在其生命周期中会处于不同的状态,主要包括:

  1. 就绪状态:进程已经准备好执行,等待CPU资源。
  2. 执行状态:进程正在使用CPU执行指令。
  3. 阻塞状态:进程因为等待某种事件(如I/O操作完成)而暂时不能执行。

进程与程序的区别

  • 程序是一组指令的集合,是静态的;而进程是程序在某个数据集上的一次执行过程,是动态的。
  • 程序是永久的,存储在磁盘上;进程是暂时的,存在于内存中,随程序的启动而创建,随程序的结束而消亡。

进程的控制和管理

操作系统负责管理和控制进程,主要包括:

  • 进程创建:操作系统为新进程分配资源,初始化PCB。
  • 进程调度:决定下一个执行的进程,将CPU分配给进程。
  • 进程同步:协调多个进程对共享资源的访问,防止冲突。
  • 进程通信:允许进程间交换信息,支持进程协同工作。
  • 进程终止:回收进程占用的资源,结束进程的生命周期。

进程的特点

1. 虚拟内存空间的分配

  • 虚拟地址空间:在典型的32位系统中,进程拥有一个4GB的虚拟地址空间,这个空间被分为两部分:
  • 用户空间(0-3GB):这部分空间专属于每个进程,用于存放代码、数据、堆和栈等,是进程的私有区域,其他进程无法直接访问。
  • 内核空间(3-4GB):这部分空间是所有进程共享的,主要存放操作系统内核代码和数据结构。当进程需要与内核交互时,如进行系统调用,会进入内核空间。

2. 时间片轮转调度

  • 时间片调度:操作系统采用时间片轮转(Round Robin, RR)调度策略来管理多个进程。每个进程在获得CPU执行权时,会被分配一个固定的时间片(通常几毫秒到几十毫秒不等),在此期间,进程独占CPU资源执行。
  • 多任务处理:通过快速地在多个进程之间切换执行(时间片轮转),操作系统可以给用户创造出多任务并行执行的错觉,即使在单核CPU上也能实现高效的多任务处理。

图解:

image.gif 编辑

image.gif 编辑

进程段

数据段(Data Segment)

  • 存储内容:数据段主要用于存储进程中的全局变量、静态变量以及动态分配的内存。全局变量是指在程序的多个函数间共享的数据,而静态变量则是在函数外部声明但在函数内部使用的变量,它们在程序的整个生命周期中都保持有效。
  • 动态数据分配:使用如 malloccallocreallocfree 等函数动态分配的内存也位于数据段中,这部分内存通常被称为“堆”(Heap),它提供了灵活的内存管理能力,允许程序在运行时根据需要动态地分配和释放内存空间。

正文段(Text Segment)

  • 存储内容:正文段,也称作代码段或文本段,主要用于存储程序的可执行代码。这些代码是预先编译好的机器指令,由操作系统加载到内存中供 CPU 执行。正文段通常具有只读属性,以防止程序意外修改自己的代码,从而提高程序的稳定性和安全性。

堆栈段(Stack Segment)

  • 存储内容:堆栈段是用于存储函数调用过程中产生的临时数据的区域,主要包括:
  • 函数的局部变量:这些变量在函数调用时创建,在函数退出时销毁。
  • 函数调用的参数:当一个函数被调用时,其参数会按照一定的顺序压入堆栈。
  • 返回地址:记录调用者的位置,以便函数执行完毕后返回到正确的指令处继续执行。
  • 工作原理:堆栈段遵循“先进后出”(LIFO, Last In First Out)的原则,通过压栈(PUSH)和弹栈(POP)操作来管理数据的进出,这种机制简化了函数调用的管理,使得函数调用和返回的控制流更加清晰和高效。

进程分类

交互进程

  • 特性与用途:交互进程通常由 shell 启动,直接与用户交互,响应用户输入并展示输出结果。这类进程既可以运行在前台,占据用户的终端界面,也可以运行在后台,不干扰用户的其他操作。典型的交互式进程包括 shell 命令、文本编辑器(如 vi 或 nano)、图形界面应用程序等。

批处理进程

  • 特性与用途:批处理进程不直接与用户交互,它们通常被提交到一个队列中,按顺序执行。这类进程主要用于处理大量数据或执行长时间运行的任务,如批量数据处理、备份作业、系统维护任务等。由于不需要用户实时干预,批处理进程可以高效利用系统资源,避免因用户交互而引起的中断和延迟。

守护进程

  • 特性与用途:守护进程是长期运行在后台的服务进程,它们在系统启动时被激活,持续运行直到系统关闭。守护进程不依附于任何终端,主要负责提供系统级的服务,如网络服务(HTTP、FTP)、系

进程状态

  1. 新建状态(New): 当一个进程被创建时,它首先进入新建状态。此时,操作系统为其分配必要的资源,如内存、文件描述符等,并初始化进程控制块(PCB)。进程尚未被调度执行。
  2. 就绪状态(Ready): 进程已经准备好执行,但由于当前CPU正忙于其他进程,它需要等待调度器的调度。在就绪状态下的进程位于就绪队列中,等待CPU时间片。一旦被调度,进程将从就绪状态转变为执行状态。
  3. 执行状态(Running): 进程正在使用CPU执行指令。在多任务操作系统中,执行状态的进程可能很快就会因为时间片到期、更高优先级的进程就绪或进程自身请求I/O操作等原因而转变为其他状态。
  4. 阻塞状态(Blocked): 也称为等待状态或睡眠状态。当进程在执行过程中遇到I/O操作或其他需要等待的事件时,它会主动或被动地进入阻塞状态。此时,进程放弃CPU使用权,等待相应的事件发生,如I/O操作完成、信号量变为可用等。阻塞状态下的进程不参与CPU调度,直到阻塞原因解除。
  5. 僵尸状态(Zombie): 当一个子进程结束执行但其父进程尚未对其进行善后处理(如调用 wait()waitpid() 函数)时,子进程会进入僵尸状态。僵尸进程仍然占用一部分系统资源,如进程表中的一个条目,但不再占用CPU时间,也不会执行任何指令。
  6. 终止状态(Terminated): 进程执行完毕或因异常而终止后,会进入终止状态。在终止状态下,进程实际上已经停止运行,但其状态信息仍保留在系统中,直到父进程清理这些信息(通过调用 wait()waitpid())。清理后,进程完全消失,释放所有资源。

进程状态切换

进程创建后,进程进入就绪态,当CPU调度到此进程时进入运行态,当时间片用完时,此进程会进入就绪态,如果此进程正在执行一些IO操作(阻塞操作)会进入阻塞态,完成IO操作(阻塞结束)后又可进入就绪态,等待CPU的调度,当进程运行结束即进入结束态。

 

image.gif 编辑

image.gif 编辑

C语言中进程函数

创建进程fork

pid_t fork(void);
功能:创建子进程
返回值:
    成功:在父进程中:返回子进程的进程号 >0
         在子进程中:返回值为0
    失败:-1并设置errno

image.gif

  1. 资源与状态的继承与分离
  • 子进程几乎完全复制了父进程的执行上下文,包括代码段、数据段、栈段、文件描述符等,但它们拥有独立的进程ID(PID)和父进程ID(PPID)。这意味着子进程继承了父进程的运行环境,但二者在系统层面是完全独立的实体。
  1. 独立的地址空间与数据独立性
  • 尽管子进程继承了父进程的内存映像,但它拥有自己独立的地址空间。因此,对全局变量、静态变量的修改在各自进程中是独立的,互不影响。这种隔离性确保了进程间的数据安全。
  1. 孤儿与僵尸进程的处理
  • 若父进程在子进程之前终止,子进程将被系统中的初始化进程(init)收养,成为孤儿进程。孤儿进程会继续执行,直到自然结束或被其他进程显式终止。
  • 当子进程结束,但父进程未能及时回收其资源时,子进程会变成僵尸进程。僵尸进程虽已结束,但仍占用系统资源,直到父进程通过 wait()waitpid() 系统调用来回收为止。应避免产生僵尸进程,以维持系统资源的高效利用。
  1. 文件描述符的共享与独立操作
  • 子进程继承了父进程打开的文件描述符,这意味着同一文件在父进程和子进程中可通过相同的描述符访问。尽管如此,文件操作(如读写位置)在各自的进程中是独立的,不会互相干扰。

fork() 调用总结

  • 执行流程与返回值
  • fork() 调用在父进程中返回子进程的PID,在子进程中返回0。若返回负数,则表示创建子进程失败。
  • fork() 调用仅执行一次,但它的效果是使得代码在父进程和子进程中分别执行一次,形成两个独立的执行流。
  • 资源复制与独立执行
  • fork() 创建子进程时,子进程获得了父进程的资源副本,包括代码段、数据段和栈段,但拥有独立的地址空间和资源管理。
  • 父子进程在 fork() 后的代码段是独立执行的,即使它们执行相同的指令序列,也不会相互影响。

注意事项

在使用 fork() 创建子进程时,应注意以下几点:

  • 资源管理:确保父进程和子进程妥善管理共享资源,如文件描述符和信号量,以避免资源泄露或竞争条件。
  • 进程清理:父进程应负责回收子进程的资源,避免产生僵尸进程,保持系统资源的健康状态。
  • 信号处理:考虑进程间的信号传递机制,以实现进程间的协调和控制,特别是在异常处理和进程终止时。

 

回收进程wait waitpid

#include <sys/types.h>
#include <sys/wait.h>
pid_t waitpid(pid_t pid, int *status, int options);// 函数原型
pid_t wait(int *status);
功能:回收子进程资源(阻塞)
参数:status:子进程退出状态,不接受子进程状态设为NULL
返回值:成功:回收的子进程的进程号
        失败:-1

image.gif

参数说明

  • pid:用于指定要等待的子进程的 PID(进程ID)或一组进程的选择标准:
  • >0:等待特定 PID 的子进程。
  • -1:等待任意一个子进程,这是最常见的用法。
  • =0:等待与调用进程具有相同进程组 ID 的任意子进程。
  • <-1:等待与 pid 绝对值对应的进程组 ID 相同的任意子进程。
  • status:指向一个整型变量的指针,用于存储子进程的退出状态。如果子进程正常结束,status 中包含的值可以被 WEXITSTATUS() 宏解析得到子进程的退出码;如果是信号导致子进程终止,status 中的值可以被 WTERMSIG() 宏解析得到终止信号。
  • options:指定 waitpid() 的行为模式:
  • 0:默认阻塞模式,父进程将暂停执行,直到子进程结束。
  • WNOHANG:非阻塞模式,立即返回,无论是否有子进程结束。如果无子进程结束,返回 0;否则返回结束子进程的 PID。

返回值

  • 正常情况:返回结束子进程的 PID。
  • 非阻塞模式且没有子进程结束:返回 0
  • 出错:返回 -1,并设置 errno 变量以指示错误原因。

image.gif 编辑

退出进程exit _exit

void exit(int status);
功能:结束进程,刷新缓存
参数:退出的状态
不返回。
void _exit(int status);
功能:结束进程,不刷新缓存
参数:status是一个整型的参数,可以利用这个参数传递进程结束时的状态。
    通常0表示正常结束;
其他的数值表示出现了错误,进程非正常结束

image.gif

image.gif 编辑

获取进程号 getpid getppid

pid_t getpid(void);
功能:获取当前进程的进程号
pid_t getppid(void);
功能:获取当前进程的父进程号

image.gif

image.gif 编辑

image.gif 编辑

return: 关键字,在子函数中返回到函数调用的位置,并不结束进程(函数的退出)

exit :函数,不管在子函数还是主函数都会结束进程(进程的退出)

总结:

       在计算中进程是无处不在的,更好的了解进程也可以让我们更好的了解计算机以及编程的原理,文章至此结束,希望可以帮到大家。万分感谢看到这里。

相关文章
|
2月前
|
机器学习/深度学习 存储 算法
程序设计:从基础到实践的艺术探索
程序设计:从基础到实践的艺术探索
20 0
|
9月前
|
消息中间件 算法 Unix
Linux系统编程(进程基础知识讲解)
Linux系统编程(进程基础知识讲解)
92 3
|
2月前
|
存储 编译器 程序员
嵌入式系统中C++基础知识精髓
嵌入式系统中C++基础知识精髓
68 0
|
2月前
|
数据采集 安全 调度
【C 言专栏】C 语言与操作系统的交互
【5月更文挑战第1天】C语言在操作系统中扮演核心角色,常用于内核及系统级应用开发。它涉及进程管理(如`fork()`创建进程)、内存分配(`malloc()`、`free()`)、文件操作(`open()`、`read()`等)和设备驱动。C语言允许直接硬件交互,支持进程间通信和文件系统管理,但也带来资源管理、错误处理和可移植性的挑战。在嵌入式系统中,C语言尤为重要。随着技术发展,C语言将继续适应新操作系统和硬件架构,与其他语言融合,推进系统编程进步。
【C 言专栏】C 语言与操作系统的交互
|
2月前
|
安全 算法 开发工具
【C 言专栏】基于 C 语言的嵌入式系统开发
【5月更文挑战第1天】本文探讨了C语言在嵌入式系统开发中的核心作用。嵌入式系统作为专用计算机系统广泛应用于家电、汽车、医疗等领域,具备实时性、低功耗等特点。C语言因其高效性、可移植性和灵活性成为开发首选。文章介绍了开发流程,包括需求分析、硬件选型、软件设计至部署维护,并强调中断处理、内存管理等关键技术。C语言在智能家居、汽车电子和医疗设备等领域的应用实例展示了其广泛影响力。面对硬件限制、实时性要求和安全挑战,开发者需不断优化和适应新技术趋势,以推动嵌入式系统创新发展。
【C 言专栏】基于 C 语言的嵌入式系统开发
|
2月前
|
Ubuntu Unix Java
【操作系统原理】—— 文件系统编程
【操作系统原理】—— 文件系统编程
18 0
|
2月前
|
云安全 安全 搜索推荐
计算机操作基础知识点总结
计算机操作基础知识点总结
|
11月前
|
存储 算法 安全
[计算机操作系统(第四版 汤小丹 汤子瀛)]第一章 操作系统引论(学习复习笔记)
[计算机操作系统(第四版 汤小丹 汤子瀛)]第一章 操作系统引论(学习复习笔记)
|
存储 算法 程序员
56张图入门操作系统——内功心法,适合所有程序员
56张图入门操作系统——内功心法,适合所有程序员
56张图入门操作系统——内功心法,适合所有程序员
|
数据库
重学操作系统原理系列 - 进程管理(二)
重学操作系统原理系列 - 进程管理(二)
150 0
重学操作系统原理系列 - 进程管理(二)

热门文章

最新文章