【探索Linux】—— 强大的命令行工具 P.7(进程 · 进程的概念)

在这里插入图片描述

前言

前面我们讲了C语言的基础知识,也了解了一些数据结构,并且讲了有关C++的一些知识,也学习了一些Linux的基本操作,也了解并学习了有关Linux开发工具vim 、gcc/g++ 使用、yum工具以及git 命令行提交代码也相信大家都掌握的不错,今天博主带大家了解一下 —— 进程(进程的概念), 下面话不多说坐稳扶好咱们要开车了!!!?

一、冯诺依曼体系结构

冯·诺依曼体系结构是一种计算机硬件设计的范式,它是以匈牙利裔美籍数学家兼计算机科学家冯·诺依曼(Von Neumann)的名字命名的。其主要特点包括:

  1. 存储程序:在冯·诺依曼体系结构中,程序和数据都存储在同一块内存中,通过地址访问。这种结构相对于之前的计算机模型,不仅提供了更大的灵活性,也使得程序编写和修改更加方便。

  2. 指令执行:计算机通过从内存中读取指令并按照顺序执行来完成特定任务。指令通常包括操作码和操作数,操作码指定要执行的操作类型(比如加法或乘法),而操作数则提供操作的具体数据。

  3. 存储器和处理器分离:在冯·诺依曼体系结构中,存储器和处理器是分离的实体。处理器负责执行指令,而存储器则用于存储指令和数据。这种分离设计使得处理器可以灵活地访问存储器中的不同位置。

  4. 顺序执行:指令按照顺序从内存中加载并执行。这也意味着指令之间存在依赖关系,后续指令必须等待前面指令完成后才能执行。这种顺序执行的方式限制了计算机的并行性能。
    在这里插入图片描述

冯·诺依曼体系结构成为了现代计算机的基石,几乎所有的通用目的计算机都采用了这种结构。它对计算机的发展起到了重要的推动作用,使得计算机变得更加灵活、可编程和易于使用。

二、操作系统(OS)

1. 概念

操作系统(Operator System)任何计算机系统都包含一个基本的程序集合,称为操作系统(OS)。笼统的理解,操作系统包括:

  • 内核(进程管理,内存管理,文件管理,驱动管理)
  • 其他程序(例如函数库,shell程序等等)
  1. 资源管理:操作系统负责管理计算机系统的资源,包括处理器、内存、磁盘、输入/输出设备、网络等。它分配和调度这些资源,以确保它们能够高效地被应用程序使用。资源管理可以通过不同的策略和算法来优化资源的利用率和性能。

  2. 进程管理:操作系统负责管理计算机中运行的进程,即程序的执行实例。它负责创建、调度和终止进程,并提供进程间的通信和同步机制。操作系统通过分时复用处理器,使得多个进程能够并发执行,从而提高系统的利用率和响应性能。

  3. 内存管理:操作系统管理计算机的内存资源,即主存。它负责分配和回收内存空间,这样应用程序就能够加载和执行。内存管理机制包括虚拟内存,它允许将内存扩展到磁盘上的虚拟内存空间,从而提供更大的内存空间和更好的内存保护。

  4. 文件系统:操作系统提供了对计算机存储设备上文件和目录的管理。它负责文件的创建、读取、写入和删除,以及文件的组织和保护机制。文件系统提供了逻辑结构来组织和访问数据,同时提供权限控制和数据备份等功能。

  5. 设备驱动程序管理:操作系统管理计算机的输入/输出设备,如键盘、鼠标、显示器、打印机等。它提供设备驱动程序来实现与设备的通信和控制,以及处理设备的中断和错误。

总而言之,操作系统是计算机系统中的核心软件,它提供了一种有效和可控的方式来管理和利用计算机的资源,同时为用户和应用程序提供一个友好和统一的环境。操作系统的设计和实现因不同的需求和平台而异,从个人电脑到服务器和嵌入式系统,每个系统都有自己特定的操作系统。
在这里插入图片描述

三、进程

1. 进程的概念

进程是计算机中正在执行的程序的实例。它是操作系统进行资源分配和调度的基本单位。每个进程都有自己的内存空间、执行状态和相关的系统资源。进程可以包含一个或多个线程,每个线程可以独立执行一段代码。

2. PCB(Process Control Block)

