[Linux]:进程(上)

news2024/12/23 19:06:37

img

✨✨ 欢迎大家来到贝蒂大讲堂✨✨

🎈🎈养成好习惯,先赞后看哦~🎈🎈

所属专栏:Linux学习
贝蒂的主页:Betty’s blog

1. 初识进程

1.1 进程的概念

在计算机世界中,进程是一个关键概念。它是程序的执行实例,一般而言,当可执行程序被加载到内存后就形成了进程。进程担当着分配系统资源(如CPU时间和内存)的重要角色,是操作系统进行资源分配和调度的基本单位,使得程序能够在系统中实际运行并完成特定任务。

1.2 进程的理解

进程信息被放在一个叫做进程控制块(PCB)的数据结构中,可以理解为进程属性的集合。PCB是进程存在的唯一标识。在Linux环境下,PCB就是task_struct,一个包含进程属性信息的结构体。

然后我们就可以将进程理解为:被进程控制块PCB所管理的可执行程序。一旦可执行程序被执行加载到内存,操作系统就会创建对应的PCB将其管理,最后就形成了进程。

画板

最后操作系统通过双向链表的形式将各个进程控制块PCB联系起来,方便管理。如果新创建一个进程就将其对应的PCB链接入双向链表中,退出一个进程就是将对于的PCB删除即可。

画板

1.3 task_struct的内容

task_structLinux当中的进程控制块,主要包含以下信息:

  1. 标示符(PID): 描述本进程的唯一标示符,用来区别其他进程。
  2. 状态: 任务状态,退出代码,退出信号等。
  3. 优先级: 相对于其他进程的优先级。
  4. 程序计数器(pc指针): 程序中即将被执行的下一条指令的地址。
  5. 内存指针: 包括程序代码和进程相关数据的指针,还有和其他进程共享的内存块的指针。
  6. 上下文数据: 进程执行时处理器的寄存器中的数据。
  • 进程的代码是不可能在很短时间运行完的,规定每个进程的时间片(单次运行的最长时间),用户感受到的多个进程同时运行,本质上是CPU的快速切换。CPU只有一套寄存器,为了保护上下文,进程的这些临时数据被写入在PCB中,再来执行时,恢复上下文。

画板

  1. I/O状态信息: 包括显示的I/O请求,分配给进程的I/O设备和被进程使用的文件列表。
  2. 记账信息: 可能包括处理器时间总和,使用的时钟数总和,时间限制,记账号等。
  • 进程创建出来,CPU要执行它对应的代码,然而CPU很少,进程很多。因此OS内有一个调度模块,负责较为均衡的调度每一个进程,较为公平的获得CPU资源。让每个进程都能获得CPU资源,让每个进程都能被执行。
  1. 其他信息。

1.4 查看进程

在根目录下存在一个proc文件夹,里面包含了大量关于进程的信息。

其中有很多关于数字的子目录,这些数字其实是某一进程的PID,记录着对应进程的各种信息。

我们可以通过指令ps aux查看所有进程信息。

我们也可以将ps指令与grep指令搭配使用,显示某一进程的信息。

2. 创建进程

2.1 进程标识符

通过系统调用函数getpidgetppid可以分别获取进程的PID(进程ID)和PPID(父进程ID)。


下面我们可以通过一个测试程序观察:

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
  while(1)
  {
     printf("proc PID :%d,parent PID:%d\n",getpid(),getppid());
     sleep(1); 
  }
  return 0;
}

我们可以通过ctrl+c或者kill -9 进程的PID杀死相应的进程。

2.2 fork函数

我们可以使用fork函数创建一个子进程。

如果上述代码加上fork之后,又会打印什么呢?

#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
int main()
{
  fork
  while(1)
  {
     printf("proc PID :%d,parent PID:%d\n",getpid(),getppid());
     sleep(1); 
  }
  return 0;
}

第一行数据是该进程的PIDPPID,第二行数据是代码中通过调用fork函数创建的子进程的PIDPPID。其中该进程的PID就是子进程的父进程PID,所以我们可以说这两个进程是父子关系。而该进程的父进程就是bash,一般而言,在命令行上运行的指令,父进程基本都是bash

并且值得注意的是:在子进程被创建之前的代码被父进程执行,而子进程被创建之后的代码,则默认情况下父子进程都可以执行。父子进程虽然代码共享,但是父子进程的数据各自开辟空间(采用写时拷贝)。

