【Linux学习】超详细——进程(2)

news2024/11/24 15:58:26

三、进程状态

3.1 准备知识

  进程阻塞:进程因为等待某种条件就绪,而导致的一种不推进的状态(例如进程卡顿),因而阻塞一定是在等待某种资源。为什么阻塞?进程需要通过等待的方式,等具体的资源被别人使用结束后,再被自己使用,因而阻塞就是进程等待某种资源就绪的过程。这些资源可以是磁盘、网卡、显卡和键盘等各种外设。而等待的过程需要考虑大量的进程,这些进程需要被管理(先描述,再组织,详见下图及模拟结构)。

//将外设抽象成数据结构
struct dev{
	struct task_struct* queue;//pcb可以被维护在不同的队列中
    //dev其他所有的属性
    
}

image-20230708152256069

图1 进程资源等待

  因而,进程阻塞就是不被调度, 一定因为当前进程需要等待某种资源就绪,一定是进程task_struct结构体需要在某种被OS管理的资源下排队,图2所示。

image-20230708153832992

图2 进程阻塞

3.2 进程状态内核源码

  为了理解正在运行的进程是什么意思,我们需要知道进程的不同状态。一个进程可以有几个状态(在Linux内核里,进程有时也叫任务)。下列代码是状态在kernel源代码里的定义:

/*
* 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 * 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 */
};
  • R运行状态,并不意味着进程一定在运行中,它表明进程要么在运行中,要么在运行队列里;
  • S休眠状态,意味着进程在等待时间完成,可中断休眠;
  • D休眠状态,不可中断休眠,这个状态的进程通常会等待IO的结束;
  • T暂停状态,可以通过发送SIGSTOP信号给进程来停止(T)进程。这个被暂停的进程可以通过发送SIGCONT信号让进程继续运行;
  • t追踪状态
  • X死亡状态,这个状态只是一个返回状态,用户不会在任务列表里看到这个状态
  • Z僵尸状态

  孤儿进程:父进程先退出,子进程就被称为孤儿进程。此外,孤儿进程被1号init进程领养,当然要有init进程回收。

3.3 进程状态查看

  进程只要是R状态,不一定正在CPU上运行。进程有自己的运行队列 ,task_struct是一个结构体,内部会包含各种属性,包含状态。

image-20230708154902133

图3 进程运行状态
//案例1--查看进程状态
#include<stdio.h>
int main(){
    while(1){
        //printf()就是向外设打印消息
        printf("我在运行码??\n");
    }
    return 0;
}

image-20230708155929197

图4 进程状态查看

  例如我们常用的VS2019等编译软件中,在debug模式下,断点调试就相当于将进程(任务)处于暂停状态。此外,我们为什么创建进程?因为我们要让进程帮我们办事,我们关心它的结果 。

  命令echo $?,可获取进程的退出码:

image-20230708164439364

图5 进程状态查看

  如果一个进程退出了,立马X状态,立马退出,作为父进程,有没有机会拿到退出的结果?详见下列代码及结果。

