【Linux】进程控制(进程创建、进程终止、进程等待、进程替换)
目录
一、进程创建
1、fork函数
2、页表权限
二、进程终止
1、main函数返回值(退出码)
2、常见错误码及其对应的错误描述:
将错误退出码转化为错误描述的方法:
3、进程退出的三种场景
4、由上我们可以知道:
5、exit和_exit
三、进程等待
1、为什么进行进程等待
2、进程等待的重要性:
3、wait函数和waitpid函数
wait测试代码:
waitpid测试代码以及参数status解析:
参数status:
4、阻塞与非阻塞
四、进程程序替换
1、什么是程序替换?
2、测试代码,运用exec*系列的系统调用:
3、几个细节
4、更改为多进程版本
5、学习各种exec*接口
(1)execlp
(2)execv
(3)、execve(唯一的系统调用)
6、问题:exec*可以替换系统指令,那可以替换成我们自己的程序吗?
7、关于程序替换与环境变量的知识:
观察现象
如果我们putenv函数新增环境变量呐?现象是什么?
如果我们想设置全新的环境变量给子进程呐?(execle接口)
8、exec*系列接口结构图:
前些天发现了一个巨牛的人工智能学习网站,通俗易懂,风趣幽默,忍不住分享一下给大家
点击跳转到网站
一、进程创建
1、fork函数
前面我们已经知道了,创建一个子进程可以使用fork函数,现在我们可以复习一下fork函数。
(1)头文件:#include <unistd.h>(2)函数原型:pid_t fork(void);(3)返回值:子进程中返回0,父进程返回子进程pid,出错返回-1;
进程调用fork,当控制转移到内核中的fork代码后,内核做:(1)分配新的内存块和内核数据结构给子进程(2)将父进程部分数据结构内容拷贝至子进程(3)添加子进程到系统进程列表当中(4)fork返回,开始调度器调度
2、页表权限
上一章节,我们提到页表的概念,其实页表内容除了有虚拟地址和物理地址,后面还有很多选项,比如页表权限:
比如说C语言里面的字符串常量具有常属性,是不能被修改的,实际上是虚拟地址映射物理地址时的页表权限为只读权限
二、进程终止
1、main函数返回值(退出码)
平时我们写C程序时,main函数都会return 0,以前都不懂为什么要return 0,下面我们来讲解:
main函数的返回值又叫退出码:
一般情况:
(1)返回0,表示进程执行成功;
(2)返回非零,表示进程执行失败;
非零情况具体到不同数字表示不同的失败原因。
注意:只有在main函数return返回时,才表示进程退出,此时的return xxx 才表示退出码,其他函数return时仅仅表示该函数调用完成。
2、常见错误码及其对应的错误描述:
常见错误码及其含义: 错误码 1: Operation not permitted 错误码 2: No such file or directory 错误码 3: No such process 错误码 4: Interrupted system call 错误码 5: Input/output error 错误码 6: Device not configured 错误码 7: Argument list too long 错误码 8: Exec format error 错误码 9: Bad file descriptor 错误码 10: No child processes 错误码 11: Resource temporarily unavailable 错误码 12: Cannot allocate memory 错误码 13: Permission denied 错误码 14: Bad address 错误码 15: Block device required
将错误退出码转化为错误描述的方法:
(1)、使用语言和系统自带的方法进行转化:strerror
我们以错误码2为例:
错误码2对应的描述为:"No such file or directory",
宏定义为:ENOENT
(2)我们自定义
#include<stdio.h> enum {success = 0,open_err,malloc_err };const char* errorToDesc(int code) {switch(code){case success:return "success\n";case open_err:return "file open error\n";case malloc_err:return "malloc error\n";default:return "unknown error\n";} }int main() {printf("%s",errorToDesc(0));printf("%s",errorToDesc(1));printf("%s",errorToDesc(2));return 0; }
3、进程退出的三种场景
(1)进程代码执行完毕,结果也是正确的;
(2)进程代码执行完毕,但结果不正确;
(3)进程代码没有执行完,进程异常退出了(收到了异常信号)。
第(3)钟情况一般是进程收到了异常信号,每个信号都有不同编号,不同编号又表示不同的异常原因,如下:
#include<stdio.h> #include <sys/types.h> #include <unistd.h>//进程收到信号的场景 int main() {while(1){printf("process is running,pid: %d\n",getpid());sleep(1);}return 0; }
4、由上我们可以知道:
任何进程最终的执行情况,我们可以使用两个数字表明具体执行情况:
(1)信号码(signumber)
(2)退出码(exit_code)
5、exit和_exit
(1) exit和_exit都是用来终止进程的,在任意一个地方调用都会终止进程,其参数就是进程的退出码。
(2)两者关系:exit是库函数,_exit是系统调用,exit封装了_exit。所以推荐使用exit,因为语言接口具有跨平台性,并且系统调用的开销会更大。
(3)当我们运行程序发现exit会刷新缓冲区,而_exit不会刷新缓冲区。所以我们之前所谈的缓冲区,比如进度条程序的缓冲区绝对不是操作系统里面的缓冲区,若是操作系统的里面的缓冲区,那么_exit系统调用就会刷新缓冲区。后面学习我们会知道,这里缓冲区是在标准库里面(也叫上层缓冲区)
三、进程等待
1、为什么进行进程等待
(1)父进程需要通过wait方式,去回收子进程的资源;
(2)父进程通过wait方式可以获取子进程的退出信息;
2、进程等待的重要性:
(1)之前讲过,子进程退出时,父进程如果不管不顾,就可能造成僵尸进程的问题,进而造成内存泄漏。另外,进程一旦变成僵尸状态,那就会“刀枪不入”,即使是信号9也杀不掉,因为僵尸状态的进程实际上已经死了。
(2)最后,我们还需要知道父进程派给子进程的任务完成的如何,结果对还是不对,有没有正常退出等等信息,父进程就是通过进程等待的方式,回收子进程资源来获取这些退出信息的。
3、wait函数和waitpid函数
(1)
wait()
和waitpid()
都是用于父进程等待子进程终止的系统调用;返回值:
特性 wait waitpid 函数原型 pid_t wait(int *status);
pid_t waitpid(pid_t pid, int *status, int options); 等待范围 等待任意一个子进程终止。 可精确指定等待某个子进程(通过 pid
参数)或任意子进程。阻塞行为 阻塞:若无子进程终止,父进程会挂起。 除了左述,可通过 WNOHANG
选项设置为非阻塞,立即返回。状态获取 通过 status
参数获取子进程退出状态。同 wait()
,但支持更多状态检查宏(如WIFEXITED()
、WIFSIGNALED()
)。僵尸进程处理 回收终止子进程的资源(避免僵尸进程)。 同 wait()
,但可选择性回收(如通过WNOHANG
)。适用场景 父进程只需等待任一子进程结束,无需控制细节。 需精确控制等待的子进程(如多子进程场景),或需要非阻塞等待。
wait测试代码:
#include<stdio.h> #include <sys/types.h> #include <unistd.h> #include<stdlib.h>//wait的测试代码 int main() {pid_t id = fork();if(id == 0){//childint cnt = 5;while(cnt){printf("Child is running,pid: %d,ppid: %d\n",getpid(),getppid());sleep(1);cnt--;}printf("子进程准备退出,马上变成僵尸进程\n");exit(0);}printf("父进程sleep中.......\n");sleep(10);//fatherpid_t rid = wait(NULL);//阻塞等待if(rid > 0){printf("wait success,rid: %d\n",rid);}printf("父进程回收僵尸成功\n");sleep(3);return 0; }
注意:wait进程的是阻塞等待。fork之后,父子进程谁先运行是不确定的,这是由调度器说了算,所以刚开始我们让父进程休眠10秒。
waitpid测试代码以及参数status解析:
#include<stdio.h> #include <sys/types.h> #include <unistd.h> #include<stdlib.h>//waitpid测试代码 int main() {pid_t id = fork();if(id == 0){//childint cnt = 5;while(cnt){printf("Child is running,pid: %d,ppid: %d\n",getpid(),getppid());sleep(1);cnt--;}printf("子进程准备退出,马上变成僵尸进程\n");exit(1);}printf("父进程sleep中.......\n");sleep(10);int status = 0;pid_t rid = waitpid(id,&status,0);//阻塞等待if(rid > 0){printf("wait success, rid: %d,status: %d\n",rid,status);}printf("父进程回收僵尸成功\n");sleep(3);return 0; }
参数status:
我们发现wait和waitpid中都有一个参数status:该参数是一个整形指针,我们可以创建一个整形变量传进去获取对应信息。
实际上status里面保存了进程的退出码和退出信号,但如上图,我们获取的status是256是怎么回事?
因为status有自己的格式,我们需要通过对status解析才能获取我们想要的信息。
参数status是一个整形:
(1)最低7位保存的是信号编号;
(2)第8位目前不用管;
(3)次8位保存的退出码;
上述我们获取的status的值为256,所以解析如下:
所以我们可以通过移位运算符去解析该参数得到对应的退出码和退出信号:
我们也可以通过宏去解析:注意下述宏都包含在头文件:
<sys/wait.h>
4、阻塞与非阻塞
(1)我们要知道wait只有阻塞等待模式;
(2)waitpid既有阻塞模式,又有非阻塞模式;
waitpid通过第三个参传入宏:WNOHANG,即可变为非阻塞模式;
所以waitpid的非阻塞模式可以进行轮询多个子进程:
while (1) {pid_t pid = waitpid(-1, &status, WNOHANG);if (pid == 0) {// 无子进程终止,处理其他任务handle_other_tasks();continue;} else if (pid > 0) {// 处理已终止的子进程printf("子进程 %d 已终止\n", pid);} else if (pid == -1 && errno == ECHILD) {printf("所有子进程都已终止\n");break;} }
四、进程程序替换
1、什么是程序替换?
一般情况,我们的程序只能执行我们的代码,但如果我们创建的子进程想要执行其他程序的代码,此时就需要通过程序替换来实现。即一个程序想执行其他程序的代码,这时就可以通过程序替换来实现。
2、测试代码,运用exec*系列的系统调用:
首先我们要知道,linux中每条指令都是一个程序,其次这里我们用到execl函数,函数原型如下:
参数解析:
(1)找到一个程序的前提是知道它的路径,所以第一个参数就是要替换执行程序的路径,可以是绝对路径(如/usr/bin/ls),也可以是相对路径(如./mybin)。
(2)第二个参数是一个可变参数列表,命令行中怎么写,参数就怎么传(例如"ls","-a","-l"),通常arg[0]是程序名字,后面是选项,最后必须以NULL结尾
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>int main() {printf("i am a process.mypid: %d\n",getpid());printf("exec begin....\n");execl("/usr/bin/ls","ls","-a","-l",NULL);printf("exec end....\n");return 0; }
上面代码我用ls指令替换程序,并传入选项"-a","-l"的,当我运行程序时,就会执行ls命令
用top指令替换程序:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>int main() {printf("i am a process.mypid: %d\n",getpid());printf("exec begin....\n");//execl("/usr/bin/ls","ls","-a","-l",NULL);execl("/usr/bin/top","top",NULL);printf("exec end....\n");return 0; }
3、几个细节
4、更改为多进程版本
首先为什么要更改为多进程版本?因为父进程可以得到执行的结果。
过程图:
因为创建子进程后,父子进程会执行同一份代码和数据,此时如果在子进程中进行程序替换,子进程原有的数据和代码就会被其他程序替换,如果不做处理,那么必定会影响父进程,这违背了进程的独立性,所以当子进程中进行程序替换时,会发生写实拷贝,从而不会影响父进程的代码和数据。
问题:这里我们就可以回答shell是如何运行一个指令的?
shell创建子进程,子进程去替换各种指令。
测试代码:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>//多进程版本的程序替换 int main() {pid_t id = fork();if(id == 0){//childsleep(3);printf("i am child process: exec begin...\n");execl("/usr/bin/ls","ls","-a","-l",NULL);printf("exec end...\n");exit(1);}pid_t rid = waitpid(id,NULL,0);if(rid > 0){printf("i am father: wait success\n");}return 0; }
5、学习各种exec*接口
(1)execlp
该接口不用告诉系统,程序的路径是什么,只需要告诉程序叫什么名字,后面系统替换的时候,会自动去PATH环境变量默认的路径中去寻找。
所以第一个参数是程序名,第二个参数与execl一致。
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>//多进程版本的程序替换 int main() {pid_t id = fork();if(id == 0){//childprintf("i am child process: exec begin...\n");//execl("/usr/bin/ls","ls","-a","-l",NULL);execlp("ls","ls","-l",NULL);printf("exec end...\n");exit(1);}pid_t rid = waitpid(id,NULL,0);if(rid > 0){printf("i am father: wait success\n");}return 0; }
我们可以看到参数:
这两个参数重复吗?答案是不重复的,第一个ls表示要替换的程序名称,第二个ls只是一个名称,即使和第一个替换程序名不一样也没有影响:
(2)execv
第一个参数也是程序的绝对路径或者相对路径
第二个参数,需要我们手动创建一个相同类型的数组进行传参,数组内容与execl第二个参数内容一致,如:
而我们所用的大多数指令(如ls)是C语言写的,所以就会有main函数,而main函数的第二个参数刚好是该数组类型,所以这里实际上我们是手动给main函数的命令行参数传参,同理main函数的第三个参数环境变量传参也是类似。
测试代码:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h>//多进程版本的程序替换 int main() {pid_t id = fork();if(id == 0){//childprintf("i am child process: exec begin...\n");//execl("/usr/bin/ls","ls","-a","-l",NULL);//execlp("ls","HF","-l",NULL);char*const argv[] = {(char*)"ls",(char*)"-l",NULL};sleep(3);execv("/usr/bin/ls",argv);printf("exec end...\n");exit(1);}pid_t rid = waitpid(id,NULL,0);if(rid > 0){printf("i am father: wait success\n");}return 0; }
(3)、execve(唯一的系统调用)
通过man指令查看手册,发现execve单独在一个手册,这是为什么呐?
看到二号手册,我们就知道了,因为这些接口中,只有execve是系统调用,其他接口全是对execve的封装。
6、问题:exec*可以替换系统指令,那可以替换成我们自己的程序吗?
无论什么语言,只要能在liunx下面运行,就可以,因为所有语言程序运行后都变成了进程。
makefile同时形成两个可执行:
7、关于程序替换与环境变量的知识:
观察现象
我们自己写了一个程序:
在另一个程序中进行程序替换:
运行结果发现,当我们进行程序替换时,明明没有传递环境变量参数,但main函数的env参数依然有环境变量数据,原因在于父进程默认可以通过地址空间继承的方式,让所有子进程拿到环境变量,所以说,程序替换,不会替换环境变量数据。
如果我们putenv函数新增环境变量呐?现象是什么?
如果我们想设置全新的环境变量给子进程呐?(execle接口)
用execle接口:
该系统调用就有第三个参数envp,也是需要创建一个同类型数组:
这样传参相当于就是给main函数的第三个参数手动传参:
8、exec*系列接口结构图:
相关文章:
【Linux】进程控制(进程创建、进程终止、进程等待、进程替换)
目录 一、进程创建 1、fork函数 2、页表权限 二、进程终止 1、main函数返回值(退出码) 2、常见错误码及其对应的错误描述: 将错误退出码转化为错误描述的方法: 3、进程退出的三种场景 4、由上我们可以知道: 5…...
java+selenium专题->启动浏览器下篇
1.简介 上一篇文章,我们已经在搭建的java项目环境中实践了,今天就在基于maven项目的环境中演示一下。 2.eclipse中新建maven项目 1.依次点击eclipse的file - new - other ,如下图所示: 2.在搜索框输入关键字“maven”ÿ…...
sqlserver 循环删除1000行
在SQL Server中,如果你想循环删除1000行数据,有几种方法可以实现,但值得注意的是,频繁使用循环删除操作可能会对数据库性能造成影响,尤其是在处理大量数据时。下面介绍几种方法,并讨论它们的优缺点。 方法…...
亚信电子与联发科技携手打造AIoT新未来
[台湾新竹讯, 2025年5月19日] 智能物联网(AIoT)融合人工智能与物联网技术,通过边缘AI的实时数据分析及设备智能联网能力,加速智能物联网创新应用的蓬勃发展。为满足AIoT产业对多网络端口的应用需求,全球半导体公司【联…...
【成品设计】基于STM32的人体健康监测系统
《基于STM32的人体健康监测系统》 Ps:有4个版本。 V1硬件设计: 主控:STM32F103C8T6:作为系统主控芯片。 血氧心率传感器:用于采集当前心率、血氧值。 温湿度传感器:用于采集当前环境温湿度。 有源低电平触发蜂鸣器&…...
【MySQL进阶】了解linux操作系统下mysql的配置文件和常用选项
前言 🌟🌟本期讲解关于linux下mysql配置选项的详细介绍~~~ 🌈感兴趣的小伙伴看一看小编主页:GGBondlctrl-CSDN博客 🔥 你的点赞就是小编不断更新的最大动力 🎆那么…...
LeetCode 219.存在重复元素 II
目录 题目: 题目描述: 题目链接: 思路: 核心思路: 思路详解: 代码: C代码: Java代码: 题目: 题目描述: 题目链接: 219. 存…...
解释:神经网络
在过去的10年里,表现最好的artificial-intelligence系统——比如智能手机上的语音识别器或谷歌最新的自动翻译——都是由一种叫做“深度学习”的技术产生的 深度学习实际上是一种被称为神经网络的人工智能方法的新名称,这种方法已经流行了70多年。1944年…...
Java 泛型详解
在 Java 的类型系统中,泛型(Generics) 是一个非常重要的特性。它让我们能够编写更通用、更安全的代码,尤其是在处理集合类(如 List、Map 等)时,泛型的使用可以大大减少类型转换的麻烦࿰…...
React集成百度【JSAPI Three】教程(001):快速入门
文章目录 1、快速入门1.1 创建react项目1.2 安装与配置1.3 静态资源配置1.4 配置百度地图AK1.5 第一个DEMO1、快速入门 JSAPI Three版本是一套基于Three.js的三维数字孪生版本地图服务引擎,一套引擎即可支持2D、2.5D、3D全能力的地理投影与数据源加载,帮助开发者轻松搞定平面…...
WPF中资源(Resource)与嵌入的资源(Embedded Resource)的区别及使用场景详解
🌟 开发WPF项目时图片、SVG、配置文件等到底该设置为哪种资源?如何正确读取、跨程序集访问?一篇文章全解答。 在使用 WPF 进行项目开发时,很多开发者在设置文件“生成操作(Build Action)”时,常常会在“资源(Resource)”和“嵌入的资源(Embedded Resource)”之间感…...
如何在 Windows 11 或 10 上安装 Fliqlo 时钟屏保
了解如何在 Windows 11 或 10 上安装 Fliqlo,为您的 PC 或笔记本电脑屏幕添加一个翻转时钟屏保以显示时间。 Fliqlo 是一款适用于 Windows 和 macOS 平台的免费时钟屏保。它也适用于移动设备,但仅限于 iPhone 和 iPad。Fliqlo 的主要功能是在用户不活动时在 PC 或笔记本电脑…...
【STM32】ST-Link V2.1制作
一、下载烧写工具及程序 下载器制作(ST-Link V2.1) 链接: 提取码:6666https://pan.baidu.com/s/1n0RYNDEw5mBT_CsTFoqrIg?pwd6666 二、安装STM32 CubeProgrammer 双击安装包,点击Next 继续点击Next 选择安装路径,再…...
day30python打卡
知识点回顾: 导入官方库的三种手段导入自定义库/模块的方式导入库/模块的核心逻辑:找到根目录(python解释器的目录和终端的目录不一致) 作业:自己新建几个不同路径文件尝试下如何导入 一、导入官方库 我们复盘下学习py…...
AI大语言模型评测体系演进与未来展望
随着人工智能技术的飞速发展,大语言模型(LLMs)已成为自然语言处理领域的核心研究方向。2025年最新行业报告显示,当前主流模型的评测体系已从单一任务评估转向多维度、全链路的能力剖析。例如,《全球首个大语言模型意识水平”识商”白盒DIKWP测评报告》通过数据、信息、知识…...
用Python将 PDF 中的表格提取为 Excel/CSV
*用Python将 PDF 中的表格提取为 Excel/CSV,*支持文本型 PDF 和 扫描件/图片型 PDF(需 OCR 识别)。程序包含以下功能: 1.自动检测 PDF 类型(文本 or 扫描件) 2.提取表格数据并保存为 Excel/CSV 3.处理多页…...
【工具】ncdu工具安装与使用指南:高效管理Linux磁盘空间
磁盘空间管理是Linux系统维护中的关键任务。当系统提示"磁盘空间不足"时,快速找出占用大量空间的文件和目录变得尤为重要。虽然传统的du命令可以完成这项工作,但其输出往往难以阅读和分析。本文介绍的ncdu(NCurses Disk Usage&…...
50天50个小项目 (Vue3 + Tailwindcss V4) ✨ | Progress Steps (步骤条)
📅 我们继续 50 个小项目挑战!—— Progress Steps 组件 仓库地址:https://github.com/SunACong/50-vue-projects 项目预览地址:https://50-vue-projects.vercel.app/ ✨ 组件目标 展示一个多步骤的进度条,指示当前所…...
数据分析—Excel数据清洗函数
在做数据分析的过程中,我们从数据库或者网页中获取的外部数据,通常是无法直接使用进行数据分析的。数据经常会有尾随的空格、奇奇怪怪的前缀和非打印字符等等问题,那么我们就需要先对数据进行清洗。下面介绍一些在数据清洗过程中常用的Excel函…...
CEF源码历史版本编译避坑指南
cef编译,网上查到的相关资料大多是官网上自动化编译的翻版,可能较新的版本按照那个步骤编译是没问题的。但是,对于历史版本的编译就会遇到各种坑。步骤大同小异,所以不再赘述,重点记录下针对历史版本编译要注意的点&am…...
看之前熟悉双亲委派加载机制,看之后了解双亲委派加载机制
今天面试被拷打双亲委派加载机制了,麻了。 首先要介绍双亲委派加载机制,就需要先搞明白啥是Java的类加载机制。 一.介绍 Java虚拟机(JVM)作为Java语言的核心运行环境,承担着将Java字节码转换为机器码并执行的重任。…...
std::ranges::views::stride 和 std::ranges::stride_view
std::ranges::views::stride 是 C23 中引入的一个范围适配器,用于创建一个视图,该视图只包含原始范围中每隔 N 个元素的元素(即步长为 N 的元素)。 基本概念 std::ranges::stride_view 是一个范围适配器,接受一个输…...
IBM Spectrum Scale (GPFS) 日常运维命令大全
目录 1. 集群管理命令 1.1 集群启动与停止 1.2 节点管理 1.3 集群配置查看与修改 2. 文件系统管理 2.1 文件系统创建与删除 2.2 文件系统挂载与卸载 2.3 文件系统属性修改 3. 存储池与磁盘管理 3.1 存储池管理 3.2 物理磁盘管理 3.3 磁盘故障处理 4. 性能监控与调优…...
IDE 使用技巧与插件推荐
在现代软件开发中,集成开发环境(IDE)不仅是代码编辑器,更是提升开发效率和代码质量的强大平台。本文将从基础使用技巧、高级功能、插件生态、定制化配置及实战案例五大方面,帮助你全面掌握 IDE,提高编程体验…...
【MySQL】使用文件进行交互
目录 准备工作 1.从文本文件中读取数据(导入) 1.1.CSV 文件 1.2.设置导入导出的路径 1.3.导入文件 1.4.将数据写入文本文件(导出) 2.从文件中读取并执行SQL命令 2.1.通过mysql监视器执行编写在文件里面的SQL语句 2.2.通过…...
Redis 学习笔记 5:分布式锁
Redis 学习笔记 5:分布式锁 在前文中学习了如何基于 Redis 创建一个简单的分布式锁。虽然在大多数情况下这个锁已经可以满足需要,但其依然存在以下缺陷: 事实上一般而言,我们可以直接使用 Redisson 提供的分布式锁而非自己创建。…...
【硬核数学】2. AI如何“学习”?微积分揭秘模型优化的奥秘《从零构建机器学习、深度学习到LLM的数学认知》
在上一篇中,我们探索了线性代数如何帮助AI表示数据(向量、矩阵)和变换数据(矩阵乘法)。但AI的魅力远不止于此,它最核心的能力是“学习”——从数据中自动调整自身,以做出越来越准确的预测或决策…...
[Java][Leetcode middle] 151. 反转字符串中的单词
思路挺简单的 自己想的,步骤挺复杂的 先统计处开头和结尾的空格数跳过开头这些空格,将单词放到数组中统计最后一个可能漏过的单词(例如:“hello word”,没有空格退出)倒序输出 public String reverseWor…...
力扣每日一题5-18
class Solution { public int colorTheGrid(int m, int n) { // 每一列可能的状态总数 每个单元有3可能 int totalState 1; for (int i 0; i < m; i) totalState * 3; // pre[k] 代表前一轮dp 状态为k 的方案总数 int [] pre new int [totalState]; // 初始化合法填色 的…...
leetcode 74. Search a 2D Matrix
题目描述 要求时间复杂度必须是log(m*n)。那么对每一行分别执行二分查找就不符合要求,这种做法的时间复杂度是m*log(n)。 方法一,对每一行分别执行二分查找: class Solution { public:bool searchMatrix(vector<vector<int>>&a…...
养生指南:重塑健康生活的实用方案
一、饮食:均衡膳食,滋养身心 三餐以 “轻盐、轻油、轻糖” 为准则。早餐搭配全麦三明治、无糖酸奶和一小把蓝莓,补充优质碳水与抗氧化物质;午餐选用糙米饭、白灼虾及蒜蓉西蓝花,保证蛋白质与膳食纤维摄入;…...
IPTABLES四表五链祥解
在Linux中,iptables 是一个强大的防火墙工具,用于管理和过滤网络流量。iptables 使用四个不同的表,每个表都包含多个链,来控制流量的处理。 一、iptables四个表 表名功能说明filter默认表,负责对进出数据包的过滤操作…...
嵌入式学习--江协51单片机day8
这个本来应该周末写的,可是一直想偷懒,只能是拖到周一了,今天把51结个尾,明天开始学32了。 学习内容LCD1602,直流电机,AD/DA,红外遥控 LCD1602 内部的框架结构 屏幕小于数据显示区ÿ…...
内网穿透与内网映射是什么?
在互联网技术快速迭代的当下,网络通信架构日益复杂,内网穿透与内网映射作为实现公网访问内网资源的核心技术,在企业办公、个人开发、智能家居等领域发挥着关键作用。尽管两者都致力于打通公网与内网的连接通道,但它们在底层原理、…...
51单片机点亮一个LED介绍
LED介绍 LED就是发光二极管,一般来说如果是直插式的,那就是长正短负,如果是贴片式的,那就带彩色标记是阴极,如果是三角形的,水平箭头指的就是阴极,通常一般的工作电压在3mA~20mA,当…...
WebRTC技术EasyRTC嵌入式音视频通信SDK助力智能电视搭建沉浸式实时音视频交互
一、方案概述 EasyRTC是一款基于WebRTC技术的开源实时音视频通信解决方案,具备低延迟、高画质、跨平台等优势。将EasyRTC功能应用于智能电视,能够为用户带来全新的交互体验,满足智能电视在家庭娱乐、远程教育、远程办公、远程医疗等多种场…...
uniapp 小程序 CSS 实现多行文本展开收起 组件
效果 组件 <template><!-- 最外层弹性盒子 --><div class"box" :style"boxStyle"><!-- 文本区域,动态类名控制展开/收起状态 --><div ref"textRef" :class"[text-cont, btnFlag ? text-unfold : t…...
嵌入式51单片机:C51
sbit TISCON^1的意思是定义TI为SCON的次低位(最低位标记为0,其次为1,再次为2)...
【回眸】香橙派zero2 嵌入式数据库SQLite
前言 SQLite介绍 安装SQLite3 SQLite 使用 创建数据库 创建一张表格 插入数据 查看数据库的记录 删除一条记录 更改一条记录 删除一张表 增加一列(性别) SQLite编程操作 前言 还有2个项目没更新完...披星戴月更新中... SQLite介绍 基于嵌入…...
vue3个生命周期解析,及setup
合理使用各生命周期,切勿乱用,不是所有东西都需要,合理使用可以提高效率和性能。 Vue 3 生命周期钩子详解 Vue 3的生命周期钩子分为以下几个阶段: onBeforeMount 调用时机:在组件挂载到DOM之前调用。使用场景…...
MySQL死锁:面试通关“三部曲”心法
想象一下,你的MySQL数据库里有两张桌子(数据表),比如一张“产品库存表”,一张“订单表”。现在来了两个顾客(并发事务),都想同时操作这两张桌子上的东西: 顾客A 先锁住了…...
Spring Boot 与 RabbitMQ 的深度集成实践(四)
实战案例 业务场景描述 在电商系统中,用户下单是一个核心业务操作。当用户成功下单后,系统需要执行一系列后续任务,如发送邮件通知用户订单已成功提交,更新库存信息以确保商品库存的准确性,以及记录订单相关的日志信…...
ES6详解
一、变量声明 let 与 const 块级作用域:替代 var 的函数作用域 const 声明常量(不可重新赋值,但对象属性可修改) if (true) {let x 10const PI 3.14 } console.log(x) // 报错 二、箭头函数 简写语法与 this 绑定 // 传统函数…...
C语言—字符函数和字符串函数
1.字符分类函数 字符控制函数:int iscntrl ( int c ); 控制字符通常不是可打印字符,该函数是用来判断参数是否为控制字符,需要的头文件为<ctype.h>标准ASCII码中,不可打印字符主要包括以下两类: 控制字符&…...
【LeetCode】大厂面试算法真题回忆(93)--优雅数组
题目描述 如果一个数组中出现次数最多的元素出现大于等于k次,被称为k-优雅数组,k也可以被称为优雅阈值。 例如,数组[1, 2, 3, 1, 2, 3, 1],它是一个3-优雅数组,因为元素1出现次数大于等于3次。数组[1, 2, 3, 1, 2]就不是一个3-优雅数组,因为其中出现次数最多的元素是1和…...
【MySQL成神之路】MySQL常用语法总结
目录 MySQL 语法总结 数据库操作 表操作 数据操作 查询语句 索引操作 约束 事务控制 视图操作 存储过程和函数 触发器 用户和权限管理 数据库操作 创建数据库: CREATE DATABASE database_name; 选择数据库: USE database_name; 删除数…...
机器学习第十六讲:K-means → 自动把超市顾客分成不同消费群体
机器学习第十六讲:K-means → 自动把超市顾客分成不同消费群体 资料取自《零基础学机器学习》。 查看总目录:学习大纲 关于DeepSeek本地部署指南可以看下我之前写的文章:DeepSeek R1本地与线上满血版部署:超详细手把手指南 K-me…...
多商户1.8.1版本前端问题优化集合指南
1、逛逛社区上传一张图时,进入详情页面显示不出来 修改路径:pages ---> discover ---> components ---> discoverDetails.vue 解读:这里是因为图片高度没有定义,图片没显示出来。修改如下: <!--逛逛类型为…...
基于正点原子阿波罗F429开发板的LWIP应用(1)——网络ping通
说在开头 正点原子F429开发板主芯片采用的是STM32F429IGT6,网络PHY芯片采用的是LAN8720A(V1)和YT8512C(V2),采用的是RMII连接,PHY_ADDR为0;在代码中将会对不同的芯片做出适配。 CubeMX版本:6.6.1; F4芯片组…...
第 1 章:数字 I/O 与串口通信(GPIO UART)
本章目标: 掌握 GPIO 的硬件原理、寄存器配置与典型驱动框架 深入理解 UART/USART 的帧格式、波特率配置、中断与 DMA 驱动 通过实战案例,将 GPIO 与 UART 结合,实现 AT 命令式外设控制 章节结构 GPIO 概述与硬件原理 GPIO 驱动实现:寄存器、中断与去抖 UART/USART 原理与帧…...