注意:使用fork函数创建子进程后就有了两个进程,这两个进程被操作系统调度的顺序是不确定的,这取决于操作系统调度算法的具体实现。

2.3 fork的返回值

因为父子进程代码共享,而且fork函数在执行return语句时已经创建好了子进程,所以return语句会被父子进程执行两次,所以fork函数肯定有两个返回值。

  1. 如果子进程创建成功,在父进程中返回子进程的PID,而在子进程中返回0。
  2. 如果子进程创建失败,则在父进程中返回 -1。

所以我们可以通过fork的返回值,让父子进程分别去执行不同的过程。代码示例如下:

#include<stdio.h> 
#include<unistd.h>      
int main()    
{    
    pid_t id = fork();    
    if(id == 0)    
    {    
        //child    
        while(1)
        {
        printf("I am child PID:%d PPID:%d\n",getpid(),getppid());
        sleep(1);
        }
    }    
    else if (id > 0)    
    {    
        //parent
        while(1)
        {
        printf("I am parent PID:%d PPID:%d\n",getpid(),getppid());
        sleep(1);
        }
    }    
    else    
    {     
        //fork error
    }    
    return 0;    
} 

并且进程之间具有独立性,即使一个进程中途异常退出,也不会影响其他进程。

3. 进程状态

Linux中,存在大量进程,有些进程可能在运行,有些进程可能在休眠。为了方便操作系统管理,我们需要对进程的状态进行表示。

Linux中,一共有七种状态,源码中是这样表示的:

/*
* The task state array is a strange "bitmap" of
* reasons to sleep. Thus "running" is zero, and
* you can test for combinations of others with
* simple bit tests.
*/
static const char *task_state_array[] = {
	"R (running)",       /*  0*/
    "S (sleeping)",      /*  1*/
    "D (disk sleep)",    /*  2*/
    "T (stopped)",       /*  4*/
    "T (tracing stop)",  /*  8*/
    "Z (zombie)",        /* 16*/
    "X (dead)"           /* 32*/
};

我们可以使用指令ps aux 或者 ps axj 查看对应的进程状态。

3.1 运行状态-R

**R运行状态(running) **: 运行状态不一定占用CPU,并不意味着进程一定在运行中,一个进程处于R状态,它只是表明进程要么是在运行中要么在运行队列里,随时可以被CPU调度 也就是说,可以同时存在多个处于R状态的进程。

比如如下所有处于运行队列的进程都处于运行状态。

画板

3.2 浅度睡眠状态-S

S浅睡眠状态(sleeping): 当需要完成某种任务,而条件不具备时,需要进程进行某种等待,此时的状态就是浅睡眠状态。

比如一下程序:

#include<stdio.h>
#include<unistd.h>
int main()
{
  printf("I am running\n");
  sleep(100);
  return 0;
}

浅度睡眠状态可以用kill指令杀死对应进程。

3.3 深度睡眠状态-D

D深度休眠状态(Disk sleep):有时候也叫不可中断睡眠(深度睡眠)状态,在这个状态的进程通常会等待IO的结束。

例如,某一进程要求对磁盘进行写入操作,那么在磁盘进行写入期间,该进程就处于深度睡眠状态,是不会被杀掉的,因为该进程需要等待磁盘的回复(是否写入成功)以做出相应的应答。

3.4 停止状态-T

T停止状态(stopped):可以通过发送SIGSTOP信号来停止进程,这个被暂停的进程可以通过发送SIGCONT 信号让进程继续运行。

3.5 僵尸状态-Z

Z僵尸状态(Zombies):当一个进程退出时,该进程曾经申请的资源并不是立即被释放,而是要暂时存储一段时间,以供操作系统或是其父进程进行读取,如果退出信息一直未被读取,则相关数据是不会被释放掉的。一旦进程若是正在等待其退出信息被读取,那么我们称该进程处于僵尸状态。

3.6 死亡状态-X

X死亡状态(dead):当一个进程的退出信息被读取后,该进程所申请的资源就会立即被释放,该进程也就不存在了。同样因为不存在了,我们也无法观察到死亡状态。

4. 僵尸进程与孤儿进程

4.1 僵尸进程

处于僵尸状态的进程,我们就称之为僵尸进程。

