【Linux篇】进程入门指南:操作系统中的第一步

步入进程世界:初学者必懂的操作系统概念

  • 一. 冯诺依曼体系结构
    • 1.1 背景与历史
    • 1.2 组成部分
    • 1.3 意义
  • 二. 进程
    • 2.1 进程概念
      • 2.1.1 PCB(进程控制块)
    • 2.2 查看进程
      • 2.2.1 使用系统文件查看
      • 2.2.2 使⽤top和ps这些⽤⼾级⼯具来获取
      • 2.2.3 通过系统调用获取进程标识符
    • 2.3 创建子进程
    • 2.4 进程状态
      • 2.4.1 查看进程状态
  • 三. 最后

前言

在计算机系统中,进程是执行程序的基本单位。它不仅仅是代码的集合,更是操作系统管理和分配资源的核心对象。每当我们运行一个应用程序,操作系统就会为其创建一个进程,使得程序能够在计算机中独立执行并进行资源管理。理解进程的概念对于深入学习操作系统和高效利用计算机资源至关重要。 接下来的篇章将带领大家深入探讨进程管理。

💬 欢迎讨论:如果你在学习过程中有任何问题或想法,欢迎在评论区留言,我们一起交流学习。你的支持是我继续创作的动力!
👍 点赞、收藏与分享:觉得这篇文章对你有帮助吗?别忘了点赞、收藏并分享给更多的小伙伴哦!你们的支持是我不断进步的动力!
🚀 分享给更多人:如果你觉得这篇文章对你有帮助,欢迎分享给更多对Linux OS感兴趣的朋友,让我们一起进步!

一. 冯诺依曼体系结构

在这里插入图片描述

1.1 背景与历史

冯·诺依曼体系结构是现代计算机的基础设计模型,由约翰·冯·诺依曼于1945年提出。该结构的核心思想是将程序和数据存储在同一个内存中,计算机通过中央处理单元(CPU)按顺序执行指令。再此向大佬致敬。👏

1.2 组成部分

  • CPU(中央处理器)由运算器和控制器组成。

存储器(内存)是CPU进行读、获取的必要手段,CPU执行程序,必须先加载,将代码及数据加载到内存,CPU执行代码,访问数据。

结构由五个基本组成部分构成:输入设备、输出设备、存储器、运算器和控制单元。

1.3 意义

它的意义在于简化了计算机设计,提高了计算机的可编程性,使得不同的程序可以通过修改存储器中的指令和数据来实现多种功能。冯·诺依曼体系结构成为现代计算机系统的标准架构,推动了计算机技术的飞速发展。

二. 进程

2.1 进程概念

进程是程序的一次执行实例,是操作系统进行资源分配和调度的基本单位。它拥有独立的内存空间、系统资源(如文件句柄、网络端口)和运行状态。

本人理解:进程 = 内核数据结构对象 + 代码和数据 (进程 = PCB(进程控制块) + 代码和数据)

2.1.1 PCB(进程控制块)

进程的所有属性全保存在PCB中,Linux操作系统下的PCB为 task_struct, 该结构体为Linux内核中的一种数据结构,它会被加载到内存(RAM)中。

task_struct具有的部分信息,另一部分的信息将在后续的博客讲解

标⽰符: 描述本进程的唯⼀标⽰符,⽤来区别其他进程。
• 状态: 任务状态,退出代码,退出信号等。
• 优先级: 相对于其他进程的优先级。
• 程序计数器: 程序中即将被执⾏的下⼀条指令的地址。
• 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针
• 上下⽂数据: 进程执⾏时处理器的寄存器中的数据[休学例⼦,要加图CPU,寄存器]。
• I∕O状态信息: 包括显⽰的I/O请求,分配给进程的I∕O设备和被进程使⽤的⽂件列表。
• 记账信息: 可能包括处理器时间总和,使⽤的时钟数总和,时间限制,记账号等。

OS如何对各种进程进行的管理的?先描述,在组织。
所有运行的进程都以 task_struct 链表的形式存在内核中。
在这里插入图片描述

2.2 查看进程

2.2.1 使用系统文件查看

命令格式:

