☰
Linux进程概念
2026/11/3 18:45:05 网站建设 项目流程

文章目录

  • 进程
    • 基本概念与基本操作
      • 描述进程-PCB
      • task_struct
      • 查看进程
      • 通过系统调用来获取进程标识符
      • 通过系统调⽤创建进程-fork初识
    • 进程状态
      • 进程状态查看
      • Z-僵尸进程
      • 僵尸进程危害
      • 孤儿进程
    • 进程优先级
      • 基本概念
      • 查看系统进程
      • PRI and NI
      • PRI vs NI
      • 查看进程优先级的命令
      • 竞争、独⽴、并⾏、并发
    • 进程切换
      • ⼀个CPU拥有⼀个runqueue
      • 优先级
      • 活动队列
      • 过期队列
      • active指针和expired指针
      • 总结
  • 环境变量
    • 基本概念
    • 常⻅环境变量
    • 查看环境变量⽅法
    • 和环境变量相关的命令
    • 环境变量的组织⽅式
    • 通过代码如何获取环境变量

进程

基本概念与基本操作

程序的一个执行实例,正在执行的程序等。
担当分配系统资源的实体。

描述进程-PCB

  • 进程信息被放在一个叫做进程控制块的数据结构中,可以理解为进程属性的集合。
  • 称为PCB(process control block),Linux操作系统下的PCB是:task_struct
  • 在Linux中描述进程的结果体叫做task_struct
  • task_struct是Linux内核的一种数据结构,他会被装载到RAM里并且包含着进程的信息。

task_struct

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

查看进程

  • 进程的信息可以通过 /proc 系统⽂件夹查看
  • ⼤多数进程信息同样可以使⽤top和ps这些⽤⼾级⼯具来获取

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

  • 进程id(PID)
  • 父进程id(PPID)
#include<stdio.h>#include<sys/types.h>#include<unistd.h>intmain(){printf("pid: %d\n",getpid());printf("ppid: %d\n",getppid());return0;}

通过系统调⽤创建进程-fork初识

  • 运⾏ man fork 认识fork
  • fork有两个返回值
  • ⽗⼦进程代码共享,数据各⾃开辟空间,私有⼀份(采⽤写时拷⻉)
#include<stdio.h>#include<sys/types.h>#include<unistd.h>intmain(){intret=fork();if(ret<0){perror("fork");return1;}elseif(ret==0){//childprintf("I am child : %d!, ret: %d\n",getpid(),ret);}else{//fatherprintf("I am father : %d!, ret: %d\n",getpid(),ret);}sleep(1);return0;}

进程状态

  • R运⾏状态(running):并不意味着进程⼀定在运⾏中,它表明进程要么是在运⾏中要么在运⾏
    队列⾥。

  • S睡眠状态(sleeping):意味着进程在等待事件完成(这⾥的睡眠有时候也叫做可中断睡眠
    (interruptible sleep))。

  • D磁盘休眠状态(Disksleep)有时候也叫不可中断睡眠状态(uninterruptiblesleep),在这个
    状态的进程通常会等待IO的结束。

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

  • X死亡状态(dead):这个状态只是⼀个返回状态,你不会在任务列表⾥看到这个状态。

进程状态查看

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

Z-僵尸进程

  • 僵死状态(Zombies)是⼀个⽐较特殊的状态。当进程退出并且⽗进程(使⽤wait()系统调⽤)没有读取到⼦进程退出的返回代码时就会产⽣僵死(⼫)进程
  • 僵死进程会以终⽌状态保持在进程表中,并且会⼀直在等待⽗进程读取退出状态代码。
  • 所以,只要⼦进程退出,⽗进程还在运⾏,但⽗进程没有读取⼦进程状态,⼦进程进⼊Z状态
#include<stdio>#include<stdlib.h>intmian(){pid_t id=fork();if(id<0){perror("fork");return1;}elseif(id>0){printf("parent[%d] is sleeping...\n",getpid());sleep(30);}else{printf("child[%d] is begin Z...\n",getpid());sleep(5);exit(EXIT_SUCCESS);}return0;}

