【Linux】进程创建、进程终止、进程等待
Linux
- 1.进程创建
- 1.fork 函数
- 2.写时拷贝
- 3.为什么要有写时拷贝?
- 2.进程终止
- 1.进程退出场景
- 2.退出码
- 3.进程常见退出方法
- 1.main函数return
- 2.exit库函数
- 3._exit系统调用
- 3.进程等待
- 1.概念
- 2.必要性
- 3.方法
- 1.wait
- 2.waitpid
- 3.参数status
- 4.参数option
- 5.非阻塞轮询
1.进程创建
1.fork 函数
在 Linux 中 fork 函数是非常重要的函数,它从已存在进程中创建以个新进程。新进程为子进程,而原进程为父进程。
#include <unistd.h>
pid_t fork(void);
//创建子进程成功:子进程中返回0,父进程返回子进程id。创建子进程失败:返回-1
进程调用 fork 函数,当控制转移到内核中的 fork 代码后,内核做:
- 为代码和数据分配新的内存块、分配内核数据结构 task_struct 给子进程。
- 将父进程部分数据结构内容拷贝给子进程(task_struct、虚拟地址空间、页表),task_struct 中个别的属性进行修改(pid、ppid)
- 添加子进程到系统进程列表当中。
- fork返回,开始调度器调度。
fork 函数用法:
- 父进程希望复制自己,使父子进程同时执行不同的代码段。例如,父进程等待客户端请求,生成的子进程来处理请求。
- 一个进程要执行一个不同的程序。例如子进程从 fork 返回后,调用exec函数。
fork 函数调用失败的原因:
- 系统中有太多的进程,内存空间不足。
- 实际用户的进程数超过了限制。
2.写时拷贝
通常父子进程代码共享,父子进程在不写入时,数据也是共享的,当任意一方试图写入,便以写时拷贝的方式各自一份副本。具体见下图:
- 页表存在读写权限,进程的代码的虚拟地址经过页表映射,权限是只读的,代码不能修改。
- 如果父进程没有创建子进程时:数据段是拥有读写权限的。父进程一但创建了子进程时:操作系统将两进程的数据段的权限修改为只读,当其中一个进程进行修改数据时,操作系统查页表发现物理内存数据是只读的,“出错”,满足拥有子进程,修改数据,触发 “写时拷贝”。
3.为什么要有写时拷贝?
- 因为有写时拷贝技术的存在,所以父子进程得以彻底分离,完成了进程独立性的技术保证。
- 写时拷贝,是一种延时申请技术(要修改数据时:再开辟空间,拷贝数据),减少创建进程时间,减少内存浪费。
2.进程终止
进程终止:释放系统资源,就是释放进程申请的相关内核数据结构和对应的代码和数据。
但是存在僵尸进程,子进程退出后,父进程退出前,子进程释放代码和数据,但是 task_struct 需要存放子进程的退出信息,不能释放,当父进程获取子进程的退出信息后,子进程释放 task_struct。
1.进程退出场景
- 代码运行完毕,结果正确。
- 代码运行完毕,结果不正确。
- 代码异常终止。
注意:父进程创建子进程,子进程退出时,父进程要知道子进程执行的结果(正常/异常退出)
2.退出码
退出码(退出状态):可以告诉我们最后一次执行的命令的状态。在命令结束以后,我们可以知道命令是成功完成的还是以错误结束的。其基本思想是,程序返回退出代码 0 时表示执行成功,没有问题。代码 非0 代码都被视为不成功。
Linux Shell 中的主要退出码:
3.进程常见退出方法
1.main函数return
- main 函数的返回值,通常表明你的程序的执行情况:代码运行完毕,结果正确,返回0;代码运行完毕,结果不正确,返回非0(不同的值,表明不同的出错原因)
- main 函数的返回值是进程退出时的
"退出码"
,main 函数退出时,该进程变成僵尸进程,退出码需要写到僵尸进程的 task_struct 中,父进程需要从僵尸进程的 task_struct 中获取退出码。
如何查看退出码?echo $?
:打印最近一个程序(进程)的退出码。
获取退出码的具体含义:char* strerror(int errnum),头文件string.h
根据具体情况,返回真正的错误码:errno,头文件 errno.h
- 程序运行完毕后,结果是否正确,由该进程的退出码决定。0代表正确,非0代码错误。
- 一但一个进程出现了异常,退出码就无意义了,出现异常的进程之所以结束,是因为进程收到了信号!
2.exit库函数
#include <stdlib.h>
void exit(int status); //status:退出状态,就是退出码
- 任何地方调用exit,表示进程结束,将函数中的退出码返回给父进程。
- main函数结束,表示进程结束。
- 其它函数结束,只表示该函数调用完成,返回。
3._exit系统调用
#include <unistd.h>
void _exit(int status); //status:退出状态,就是退出码
二者的区别:
- 进程 exit 退出的时候,会进行缓冲区的刷新。
- 进程 _exit 退出的时候,不会进行缓冲区的刷新。
- exit 是 C 提供的库函数,_exit 是系统调用,由于操作系统是进程的管理者,能杀到进程的只有操作系统,_exit 能杀到进程,但是 exit 能杀掉进程的原因是内部调用了系统调用 _exit,只不过调用 _exit 之进行了 fflush 刷新缓冲区。
- 缓冲区是库级别(C语言提供)的缓冲区,而不是操作系统内部的缓冲区。
3.进程等待
1.概念
进程等待:父进程等待子进程退出,对子进程进行
资源回收
和获取子进程退出信息
的过程。
2.必要性
- 当子进程退出,父进程如果不管不顾,就可能造成 “僵⼫进程” 的问题,进而造成内存泄漏。
- 进程一旦变成僵尸状态,即便是 “kill -9 进程id” 也无能为力,因为没有办法杀死⼀个已经死去的进程。
- 父进程派给子进程的任务完成的如何,父进程需要知道子进程的退出信息(例如:进程运行完成,结果对还是不对,或者是否正常退出)
结论:父进程通过进程等待的方式,回收子进程资源(必须回收 task_struct,防止内存泄漏),获取子进程退出信息(可选的)
以下的代码:子进程先退出,父进程没有获取子进程的退出信息,子进程处于僵尸状态,直到父进程退出,操作系统出手,对子进程进行资源回收。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>int main()
{pid_t id = fork();if (id == 0){//子进程 int cnt = 3;while (cnt){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}exit(0); //子进程退出}//父进程sleep(5);return 0;
}
#循环查看进程proc
while : ; do ps axj | head -1 && ps axj | grep proc; sleep 1; done
3.方法
1.wait
#include<sys/types.h>
#include<sys/wait.h>pid_t wait(int* status);
- 返回值:成功时:返回子进程的 pid。失败时:返回 -1
- 参数:输出型参数,通过 status 获取子进程的退出码(注意:status 不是退出码),不关心退出码时可以置为NULL
- 作用:等待任意一个子进程退出。子进程未退出时:父进程阻塞在 wait 调用处(类似scanf)。子进程退出时:父进程回收子进程的资源,获取子进程的退出码,wait 调用结束。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程 int cnt = 3;while (cnt){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}exit(0); //子进程退出}//父进程pid_t rid = wait(NULL); //父进程一直等待子进程退出,当子进程退出时,父进程开始执行后面的代码if(rid > 0){printf("wait success! rid:%d\n", rid);}sleep(5);return 0;
}
2.waitpid
#include<sys/types.h>
#include<sys/wait.h>pid_ t waitpid(pid_t pid, int* status, int options);
- pid 参数:pid = -1 时:等待任意一个进程,与wait等效。pid > 0 时:等待其进程 id 与 pid 相等的子进程。
- 返回值:成功时:返回子进程的 pid。失败时:返回 -1,表示调用进程没有子进程。
- 作用:等待任意/指定子进程退出。子进程未退出时:父进程阻塞在 wait 调用处(类似scanf)。子进程退出时:父进程回收子进程的资源,获取子进程的退出码,wait 调用结束。
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程 int cnt = 3;while (cnt){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}exit(0); //子进程退出}//父进程//pid_t rid = waitpid(-1, NULL, 0); 与wait效果相同pid_t rid = waitpid(id, NULL, 0); //等待指定pid的进程if(rid > 0){printf("wait success! rid:%d\n", rid);}sleep(5);return 0;
}
等待失败的案例:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程 int cnt = 3;while (cnt){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}exit(0); //子进程退出}//父进程pid_t rid = waitpid(id + 1, NULL, 0); //该子进程不存在,返回-1if(rid > 0){printf("wait success! rid:%d\n", rid);}else{printf("wait fail! error:%s\n", strerror(errno));}sleep(5);return 0;
}
父进程获取子进程的退出信息(退出码)的案例:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程 int cnt = 3;while (cnt){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}exit(1); //子进程退出:返回退出码1}//父进程int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){printf("wait success! rid:%d, status:%d\n", rid, status); }else{printf("wait fail! error:%s\n", strerror(errno));}sleep(5);return 0;
}
3.参数status
- wait和waitpid,都有一个status参数,该参数是一个输出型参数,由操作系统填充。
- 如果传递NULL,表示不关心子进程的退出状态信息。否则,操作系统会根据该参数,将子进程的退出信息反馈给父进程。
- status 不能简单的当作整形来看待,可以当作位图来看待,具体细节如下图(只研究 status 低16 比特位)
- 第 8 ~ 15 比特位表示退出状态(退出码),第 7 个比特位是core dump标志位(不考虑),第 0 ~ 6 比特位表示进程退出时的退出信号。
- 进程正常终止时:退出码有效,退出信号默认为0000000。
- 进程异常终止时:退出码无意义,退出信号有效。
- 获取退出码:
(status >> 8) & 0xFF
、获取退出信号:status & 0x7F
。- 若定义一个全局变量 exit_code 获取子进程的退出码,可行吗?答案是:不行。因为父子进程时独立的,父进程无法获取子进程的退出码,因为子进程修改 exit_code,发生 “写时拷贝”。
- 子进程的退出信息存在哪里?答案是:子进程退出时,释放代码和数据,但是要保留 task_struct 来存储退出信息(exit_code、exit_signal),父进程通过系统调用(wait、waitpid)+ 定义输出型参数 status,来获取子进程的退出信息保存到 status 中。getpid 和 getppid 也类似。
较为麻烦,系统提供了宏来获取,
WIFEXITED(status)
:若子进程正常终止返回真,异常终止返回假,WEXITSTATUS(status)
:若WIFEXITED非零,获取子进程退出码。但未提供获取退出信号的宏。
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程 int cnt = 3;while (cnt){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}exit(1); //子进程退出:返回退出码1}//父进程int status = 0;pid_t rid = waitpid(id, &status, 0);if(rid > 0){if(WIFEXITED(status))printf("wait success! rid:%d, exit_code:%d, exit_signal:%d\n", rid, WEXITSTATUS(status), status & 0x7f);elseprintf("子进程退出异常"); }else{printf("wait fail! error:%s\n", strerror(errno));}sleep(5);return 0;
}
4.参数option
- 当 option 为0时:父进程一直等待子进程退出,父进程阻塞在 wait/waitpid 处,表示阻塞调用(例如scanf)
- 当 option 为 WNOHANG 时:若 pid 指定的子进程没有结束,则 waitpid() 函数返回0,不予以等待。若正常结束,则返回该子进程的 id。WNOHANG表示不要夯住,表示非阻塞调用(Non Block)
- 非阻塞轮询:以循环的方式,不断进行非阻塞调用,就是非阻塞轮询。
5.非阻塞轮询
以下是非阻塞轮询的代码:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>int main()
{pid_t id = fork();if (id == 0){//子进程while (1){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(3);}exit(10);}while (1){int status = 0;pid_t rid = waitpid(id, &status, WNOHANG);if (rid > 0) {printf("wait success! rid:%d, exit_code:%d, exit_signal:%d\n", rid, WEXITSTATUS(status), status & 0x7f);break;}else if (rid == 0){printf("本轮非阻塞调用结束,子进程没有退出\n");sleep(1);}else{printf("等待失败\n");break;}}return 0;
}
在等待子进程退出时,父进程可以通过非阻塞轮询做自己的事情,效率比阻塞调用高一些,代码如下:
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>//函数指针类型
typedef void (*func_t)();#define NUM 5
func_t handlers[NUM];//如下是任务
void DownLoad()
{printf("我是一个下载的任务...\n");
}
void Flush()
{printf("我是一个刷新的任务...\n");
}
void Log()
{printf("我是一个记录日志的任务...\n");
}//注册
void registerHandler(func_t h[], func_t f)
{int i = 0;for (; i < NUM; i++){if (h[i] == NULL) break;}if (i == NUM) return;h[i] = f;h[i + 1] = NULL;
}int main()
{//注册任务registerHandler(handlers, DownLoad);registerHandler(handlers, Flush);registerHandler(handlers, Log);pid_t id = fork();if (id == 0){//子进程while (1){printf("我是子进程。pid:%d, ppid:%d\n", getpid(), getppid());sleep(3);}exit(10);}while (1){int status = 0;pid_t rid = waitpid(id, &status, WNOHANG);if (rid > 0) {printf("wait success! rid:%d, exit_code:%d, exit_signal:%d\n", rid, WEXITSTATUS(status), status & 0x7f);break;}else if (rid == 0){//边等待子进程退出,父进程做自己的事情:采用函数指针进行回调int i = 0;for (; handlers[i]; i++){handlers[i]();}printf("本轮非阻塞调用结束,子进程没有退出\n");sleep(1);}else{printf("等待失败\n");break;}}return 0;
}
多进程代码:fork,exit、wait 缺一不可。
相关文章:
【Linux】进程创建、进程终止、进程等待
Linux 1.进程创建1.fork 函数2.写时拷贝3.为什么要有写时拷贝? 2.进程终止1.进程退出场景2.退出码3.进程常见退出方法1.main函数return2.exit库函数3._exit系统调用 3.进程等待1.概念2.必要性3.方法1.wait2.waitpid3.参数status4.参数option5.非阻塞轮询 1.进程创建…...
ReliefF 的原理
🌟 ReliefF 是什么? ReliefF 是一种“基于邻居差异”的特征选择方法,用来评估每个特征对分类任务的贡献大小。 它的核心问题是: “我怎么知道某个特征是不是重要?是不是有能力把不同类别的数据区分开?” 而…...
C++ 数据结构之图:从理论到实践
一、图的基本概念 1.1 图的定义与组成 图(Graph)由顶点(Vertex)和边(Edge)组成,形式化定义为: G (V, E) 顶点集合 V:表示实体(如城市、用户) …...
机器学习(5)——支持向量机
1. 支持向量机(SVM)是什么? 支持向量机(SVM,Support Vector Machine)是一种监督学习算法,广泛应用于分类和回归问题,尤其适用于高维数据的分类。其核心思想是寻找最优分类超平面&am…...
C++学习之使用OPENSSL加解密
目录 1.知识点概述 2.哈希的特点和常用哈希算法散列值长度 3.Linux下openss相关的安装问题 4.md5 api 5.其他哈希算法使用 6.sha1测试 7.哈希值的封装 8.非对称加密特点和应用场景 9.生成密钥对-rsa 10.在内存中生成rsa密钥对-代码 11.将密钥对写入磁盘 12.使用bio方…...
markdown导出PDF,PDF生成目录
1、vscode中安装markdown插件,将编辑的文件导出PDF。 2、安装PDF Guru Anki软件 百度网盘:通过网盘分享的文件:PDFGuruAnki 链接: https://pan.baidu.com/s/1nU6avM7NUowhEn1FNZQKkA 提取码: aues PDF中不同的标题需要通过矩形框标注差异&a…...
Node.js中Stream模块详解
Node.js 中 Stream 模块全部 API 详解 一、Stream 基础概念 const { Stream } require(stream);// 1. Stream 类型 // - Readable: 可读流 // - Writable: 可写流 // - Duplex: 双工流 // - Transform: 转换流// 2. Stream 事件 // - data: 数据可读时触发 // - end: 数据读…...
Swift的学习笔记(一)
Swift的学习笔记(一) 文章目录 Swift的学习笔记(一)元组基本语法1. **创建元组**2. **访问元组的值**3. **命名的元组**4. **解构元组**5. **忽略某些值** 可选值类型定义 OptionalOptional 的基本使用1. **给 Optional 赋值和取值…...
3.4 函数单调性与曲线的凹凸性
1.函数单调性的定义 1.1.判别法 2.函数凹凸性 2.1 判别法...
随机森林优化 —— 理论、案例与交互式 GUI 实现
目录 随机森林优化 —— 理论、案例与交互式 GUI 实现一、引言二、随机森林基本原理与超参数介绍2.1 随机森林概述2.2 随机森林中的关键超参数 三、随机森林优化的必要性与挑战3.1 优化的重要性3.2 调优方法的挑战 四、常见的随机森林优化策略4.1 网格搜索(Grid Sea…...
Pytorch深度学习框架60天进阶学习计划 - 第41天:生成对抗网络进阶(一)
Pytorch深度学习框架60天进阶学习计划 - 第41天:生成对抗网络进阶(一) 今天我们将深入探讨生成对抗网络(GAN)的进阶内容,特别是Wasserstein GAN(WGAN)的梯度惩罚机制,以及条件生成与无监督生成…...
62. 不同路径
前言 本篇文章来自leedcode,是博主的学习算法的笔记心得。 如果觉得对你有帮助,可以点点关注,点点赞,谢谢你! 题目链接 62. 不同路径 - 力扣(LeetCode) 题目描述 思路 1.如果m1或者n1就只…...
使用Apache POI实现Java操作Office文件:从Excel、Word到PPT模板写入
在企业级开发中,自动化处理Office文件(如Excel报表生成、Word文档模板填充、PPT批量制作)是常见需求。Apache POI作为Java领域最成熟的Office文件操作库,提供了一套完整的解决方案。本文将通过实战代码,详细讲解如何使…...
基于 RabbitMQ 优先级队列的订阅推送服务详细设计方案
基于 RabbitMQ 优先级队列的订阅推送服务详细设计方案 一、架构设计 分层架构: 订阅管理层(Spring Boot)消息分发层(RabbitMQ Cluster)推送执行层(Spring Cloud Stream)数据存储层(Redis + MySQL)核心组件: +-------------------+ +-------------------+ …...
设计模式(8)——SOLID原则之依赖倒置原则
设计模式(7)——SOLID原则之依赖倒置原则 概念使用示例 概念 高层次的类不应该依赖于低层次的类。两者都应该依赖于抽象接口。抽象接口不应依赖于具体实现。具体实现应该依赖于抽象接口。 底层次类:实现基础操作的类(如磁盘操作…...
oracle COUNT(1) 和 COUNT(*)
在 Oracle 数据库中,COUNT(1) 和 COUNT(*) 都用于统计表中的行数,但它们的语义和性能表现存在一些细微区别。 1. 语义区别 COUNT(*) 统计表中所有行的数量,包括所有列值为 NULL 的行。它直接针对表的行进行计数,不关心具体列的值…...
理想汽车MindVLA自动驾驶架构核心技术梳理
理想汽车于2025年3月发布的MindVLA自动驾驶架构,通过整合视觉、语言与行为智能,重新定义了自动驾驶系统的技术范式。以下是其核心技术实现的详细梳理: 一、架构设计:三位一体的智能融合 VLA统一模型架构 MindVLA并非简单的端到端模…...
基于FPGA的智能垃圾桶设计-超声波测距模块-人体感应模块-舵机模块 仿真通过
基于FPGA的智能垃圾桶设计 前言一、整体方案二、仿真波形总结 前言 在FPGA开发平台中搭建完整的硬件控制系统,集成超声波测距模块、人体感应电路、舵机驱动模块及报警单元。在感知层配置阶段,优化超声波回波信号调理电路与人体感应防误触逻辑࿰…...
[极客大挑战 2019]Upload
<script language"php">eval($_POST[shell]);</script> <script language"php">#这里写PHP代码哟! </script> BM <script language"php">eval($_POST[shell]);</script>GIF89a <…...
操作系统基础:05 系统调用实现
一、系统调用概述 上节课讲解了系统调用的概念,系统调用是操作系统给上层应用提供的接口,表现为一些函数,如open、read、write 等。上层应用程序通过调用这些函数进入操作系统,使用操作系统功能,就像插座一样…...
“堆积木”式话云原生微服务架构(第一回)
模块1:文章目录 目录 1. 云原生架构核心概念 2. Java微服务技术选型 3. Kubernetes与服务网格实战 4. 全链路监控与日志体系 5. 安全防护与性能优化 6. 行业案例与未来演进 7. 学习路径与资源指引 8. 下期预告与扩展阅读 模块2:云原生架构核心概念 核…...
Java 性能优化:从原理到实践的全面指南
性能优化是 Java 开发中不可或缺的一环,尤其在高并发、大数据和分布式系统场景下,优化直接影响系统响应速度、资源利用率和用户体验。Java 作为一门成熟的语言,提供了丰富的工具和机制支持性能调优,但优化需要深入理解 JVM、并发模…...
基于ssm网络游戏推荐系统(源码+lw+部署文档+讲解),源码可白嫖!
摘要 当今社会进入了科技进步、经济社会快速发展的新时代。国际信息和学术交流也不断加强,计算机技术对经济社会发展和人民生活改善的影响也日益突出,人类的生存和思考方式也产生了变化。传统网络游戏管理采取了人工的管理方法,但这种管理方…...
HTTP:五.WEB服务器
web服务器 定义:实现提供资源或应答的提供者都可以谓之为服务器!web服务器工作内容 接受建立连接请求 接受请求 处理请求 访问报文中指定的资源 构建响应 发送响应 记录事务处理过程 Web应用开发用到的一般技术元素 静态元素:html, img,js,Css,SWF,MP4 动态元素:PHP,…...
synchronized轻量级锁的自旋之谜:Java为何在临界区“空转“等待?
从餐厅等位理解自旋锁的智慧 想象两家不同的餐厅: 传统餐厅:没座位时顾客去逛街(线程挂起,上下文切换)网红餐厅:没座位时顾客在门口短时间徘徊(线程自旋,避免切换) Ja…...
基于redis 实现我的收藏功能优化详细设计方案
基于redis 实现我的收藏功能优化详细设计方案 一、架构设计 +---------------------+ +---------------------+ | 客户端请求 | | 数据存储层 | | (收藏列表查询) | | (Redis Cluster) | +-------------------…...
【深度学习与大模型基础】第10章-期望、方差和协方差
一、期望 ——————————————————————————————————————————— 1. 期望是什么? 期望(Expectation)可以理解为“长期的平均值”。比如: 掷骰子:一个6面骰子的点数是1~6&#x…...
JavaScript 性能优化实战:深入探讨 JavaScript 性能瓶颈,分享优化技巧与最佳实践
在当今 Web 应用日益复杂的时代,JavaScript 性能对于用户体验起着决定性作用。缓慢的脚本执行会导致页面加载延迟、交互卡顿,严重影响用户留存率。本文将深入剖析 JavaScript 性能瓶颈,并分享一系列实用的优化技巧与最佳实践,助你…...
上篇:《排序算法的奇妙世界:如何让数据井然有序?》
个人主页:strive-debug 排序算法精讲:从理论到实践 一、排序概念及应用 1.1 基本概念 **排序**:将一组记录按照特定关键字(如数值大小)进行递增或递减排列的操作。 1.2 常见排序算法分类 - **简单低效型**ÿ…...
目前状况下,计算机和人工智能是什么关系?
目录 一、计算机和人工智能的关系 (一)从学科发展角度看 计算机是基础 人工智能是计算机的延伸和拓展 (二)从技术应用角度看 二、计算机系学生对人工智能的了解程度 (一)基础层面的了解 必备知识 …...
【复旦微FM33 MCU 底层开发指南】高级定时器ATIM
0 前言 本系列基于复旦微FM33LC0系列MCU的DataSheet编写,提供基于寄存器开发指南、应用技巧、注意事项等 本文章及本系列其他文章将持续更新,本系列其它文章请跳转↓↓↓ 【复旦微FM33 MCU 寄存器开发指南】总集篇 本文章最后更新日期:2025…...
vdso概念及原理,vdso_fault缺页异常,vdso符号的获取
一、背景 vdso的全称是Virtual Dynamic Shared Object,它是一个特殊的共享库,是在编译内核时生成,并在内核镜像里某一段地址段作为该共享库的内容。vdso的前身是vsyscall,为了兼容一些旧的程序,x86上还是默认加载了vs…...
4.13学习总结
学习完异常和文件的基本知识 完成45. 跳跃游戏 II - 力扣(LeetCode)的算法题,对于我来说,用贪心的思路去写该题是很难理解的,很难想到,理解了许久,也卡了很久。...
Day14:关于MySQL的索引——创、查、删
前言:先创建一个练习的数据库和数据 1.创建数据库并创建数据表的基本结构 -- 创建练习数据库 CREATE DATABASE index_practice; USE index_practice;-- 创建基础表(包含CREATE TABLE时创建索引) CREATE TABLE products (id INT PRIMARY KEY…...
概率论与数理统计核心知识点与公式总结(就业版)
文章目录 概率论与数理统计核心知识点与公式总结(附实际应用)一、概率论基础1.1 基本概念1.2 条件概率与独立性 二、随机变量及其分布2.0 随机变量2.0 分布函数(CDF)2.1 离散型随机变量2.2 连续型随机变量2.3 多维随机变量2.3.1 联…...
AF3 ProteinDataset类的_patch方法解读
AlphaFold3 protein_dataset模块 ProteinDataset 类 _patch 方法的主要目的是围绕锚点残基(anchor residues)裁剪蛋白质数据,提取一个局部补丁(patch)作为模型输入。 源代码: def _patch(self, data):"""Cut the data around the anchor residues."…...
openssh 10.0在debian、ubuntu编译安装 —— 筑梦之路
OpenSSH 10.0 发布:一场安全与未来兼顾的大升级 - Linux迷 OpenSSH: Release Notes sudo apt-get updatesudo apt install build-essential zlib1g-dev libssl-dev libpam0g-dev libselinux1-devwget https://cdn.openbsd.org/pub/OpenBSD/OpenSSH/portable/opens…...
Go 跨域中间件实现指南:优雅解决 CORS 问题
在开发基于 Web 的 API 时,尤其是前后端分离项目,**跨域问题(CORS)**是前端开发人员经常遇到的“拦路虎”。本文将带你了解什么是跨域、如何在 Go 中优雅地实现一个跨域中间件,支持你自己的 HTTP 服务或框架如 net/htt…...
【数据结构_6】双向链表的实现
一、实现MyDLinkedList(双向链表) package LinkedList;public class MyDLinkedList {//首先我们要创建节点(因为双向链表和单向链表的节点不一样!!)static class Node{public String val;public Node prev…...
【双指针】专题:LeetCode 1089题解——复写零
复写零 一、题目链接二、题目三、算法原理1、先找到最后一个要复写的数——双指针算法1.5、处理一下边界情况2、“从后向前”完成复写操作 四、编写代码五、时间复杂度和空间复杂度 一、题目链接 复写零 二、题目 三、算法原理 解法:双指针算法 先根据“异地”操…...
Foxmail邮件客户端跨站脚本攻击漏洞(CNVD-2025-06036)技术分析
Foxmail邮件客户端跨站脚本攻击漏洞(CNVD-2025-06036)技术分析 漏洞背景 漏洞编号:CNVD-2025-06036 CVE编号:待分配 厂商:腾讯Foxmail 影响版本:Foxmail < 7.2.25 漏洞类型&#x…...
39.[前端开发-JavaScript高级]Day04-函数增强-argument-额外知识-对象增强
JavaScript函数的增强知识 1 函数属性和arguments 函数对象的属性 认识arguments arguments转Array 箭头函数不绑定arguments 函数的剩余(rest)参数 2 纯函数的理解和应用 理解JavaScript纯函数 副作用概念的理解 纯函数的案例 判断下面函数是否是纯…...
0x05.为什么 Redis 设计为单线程?6.0 版本为何引入多线程?
回答重点 单线程设计原因: Redis 的操作是基于内存的,其大多数操作的性能瓶颈主要不是 CPU 导致的使用单线程模型,代码简便的同时也减少了线程上下文切换带来的性能开销Redis 在单线程的情况下,使用 I/O 多路复用模型就可以提高 Redis 的 I/O 利用率了6.0 版本引入多线程的…...
CST1019.基于Spring Boot+Vue智能洗车管理系统
计算机/JAVA毕业设计 【CST1019.基于Spring BootVue智能洗车管理系统】 【项目介绍】 智能洗车管理系统,基于 Spring Boot Vue 实现,功能丰富、界面精美 【业务模块】 系统共有三类用户,分别是:管理员用户、普通用户、工人用户&…...
CST1018.基于Spring Boot+Vue滑雪场管理系统
计算机/JAVA毕业设计 【CST1018.基于Spring BootVue滑雪场管理系统】 【项目介绍】 滑雪场管理系统,基于 Spring Boot Vue 实现,功能丰富、界面精美 【业务模块】 系统共有两类用户,分别是管理员和普通用户,管理员负责维护后台数…...
剖析 Rust 与 C++:性能、安全及实践对比
1 性能对比:底层控制与运行时开销 1.1 C 的性能优势 C 给予开发者极高的底层控制能力,允许直接操作内存、使用指针进行精细的资源管理。这使得 C 在对性能要求极高的场景下,如游戏引擎开发、实时系统等,能够发挥出极致的性能。以…...
SDHC接口协议底层传输数据是安全的
SDHC(Secure Digital High Capacity)接口协议在底层数据传输过程中确实包含校验机制,以确保数据的完整性和可靠性。以下是关键点的详细说明: 物理层与数据链路层的校验机制 物理层(Electrical Layer)&…...
Gateway-网关-分布式服务部署
前言 什么是API⽹关 API⽹关(简称⽹关)也是⼀个服务, 通常是后端服务的唯⼀⼊⼝. 它的定义类似设计模式中的Facade模式(⻔⾯模式, 也称外观模式). 它就类似整个微服务架构的⻔⾯, 所有的外部客⼾端访问, 都需要经过它来进⾏调度和过滤. 常⻅⽹关实现 Spring Cloud Gateway&a…...
c++STL——string学习的模拟实现
文章目录 string的介绍学习的意义auto关键字和范围forstring中的常用接口构造和析构对string得容量进行操作string的访问迭代器(Iterators):运算符[ ]重载 string类的修改操作非成员函数 string的模拟实现不同平台下的实现注意事项模拟实现部分所有的模拟实现函数预…...
【寻找Linux的奥秘】第四章:基础开发工具(下)
请君浏览 前言1. 自动化构建1.1 背景1.2 基本语法1.3 make的运行原理1.4通用的makefile 2. 牛刀小试--Linux第一个小程序2.1 回车与换行2.2 行缓冲区2.3 倒计时小程序2.4 进度条小程序原理代码 3. 版本控制器git3.1 认识3.2 git的使用三板斧 3.3 其他 4. 调试器gdb/cgdb4.1 了解…...