ls /proc/ [pid]

  • 功能

查看指定进程的进程信息。不加pid则查看所有进程的信息

2.2.2 使⽤top和ps这些⽤⼾级⼯具来获取

命令格式:

  • ps ajx | grep myprocess | grep -v grep

2.2.3 通过系统调用获取进程标识符

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{
printf("pid: %d\n", getpid());
printf("ppid: %d\n", getppid());
return 0;
}
  • getpid()系统调用,用于获取当前进程的 PID(Process ID),在子进程中调用 getpid(),返回的是子进程自身的 PID。
  • getppid()系统调用,用于获取当前进程的父进程PID。

2.3 创建子进程

示例代码:

#include<stdio.h>
#include<unistd.h>
#include <sys/types.h>int gval = 100;int main()
{printf("父进程开始运行 ,pid: %d\n",getpid());pid_t id = fork();if(id < 0){perror("id");return 1;}else if(id == 0){printf("我是一个子进程 !, 我的pid:%d, 我的父进程id:%d, gval: %d\n",getpid(),getppid(),gval);sleep(5);//childwhile(1){sleep(1);printf("子进程修改变量 : %d -> %d", gval,  gval+10);gval+=10;printf("我是一个子进程 !, 我的pid:%d, 我的父进程id:%d\n",getpid(),getppid());}}else{//fatherwhile(1){sleep(1);printf("我是一个父进程 !, 我的pid:%d, 我的父进程id:%d, gval: %d\n",getpid(),getppid(),gval);}}printf("进程开始运行 ,pid: %d\n",getpid());return 0;
}

子进程会共享父进程的代码和数据,如果父子任何一方对数据进行修改,OS系统会在底层第数据进行拷贝,让目标进程修改这个拷贝。

问题1:为什么fork给父子返回各自不同的返回值?

  • 父进程中的返回值:

fork() 在父进程中返回 子进程的 PID(进程 ID)。这个 PID 是一个正整数,唯一标识子进程。父进程可以使用该 PID 来跟踪子进程,执行如等待子进程结束、获取子进程的状态等操作。

  • 子进程中的返回值:

在子进程中,fork() 返回 0。子进程通过这个返回值可以判断自己是否是子进程,父进程通过返回值判断是否是父进程。

问题2:为什么一个函数会返回两次?

  • 父进程的情况:

返回值是子进程的 PID(进程ID),是一个正整数。

父进程用这个 PID 来识别和管理子进程。父进程可以使用这个 PID 来执行如 wait()、waitpid() 等系统调用,等待子进程终止或获取子进程的退出状态。

  • 子进程的情况:

返回值是 0。

子进程用返回值 0 来判断自己是子进程,以便执行不同于父进程的代码。子进程可能会通过这个返回值执行某些特定的初始化工作或处理。

结论:fork() 会返回两次是因为它在父进程和子进程中分别执行,父进程获得的是子进程的 PID,子进程获得的是 0。通过这个返回值,父进程和子进程可以执行不同的代码,保证了进程的正确管理和并行执行。

问题3:fork两个返回值各种给⽗⼦如何返回?

  • 父进程:父进程使用返回子进程的PID,来管理或等待子进程。
  • 子进程:子进程返回0,子进程用这个值来判断自己是子进程,以执行不同于父进程代码逻辑(比如初始化、执行任务等)。
  • fork返回负值,表示fork调用失败(资源不足等)它会返回 -1,并且没有子进程创建。操作系统会设置 errno 来表示具体的错误原因。

2.4 进程状态

请看Linux内核关于进程状态的描述:

static const char *const task_state_array[] = {
“R (running)”, /*0 */
“S (sleeping)”, /*1 */
“D (disk sleep)”, /*2 */
“T (stopped)”, /*4 */
“t (tracing stop)”, /*8 */
“X (dead)”, /*16 */
“Z (zombie)”, /*32 */
};

  1. R(running): 运行状态,表示某进程正在运行或准备运行。
  2. S(sleep): 睡眠状态,进程在等待某些事件完成(也称为浅度睡眠,可中断睡眠)。
  3. D(disk sleep): 磁盘休眠状态,进程通常在等待I/O操作完成(也称为深度睡眠,不可中断睡眠)。
  4. T(stopped): 停止状态,可以通过发送信号让该进程继续进行。
  5. t(Tracing stop): 通常是在进行调试或进程跟踪时出现的状态。
  6. X(dead): 死亡状态,不会出现在进程列表中。
  7. Z(Zombie): 僵尸状态,子进程已结束,父进程为获取子进程的退出信息。

2.4.1 查看进程状态

使用ps aux 或 ps ajx 命令可以查看进程的详细状态。
命令选项含义:

  • a:显⽰⼀个终端所有的进程,包括其他⽤⼾的进程。
  • x:显⽰没有控制终端的进程,例如后台运⾏的守护进程。
  • j:显⽰进程归属的进程组ID、会话ID、⽗进程ID,以及与作业控制相关的信息。
  • u:以⽤⼾为中⼼的格式显⽰进程信息,提供进程的详细信息,如⽤⼾、CPU和内存使⽤情况等。

僵尸进程危害
进程的退出状态必须被维持下去,因为他要告诉关⼼它的进程(⽗进程),你交给我的任务,我办的怎么样了。可⽗进程如果⼀直不读取,那⼦进程就⼀直处于Z状态。
• 维护退出状态本⾝就是要⽤数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,
换句话说,Z状态⼀直不退出,PCB⼀直都要维护?是的!
• 那⼀个⽗进程创建了很多⼦进程,就是不回收,是不是就会造成内存资源的浪费。因为数据结构对象本⾝就要占⽤内存,想想C中定义⼀个结构体变量(对象),是要在内存的某个位置进⾏开辟空间!
• 内存泄漏

孤儿进程

⽗进程如果提前退出,那么⼦进程后退出,进⼊Z之后,那该如何处理呢?
• ⽗进程先退出,⼦进程就称之为“孤⼉进程”
• 孤⼉进程被1号init进程领养,当然要有init进程回收

如何避免孤儿进程

  1. 父进程等待子进程退出(避免子进程成为孤儿进程)

父进程可以通过适当的进程管理确保它会在子进程结束时正确地回收子进程的资源,避免进程成为孤儿进程。常用的技术包括:

wait() 或 waitpid():父进程可以使用 wait() 或 waitpid() 来等待子进程结束,并获取子进程的退出状态,从而清理和回收子进程的资源。这样,即使父进程在结束之前退出,它也能确保子进程的资源得到回收。

  1. 使用 signal() 或 sigaction() 捕获终止信号

如果父进程结束时没有来得及清理子进程的资源,可以通过捕获特定信号(如 SIGCHLD)来及时回收子进程的资源。通过设置 SIGCHLD 信号处理函数,父进程可以在子进程结束时自动清理。

  1. 避免父进程直接退出

父进程应该在子进程完成后再退出,例如通过使用 wait() 等系统调用等待子进程完成后再退出。如果父进程在子进程结束前退出,可能导致子进程变成孤儿进程。

定期检查子进程状态,使用 waitpid() 等方法主动回收子进程的资源。

三. 最后

本文主要介绍了操作系统中的进程管理,包括冯诺依曼体系结构、进程概念、进程控制块(PCB)、进程状态及如何查看和管理进程。
冯诺依曼体系结构是现代计算机设计的基础,它将程序和数据存储在同一内存中,提升了计算机的可编程性。进程则是程序的执行实例,操作系统通过进程控制块(PCB)来管理进程的资源和状态。进程通过 fork() 创建子进程,父子进程通过返回值区分身份。进程有不同的状态,如运行、睡眠、停止等,父进程可以通过 wait() 等系统调用回收子进程资源,避免孤儿进程的产生。如果父进程提前退出,子进程可能成为孤儿进程,操作系统会由 init 进程接管。为了避免孤儿进程,父进程应在子进程结束后再退出,并通过信号捕获机制及时清理资源。

路虽远,行则将至;事虽难,做则必成

亲爱的读者们,下一篇文章再会!! \color{Red}亲 爱 的 读 者 们 , 下 一 篇 文 章 再 会 ! ! 亲爱的读者们,下一篇文章再会!!

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

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

相关文章

销售易vs纷享销客:制造行业CRM选型深度解析

“以客户为中心”&#xff0c;顾名思义就是指让客户贯穿企业市场、研发、生产、销售、服务全流程&#xff0c;以客户需求为导向进行经营。CRM作为企业数字化建设基础设施&#xff0c;在企业高质量发展进程中扮演着重要角色。在众多CRM解决方案中&#xff0c;腾讯旗下CRM销售易凭…

【JavaScript】九、JS基础练习

文章目录 1、练习&#xff1a;对象数组的遍历2、练习&#xff1a;猜数字3、练习&#xff1a;生成随机颜色 1、练习&#xff1a;对象数组的遍历 需求&#xff1a;定义多个对象&#xff0c;存数组&#xff0c;遍历数据渲染生成表格 let students [{ name: 小明, age: 18, gend…

代码随想录day31 贪心part05

56.合并区间 以数组 intervals 表示若干个区间的集合&#xff0c;其中单个区间为 intervals[i] [starti, endi] 。请你合并所有重叠的区间&#xff0c;并返回 一个不重叠的区间数组&#xff0c;该数组需恰好覆盖输入中的所有区间 。 示例 1&#xff1a; 输入&#xff1a;in…

《C++11:通过thread类编写C++多线程程序》

关于多线程的概念与理解&#xff0c;可以先了解Linux下的底层线程。当对底层线程有了一定程度理解以后&#xff0c;再学习语言级别的多线程编程就轻而易举了。 【Linux】多线程 -&#xff1e; 从线程概念到线程控制 【Linux】多线程 -&#xff1e; 线程互斥与死锁 语言级别的…

c++位运算总结

在C中&#xff0c;位运算是对二进制位进行操作的运算&#xff0c;主要有以下几种&#xff1a; 1. 按位与&#xff08; & &#xff09;&#xff1a;两个操作数对应位都为1时&#xff0c;结果位才为1&#xff0c;否则为0。例如 3 & 5 &#xff0c; 3 二进制是 0000 0011…

1.1 计算机网络的概念

首先来看什么是计算机网络&#xff0c;关于计算机网络的定义并没有一个统一的标准&#xff0c;不同的教材有 不同的说法&#xff08;这是王道书对于计算机网络的定义&#xff09;&#xff0c;我们可以结合自己的生活经验去体会这个 定义。 可以用不同类型的设备去连接计算机网络…

用LLama factory时报类似Process 2504721 got signal: 1的解决方法

之前用nohup来远程跑LLama factory微调脚本&#xff0c;是没有问题的&#xff0c;但今天发现运行类似下面这个命令时&#xff0c; nohup llamafactory-cli train examples/train_qlora/qwen_lora.yaml 只要一关闭ssh session&#xff0c;就会终止训练&#xff0c;报类似&…

python常用内置时间函数+蓝桥杯时间真题

1.time 1.1 time.time() 时间戳指&#xff1a;1970年1月1日开始到现在所经过的秒数 import time print(time.time()) # 输出可得1970年1月1日开始到执行此代码所经过的秒数 1.2 time.localtime() 返回一个当前时间的时间对象&#xff0c;具体信息&#xff0c;并且可以单独…

一个用 C 语言打印出所有三位数水仙花数的程序

水仙花数&#xff08;Narcissistic number&#xff09;是指一个三位数&#xff0c;其各位数字的立方和等于该数本身。例如&#xff1a;153 是一个水仙花数&#xff0c;因为 (1^3 5^3 3^3 153)。 以下是一个用 C 语言打印出所有三位数水仙花数的程序&#xff1a; 代码实现 …

利用 VSCode 配置提升 vibe coding 开发效率

利用 VSCode 配置提升 vibe coding 开发效率 Vibe Coding&#xff08;氛围编程&#xff09;是一种基于AI的编程方法&#xff0c;其核心在于通过自然语言描述软件需求&#xff0c;再由大规模语言模型&#xff08;LLM&#xff09;自动生成代码&#xff0c;从而实现对传统手写编程…

练习题:110

目录 Python题目 题目 题目分析 需求理解 关键知识点 实现思路分析 代码实现 代码解释 函数定义&#xff1a; 计算值的总和&#xff1a; 测试函数&#xff1a; 运行思路 结束语 Python题目 题目 定义一个函数&#xff0c;接受一个字典作为参数&#xff0c;返回字…

处理 Linux 信号:进程控制与异常管理的核心

个人主页&#xff1a;chian-ocean 文章专栏-Linux 前言&#xff1a; 在 Linux 操作系统中&#xff0c;信号是用于进程间通信的一种机制&#xff0c;能够向进程发送通知&#xff0c;指示某些事件的发生。信号通常由操作系统内核、硬件中断或其他进程发送。接收和处理信号是 Li…

通信协议之串口

文章目录 简介电平标准串口参数及时序USART与UART过程引脚配置 简介 点对点&#xff0c;只能两设备通信只需单向的数据传输时&#xff0c;可以只接一根通信线当电平标准不一致时&#xff0c;需要加电平转换芯片&#xff08;一般从控制器出来的是信号是TTL电平&#xff09;地位…

Unity编辑器功能及拓展(1) —特殊的Editor文件夹

Unity中的Editor文件夹是一个具有特殊用途的目录&#xff0c;主要用于存放与编辑器扩展功能相关的脚本和资源。 一.纠缠不清的UnityEditor 我们Unity中进行游戏构建时&#xff0c;我们经常遇到关于UnityEditor相关命名空间丢失的报错&#xff0c;这时候&#xff0c;只得将报错…

工具类-csv文件导入数据库思路

首先&#xff0c;让我们来看下数据库建表语句&#xff1a; CREATE TABLE behavior_reports (id BIGINT PRIMARY KEY AUTO_INCREMENT COMMENT 报告ID,report_type VARCHAR(50) NOT NULL COMMENT 报告类型(daily, weekly, monthly),start_date DATE NOT NULL COMMENT 开始日期,e…

软件工程之软件开发模型(瀑布、迭代、敏捷、DevOps)

1. 瀑布模型&#xff08;Waterfall Model&#xff09; 定义与流程 瀑布模型是线性顺序的开发流程&#xff0c;包含需求分析、设计、编码、测试、维护等阶段&#xff0c;每个阶段完成后才能进入下一阶段&#xff0c;类似“瀑布流水”逐级推进。 核心特点 严格阶段划分&#…

FreeRTOS与RT-Thread内存分配对比分析

一、动态内存分配策略 ​FreeRTOS ​分配算法多样性&#xff1a;提供5种动态内存管理算法&#xff08;heap_1至heap_5&#xff09;&#xff0c;覆盖从简单到复杂的场景。例如&#xff1a; heap_1&#xff1a;仅支持分配不支持释放&#xff0c;适用于固定任务栈分配。heap_4&…

202519 | Mybatis-Plus

快速入门 MyBatis-Plus&#xff08;简称 MP&#xff09;是 MyBatis 的增强工具&#xff0c;它在 MyBatis 的基础上只做增强不做改变&#xff0c;简化了开发&#xff0c;提高了效率。以下是 MyBatis-Plus 的快速入门指南&#xff0c;帮助您快速上手使用。 1. 环境准备 JDK&…

Linux C语言调用第三方库,第三方库如何编译安装

在 Linux 环境下使用 C 语言调用第三方库时&#xff0c;通常需要先对第三方库进行编译和安装。以下为你详细介绍一般的编译安装步骤&#xff0c;并给出不同类型第三方库&#xff08;如使用 Makefile、CMake 构建系统&#xff09;的具体示例。 一般步骤 1. 获取第三方库源码 …

linux基本命令(1)--linux下的打包命令 -- tar 和gzip

tar 解压 &#xff0c;打包 语法&#xff1a;tar [主选项辅选项] 文件或者目录 使用该命令时&#xff0c;主选项是必须要有的&#xff0c;它告诉tar要做什么事情&#xff0c;辅选项是辅助使用的&#xff0c;可以选用。 主选项&#xff1a; c 创建新的档案文件。如果用户想备…