[操作系统] 进程状态详解
在操作系统中,进程是程序执行的基本单位,操作系统负责管理进程的生命周期。为了高效地管理进程,操作系统通过定义不同的进程状态来表示进程在不同时间点的行为。本文将详细介绍常见的进程状态及其相互之间的转换过程。
进程状态概述
在kernel
进程有时候也叫事件。进程在执行过程中,可能会处于不同的状态。每个进程状态代表了进程在生命周期中的一种阶段,操作系统根据进程的当前状态采取不同的调度策略。
进程状态在<font style="color:rgb(100,106,115);">kernel</font>
源代码里的进程状态定义如下:
/*
*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):并不意味着进程一定在实际运行中。它表明进程要么正在运行,要么处于运行队列中(Ready),等待操作系统调度器将其调度到CPU上执行。换句话说,处于运行状态的进程已经具备了运行的条件,只是可能还在等待CPU资源的分配。
- 睡眠状态(S):意味着进程正在等待某个事件的完成。这种状态有时也被称为可中断睡眠(Interruptible Sleep)。在可中断睡眠状态下,进程可以被外部信号唤醒。例如,当进程等待某个I/O操作完成时,它会进入睡眠状态,但如果在此期间接收到一个信号,进程可以被唤醒并继续执行。
- 磁盘休眠状态(D):也称为不可中断睡眠状态(Uninterruptible Sleep)。处于这个状态的进程通常在等待I/O操作的完成,如磁盘读写操作。与可中断睡眠状态不同,不可中断睡眠状态的进程不能被外部信号唤醒。这是因为I/O操作的完成是进程继续执行的必要条件,任何外部信号都不能中断这一过程。例如,当进程正在从磁盘读取数据时,它会进入不可中断睡眠状态,直到数据读取完成。
- 停止状态(T)表示进程被暂停执行。可以通过发送SIGSTOP信号给进程来将其置于停止状态。被暂停的进程可以通过发送SIGCONT信号让其继续运行。例如,在调试过程中,开发者可能会发送SIGSTOP信号来暂停进程,以便检查其状态和变量,然后通过SIGCONT信号恢复进程的执行。
- 死亡状态(X)是一个特殊的返回状态,表示进程已经完成其任务并退出。你不会在任务列表中看到处于死亡状态的进程。当进程完成执行后,操作系统会回收其资源,并将其从进程表中删除。这个状态主要用于表示进程的生命周期已经结束。
进程状态转换
进程状态之间的转换并非是线性的,实际上,进程的生命周期充满了状态的切换。系统中的进程调度策略和资源分配机制都会影响这些转换的频率和时机。
运行:
运行和就绪可以看做一种方式。
一个CPU对应一个调度队列(runqueue),队列。
- 进程创建:进程创建时,会创建一个
<font style="color:rgb(100,106,115);">task_struct</font>
数据结构,其中包含进程的所有信息。每个进程在创建时都会被放入一个 就绪队列(runqueue),该队列是一个先进先出(FIFO)队列。 - 进程就绪:当进程处于就绪状态时,它会被加入到 CPU 的就绪队列(runqueue)中,等待 CPU 调度器的分配。这个状态是由操作系统管理的,表示进程已经可以运行,但还没有分配到 CPU。
- 进程运行:CPU 调度器从就绪队列中选择一个进程并开始执行它。此时,进程进入 运行状态,并且会根据时间片来轮流使用 CPU。
阻塞:
添加和移除等待队列的示意图
从运行队列变成阻塞的本质就是把PCB链入不同的状态队列之中。当调度当前进程的时候,硬件设备未响应,该进程被链入等待队列。当硬件响应后,该进程被重新链回运行调度队列,当调度的时候再将响应的硬件信息读取。
- 进程等待:如果进程在运行过程中需要等待某些事件(例如等待硬件设备操作完成,程序有
<font style="color:rgb(100,106,115);">scanf</font>
时需要等待键盘输入),它会被移到 等待队列(wait queue) 中。在等待队列中,进程会处于阻塞状态,直到所需事件发生。事件包括,键盘,显示器,网卡,磁盘,摄像头,话筒…
OS管理系统中的各种硬件资源同样遵循:先描述,在组织!
- 进程阻塞:例如,当进程等待硬件设备时,设备会进入
<font style="color:rgb(100,106,115);">device</font>
阻塞队列,这个队列是一个结构体<font style="color:rgb(100,106,115);">device</font>
组织的链表,存储着等待的进程。硬件设备在准备好后,会唤醒相关的进程。
挂起就是把内存资源换到磁盘上,挂起后的资源在恢复时进行换出。
阻塞挂起(极端条件):
操作系统内存资源紧张时,将没有被调度的进程相关的内存块(代码、数据等)交换到到磁盘的swap交换分区上,只在原先队列保留PCB。
当操作系统感知到恢复,操作系统就会把交换到磁盘的内存块重新加载回内存,重新构建指针映射等,然后将内存块代码等信息重新加载进内存。
就绪挂起(更极端条件):
当内存空间及其短缺时,就会把就绪状态的进程也挂起。
进程结束后task_struct
被销毁
再次理解:内核中连接管理进程的数据结构是双向链表
通过进程状态是如何转换得知,每个状态都有一对应的队列,当进程进入对应状态时就会将该进程连接到对应的队列中去。但我们又在前文所提,kernel
中管理进程的数据结构是双向链表。那么进程是如何保证即不违背内核管理制度,又可以进行不同状态的转换呢?
在了解整体结构之前可以先了解管理进程的双向链表是怎么形成的。
普通的双向链表是由prev
和next
进行管理连接,但是进程是由task_struct
和数据组成的,那应该怎么将双向链表的管理方式引入呢?
实际上在task_struct
中维护了结构体list_head
,该结构体组成如下:
struct list_head
{struct list_head *next, *prev;
}
task_struct
(伪):
struct task_struct
{int x;int y;list_head links;......
}
有了list_head
,每个进程之间就可以通过task_struct
中的list_head
进行双向连接。前节点的next
指向下一个进程的list_head
,下一个进程的prev
指向前一个进程的list_head
。
既然是通过嵌套的双向链表来管理,那么又引入了新问题:通过list_head
连接管理的双向链表在遍历的时候我们只能遍历到每一个list_head
的地址,那么我们要如何通过一个进程task_struct
中的list_head
得到task_struct
的地址,从而对进程进行访问、修改或者删除?
在C语言结构体的知识中,结构体中关于变量和各种类型的存储是依赖偏移量的,所以我们只要得到在当前task_struct
中list_head
的偏移量,那么就可以通过以下计算公式得到task_struct
:
(struct task_struct *)((char *)ptr - (char *)&(((struct task_struct *)0)->links))
这个公式的关键在于理解为什么可以通过假设 task_struct
起始地址为 0
来计算出 links
的偏移量,以及这种假设背后的原理。
假设 ptr
是指向list_head
节点 links
的实际地址,我们通过以下计算反推出 task_struct
的起始地址。
(((struct task_struct *)0)->links)
:- 编译期计算
links
在task_struct
中的偏移量(例如:8
字节)。这段代码的确会得到links
在struct task_struct
类型的结构体中的偏移量,这是编译器通过结构体的定义和内存布局规则计算出来的,而不是通过访问实际内存得到的。
- 编译期计算
(char *)&(((struct task_struct *)0)->links)
:- 将
links
的偏移量转换为字节地址(方便后续指针运算)。
- 将
(char *)ptr - 偏移量
:- 从
ptr
(links
的实际地址)减去偏移量,回退到结构体的起始地址。
- 从
(struct task_struct *)
:- 将起始地址转换为
struct task_struct *
类型。
- 将起始地址转换为
以上就是如何通过一个进程task_struct
中的list_head
得到task_struct
的地址的方法理解。
在解决了内核中全局管理PCB
的双链表构成和使用后,又回到了之前的问题,如何在保证PCB既在全局双链表中,又可以在多个状态转换,在对应的状态队列连接和断开?
实际上,在Linux中许多数据结构都是网状的。task_struct
中list_head links
并不是只存在一个,而是每一种状态都对应一个links
(包括全局双向链表)。
只不过各个状态的links
遵循FIFO
原则,与队列的管理方法保持一致,所以我们叫这些状态管理的双链表数据结构叫做队列。
一个PCB在内核中只存在一个,不是拷贝在许多个队列中进行管理,而是通过上述方法,可以隶属于多个数据结构。即使这个PCB在不同的状态中来回转换,也会始终保持在全局双链表中连接。
这就是为什么PCB即使在各种状态队列中,但我们认为PCB是通过双链表管理的。
+-----------------+ +-----------------+ +-----------------+ +-----------------+
| task_struct | | task_struct | | task_struct | | task_struct |
+-----------------+ +-----------------+ +-----------------+ +-----------------+
| next | | next | | next | | next |
| prev | | prev | | prev | | prev |
+-----------------+ +-----------------+ +-----------------+ +-----------------+
| next | | next | | next | | next |
| prev | | prev | | prev | | prev |
+-----------------+ +-----------------+ +-----------------+ +-----------------+
| next | | next | | next | | next |
| prev | | prev | | prev | | prev |
+-----------------+ +-----------------+ +-----------------+ +-----------------+
| next | | next | | next | | next |
| prev | | prev | | prev | | prev |
+-----------------+ +-----------------+ +-----------------+ +-----------------+
如何查看进程的状态
<font style="color:rgb(44, 44, 54);">ps</font>
命令
<font style="color:rgb(44, 44, 54);">ps</font>
显示当前快照中的进程信息。它不会动态更新,因此适合用来获取某一时刻的进程状态。
ps aux / ps axj 命令
ps ajx | head -1; ps ajx | grep myprocess // head -1 用来显示第一行的列信息展示,方便辨认信息// ; 用来分割,使一次执行多个命令// grep myprocess 只看myprocess这个程序的进程信息
- a:显⽰⼀个终端所有的进程,包括其他⽤⼾的进程。
- x:显⽰没有控制终端的进程,例如后台运⾏的守护进程。
- j:显⽰进程归属的进程组ID、会话ID、⽗进程ID,以及与作业控制相关的信息
- u:以⽤⼾为中⼼的格式显⽰进程信息,提供进程的详细信息,如⽤⼾、CPU和内存使⽤情况等
<font style="color:rgb(44, 44, 54);">top</font>
命令
<font style="color:rgb(44, 44, 54);">top</font>
提供了实时的、动态更新的进程视图,默认情况下每三秒钟刷新一次。它非常适合监控系统的性能和进程活动。
常见交互式命令(在<font style="color:rgb(44, 44, 54);">top</font>
运行时按下):
<font style="color:rgb(44, 44, 54);">h</font>
:帮助页面,显示可用的交互命令。<font style="color:rgb(44, 44, 54);">k</font>
:杀死一个或多个进程,需要输入PID。<font style="color:rgb(44, 44, 54);">r</font>
:调整进程的优先级(nice值),同样需要输入PID。<font style="color:rgb(44, 44, 54);">f</font>
或<font style="color:rgb(44, 44, 54);">F</font>
:进入字段管理器,选择要显示的列。<font style="color:rgb(44, 44, 54);">o</font>
或<font style="color:rgb(44, 44, 54);">O</font>
:设置显示的排序方式。<font style="color:rgb(44, 44, 54);">M</font>
:按照内存使用排序。<font style="color:rgb(44, 44, 54);">P</font>
:按照CPU使用排序。<font style="color:rgb(44, 44, 54);">T</font>
:按照运行时间排序。<font style="color:rgb(44, 44, 54);">q</font>
:退出<font style="color:rgb(44, 44, 54);">top</font>
。
常见管道搭配:
虽然<font style="color:rgb(44, 44, 54);">top</font>
本身是一个交互式的工具,不常与管道直接搭配使用,但可以通过一些技巧来结合使用:
<font style="color:rgb(44, 44, 54);">top -b -n 1 | grep <process_name></font>
:使用批处理模式(<font style="color:rgb(44, 44, 54);">-b</font>
)运行<font style="color:rgb(44, 44, 54);">top</font>
一次(<font style="color:rgb(44, 44, 54);">-n 1</font>
),然后通过<font style="color:rgb(44, 44, 54);">grep</font>
筛选出特定的进程。<font style="color:rgb(44, 44, 54);">top -b -n 1 | head -n 8</font>
:获取<font style="color:rgb(44, 44, 54);">top</font>
输出的前几行,通常包含系统负载和资源使用概览。
状态的详细讲解
在 Linux 操作系统中,每个进程都有一个状态,用于反映进程在特定时刻的执行情况。常见的进程状态包括运行、睡眠、磁盘睡眠、停止、追踪停止、死亡和僵尸状态。下面将逐一解释这些状态。
运行/就绪状态R (running)
表示进程正在 CPU 上运行或准备运行。进程被调度程序选中并获得 CPU 时间片。
解释:
- 处于“运行”状态的进程正在执行程序代码,或者正在等待 CPU 执行。
- 因为一个CPU对应一个运行队列,所以单核CPU中只有一个进程可以处于运行状态,多核 CPU 可以并行运行多个进程。
模拟代码:
#include <unistd.h>
#include <stdio.h>int main() {printf("Process is running...\n");sleep(10); // 模拟进程正在运行,等待10秒return 0;
}
查看进程状态:
ps -eo pid,state,cmd | grep <your_pid>
如果进程处于运行状态,<font style="color:rgb(100,106,115);">state</font>
列会显示为 R。
S (sleeping) – 睡眠状态(浅睡眠)
表示进程处于可中断睡眠状态,通常是进程正在等待某些事件(如 I/O 操作完成)或信号。
解释:
- 进程被挂起并等待某些外部事件(例如文件 I/O 操作)或等待信号中断。
- 这种睡眠是可中断的,意味着如果有外部事件(如信号)发生,进程可以被唤醒。
模拟代码:
#include <unistd.h>
#include <stdio.h>int main() {printf("Process is sleeping...\n");sleep(10); // 进程进入睡眠状态,等待10秒return 0;
}
查看进程状态:
ps -eo pid,state,cmd | grep <your_pid>
如果进程处于睡眠状态,<font style="color:rgb(100,106,115);">state</font>
列会显示为 S。
D (disk sleep) – 磁盘睡眠状态(深度睡眠)
表示进程处于不可中断睡眠状态,通常是由于等待某些硬件操作完成,凡是涉及到对磁盘这种关键存储设备进行访问,进程进行高IO的时候,不设置为S,设置为D,防止数据丢失。操作系统无权<font style="color:rgb(100,106,115);">kill</font>
进程(区别于S的可中断进程),只能等待自己结束进程。也就是说磁盘 I/O 操作时,进程无法被中断,必须等到 I/O 操作完成。
解释:
- 进程正在等待某些硬件资源(如磁盘读写)完成,且无法被外部信号打断。
- 这种状态可能会导致系统响应较慢。
模拟代码:
#include <stdio.h>
#include <fcntl.h>
#include <unistd.h>int main() {int fd = open("/path/to/large/file", O_RDONLY); // 触发磁盘 I/O 操作if (fd < 0) {perror("Failed to open file");return 1;}sleep(10); // 进程会因等待磁盘 I/O 完成而进入不可中断睡眠状态close(fd);return 0;
}
查看进程状态:
ps -eo pid,state,cmd | grep <your_pid>
如果进程处于磁盘睡眠状态,<font style="color:rgb(100,106,115);">state</font>
列会显示为 D。
T (stopped) – 停止状态
表示进程已被暂停,通常是由于接收到 <font style="color:rgb(100,106,115);">SIGSTOP</font>
信号或在调试过程中被暂停。
解释:
- 进程不再运行,但仍保留其资源和状态,直到恢复。
- 调试程序时,进程也会进入停止状态。
模拟代码:
sleep 10 # 启动一个进程
kill -STOP <pid> # 发送 SIGSTOP 信号暂停进程
查看进程状态:
ps -eo pid,state,cmd | grep <your_pid>
如果进程处于停止状态,<font style="color:rgb(100,106,115);">state</font>
列会显示为 T。
t (tracing stop) – 追踪停止状态
表示进程因调试而被暂停,通常发生在进程被调试器(如 <font style="color:rgb(100,106,115);">gdb</font>
)附加时。
解释:
- 进程处于调试过程中,打断点后进行
<font style="color:rgb(100,106,115);">run</font>
,遇到断点时执行被暂停,以允许调试器检查进程状态。
模拟代码:
sleep 10 # 启动一个进程
gdb -p <pid> # 附加调试器
查看进程状态:
ps -eo pid,state,cmd | grep <your_pid>
如果进程处于追踪停止状态,<font style="color:rgb(100,106,115);">state</font>
列会显示为 t。
X (dead) – 死亡状态
表示进程已终止或死亡,通常是进程已完全退出,但其进程控制块(PCB)仍未被操作系统回收。
解释:
- 进程已终止并退出,但操作系统尚未完全清理与该进程相关的资源。
查看进程状态:
ps -eo pid,state,cmd | grep <your_pid>
如果进程处于死亡状态,<font style="color:rgb(100,106,115);">state</font>
列会显示为 X,但是一般死亡状态会将PCB数据进行删除,所以一般情况下无法查看到。
僵尸进程进行单独讲解。
僵尸进程 (zombie)
什么是僵尸进程?
僵尸进程(Zombie Process)是指一个已经终止的进程,但其父进程尚未调用 wait()
或 waitpid()
来读取该进程的退出状态,从而使得操作系统无法回收该进程的资源。简单来说,原因是,⽗进程还在运⾏,但⽗进程没有读取⼦进程状态,虽然子进程已经结束了,但是父进程没有宣告子进程的死亡,父进程需要调用函数来获取子进程的退出状态,所以⼦进程进⼊Z状态。结果是,僵尸进程的进程号(PID)仍然被占用,但其内存空间和其他资源已被释放。
子进程的结果相关信息存放在
task_struct
中。
用代码进行模拟僵尸进程出现的情况:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <errno.h>int main()
{printf("我是fork前的进程!我的pid是:%d\n", getpid());pid_t id = fork();if (id < 0){perror("fork");return 1;}else if (id == 0){// child processint i = 5;while (i--){sleep(1);}printf("子进程即将结束,我将变成僵尸进程。\n");// 子进程在这里结束,但父进程不会等待它,所以它会变成僵尸进程。}else{// father process// 父进程进入无限循环,不调用wait或waitpidwhile (1){sleep(1);printf("我是父进程!我的pid:%d,我的父进程id:%d\n", getpid(), getppid());}}return 0;
}
僵尸进程的生成过程:
- 子进程结束:
- 当一个进程结束时,操作系统会保留它的退出状态信息,直到父进程能够读取该状态信息(通过调用
wait()
或waitpid()
)。 - 进程结束后,操作系统会将其状态标记为“僵尸”,这时它会占用一个 PID,但没有实际的运行资源。
- 当一个进程结束时,操作系统会保留它的退出状态信息,直到父进程能够读取该状态信息(通过调用
- 父进程未读取退出状态:
- 如果父进程在子进程终止后没有读取它的退出状态,操作系统就无法回收这个子进程的 PID 和一些控制信息,因此该子进程仍然存在,成为僵尸进程。
僵尸进程的危害
- 占用系统资源:
- 每个进程都占用一个进程号(PID)。即使僵尸进程不再运行,但由于它仍然存在于进程表中,它占用了系统资源,特别是有限的 PID 资源。
- 如果僵尸进程没有被清除,系统最终会耗尽 PID,导致无法启动新的进程。
- 导致系统性能下降:
- 如果有大量的僵尸进程存在,它们仍然占用一些系统内存和资源,这可能导致系统性能下降,尤其是当系统负载较高时。
- 父进程可能变得不稳定:
- 如果父进程不能正确地回收子进程(调用
wait()
或waitpid()
),可能会导致父进程的逻辑出现问题,甚至可能会在某些情况下导致父进程的崩溃。
- 如果父进程不能正确地回收子进程(调用
- 系统管理上的难题:
- 系统管理员通常需要手动查找并清理僵尸进程。过多的僵尸进程可能增加系统管理的复杂度,尤其是在长时间运行的系统上。
如何避免或清理僵尸进程?
- 父进程调用
wait()
或waitpid()
:- 父进程在子进程结束后应该及时调用
wait()
或waitpid()
来回收子进程的退出状态,防止僵尸进程的产生。
- 父进程在子进程结束后应该及时调用
- 采用
signal
机制:- 父进程可以捕捉到子进程结束的信号(如
SIGCHLD
),并在接收到信号时及时清理子进程。
- 父进程可以捕捉到子进程结束的信号(如
- 通过
init
进程回收:- 如果父进程在子进程结束后还没有回收它,
init
进程(PID 1)会自动回收这些孤儿进程,防止它们变成僵尸进程。
- 如果父进程在子进程结束后还没有回收它,
- 使用
nohup
或disown
:- 对于后台运行的任务,使用
nohup
或disown
可以确保进程结束后不会留下僵尸进程。
- 对于后台运行的任务,使用
程序结束后,程序运行时的所有进程都会全部删除,不会永久保留,僵尸进程只是在程序运行时的不规范编码造成的进程问题。但是像常驻内存进程,那些被设计为在系统启动时自动启动,并且在整个系统运行期间持续存在于内存中的进程,造成的内存泄漏会十分严重!
slab机制
在 Linux 中,为了提高内存的分配和回收效率,特别是针对内核使用的频繁的数据结构,采用了 Slab 分配器(Slab Allocator)来进行内存管理。这种机制有助于提高性能,并减少内存碎片。
Slab 机制的基本概念
Slab 分配器是一种用于内核内存管理的高效机制,它通过预分配内存块(称为 slab)来管理频繁使用的数据结构对象。这样做可以有效减少频繁的内存分配和释放操作带来的性能开销。
Slab 的工作原理
- Slab 缓存:
- Slab 分配器为内核中常用的数据结构(如
task_struct
等)预分配内存,称为 slab 缓存。 - 每个缓存分配了一定数量的内存块,内存块大小与数据结构大小匹配,内存块是可重用的。
- Slab 分配器为内核中常用的数据结构(如
- Slab 分配与复用:
- 当内核需要某个数据结构时,Slab 分配器会从已有的 slab 缓存中分配内存块,如果没有空闲的内存块,则会分配新的 slab。
- 内存块分配完毕后,不再需要时会返回给 Slab 分配器,进行复用。这减少了对内存的频繁分配和释放操作。
- 内存块状态:
- 每个 slab 的内存块有 3 种状态:
- 空闲:内存块没有被分配,可以用来存储数据。
- 已分配:内存块被使用,存储了有效数据。
- 被释放:内存块被释放,处于空闲状态,准备重新使用。
- 每个 slab 的内存块有 3 种状态:
- Slab 分配器的优点:
- 提高性能:通过缓存机制,Slab 分配器避免了频繁的内存分配和释放,提高了性能。
- 减少内存碎片:由于内存块大小与数据结构大小一致,Slab 分配器减少了内存碎片的产生。
- 优化内存使用:内存块的复用保证了相同数据结构在整个内核中的一致性,减少了内存浪费。
如何复用内存块?
task_struct
** 的示例**:- 假设内核有一个
task_struct
数据结构,它在进程管理中非常常见。Slab 分配器为task_struct
创建了一个缓存,并分配了一些内存块来存储多个task_struct
对象。
- 假设内核有一个
- 分配内存块:
- 当创建新进程时,Slab 分配器会从
task_struct
缓存中分配一个内存块,填充进程信息。
- 当创建新进程时,Slab 分配器会从
- 释放内存块:
- 当进程结束时,
task_struct
对象会被释放。此时,Slab 分配器会将该内存块标记为“空闲”,以便在需要时重新使用。
- 当进程结束时,
- 避免重复分配:
- 通过复用已分配的内存块,Slab 分配器避免了内存的碎片化,也减少了分配和释放内存的开销。
总结
Slab 分配器是一种用于优化内存分配和回收的机制,它通过缓存和复用内存块来提高内存管理的效率。内核通过为常用数据结构(如 task_struct
)创建 Slab 缓存来减少内存碎片和提升性能。这使得 Linux 内核在内存使用上更加高效,同时减少了内存分配和释放的复杂度。
相关文章:

[操作系统] 进程状态详解
在操作系统中,进程是程序执行的基本单位,操作系统负责管理进程的生命周期。为了高效地管理进程,操作系统通过定义不同的进程状态来表示进程在不同时间点的行为。本文将详细介绍常见的进程状态及其相互之间的转换过程。 进程状态概述 在kerne…...

[论文阅读] (36)CS22 MPSAutodetect:基于自编码器的恶意Powershell脚本检测模型
《娜璋带你读论文》系列主要是督促自己阅读优秀论文及听取学术讲座,并分享给大家,希望您喜欢。由于作者的英文水平和学术能力不高,需要不断提升,所以还请大家批评指正,非常欢迎大家给我留言评论,学术路上期…...

【Maui】下拉框的实现,绑定键值对
文章目录 前言一、问题描述二、解决方案三、软件开发(源码)3.1 创建模型3.2 视图界面3.3 控制器逻辑层 四、项目展示 前言 .NET 多平台应用 UI (.NET MA…...

Oracle 深入学习 Part 14:Managing Password Security and Resources(管理密码安全性和资源)
Profiles Profile 是一个以名称标识的集合,用于管理 密码 和 资源限制。 每个用户都对应一个profiles,可以通过 CREATE USER 或 ALTER USER 命令分配给用户。 Profiles 可以启用或禁用。 Profiles 可以关联到默认的 DEFAULT Profile。 密码管理&…...

C语言:位段
位段的内存分配: 1. 位段的成员可以是 int unsigned int signed int 或者是char (属于整形家族)类型 2. 位段的空间上是按照需要以4个字节( 类型 int )或者1个字节( char )的方式来开辟的。 3. 位段涉及…...

MPLS VPN 原理与配置
一.简介 MPLS,称之为多协议标签交换,在九十年代中期被提出来,用于解决传统IP报文依赖查表转发而产生的瓶颈,现多用于VPN技术,MPLS报头封装在数据链路层之上,网络层之下。本文为结合了华为技术和新华三技术…...

稳定的通信桥梁,CCLINKIE转ModbusTCP网关实现AGV运输的光速效应
三菱PLC与AGV机器人搬运车通过稳联技术协议转换网关建立通信 一、现场情况概述 - 三菱PLC:使用CC-Link IE协议进行通信。 - AGV机器人搬运车:使用Modbus TCP协议进行通信。 - 协议转换网关:使用稳联技术的协议转换网关将PLC和AGV连接…...
Leetcode 3428. Maximum and Minimum Sums of at Most Size K Subsequences
Leetcode 3428. Maximum and Minimum Sums of at Most Size K Subsequences 1. 解题思路2. 代码实现 题目链接:3428. Maximum and Minimum Sums of at Most Size K Subsequences 1. 解题思路 这一题不需要连续性,因此我们就是考虑取得子串长度为别为1…...
第2章:Python TDD构建Dollar类基础
写在前面 这本书是我们老板推荐过的,我在《价值心法》的推荐书单里也看到了它。用了一段时间 Cursor 软件后,我突然思考,对于测试开发工程师来说,什么才更有价值呢?如何让 AI 工具更好地辅助自己写代码,或许…...
【算法学习笔记】34:扩展欧几里得算法
裴蜀定理 描述 对于任意正整数 a a a、 b b b,一定存在整数系数 x x x, y y y,使得: a x b y g c d ( a , b ) ax by gcd(a, b) axbygcd(a,b) 并且 g c d ( a , b ) gcd(a, b) gcd(a,b)是对于任意的系数 x x x和 y y y放在…...
云原生周刊:K8s 生产环境架构设计及成本分析
开源项目推荐 KubeZoneNet KubeZoneNet 旨在帮助监控和优化 Kubernetes 集群中的跨可用区(Cross-Zone)网络流量。这个项目提供了一种简便的方式来跟踪和分析 Kubernetes 集群中跨不同可用区的通信,帮助用户优化集群的网络架构、提高资源利用…...

WGAN - 瓦萨斯坦生成对抗网络
1. 背景与问题 生成对抗网络(Generative Adversarial Networks, GANs)是由Ian Goodfellow等人于2014年提出的一种深度学习模型。它包括两个主要部分:生成器(Generator)和判别器(Discriminator)…...

海量数据的处理
一般来说都是针对数据量特别大,内存有限制的。 第一类:topk问题 比如,在海量数据中找前50大的数据怎么办? 方法一:使用小顶堆,用小顶堆维护这50个元素,当有新元素到来时,直接与堆…...
区块链的数学基础:核心原理与应用解析
引言 区块链技术作为分布式账本系统,成功地解决了传统中心化系统中的信任问题。其背后隐藏着复杂而精妙的数学原理,包括密码学、哈希函数、数字签名、椭圆曲线、零知识证明等。这些数学工具不仅为区块链提供了安全保障,也为智能合约和去中心…...
1.5 GPT 模型家族全解析:从 GPT-1 到 GPT-4 的演进与创新
GPT 模型家族全解析:从 GPT-1 到 GPT-4 的演进与创新 随着人工智能技术的飞速发展,GPT(Generative Pre-trained Transformer)模型家族已经成为了现代自然语言处理(NLP)领域的标杆。从初代的 GPT-1 到最新的 GPT-4,每一代模型的发布都标志着人工智能技术的一个飞跃,并推…...

自动驾驶之DriveMM: All-in-One Large Multimodal Model for Autonomous Driving
1. 写在前面 工作之后,主要从事于偏工程比较多的内容, 很少有机会读论文了,但2025年,由于之前有些算法的背景, 后面可能会接触一些多模态大模型相关的工作,所以又调头有点往算法的方向偏移, 而算法呢,很重要的一点就是阅读论文。2025年,再拾起论文这块的工作。 今天…...

Spring Boot 配置(官网文档解读)
目录 摘要 Spring Boot 配置加载顺序 配置文件加载顺序 Spring Boot 配置加载方式 Value Value 注解简单示例 ConfigurationProperties 启动 ConfigurationProperties ConfigurationProperties 验证 ConfigurationProperties 与 Value 对比 Autowired Autowired 自…...

SparkSQL数据源与数据存储
文章目录 1. 大数据分析流程2. Spark SQL数据源2.1 SparkSQL常见数据源2.2 SparkSQL支持的文本格式2.3 加载外部数据源步骤 3. 本地文件系统加载数据3.1 本地文件系统加载JSON格式数据3.1.1 概述3.1.2 案例演示 3.2 本地文件系统加载CSV格式数据3.2.1 概述3.2.2 案例演示 3.3 本…...

【BQ3568HM开发板】开箱测试
引言 很荣幸入选了“电子发烧友”的贝启科技BQ3568HM开源鸿蒙开发板评测活动,上周在出差,今天才有机会开箱一下开发板,简单测试一下。 开机测试 插上电源开机后,系统显示的是润和的DAYU的logo,看来厂商提供的软件包…...
3D 模型格式转换之 STP 转 STL 深度解析
在 3D 模型的多元世界中,格式如同语言,不同格式适用于不同场景。STP 和 STL 是两种常见格式,本文将深入剖析 STP 转 STL 的相关内容。 一、STP 与 STL 格式基础 (一)STP 格式剖析 STP,即标准交换格式&am…...

[大语言模型]在个人电脑上部署ollama 并进行管理,最后配置AI程序开发助手.
ollama官网: 下载 https://ollama.com/ 安装 查看可以使用的模型 https://ollama.com/search 例如 https://ollama.com/library/deepseek-r1/tags # deepseek-r1:7bollama pull deepseek-r1:7b改token数量为409622 16384 ollama命令说明 ollama serve #:…...

给网站添加live2d看板娘
给网站添加live2d看板娘 参考文献: stevenjoezhang/live2d-widget: 把萌萌哒的看板娘抱回家 (ノ≧∇≦)ノ | Live2D widget for web platformEikanya/Live2d-model: Live2d model collectionzenghongtu/live2d-model-assets 前言 网站环境如下,文章也主…...
深入浅出Diffusion模型:从原理到实践的全方位教程
I. 引言:生成式AI的黎明 – Diffusion模型是什么? 近年来,生成式人工智能(Generative AI)领域取得了爆炸性的进展,模型能够根据简单的文本提示创作出逼真的图像、连贯的文本,乃至更多令人惊叹的…...
学习一下用鸿蒙DevEco Studio HarmonyOS5实现百度地图
在鸿蒙(HarmonyOS5)中集成百度地图,可以通过以下步骤和技术方案实现。结合鸿蒙的分布式能力和百度地图的API,可以构建跨设备的定位、导航和地图展示功能。 1. 鸿蒙环境准备 开发工具:下载安装 De…...

轻量级Docker管理工具Docker Switchboard
简介 什么是 Docker Switchboard ? Docker Switchboard 是一个轻量级的 Web 应用程序,用于管理 Docker 容器。它提供了一个干净、用户友好的界面来启动、停止和监控主机上运行的容器,使其成为本地开发、家庭实验室或小型服务器设置的理想选择…...
使用python进行图像处理—图像滤波(5)
图像滤波是图像处理中最基本和最重要的操作之一。它的目的是在空间域上修改图像的像素值,以达到平滑(去噪)、锐化、边缘检测等效果。滤波通常通过卷积操作实现。 5.1卷积(Convolution)原理 卷积是滤波的核心。它是一种数学运算,…...
STL 2迭代器
文章目录 1.迭代器2.输入迭代器3.输出迭代器1.插入迭代器 4.前向迭代器5.双向迭代器6.随机访问迭代器7.不同容器返回的迭代器类型1.输入 / 输出迭代器2.前向迭代器3.双向迭代器4.随机访问迭代器5.特殊迭代器适配器6.为什么 unordered_set 只提供前向迭代器? 1.迭代器…...

Qt 按钮类控件(Push Button 与 Radio Button)(1)
文章目录 Push Button前提概要API接口给按钮添加图标给按钮添加快捷键 Radio ButtonAPI接口性别选择 Push Button(鼠标点击不放连续移动快捷键) Radio Button Push Button 前提概要 1. 之前文章中所提到的各种跟QWidget有关的各种属性/函数/方法&#…...

可下载旧版app屏蔽更新的app市场
软件介绍 手机用久了,app越来越臃肿,老手机卡顿成常态。这里给大家推荐个改善老手机使用体验的方法,还能帮我们卸载不需要的app。 手机现状 如今的app不断更新,看似在优化,实则内存占用越来越大,对手机性…...

Redis专题-实战篇一-基于Session和Redis实现登录业务
GitHub项目地址:https://github.com/whltaoin/redisLearningProject_hm-dianping 基于Session实现登录业务功能提交版本码:e34399f 基于Redis实现登录业务提交版本码:60bf740 一、导入黑马点评后端项目 项目架构图 1. 前期阶段2. 后续阶段导…...