PCB是操作系统中用于管理和控制进程的数据结构。每个进程都有一个对应的PCB,它包含了进程的相关信息和状态
下面是PCB的一些主要内容:

  1. 进程标识符(Process ID):用于唯一标识一个进程。

  2. 程序计数器(Program Counter):记录了进程当前执行的指令地址。

  3. 寄存器集合(Register Set):保存了进程的寄存器状态,包括通用寄存器、程序状态字等。

  4. 进程状态(Process State):表示进程当前的执行状态,如运行、就绪、阻塞等。

  5. 进程优先级(Process Priority):用于确定进程在调度时的优先级顺序。

  6. 进程调度信息(Scheduling Information):包括进程的调度策略、调度队列等信息。

  7. 内存管理信息(Memory Management Information):记录了进程的内存分配情况,包括代码段、数据段、堆栈等。

  8. 文件管理信息(File Management Information):记录了进程打开的文件和文件描述符等信息。

  9. 进程间通信信息(Interprocess Communication Information):记录了进程与其他进程进行通信的方式和相关信息。

PCB是操作系统中非常重要的数据结构,它存储了进程的关键信息,操作系统通过对PCB的管理和操作,实现了对进程的控制和调度。当操作系统需要切换到另一个进程时,它会保存当前进程的PCB,并加载下一个进程的PCB,以实现进程的切换和执行。

3. 查看进程

  1. ps:显示当前运行的进程快照。常用的选项有:

    • ps aux:显示所有用户的所有进程。
    • ps -ef:显示所有进程的完整信息。
    • ps -eL:显示线程的相关信息。
  2. top:实时显示当前系统的进程状态和系统资源使用情况。按下 “q” 键可退出。

  3. htop:类似于 top,但提供更加直观、交互式的界面,可以进行更多操作。需要先安装 htop。

  4. pstree:以树状形式显示进程的层次关系。

  5. pgrep:根据进程名或其他条件查找进程的PID。常用的选项有:

    • pgrep process_name:根据进程名查找相应进程的PID。
  6. pidof:根据进程名查找相应进程的PID。

  7. tophtoppstree命令都是实时显示进程信息的命令,可以使用 Ctrl+C 终止。

这些命令可以提供进程的基本信息,如进程ID(PID)、父进程ID(PPID)、进程状态、CPU和内存的占用情况等。根据实际需要选择适合的命令进行查看。

四、fork函数

1. 函数简介

fork() 是一个在类 UNIX 操作系统下常用的系统调用函数,用于创建一个新的进程。它的作用是在当前进程的执行空间中创建一个与当前进程几乎完全相同的子进程。子进程拥有父进程的代码、数据和资源副本。

2. 调用方式

fork() 函数的调用方式如下:

#include <sys/types.h>
#include <unistd.h>

pid_t fork(void);

3. 返回值

fork() 函数的返回值有以下三种情况:

  • 如果返回值为负数,表示创建子进程失败。
  • 如果返回值为 0,表示当前进程为子进程。
  • 如果返回值大于 0,表示当前进程为父进程,返回值是子进程的进程ID。

4. 使用示例

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>

int main() {
    pid_t pid = fork();

    if (pid < 0) {
        // 创建子进程失败
        fprintf(stderr, "Fork failedn");
        return 1;
    } else if (pid == 0) {
        // 子进程
        printf("This is the child processn");
        // 子进程可以在此处执行自己的逻辑
    } else {
        // 父进程
        printf("This is the parent processn");
        // 父进程可以在此处执行自己的逻辑
    }

    return 0;
}

在这个示例中,当调用 fork() 函数后,父进程中的 pid 是子进程的进程ID,而子进程中的pid 是 0。通过判断 pid 的值,可以在父子进程中执行不同的逻辑。

?注意:fork() 函数会尽可能地复制父进程的所有资源给子进程,包括代码、数据、打开的文件等。但是,父进程和子进程之间的执行顺序是不确定的,取决于操作系统对进程调度的策略

五、进程的几种状态

1. 状态简介

在操作系统(OS)中,进程可以处于以下几种状态:

  1. 创建(Created):进程刚被创建,但还没有开始执行。

  2. 就绪(Ready):进程已经准备好执行,但还没有被调度执行。

  3. 运行(Running):进程正在执行。

  4. 阻塞(Blocked):进程由于某些原因无法继续执行,例如等待某个事件发生或等待某个资源的释放。

  5. 终止(Terminated):进程已经执行完毕或被终止。

