当前位置: 首页 > article >正文

Linux——进程基础

进程概念引入

操作系统为了管理加载到内存中的程序也就是进程,需要对其“先描述,在组织”。先描述指的是:利用struct结构体包含进程的各种属性和数据,例如:

struct PCB//process ctrl block 进程控制块
{
    //状态
    //优先级
    //内存指针字段(例如指向可执行程序代码和数据的指针字段)
    //标识符
    //...(包含进程几乎所有的属性字段)
    struct PCB*next
}  

当可执行程序加载到内存中时,操作系统会创建该进程对应的结构体对象,把该进程的各种属性填写到各个字段当中。操作系统为了方便管理进程的PCB,利用PCB中的struct PCB*next字段来连接另外的进程PCB,进而管理内存中所有的PCB。由此对进程的管理,就转化成为对PCB对象的管理,也就是对PCB链表的增删查改。

进程加载到内存


进程的具体理解

进程=内核PCB对象+可执行程序,如下图:

进程的组成

  • 操作系统中有很多的PCB对象,进程也可以是内核数据结构+可执行程序

  • 对所有进程的控制和操作,都只和进程的PCB相关,和进程的可执行程序没有关系

  • PCB是操作系统学科的叫法。对于具体的操作系统Linux,则叫task_struct


标识符PID

标识符描述一个进程的唯一标识,用来区别其他进程,可以使用指令ps axj查看系统的进程,例如:

进程标识符PID

  • 其中PID这一行就是对应的进程的标识符。

  • 一般在Linux中,普通进程都有它的父进程,因为该进程是被其父进程创建出来的,所以PPID这一列就是父进程的PID

查看进程PID的函数

// 头文件
#include <sys/types.h>
#include <unistd.h>
pid_t getpid(void); //用来获取程序的PID,pid_t类型本质为C语言整数类型
pid_t getppid(void); //用来获取

查看系统进程信息

在Linux系统中,使用ll /proc指令可以查看进程的相关信息,如下图:

进程目录

/proc目录下的目录文件都是以数字命名,这个数字就是指一个进程的PID

当一个程序运行起来,根据其PID进入到其进程目录中
进程目录内容

  • cwd:所指的是当前工作目录
  • exe:所指的是可执行程序所在的目录

在C语言中利用fopen函数创建一个文件时,文件会被创建到进程的cwd也就是当前工作目录下

在这里介绍一个改变进程工作目录的函数chdir

// 头文件
#include <unistd.h>
//作用更改进程的当前工作目录
int chdir(const char*path);
//path参数填入新的路径,返回0就是成功,-1就是失败

创建进程

在Linux中可以使用fork函数创建子进程

//所需头文件
#include <unistd.h>
//函数作用:创建子进程
 pid_t fork(void);
//子进程的返回值是0,父进程返回其自己的pid

举例:

#include <stdio.h>
#include <unistd.h>
int main()
{
    printf("fork前, I am a process! pid:%d, ppid:%d\n", getpid(), getppid());
    fork();
    printf("fork后, I am a process! pid:%d, ppid:%d\n", getpid(), getppid())
        return 0;
}

上面这段代码的执行结果是

fork后结果

因为在调用fork之前只有父进程会执行printf("fork前, I am a process! pid:%d, ppid:%d\n", getpid(), getppid());,而在调用fork之后父子进程都会执行printf("fork后, I am a process! pid:%d, ppid:%d\n", getpid(), getppid())这句代码,所以会打印三行结果

因为在fork之后父子进程的代码是会共享的

在使用fork函数创建子进程之后,如果想让子进程执行特定的事情,我们可以利用fork函数的返回值,如果fork返回值为0,则代表为子进程,若大于0就是父进程,详细代码如下:

int main()
{
    pid_t id=fork();
    if(id<0) return 1;
    if(id==0)
    {
        //子进程
    }
    else
    {
        //父进程
    }
    return 0;
}

fork函数创建的子进程的其PCB里面的数据大多数都是拷贝的父进程的,即子进程是以父进程为模版,所以子进程所要执行的代码也是跟父进程指向同一块区域