僵尸进程危害

  • 进程的退出状态必须被维持下去,因为他要告诉关⼼它的进程(⽗进程),你交给我的任务,我
    办的怎么样了。可⽗进程如果⼀直不读取,那⼦进程就⼀直处于Z状态?是的!

  • 维护退出状态本⾝就是要⽤数据维护,也属于进程基本信息,所以保存在task_struct(PCB)中,
    换句话说,Z状态⼀直不退出,PCB⼀直都要维护?是的!

  • 那⼀个⽗进程创建了很多⼦进程,就是不回收,是不是就会造成内存资源的浪费?是的!因为数
    据结构对象本⾝就要占⽤内存,想想C中定义⼀个结构体变量(对象),是要在内存的某个位置
    进⾏开辟空间!

  • 内存泄漏?是的!

孤儿进程

  • 父进程如果提前退出,那么⼦进程后退出,进⼊Z之后,那该如何处理呢?
  • ⽗进程先退出,⼦进程就称之为“孤⼉进程”
  • 孤⼉进程被1号init进程领养,当然要有init进程回收喽。
#include<stdio>#include<stdlib.h>#include<unistd.h>intmian(){pid_t id=fork();if(id<0){perror("fork");return1;}elseif(id>0){printf("I am parent, pid: %d\n",getpid());sleep(3);exit(0);}else{printf("I am child, pid : %d\n",getpid());sleep(5);}return0;}

进程优先级

基本概念

  • cpu资源分配的先后顺序,就是指进程的优先权(priority)。
  • 优先权⾼的进程有优先执⾏权利。配置进程优先权对多任务环境的linux很有⽤,可以改善系统性
    能。
  • 还可以把进程运⾏到指定的CPU上,这样⼀来,把不重要的进程安排到某个CPU,可以⼤⼤改善
    系统整体性能。

查看系统进程

  • ⽤ps‒l命令则会类似输出以下⼏个内容:
  • UID:代表执⾏者的⾝份
  • PID:代表这个进程的代号
  • PPID:代表这个进程是由哪个进程发展衍⽣⽽来的,亦即⽗进程的代号
  • PRI:代表这个进程可被执⾏的优先级,其值越⼩越早被执⾏
  • NI:代表这个进程的nice值

PRI and NI

  • PRI也还是⽐较好理解的,即进程的优先级,或者通俗点说就是程序被CPU执⾏的先后顺序,此
    值越⼩进程的优先级别越⾼
  • 那NI呢?就是我们所要说的nice值了,其表⽰进程可被执⾏的优先级的修正数值
  • PRI值越⼩越快被执⾏,那么加⼊nice值后,将会使得PRI变为:PRI(new)=PRI(old)+nice
  • 这样,当nice值为负值的时候,那么该程序将会优先级值将变⼩,即其优先级会变⾼,则其越快被执⾏
  • 所以,调整进程优先级,在Linux下,就是调整进程nice值
  • nice其取值范围是-20⾄19,⼀共40个级别

PRI vs NI

  • 需要强调⼀点的是,进程的nice值不是进程的优先级,他们不是⼀个概念,但是进程nice值会影响到进程的优先级变化。
  • 可以理解nice值是进程优先级的修正修正数据

查看进程优先级的命令

  • ⽤top命令更改已存在进程的nice:
  • 进⼊top后按“r”‒>输⼊进程PID‒>输⼊nice值

竞争、独⽴、并⾏、并发

  • 竞争性:系统进程数⽬众多,⽽CPU资源只有少量,甚⾄1个,所以进程之间是具有竞争属性的。为了⾼效完成任务,更合理竞争相关资源,便具有了优先级
  • 独⽴性:多进程运⾏,需要独享各种资源,多进程运⾏期间互不⼲扰
  • 并⾏:多个进程在多个CPU下分别,同时进⾏运⾏,这称之为并⾏
  • 并发:多个进程在⼀个CPU下采⽤进程切换的⽅式,在⼀段时间之内,让多个进程都得以推进,称之为并发

进程切换

  • CPU上下⽂切换:其实际含义是任务切换,或者CPU寄存器切换。当多任务内核决定运⾏另外的任务时,它保存正在运⾏任务的当前状态,也就是CPU寄存器中的全部内容。这些内容被保存在任务⾃⼰的堆栈中,⼊栈⼯作完成后就把下⼀个将要运⾏的任务的当前状况从该任务的栈中重新装⼊CPU寄存器,并开始下⼀个任务的运⾏,这⼀过程就是contextswitch。