进程的状态转换是动态的,进程可以在不同的状态之间切换。操作系统通过合理的调度算法和策略来控制进程的状态转换,以实现对进程的管理和调度。

2. 进程状态查看

在Linux系统中,可以使用多种方法查看进程的状态。

  1. ps命令:ps命令是Linux中最基础和常用的进程查看命令。它可以显示当前系统中运行的进程信息,包括进程状态。常用的命令有:

    • ps aux :显示所有用户的所有进程信息,包括进程状态。
    • ps -ef :显示所有进程的完整信息,包括进程状态。
    • ps -e -o pid,cmd,state :仅显示进程的PID、命令和状态。
  2. top命令:top命令可以实时动态地显示系统中运行的进程和系统资源的使用情况。它的界面可以按CPU使用率、内存使用率等来排序,并显示每个进程的状态。在top的进程列表中,进程的状态将在STAT列中显示,常见的状态有:

    • R:运行状态(Running)。
    • S:睡眠状态(Sleeping)。
    • D:不可中断状态(Uninterruptible sleep)。
    • Z:僵尸状态(Zombie)。
    • T:停止状态(Stopped)。
  3. htop命令:htop是一个交互式的进程查看命令,功能类似于top命令,但界面更加友好和丰富。htop可以显示进程的状态、CPU使用率、内存使用率等信息,并支持通过键盘快捷键进行交互式操作。

  4. pstree命令:pstree命令以树状的形式显示当前系统中的进程关系。它可以清晰地展示进程树中的父子关系,以及每个进程的状态。

3. Z(zombie)-僵尸进程

⭕ 概念

在Linux系统中,僵尸进程(Zombie process)是一种特殊的进程状态。当一个子进程终止后,父进程必须调用wait()waitpid()等系统调用来获取子进程的终止状态并进行资源清理。如果父进程没有正确处理子进程的终止状态,被终止的子进程将会变成僵尸进程。

⭕僵尸进程危害

僵尸进程在系统中占用了一部分进程标识符(PID)等资源,但它们已经完成了它们的执行过程,不再占用CPU资源。僵尸进程的存在并不会对系统运行产生直接影响,因为它们已经成为了无害的静悄悄的进程。

僵尸进程本身并不会对系统的稳定性或性能产生直接的危害,因为它们已经完成了它们的执行过程并不再占用CPU资源。然而,过多的僵尸进程可能会导致一些间接的危害和问题:

  1. 资源浪费:僵尸进程会占用一部分进程标识符(PID)等系统资源,在系统中积累过多的僵尸进程会浪费一定量的资源。

  2. 进程表溢出:每个进程都需要在操作系统内核中占用一部分内存空间,包括进程描述符等信息。过多的僵尸进程会占用进程表中的条目,导致进程表溢出,影响系统的正常运行。

  3. 可用PID不足:在Linux系统中,PID是有限的资源,每个进程都会被分配一个唯一的PID。如果系统中有大量的僵尸进程,那么可用的PID数量就会变少,当可用的PID不足时,新的进程将无法创建。

  4. 难以诊断和调试:过多的僵尸进程可能会使进程状态的监测和诊断变得困难。系统管理员和开发人员可能会难以确定是哪些进程造成了问题,以及如何解决这些问题。

尽管僵尸进程本身并不会造成直接的危害,但积累过多的僵尸进程可能会导致系统资源浪费和其他间接的问题。因此,及时清理僵尸进程是一种良好的实践,可以通过适当的处理子进程的终止状态来避免僵尸进程的积累

⭕如何避免僵尸进程

要解决僵尸进程问题,父进程可以通过以下方式之一来处理子进程的终止状态:

  1. 调用wait()waitpid()等系统调用,获取子进程的终止状态。
  2. 设置SIGCHLD信号的处理函数,并在处理函数中调用waitpid()等来回收僵尸进程。
  3. 在父进程启动时忽略SIGCHLD信号,这样子进程终止时不会变成僵尸进程。

4. 孤儿进程

⭕ 概念

孤儿进程(Orphan process)是指在父进程终止或意外退出后,子进程仍然在系统中运行,并成为一个没有父进程的进程。在这种情况下,操作系统会自动将孤儿进程的父进程设置为init进程(进程ID为1),init进程负责接管孤儿进程的管理。