进程状态

进程状态——运行、阻塞、挂起。进程所谓的状态,其实本质就是task_struct中的一个整形变量,使用不同的数值,来表示不同的运行状态

  1. 运行状态R
    一个CPU一个运行队列,当一个进程的PCB准备就绪,会被链入到运行队列中,等待被CPU调度,如下图:运行状态

    处在运行队列里面的进程PCB就是运行状态R

  2. 阻塞状态
    当一个进程需要键盘、网卡、磁盘等硬件资源时,操作系统会将该进程PCB链入到对应的设备队列中,等待硬件资源就绪,此时进程的PCB状态会变成阻塞,当硬件资源获取完毕就会将该进程的PCB继续链入到运行队列中,进程状态又会变成R运行状态
    当进程在进行等待软硬件资源的时候,资源如果没有就绪,会进行:

    1. 操作系统会将该进程设置为阻塞状态
    2. 将该进程PCB链入等待的资源提供的等待队列中
  3. 挂起状态
    前提:计算机资源已经比较吃紧了
    操作系统会将内存中部分阻塞的进程的代码和数据拷贝到外设即:磁盘的特定区域swap分区,从而减小内存的压力,此时这部分进程的状态就是挂起。当CPU需要调度这些进程时,就会将这些进程对应的代码和数据从swap分区加载到内存中,如下示意图:
    阻塞挂起示意图


进程优先级

进程需要访问某种资源,进程通过一定的方式(排队),确认使用资源的先后顺序

使用以下指令可以查看进程的优先级等信息

ps -l

进程优先级

这一列PRI指的就是一个进程的priority优先级,实际在该进程的PCB中是就是一个整型变量值

Linux的默认优先级是80,优先级范围是60到99,数字越小,优先级就越高

Linux系统允许用户调整优先级,但是不能直接修改pri,而是修改NI值,这个NI值叫做优先级的修正数据,取值范围是-20到19

原来的PRI值都是从80开始的

调整进程优先级会受限制的原因是:如果不加限制,开发人员就会将自己进程的优先级调整得非常高,别人的优先级调整得非常低,就会导致常规进程很难被CPU调度(进程饥饿问题),所以会增加限制


http://www.kler.cn/news/364048.html

相关文章:

  • rootless模式下istio ambient鉴权策略
  • Kafka-Windows搭建全流程(环境,安装包,编译,消费案例,远程连接,服务自启,可视化工具)
  • Unity3D 自动化资源打AB包详解
  • 2024 睿抗机器人开发者大赛(RAICOM)-【网络安全】CTF 部分WP
  • 什么是分库分表?为什么要分库分表?什么时候需要分库分表?怎么样拆分?(数据库分库分表详解)
  • 【C++】拆分详解 - 模板
  • 智联招聘×Milvus:向量召回技术提升招聘匹配效率
  • 华为配置 之 远程管理配置
  • 在pycharm中使用sqllite
  • 通过使用Visual Studio将你的程序一键发布到Docker
  • JavaWeb合集15-线程局部变量ThreadLocal
  • sentinel原理源码分析系列(八)-熔断
  • 十六、行为型(责任链模式)
  • 2024年9月中国电子学会青少年软件编程(Python)等级考试试卷(三级)答案 + 解析
  • PHP企业门店订货通进销存系统小程序源码
  • 定位中的信号干扰与噪声处理
  • 手机群控软件苹果iOS批量控制进化历程 解析及最新动态
  • 普通数组矩阵
  • 西南大学软件专硕考研难度分析!
  • 配置观察端口
  • 【主机漏洞扫描常见修复方案】:Tomcat安全(机房对外Web服务扫描)
  • 图集短视频去水印云函数开发实践——小红书
  • 小白投资理财 - 解读市销率,市现率
  • 新电脑Win11家庭中文版跳过联网激活方法(教程)
  • Go 语言教程:8.数组
  • 毕业设计 基于STM32单片机健康检测/老人防跌倒系统 心率角度检测GSM远程报警 (程序+原理图+元件清单全套资料)