零基础进程最详解:进程状态、僵尸进程、孤儿进程、阻塞态、挂起态、进程切换、进程常用命令、进程创建、队列优先级

目录

强烈建议全文阅读!!!

强烈建议全文阅读!!!

强烈建议全文阅读!!!

一、进程状态

二、僵尸和孤儿进程

1、僵尸进程 Z(zombie)

2、孤儿进程:

三、进程的阻塞和挂起、运行、切换

1、阻塞态

2、挂起状态

3、进程的运行

4、进程切换

四、进程相关命令

五、进程的创建

五、队列优先级

1、什么是优先级

2、为什么要优先级

3、如何实现优先级

4、如何修改优先级

1、nice值调整

2、r(renice) 命令(常用)

3、setpriority 系统调用


一、进程状态

Linux进程状态:进程状态指的是的是进程在cpu中执行的状态

具体长什么样子?

//展示第一个进程,带头列表
ps axj | head -2

R(running)进程运行的状态
S(sleeping休眠状态(等待“资源”就绪 / 可中断睡眠)
T(stopped)让进程暂停,等待进一步被唤醒
t(trcing stop)进程遇到追踪,进程暂停,例如代码调试
X(dead)死亡状态
D(disk sleep)Linux下特有的一个进程状态,不可被杀、深度睡眠、不可中断睡眠(如何消除D状态?进程自己醒来 / 重启 -- 断电)
Z(zombie)僵尸进程


+号:表示前台运行,没有+表示后台运行

调式代码的过程,就是通过进程暂停来实现的

杀死进程命令:

kill -number process:

查看所有的进程控制命令:

kill -l 

一个数字对应一种控制

例如: 9 :杀掉进程、18:继续命令、19:暂停命令

信号名称信号编号含义
SIGHUP1Hangup,终端连接断开
SIGINT2Interrupt,终端中断信号
SIGQUIT3Quit,终端退出信号
SIGILL4Illegal instruction,非法指令
SIGTRAP5Trace/Breakpoint trap,追踪/断点陷阱
SIGABRT6Abort,异常终止
SIGBUS7Bus error,总线错误
SIGFPE8Floating point exception,浮点异常
SIGKILL9Kill,强制终止
SIGUSR110User-defined signal 1,用户自定义信号1
SIGSEGV11Segmentation fault,段错误
SIGUSR212User-defined signal 2,用户自定义信号2
SIGPIPE13Broken pipe,管道破裂
SIGALRM14Alarm clock,闹钟信号
SIGTERM15Termination,终止信号
SIGSTKFLT16Stack fault,协处理器栈错误
SIGCHLD17Child status has changed,子进程状态改变
SIGCONT18Continue,继续执行暂停的进程
SIGSTOP19Stop,停止进程的执行
SIGTSTP20Terminal stop,终端停止信号
SIGTTIN21Background read from tty,后台进程尝试读取控制终端
SIGTTOU22Background write to tty,后台进程尝试写控制终端
SIGURG23Urgent condition on socket,套接字上的紧急情况
SIGXCPU24CPU time limit exceeded,超出CPU时间限制
SIGXFSZ25File size limit exceeded,超出文件大小限制
SIGVTALRM26Virtual alarm clock,虚拟闹钟信号
SIGPROF27Profiling alarm clock,分析器闹钟信号
SIGWINCH28Window size change,窗口大小改变
SIGIO29I/O now possible,异步I/O事件
SIGPWR30Power failure,电源故障
SIGSYS31Bad system call,系统调用错误

 

二、僵尸和孤儿进程

1、僵尸进程 Z(zombie)

进程已经运行完毕,但是需要维持自己的退出信息,在自己的进程PCB中会记录自己的退出信息,未来让父进程来读取
如果没有读取,就会一直维持僵尸进程状态
进程 = 内核数据结构task_struct + 进程代码和数据
当运行完毕时,进程代码和数据会被释放,凡是内核数据结构不会被释放,依然存在,等待被读取
就会存在占用内存空间,也就是内存泄漏
僵尸进程不能被kill
因为僵尸进程也相当于死掉了,不能死两次
X(dead):死亡状态,例如用waitpid(等待队列)访问僵尸进程后,就会显示X状态

2、孤儿进程:

父进程先退出,子进程就变成了孤儿进程,因为爹死了,没有爹了
孤儿进进程一般都会被1号进程(OS)领养,以保证子进程被正常回收
命令行中启动的子进程的父进程是bash,bash会自动回收子进程

三、进程的阻塞和挂起、运行、切换

1、阻塞态

相当于S(sleep)状态,D(dead)也算
进程本身就是一个软件

操作系统对外部设备的管理,其原理也是类似于硬件的方式,先描述,在组织
那么,每一个设备都对应一个设备结构体对象
在这个设备结构体内部有一个等待队列
例如,当进程A需要用到键盘这个设备时
就把进程A这个PCB放到键盘的等待队列中进行排队
其他进程对应需要对应资源,也是这个道理
因此,不是只有CPU才有运行队列
各个设备也有各自的等待队列
那么,当把进程从CPU的运行队列拿到设备的等待队列时
此时该进程就不在运行队列,就不会被调度
这个状态,就叫做进程的阻塞状态

有了这个理解,对于进程的状态变化,还包括PCB被放到不同的队列中
同时,进入等待队列/运行队列的是进程的PCB而不是代码和数据

2、挂起状态

当一个进程处于阻塞状态时,他的PCB是在等待某个设备的资源,也就是在等待某个设备的等待队列中
此时,该进程不在CPU的运行队列中,那么也就不会被调度,不会被调度,那么进程的代码和数据也就没有用
所以,为了让操作系统的内存有更多的空间,就暂时的把该进程的代码和数据唤出到磁盘的swap区
当该进程被CPU调度执行时,又把对应的代码和数据唤入到内存中
这是操作系统的一种运行策略,相当于在已有硬件的条件下实现了2倍的内存空间
但是频繁换入唤出会导致效率问题(时间换空间策略)

3、进程的运行

每个CPU都有一个运行队列

struct runqueue
{
    task_struct *head;
    ...
};

进程在运行队列中,进程状态就处于R(running)状态

时间片:
一个进程进入CPU运行,不会一直运行到结束才停止,而是基于时间片进行调度
在task_struct的一个属性中,有一个与调度相关的信息,叫做时间片
这个时间片用来分配进程使用CPU的时间
当运行时间片结束后,CPU就会把该进程移除,再放到运行队列尾部
同时,让多个进程以切换的方式进行调度,使得在一个时间段内同时得以推进代码,这就叫做并发

但是Linux实际上不是这样调度的,而是一个叫做O(1)调度的算法
多个CPU对应多个运行队列
此时,任何时刻都会有多个进程在真正意义上的同时运行,即并行

4、进程切换

CPU寄存器保存进程的所有临时数据
例如,当A进程执行到一半时,或由于优先级 / 时间片等因素进程切换时
CPU需要切换调度进程,执行新的进程B
此时,寄存器中原来保存的进程A的临时数据会被保存在A进程的PCB中
这个数据,叫做上下文信息
当,A进程再次被调度时,进程A要从上次被执行的位置开始
此时,寄存器需要恢复原来A进程的,即什么时候结束,什么时候开始

所以,进程切换最最重要的事情是,上下文数据的保护和恢复
CPU内部的所有寄存器的临时数据,叫做进程的上下文 

四、进程相关命令

父进程是bash,命令解释器;函数用man查找

查看所有进程:

ps axj 

行过滤:

| grep

显示进程属性和进程信息:

ps axj | head -1 && ps axj grep XXX

获取进程id :这是一个函数

gitpid()

获取父进程id:

ditppid()

创建子进程:

fork()


启动进程:

./XXX 

结束进程:

Ctrl + c

杀掉进程:

kill -9 pid

更该cwd,当前工作目录

chdir

cwd:current work dir

获取环境变量

getenv()

查看进程优先级 +a所有进程

ps -al

查看进程:在根目录,/proc文件,实时保存内存中运行的进程(process)

cd ~

cd /proc

五、进程的创建

为什么要创建子进程?
想让子进程执行和父进程不一样的任务

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

int main() {
    pid_t pid;

    // 创建子进程
    pid = fork();

    if (pid < 0) {
        // 错误处理
        fprintf(stderr, "进程创建失败\n");
        return 1;
    } else if (pid == 0) {
        // 子进程执行代码
        printf("这是子进程,PID为:%d\n", getpid());
        // 在这里可以执行子进程的任务
    } else {
        // 父进程执行代码
        printf("这是父进程,PID为:%d,子进程PID为:%d\n", getpid(), pid);
        // 在这里可以执行父进程的任务
    }

    return 0;
}

fork的返回值:会有两个返回值,返回两次
0,子进程
-1,创建进程失败
>0,子进程的pid,执行父进程
为什么返回两次?
(为什么?这个知识有关地址空间,可以看博主相关文章)

进程特点:具有独立性


五、队列优先级

1、什么是优先级

本质是指定一个进程获取某种资源的先后顺序
优先级如何控制?是PCB的内部字段,int prio
在Linux中,优先级数字越小,优先级越高

2、为什么要优先级

因为CPU的资源是有限的,而进程很多

3、如何实现优先级

进程优先级值和一个nice值,分别是PRI

PRI(Process Priority):进程优先级,值越小优先级越高
NI(nice):进程优先级的修正数据,当时一个进程执行完毕时,如果需要修改优先级,就会用nice值来调整优先级,新的优先级 = 优先级(80) + nice值,达到对应进程优先级动态修正的过程
nice值是有范围的,一般是[-20 ,19],40个数字,即40个级别,目的是确保资源分配的公平
每次调整优先级,都是从80开始

4、如何修改优先级

(一般不推荐调整进程的优先级)

在Linux系统中进行Nice 值调整:

1、nice值调整

使用 nice 命令或系统调用 setpriority 来调整进程的优先级。
nice 命令可以通过设置不同的优先级来启动命令或进程。较小的值表示较高的优先级。
示例:

nice -n -10 ./my_process  # 启动一个优先级较高的进程

2、r(renice) 命令(常用)

可以使用 renice / r命令来更改正在运行的进程的优先级。
示例:

renice -n 10 -p 12345  # 将进程PID为12345的优先级调整为更低

3、setpriority 系统调用

可以通过编程方式使用 setpriority 系统调用来调整进程优先级。
示例(C语言):

#include <sys/time.h>
#include <sys/resource.h>
#include <unistd.h>
#include <stdio.h>

int main() {
    int which = PRIO_PROCESS;
    id_t pid = getpid();
    int priority = -10;  // 设置较高的优先级

    if (setpriority(which, pid, priority) != 0) {
        perror("设置优先级失败");
        return 1;
    }

    printf("优先级已设置为 %d\n", getpriority(which, pid));
    return 0;
}

应用卡住的本质是:进程没有被执行调度

  • 10
    点赞
  • 7
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

二十5画生

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值