⼀个CPU拥有⼀个runqueue

  • 如果有多个CPU就要考虑进程个数的负载均衡问题

优先级

  • 普通优先级:100〜139(我们都是普通的优先级,想想nice值的取值范围,可与之对应!)
  • 实时优先级:0〜99(不关⼼)

活动队列

  • 时间⽚还没有结束的所有进程都按照优先级放在该队列
  • nr_active: 总共有多少个运⾏状态的进程
  • queue[140]:⼀个元素就是⼀个进程队列,相同优先级的进程按照FIFO规则进⾏排队调度,所以,数组下标就是优先级!
  • 从该结构中,选择⼀个最合适的进程,过程是怎么的呢?
    1.从0下表开始遍历queue[140]
    2.找到第⼀个⾮空队列,该队列必定为优先级最⾼的队列
    3.拿到选中队列的第⼀个进程,开始运⾏,调度完成!
  1. 遍历queue[140]时间复杂度是常数!但还是太低效了!
  • bitmap[5]:⼀共140个优先级,⼀共140个进程队列,为了提⾼查找⾮空队列的效率,就可以⽤5*32个⽐特位表⽰队列是否为空,这样,便可以⼤⼤提⾼查找效率!

过期队列

  • 过期队列和活动队列
  • 过期队列上放置的进程,都是时间⽚耗尽的进程结构⼀模⼀样
  • 当活动队列上的进程都被处理完毕之后,对过期队列的进程进⾏时间⽚重新计算

active指针和expired指针

  • active指针永远指向活动队列
  • expired指针永远指向过期队列
  • 可是活动队列上的进程会越来越少,过期队列上的进程会越来越多,因为进程时间⽚到期时⼀直都存在的。
  • 没关系,在合适的时候,只要能够交换active指针和expired指针的内容,就相当于有具有了⼀批
    新的活动进程!

总结

  • 在系统当中查找⼀个最合适调度的进程的时间复杂度是⼀个常数,不随着进程增多⽽导致时间成本增加,我们称之为进程调度O(1)算法!

环境变量

基本概念

  • 环境变量(environmentvariables)⼀般是指在操作系统中⽤来指定操作系统运⾏环境的⼀些参数
  • 我们在编写C/C++代码的时候,在链接的时候,从来不知道我们的所链接的动态静态库在哪⾥,但是照样可以链接成功,⽣成可执⾏程序,原因就是有相关环境变量帮助编译器进⾏查找。
  • 环境变量通常具有某些特殊⽤途,还有在系统当中通常具有全局特性

常⻅环境变量

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

查看环境变量⽅法

echo$NAME//NAME:你的环境变量名称

测试PATH
1.创建hello.c⽂件

#include<stdio.h>intmain(){printf("hello world!\n");return0;}

2.对⽐./hello执⾏和之间hello执⾏
3. 为什么有些指令可以直接执⾏,不需要带路径,⽽我们的⼆进制程序需要带路径才能执⾏?
4. 将我们的程序所在路径加⼊环境变量PATH当中,export PATH=$PATH:hello程序所在路径

和环境变量相关的命令

1.echo:显⽰某个环境变量值
2.export: 设置⼀个新的环境变量
3.env: 显⽰所有环境变量
4.unset: 清除环境变量
5.set: 显⽰本地定义的shell变量和环境变量

环境变量的组织⽅式


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

通过代码如何获取环境变量

#include<stdio.h>#include<stdlib.h>#include<unistd.h>externchar**environ;intmain(intargc,char*argv[],char*env[]){for(inti=0;environ[i];++i){printf("environ[%d]->%s\n",i,environ[i]);}char*value=getenv("PATH");if(value==NULL){return1;}printf("PATH->%s\n",value);for(inti=0;env[i];++i){printf("env[%d]->%s\n",i,env[i]);}for(inti=0;i<argc;++i){printf("argv[%d],%s\n",i,argv[i]);}return0;}

libc中定义的全局变量environ指向环境变量表,environ没有包含在任何头⽂件中,所以在使⽤时要⽤extern声明。
觉得我回答有用的话,记得点个关注哟!谢谢支持!

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询