例如,以下这段代码,fork函数创建的子进程在打印3次信息后会退出,而父进程会一直打印信息。也就是说,子进程退出了,父进程还在运行,此时父进程没有读取子进程的退出信息,那么此时子进程就进入了僵尸状态。

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
int main() {
    pid_t id = fork();
    if (id == 0) {
        // child
        int count = 3;
        while (count > 0) {
            printf("I am child,PID:%d PPID: %d\n", getpid(), getppid());
            sleep(2);
            count--;
        }
        printf("I am child, I quit\n");
    } else if (id > 0) {
        // parent
        while (1) {
            printf("I am parent,PID:%d PPID:%d\n", getpid(), getppid());
            sleep(2);
        }
    } else {
        // fork error
        printf("fork error\n");
    }
    return 0;
}

运行开始我们可以通过该脚本指令检测:while :; do ps axj | head -1 && ps axj | grep pro.out | grep -v grep;echo "######################";sleep 1;done

如果僵尸进程一直不回收,子进程就会一直处于僵尸状态,而维护子进程系统会创建对应PCB,进而造成系统资源的浪费。并且随着僵尸进程的增多,实际使用的资源就会越少,会造成严重的内存泄漏问题。

4.2 孤儿进程

若子进程先退出而父进程没有对子进程的退出信息进行读取,那么我们称该进程为僵尸进程。但若是父进程先退出,那么此时子进程没有父进程对其进行处理,此时该子进程就称之为孤儿进程。若是一直不处理孤儿进程,那么孤儿进程就会一直占用资源,造成内存泄漏。因此,当出现孤儿进程的时候,孤儿进程一般会被1号init进程领养。

比如说下面这段代码,fork函数创建的子进程会一直打印信息,而父进程在打印3次信息后会退出,此时该子进程就变成了孤儿进程。

5. 进程的优先级

5.1 优先级的概念

优先级实际上就是获取某种资源的先后顺序,而进程优先级实际上就是进程获取CPU资源分配的先后顺序,就是指进程的优先权(priority),优先权高的进程有优先执行的权力。

优先级存在的主要原因就是资源是有限的,一个CPU一次只能跑一个进程,而进程是可以有多个的,所以需要存在进程优先级,来确定进程获取CPU资源的先后顺序。

5.2 查看优先级

我们可以通过指令ps -l查看进程的优先级。

  • PRI:代表这个进程可被执行的优先级,其值越小越早被执行。在Linux操作系统当中,PRI(old)默认为80,即PRI = 80 + NI
  • NI:代表这个进程的nice值。NI的取值范围是-20至19,一共40个级别。

5.3 修改优先级

因为PRI = 80 + NI,所以我们只需要修改NI值就能达到修改优先级的目的。

修改NI值一共有两种方法:

第一种就是输入top指令,然后按下R键输入要修改进程的PID,最高输入要修改的NI值。比如下列我们将进程a.outNi修改为10,其优先级PRI变为90。

第二种就是使用renice指令,语法为renice NI PID。比如说我们下面将a.outNI修改为15,其优先级PRI变为95。

其中无论哪种方法,如果想将NI值调为负值,也就是将进程的优先级调高,都需要使用sudo提升权限。

6. 进程调度队列

我们以Linux 2.6版本为例,详细谈一谈进程调度队列。

画板

  • active指针:永远指向活动队列。
  • expired指针:永远指向过期队列。
  • nr_active:代表总共有多少个运行状态的进程。
  • queue[140]:前面说到nice值的取值范围是-20~19,共40个级别,依次对应queue当中普通优先级的下标100~139,相同优先级的进程按照FIFO规则进行排队调度。而下标0~99对应的实时进程,实时进程是指先将一个进程执行完毕再执行下一个进程,现在基本不存在这种机器了,所以对于queue当中下标为0~99的元素我们不关心。
  • bitmap[5]:这是一个位图,queue数组当中一共有140个元素,即140个优先级,一共140个进程队列,为了提高查找非空队列的效率,就可以用5 × 32个比特位表示每个队列是否为空。