#include<stdio.h>
#include<sys/types.h>
#include<unistd.h>
int main(){
pid_t id = fork();
    if(id == 0){
        //子进程
        while(1){
            printf("我是子进程, 我在运行, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(1);
        }
    }else if(id > 0){
        //父进程
        while(1){
            printf("我是父进程, 我在运行, pid: %d, ppid: %d\n", getpid(), getppid());
            sleep(1);
        }
    }
    return 0;
}

image-20230708164322003

图6 进程状态查看

  在图6中,通过指令kill -9 8961杀死子进程后,子进程不会立即彻底退出,而是维持Z状态(僵尸状态),以方便后续父进程(操作系统)读取该子进程退出的退出结果。

3.4 僵尸进程危害

  • 进程的退出状态必须被维持下去,因为它要告知父进程,父进程交待的任务,完成得怎么样了。父进程若一直不读取,那么子进程就会一直处于僵死状态;
  • 维护退出状态本身就是要用数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,换句话说,僵尸状态一直不退出,PCB就要一直维护;
  • 若一个父进程创建了很多子进程,就是不回收,就会造成内存资源的浪费。因为数据结构对象本身就要占用内存资源(例如C语言中定义一个结构体变量(对象),是要在内存的某个位置进行开辟空间);
  • 此外,过多的僵尸进程会引起内存泄漏。

四、环境变量

4.1 基本概念

  环境变量一般是指在操作系统中用来指定操作系统运行环境的一些参数。例如,我们在编写C/C++代码的时候,在代码链接时,从来不知道我们所链接的动静态库在哪里,但是照样可以链接成功,生成可执行程序,因为相关环境变量帮助编译器进行查找的。环境变量在系统中通常具有全局性。

4.1.1 常见环境变量

  • PATH:指定命令的搜索路径;
  • HOME:指定用户的主工作目录(即用户登录到Linux系统中时,默认的目录);
  • SHELL:当前Shell,它的值通常是/bin/bash。

4.1.2 PATH环境变量

  Linux系统中导入环境变量的方法

export PATH=$PATH:地址

注意:在Linux中,把可执行程序拷贝到系统默认路径下,让我们可以直接访问的方式 相当于Linux软件下的安装如cp -rf "可执行程序名" /usr/bin,删除则可以使用rm /usr/bin/"可执行程序名"

查看环境变量的方法:

echo $NAME //NAME,用户的环境变量名称

查看系统环境变量直接使用命令env

image-20230710225944742

图7 系统环境变量

4.2 环境变量的组织方式

  每个程序都会收到一张环境表,环境表是一个字符指针数组,每个指针指向一个以\0结尾的环境字符串。

image-20230712170541334

图8 环境变量表的组织方式

4.3 通过代码获取环境变量

1、命令行第三个参数:

int main(int argc, char *argv[], char *envp[]){   
   for(int i = 0; envp[i]; i++){
        printf("envp[%d] -> %s\n", i, envp[i]);
    }
    return 0;
 }

打印该进程的环境变量表:

image-20230712170317692

图9 进程环境变量表

2、通过第三方变量environ获取

int main(){
    extern char **environ;
    for(int i = 0; environ[i]; i++){
        printf("environ[i] -> %s\n", i ,environ[i]);
    }
}

image-20230712171232400

图10 第三方变量获取进程环境变量

3、 通过系统调用获取或设置环境变量

使用getenv()来访问特定的环境变量:

image-20230710232608317

图11 getenv()函数使用方法
int main(){
    char *user = getenv("USER");
    if(user == NULL){
        perror("USER");
    }else{
        printf("USER: %s\n",user);
    }
    return 0;
}

image-20230710232911801

图12 系统函数调用获取环境变量

4.4 实验

1、模拟Linux中用户的判别:

#include<stdio.h>
#include<stdlib.h>
#include<string.h>

#define NAME "ChuHsiang"
int main(){
    char *own = getenv("USER");
    if(strcmp(own,NAME) == 0){
        printf("这个程序已经执行啦...\n");
    }else{
        printf("当前用户%s是一个非法用户, 无法执行\n", own);
    }

    return 0;
}

image-20230710233434922

图13 系统指令模拟

  环境变量本质就是一个内存级的一张表(环境变量对应的数据是从系统的相关配置文件中读取的,例如.bash_profile.bashrc),这张表由用户在登录系统的时候,进行给特定用户形成属于自己的环境变量表;环境变量中每一个,都有自己的用途:有的是进行路径查找,有的是进行身份认证,有的是动态库查找,有的是进行确认当前用户路径等等;每一个环境变量都有自己的特定应用场景,每一个元素都是KV结构。

扩展:

命令行式自定义变量,shell本地变量,不能被环境变量继承

image-20230710234548268

命令行式自定义变量导入环境变量

image-20230710234948782

int main(){
 	printf("myenv: %s\n", getenv("hello"));
 	return 0;
}

image-20230710235251768

int main(int argc, char *argv[]){
    //char *argv[]
    for(int i = 0; i < argc; i++){
        printf("argv[%d] -> %s\n", i, argv[i]);
    }
    return 0;
}

image-20230711000637487

bash制作这个表

image-20230711002126195

图14 进程模拟系统指令

命令行参数

void Usage(const char *name){
    printf("\nUsage: %s -[a|b|c]\n", name);
    exit(0);//终止进程
}

int main(int argc, char *argv[]){
    //eg ./myproc arg
    if(argc != 2){
        Usage(argv[0]);
    }

    if(strcmp(argv[1], "-a") == 0){
        printf("打印当前目录下的文件名\n");
    }else if(strcmp(argv[1], "-b") == 0){
        printf("打印当前目录下的文件的详细信息\n");
    }else if(strcmp(argv[1], "-c") == 0){
        printf("打印当前目录下的文件名(包含隐藏文件)\n");
    }else{
        printf("其他功能, 待开发\n");
    }

    return 0;
}

image-20230711003507618

图15 进程模拟系统指令结果

4.5 进程的优先级

4.5.1 基本概念

  • CPU资源分配的先后顺序,就是指进程的优先权;
  • 优先权高的进程有优先执行的权力。配置进程优先权对多任务环境的Linux很有用,可以改善系统性能;
  • 还可以将进程运行到指定的CPU上,这样一来,将不重要的进程安排到某个CPU,可以大大改善系统整体性能。

  对于优先级和权限,我们通常会有一定的误解;权限代表能或不能;优先级已经能,但是谁先谁后的问题。

  为什么有优先级?因为CPU资源有限,而进程个数较多。

4.5.2 查看系统进程

在Linux中,使用ps -l命令则会输出下图内容:

image-20230711004003000

图16 系统进程

上图中,我们注意到几个重要信息:

  • UID:表示执行者身份;
  • PID:表示这个进程的代号;
  • PPID:父进程的代号;
  • PRI:表示这个进程可被执行的优先级,其值越小越早被执行;
  • NI:表示这个进程的nice值。

PRI和NI的理解

  • PRI进程的优先级,即程序被CPU执行的先后顺序,此值越小进程的优先级越高;
  • NI即nice,表示进程可被执行的优先级的修正数值;
  • PRI值越小越快被执行,加入nice值后,PRI变为:PRI(new)=PRI(old)+nice
  • nice取值范围-20~19,一共40个级别;

注意:进程的nice值不是进程的优先级,两者不是同一个概念,但进程nice值会影响到进程优先级的变化;可以理解nice值是进程优先级的修正数据。

4.5.3 查看进程优先级的命令

用top命令更改已存在进程的nice值:

  • top
  • 进入top后,按r->输入进程PID->输入nice值

五、进程地址空间

5.1 地址空间知识回顾

  64位计算机相较于32位计算机复杂了些,但原理基本相似,本文以32位平台为基础,后续不再重复叙述。图17所示32位平台的程序地址空间布局图是我们在计算机相关课程学习到的,但对其原理知之甚少。例如为什么地址空间是4GB大小?因为2^32bit = 4 * 2^10 * 2^ 10 * 2^10bit = 4 * 2^10 * 2^10Byte = 4 * 2^10 MB = 4 GB

image-20230711224109710

图17 地址空间结构图

地址空间是内核的数据结构,详如:

struct mm_struct //4GB
{
    long code_start;
    long code_end;
    long init_start;
    long init_end;
    ...
    long brk_start;
    long brk_end;
    long stack_start;
    long stack_end;    
};
//如果限定了区域那么区域之间的数据 叫虚拟地址或线性地址

代码区:存放程序的代码,即CPU执行的机器指令,并且只读;

堆区:由程序员调用malloc等函数来主动申请,需要free()函数来释放内存;申请堆区内存后,忘记释放容易造成内存泄漏;

区域划分:对线性区域进行指定start和end即可完成区域划分,地址空间本质就是一个线性区域。

含有子进程的进程的地址空间实验

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

//全局变量
int g_value = 100;

int main(){
    pid_t id = fork();
    //fork()在返回的时候,父子都有了,return两次,id不是是pid_t类型的变量,返回本质就是写入
    //谁先返回,就让OS发生写时拷贝
    assert(id >= 0);
    if(id == 0){
        //子进程
        while(1){
            printf("我是子进程, 我的ID是: %d, 我的父进程是: %d, g_value: %d, &g_value: %p\n", getpid(), getppid(), g_value++, &g_value);
            sleep(1);
        }

    }else{
        //父进程
        while(1){
            printf("我是父进程, 我的ID是: %d, 我的父进程是: %d, g_value: %d, &g_value: %p\n", getpid(), getppid(), g_value ,&g_value);
            sleep(1);
        }
    }
    return 0;
}

image-20230711230059491

图18 进程地址空间实验

  上图中,我们发现:输出的变量g_value的地址一模一样,但变量的内容不一样。可知子进程对全局数据修改,并不影响父进程,因而进程具有独立性(由于写时拷贝);这里内存地址为0x60105c不是物理地址,一般称之为虚拟地址线性地址;我们在用C/C++语言所看到的地址,全部是虚拟地址,物理地址用户一概无法看到,其由操作系统统一管理。操作系统负责将虚拟地址转化成物理地址

注意:物理内存不存在读取同一个变量的地址,会读到不同的数值。进程 = 内核数据结构 + 代码和数据

5.2 进程地址空间

image-20230711235800854

图19 页表及地址空间

上图可知:

  • 数据和代码真正只能存在内存中;

  • 我们直接用的是虚拟地址;

  • 找到地址不是目的(是手段),该地址多对应的内容。

5.2.1 地址空间为什么要存在?

  • 防止地址随意访问,保护物理内存与其他进程;
  • 将进程管理和内存管理进行解耦合;
  • 可以让进程以统一的视角,看待自己的代码和数据

5.2.2 malloc的本质

  • 向操作系统申请内存,OS不会立马给用户,在用户需要的时候才给 --> 因为操作系统不允许任何浪费(或不高效的行为);

  • 在用户申请成功之后,在使用之前,有一小段的时间窗口,这个空间没有被正常使用,但是别人用不了(闲置状态) 缺页状态。

5.2.3 重新理解地址空间

  • 我们程序在没有被编译的时候,没有被加载到内存,我们程序内部是有地址的

  • 源代码被编译的时候,就是按照虚拟地址空间的方式进行对代码和数据早就已经编好了对应的编制

  • 虚拟地址空间不仅仅会影响操作系统,还会让编译器遵守它的规则(ELF格式)

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

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

相关文章

SpringCloudAlibaba微服务实战系列(二)Nacos配置中心

SpringCloudAlibaba Nacos配置中心 在java代码中或者在配置文件中写配置&#xff0c;是最不雅的&#xff0c;意味着每次修改配置都需要重新打包或者替换class文件。若放在远程的配置文件中&#xff0c;每次修改了配置后只需要重启一次服务即可。话不多说&#xff0c;直接干货拉…

java实现ffmpeg音频文件分割

项目中需要将视频会议中录入的音频文件通过阿里云语音识别为文件&#xff0c;但是阿里云语音识别对音频大小有限制&#xff0c;因此通过ffmpeg将大音频文件分割为几个短音频文件&#xff0c;并进行语音识别操作。 代码如下&#xff1a; package com.vion.utils; import java.i…

openGauss学习笔记-18 openGauss 简单数据管理-WHERE子句

文章目录 openGauss学习笔记-18 openGauss 简单数据管理-WHERE子句18.1 语法格式18.2 参数说明18.3 示例 openGauss学习笔记-18 openGauss 简单数据管理-WHERE子句 当我们需要根据指定条件从表中查询数据时&#xff0c;就可以在SELECT语句中添加WHERE子句&#xff0c;从而过滤…

Install Ansible on CentOS 8

环境准备&#xff1a; 1.至少俩台linux主机&#xff0c;一台是控制节点&#xff0c;一台是受控节点 2.控制节点和受控节点都需要安装Python36 3.控制节点需要安装ansible 4.控制节点需要获得受控节点的普通用户或root用户的权限&#xff0c;控制节点需要ssh客户端&#xff0c;…

24考研数据结构-——绪论

数据结构 引用文章第一章&#xff1a;绪论1.0 数据结构在学什么1.1 数据结构的基本概念1.2 数据结构的三要素1.3 算法的基本概念 引用文章 在此基础上增加自己的学习过程: 《王道》数据结构笔记整理2022 1.2数据结构三要素——逻辑结构和物理结构与数据运算之间的关系 1.3抽象…

JMeter+提取token变成全局变量

注&#xff1a;没打码&#xff0c;就代码乱写的接口&#xff0c;具体请按照你要跑的接口来输入值 一、创建线程组 二、配置HTTP请求默认值 IP地址一模一样&#xff0c;可以配置一个默认值&#xff0c;就不用每次都输入IP地址了 三、配置登陆ip 配置登陆地址&#xff0c;通过…

iOS 测试 iOS 端 Monkey 测试

说起 Monkey 测试&#xff0c;大家想到的是 monkey 测试只有安卓有&#xff0c;monkey 测试只针对安卓 app&#xff0c;今天给大家分享一下 Monkey 测试在 iOS 端也能跑&#xff01;iOS 端 app 也能使用 Monkey 测试来执行稳定性测试。 一、环境准备 1、准备 Mac 设备&#x…

物业小区管理系统登录页面以及逻辑实现

学习vue3和springboot那肯定是少不了写项目的&#xff0c;在各个项目中肯定是离不开登录和注册的事情的&#xff0c;这也是一个项目起步的需求。 接下来我们来看看我们所写的项目起步。首先搭建vue3和springboot的项目环境&#xff0c;这些搭建大家自行完成即可&#xff0c;架子…

windows 修改 RDP 远程桌面端口号

打开 PowerShell &#xff0c; 执行regedit 依次展开 PortNumber HKEY_LOCAL_MACHINE \SYSTEM \CurrentControlSet \Control \Terminal Server \WinStations \RDP-Tcp 右边找到 PortNumber &#xff0c;对应修改自己的端口号 HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Co…

深度学习:tf.keras实现模型搭建、模型训练和预测

在sklearn中&#xff0c;模型都是现成的。tf.Keras是一个神经网络库,我们需要根据数据和标签值构建神经网络。神经网络可以发现特征与标签之间的复杂关系。神经网络是一个高度结构化的图&#xff0c;其中包含一个或多个隐藏层。每个隐藏层都包含一个或多个神经元。神经网络有多…

echarts3d饼图实现

一、vue中使用3d饼图 效果图&#xff1a; 二、使用步骤 1.引入库 安装echarts 在package.json文件中添加 "dependencies": {"echarts": "^5.1.2""echarts-gl": "^1.1.0",// "echarts-gl": "^2.0.8&quo…

基于AutoEncoder自编码器的MNIST手写数字数据库识别matlab仿真

目录 1.算法理论概述 2.部分核心程序 3.算法运行软件版本 4.算法运行效果图预览 5.算法完整程序工程 1.算法理论概述 MNIST手写数字数据库是机器学习中常用的数据集&#xff0c;包含了0到9这10个数字的手写图片。本文介绍一种基于AutoEncoder自编码器的MNIST手写数字识别算…

高校大数据教材推荐-Hadoop大数据开发基础(第2版)(微课版)

Hadoop大数据开发基础(第2版)(微课版)是“十四五”职业教育国家规划教材&#xff0c;是大数据应用开发“1X”职业技能等级证书配套系列教材&#xff0c;也是“以纸质教材为核心、以互联网为载体”的新形态教材&#xff0c;配套39个微课视频&#xff08;二维码随扫随学&#xff…

DASCTF 2023 0X401七月暑期挑战赛 Reverse部分题解

文章目录 controlflow1. 异或0x4012. 加i*i3. 异或i*(i1)4. 减i5. 乘36. swap7. judge解题脚本 webserver1.关键函数2. 求约束条件3.Z3求解 controlflow 动态调试观察执行情况 1. 异或0x401 2. 加i*i 3. 异或i*(i1) 注意这里是从data[10i]开始 4. 减i 5. 乘3 6. swap 注意…

redis的简单入门

文章目录 一、前言1.1、什么是Redis? 二、简介三、Redis下载与安装四、Redis服务启动与停止五、Redis设置密码进行远程连接5.1、设置密码5.2、远程连接 六、Redis数据类型七、Redis常用命令7.1、字符串String命令7.2、哈希hash操作命令7.3、列表list操作命令7.4、集合set操作命…

机器人SLAM导航学习-All in one

参考引用 张虎&#xff0c;机器人SLAM导航核心技术与实战[M]. 机械工业出版社&#xff0c;2022.本博客未详尽之处可自行查阅上述书籍 一、编程基础篇 1. ROS 入门必备知识 ROS学习笔记&#xff08;文章链接汇总&#xff09; 2. C 编程范式 《21天学通C》读书笔记&#xff0…

leetcode743. 网络延迟时间 DJ

https://leetcode.cn/problems/network-delay-time/ 有 n 个网络节点&#xff0c;标记为 1 到 n。 给你一个列表 times&#xff0c;表示信号经过 有向 边的传递时间。 times[i] (ui, vi, wi)&#xff0c;其中 ui 是源节点&#xff0c;vi 是目标节点&#xff0c; wi 是一个信…

python_day13

reduceByKey算子&#xff0c;聚合 列表中存放二元元组&#xff0c;元组中第一个为key&#xff0c;此算子按key聚合&#xff0c;传入计算逻辑 from pyspark import SparkConf, SparkContext import osos.environ["PYSPARK_PYTHON"] "D:/dev/python/python3.10…

Geriit使用出错记录

拉取服务器代码&#xff08;clone &#xff09; 1、执行&#xff1a;git clone ssh:xxxxxx && scp -p -P 29418 xxxxxxxxx 1、报错&#xff1a;Unable to negotiate with XX.XX.XX.XX port XX:: no matching key exchange method found. Their offer: diffie-hellman…

第五章 编程之免交互

免交互&#xff1a;不需要人为控制就可以完成的自动化操作&#xff08;自动化运维&#xff09; shell脚本和面交互是一个概念&#xff0c;但是两种写法 shell&#xff1a;默认解释器是bash 使用i/o&#xff08;输入/输出&#xff09;重定向的方式&#xff0c;将命令的列表提供…