⭕孤儿进程产生的原因

  1. 父进程意外终止:当父进程意外终止(如崩溃、被强制终止等),它没有机会或能力去合理地终止它创建的子进程。这样子进程就会成为孤儿进程。

  2. 父进程在子进程终止前退出:父进程可能在子进程完成其任务之前就退出了。这可能是由于父进程的设计或逻辑错误导致的。因此,子进程被“遗弃”并成为孤儿进程。

  3. 父进程未正确等待子进程的终止状态:父进程没有正确使用wait()waitpid()等系统调用来等待子进程的终止状态。这样子进程虽然终止了,但其终止状态无法被父进程正确获取,从而导致子进程成为孤儿进程。

需要注意的是,孤儿进程的产生是一种意外或错误的现象,通常不应该出现在正常的进程管理中。

⭕孤儿进程的危害

孤儿进程本身并没有直接的危害,因为操作系统会接管孤儿进程并负责清理其资源。当孤儿进程终止后,操作系统会立即回收它们的资源,并将其进程描述符等信息从进程表中清除。因此,孤儿进程并不会占用过多的系统资源

然而,孤儿进程可能对系统产生一些间接的影响,包括:

  1. 造成系统资源浪费:孤儿进程可能仍然占用一些系统资源,如进程标识符(PID)、内存等。尽管这些资源在孤儿进程终止后会被回收,但在孤儿进程存在的期间,这些资源仍然无法被其他进程使用。

  2. 影响进程标识符的分配:操作系统会为每个进程分配唯一的进程标识符(PID)。孤儿进程终止后,其进程标识符会被回收并可以重新分配给其他进程使用。然而,在孤儿进程存在的期间,其进程标识符将被保留,可能会影响后续进程标识符的分配。

  3. 可能导致资源泄漏或其他问题:如果孤儿进程打开了文件描述符、网络连接或其他资源,并且没有正确释放这些资源,可能会导致资源泄漏或其他问题。这可能会影响系统的稳定性和正常运行。

总结

进程是计算机中正在运行的程序的实例。每个进程都有一个唯一的进程标识符(PID)和一组相关的资源,如内存、文件描述符等。进程之间是相互独立的,它们通过操作系统进行调度和管理。PCB是操作系统用来管理和跟踪进程的数据结构。PCB 包含了进程的状态信息、程序计数器、寄存器值、内存分配信息等。操作系统通过修改 PCB 中的信息来控制和调度进程的执行。通过查看进程列表,可以获取当前系统中正在运行的进程的信息。不同的操作系统提供了不同的命令和工具来查看进程列表,如Linux中的ps命令和Windows中的任务管理器。

fork函数是一个在Unix和类Unix系统中常用的函数,用于创建一个新的进程。fork函数会复制当前进程的副本,并在新的进程中继续执行。新进程和原进程几乎完全相同,包括代码、数据、打开的文件等。fork函数的返回值不同,对于原进程,返回新进程的PID,对于新进程,返回0。

进程有几种不同的状态,包括运行态、就绪态、阻塞态等。进程的状态可以通过操作系统提供的工具或命令来查看。其中,僵尸进程是一种特殊的状态,它是指一个已经终止但是其父进程尚未对其进行善后处理的进程。僵尸进程不会占用系统资源,但可能会导致一些问题,如资源泄漏。为了避免僵尸进程的产生,父进程应该及时对其进行处理。

孤儿进程是指其父进程已经终止或不存在的进程。孤儿进程会被操作系统接管,并由init进程(PID为1)来作为其父进程。孤儿进程本身并没有直接的危害,但可能会占用一些系统资源,并影响进程标识符的分配。为了保持系统的稳定和资源的有效利用,应该及时清理孤儿进程。

温馨提示

感谢您对博主文章的关注与支持!如果您喜欢这篇文章,可以点赞、评论和分享给您的同学,这将对我提供巨大的鼓励和支持。另外,我计划在未来的更新中持续探讨与本文相关的内容。我会为您带来更多关于Linux以及C++编程技术问题的深入解析、应用案例和趣味玩法等。如果感兴趣的话可以关注博主的更新,不要错过任何精彩内容!

再次感谢您的支持和关注。我们期待与您建立更紧密的互动,共同探索Linux、C++、算法和编程的奥秘。祝您生活愉快,排便顺畅!
在这里插入图片描述