所以调度过程如下:

  1. 首先从0下标开始遍历活动队列queue[140]
  2. 选中队列的第一个非空进程即优先级最高的基础,开始运行,调度完成后放入过期队列。
  3. 继续选中队列的第二个非空进程进行调度,直到所有活动队列都被调度,即bitmap[5]等于0。
  4. 如果活动队列全部被调度完毕,交换将active指针和expired指针的内容,让过期队列变成活动队列,活动队列变成过期队列,这样就又有了一批新的活动进程,如此循环进行即可。
  • 如果是新创建的进程则放入过期队列。

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

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

相关文章

机器学习中的聚类艺术:探索数据的隐秘之美

一 什么是聚类 聚类是一种经典的无监督学习方法&#xff0c;无监督学习的目标是通过对无标记训练样本的学习&#xff0c;发掘和揭示数据集本身潜在的结构与规律&#xff0c;即不依赖于训练数据集的类标记信息。聚类则是试图将数据集的样本划分为若干个互不相交的类簇&#xff…

Confluence8.5.14安装

一、Centos8、安装jdk11(略) 二、mysql数据库 1、mysql安装包下载: MySQL :: Download MySQL Community Server 2、安装: https://downloads.mysql.com/archives/get/p/23/file/mysql-8.0.37-1.el8.x86_64.rpm-bundle.tar tar -xvf mysql-8.0.37-1.el8.x86_64.rpm-bund…

浏览器剪贴板 API Clipboard API

在 Web 开发领域&#xff0c;Clipboard API 就是一个备受关注的新利器&#xff0c;它为我们提供了在网页中访问和操作剪贴板的能力&#xff0c;极大地丰富了用户交互体验。本文将深入探讨 Clipboard API 的使用方法和潜在应用场景。 一. 什么是 Clipboard API&#xff1f; Cl…

集合及映射

1、集合类图 1&#xff09;ArrayList与LinkedList 区别 LinkedList 实现了双向队列的接口&#xff0c;对于数据的插入速度较快&#xff0c;只需要修改前后的指向即可&#xff1b;ArrayList对于特定位置插入数据&#xff0c;需要移动特定位置后面的数据&#xff0c;有额外开销 …

Windows 安装mysql 教程,mysql 多版本共存教程,傻瓜式安装教程

mysql 各版本官方下载地址&#xff1a;⬇ ⬇⬇⬇⬇⬇⬇⬇⬇⬇(点击下面链接前往)MySQL :: Download MySQL Community Server (Archived Versions)https://downloads.mysql.com/archives/community/ 首先我本地安装了 mysql8.0版本了&#xff0c;通过msi 进行安装的也就是傻瓜式…

SprinBoot+Vue高校网上缴费综合务系统的设计与实现

目录 1 项目介绍2 项目截图3 核心代码3.1 Controller3.2 Service3.3 Dao3.4 application.yml3.5 SpringbootApplication3.5 Vue 4 数据库表设计5 文档参考6 计算机毕设选题推荐7 源码获取 1 项目介绍 博主个人介绍&#xff1a;CSDN认证博客专家&#xff0c;CSDN平台Java领域优质…

文心快码前端工程师观点分享:人机协同新模式的探索之路(三)

本系列视频来自百度工程效能部的前端研发经理杨经纬&#xff0c;她在由开源中国主办的“AI编程革新研发效能”OSC源创会杭州站105期线下沙龙活动上&#xff0c;从一款文心快码&#xff08;Baidu Comate&#xff09;前端工程师的角度&#xff0c;分享了关于智能研发工具本身的研…

AIGC是如何颠覆文旅行业的?

AI技术正在以前所未有的速度和规模&#xff0c;颠覆着各行各业的发展。在文旅行业&#xff0c;这种颠覆尤为显著。今天&#xff0c;我们深入探讨AIGC是如何颠覆文旅行业的。 传统的文旅内容创作方式&#xff0c;往往需要大量的人力、物力和财力投入。拍摄、录制、剪辑&#xf…

第二天旅游线路规划和预览

第二天&#xff1a;从克拉玛依市乌尔禾区到五彩滩&#xff0c;晚上住宿贾登峪&#xff1b; 规划结果见下图&#xff1a; 1、行程安排 根据上面的耗时情况&#xff0c;规划一天的行程安排如下&#xff1a; 1&#xff09;早上7&#xff1a;30起床&#xff0c;吃完早饭&#xff0c…

微信小程序页面制作——本地生活(含代码)

✅作者简介&#xff1a;2022年博客新星 第八。热爱国学的Java后端开发者&#xff0c;修心和技术同步精进。 &#x1f34e;个人主页&#xff1a;Java Fans的博客 &#x1f34a;个人信条&#xff1a;不迁怒&#xff0c;不贰过。小知识&#xff0c;大智慧。 &#x1f49e;当前专栏…

基于ASP+ACCESS的教师信息管理系统

摘要 随着我国社会主义市场经济的发展和改革开放的不断深入&#xff0c;计算机的应用已遍及国民经济的各个领域&#xff0c;计算机来到我们的工作和生活中&#xff0c;改变着我们和周围的一切。在以前&#xff0c;学校用手工处理教师档案以及工资发放等繁多的工作和数据时&…

谷粒商城の缓存篇

文章目录 前言一、本地缓存和分布式缓存1.本地缓存2.分布式缓存 二、项目实战1.配置Redis2.整合业务代码2.1 缓存击穿2.2 缓存雪崩2.3 缓存穿透2.4 业务代码1.0版2.5 分布式锁1.0版2.6 分布式锁2.0版2.7 Spring Cache及缓存一致性问题2.7.1 Spring Cache2.7.2 缓存一致性问题2.…

[003].第3节.在Windows环境中搭建Redis(单机版)环境

我的后端学习大纲 我的Redis学习大纲 1.Redis下载: 1.中文2.英文 2.Windows下搭建Redis环境&#xff1a; 2.1.单机

[论文笔记]Making Large Language Models A Better Foundation For Dense Retrieval

引言 今天带来北京智源研究院(BAAI)团队带来的一篇关于如何微调LLM变成密集检索器的论文笔记——Making Large Language Models A Better Foundation For Dense Retrieval。 为了简单&#xff0c;下文中以翻译的口吻记录&#xff0c;比如替换"作者"为"我们&quo…

深入理解C语言中的POSIX定时器

引言 在Unix和类Unix系统中&#xff0c;定时器是一种常见的机制&#xff0c;用于在特定时间间隔后执行某些操作。POSIX定时器因其灵活性和功能丰富而被广泛采用。本文将深入探讨POSIX定时器的工作原理、内部机制、使用方法及其在实际开发中的应用。 POSIX定时器基础 POSIX定…

【视频讲解】Python贝叶斯卷积神经网络分类胸部X光图像数据集实例

全文链接&#xff1a;https://tecdat.cn/?p37604 分析师&#xff1a;Yuanchun Niu 在人工智能的诸多领域中&#xff0c;分类技术扮演着核心角色&#xff0c;其应用广泛而深远。无论是在金融风险评估、医疗诊断、安全监控还是日常的交互式服务中&#xff0c;有效的分类算法都是…

数据仓库理论知识

1、数据仓库的概念 数据仓库&#xff08;英文&#xff1a;Date Warehouse&#xff0c;简称数仓、DW&#xff09;&#xff0c;是一个用于数据存储、分析、报告的数据系统。数据仓库的建设目的是面向分析的集成化数据环境&#xff0c;其数据来源于不同的外部系统&#…

Git 修改Push后的Commit Message

向远程仓库push代码之后&#xff0c;在IDEA中无法直接修改Commit Message&#xff0c;需要在终端或控制台中输入以下命令&#xff08;HEAD~1中的1表示只对最后一个提交进行修改&#xff0c;因此1可以自定义&#xff09; git rebase -i HEAD~1执行完rebase指令后&#xff0c;会…

带AI功能朵米客服系统3.5无限制开心版+搭建文档

带AI功能朵米客服系统3.5无限制开心版搭建文档&#xff0c;朵米客服系统是一款全功能的客户服务解决方案&#xff0c;提供多渠道支持&#xff08;如在线聊天、邮件、电话等&#xff09;&#xff0c;帮助企业建立与客户的实时互动。该系统具有智能分流功能&#xff0c;可以快速将…

0基础跟德姆(dom)一起学AI Python进阶07-多线程_生成器

* 多进程案例 * 带参数的多进程代码**(重点)** * 查看进程的id * 演示: 进程之间数据是相互隔离的 * 多线程案例 * 入门案例 * 带参数的多线程代码**(重点)** * 演示: 线程之间数据是相互共享的 * 互斥锁 * 上下文管理器**(重点)** > 解析: with open原理, 为啥…