当前位置: 首页 > news >正文

[Linux]进程等待

文章目录

  • 3.进程等待
    • 3.1什么是进程等待
    • 3.2为什么要进程等待
    • 3.3如何进行进程等待?
      • 1.wait
      • 2.waitpid
        • 2.1函数的讲解
        • 2.2status的理解
        • 2.3代码理解
    • 3.4学后而思
      • 1.直接用全局变量获取子进程退出码可以吗?如下
      • 2.进程具有独立性 退出码是子进程的数据 父进程是如何拿到退出码的
      • 3.对内存泄露的认识
      • 4.对于上文中提到的系统设置的宏
      • 5.阻塞等待和非阻塞等待
      • 6.status相关宏加入后的简便写法
      • 7.非阻塞式等待的实现

3.进程等待

3.1什么是进程等待

进程等待: 进程的一种状态 父进程等待子进程退出时的一个过程

3.2为什么要进程等待

进程退出时 会关闭所有的文件描述符 释放在内存中的代码和数据 内核数据结构task_struct会暂时保留 里面存放着进程的退出状态以及统计信息等 父进程创建子进程 让子进程来处理事务 父进程需要得知子进程对任务的完成情况即上述的三种情况在这里插入图片描述且需要获取子进程的退出状态 如果子进程先于父进程退出 父进程则无法获取子进程的退出状态 子进程此时就会处于僵尸状态

所以进程等待的两大原因:

  1. 获取子进程的退出状态 避免出现僵尸进程 减少内存泄漏的概率[回收子进程资源]
  2. 获取子进程对任务的完成情况[获取子进程退出信息]

3.3如何进行进程等待?

在这里插入图片描述
在这里插入图片描述

1.wait

在这里插入图片描述

  1. 返回值:
    函数调用成功+目标进程成功改变状态 返回目标进程的pid
    失败返回-1
  2. 参数:
    输出型参数 获取子进程退出状态 不关心则可以设置成为NULL 表示不获取
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int code = 0;int main()
{pid_t id = fork();if (id < 0){perror("fork");exit(1);        //进程终止 结果不正确}else if (id == 0){//子进程int cnt = 5;while (cnt){printf("cnt: %d, 我是子进程, pid: %d, ppid : %d\n", cnt, getpid(), getppid());sleep(1);cnt--;}exit(0);     //子进程运行5s后正常退出}else{//父进程printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());sleep(6);pid_t ret = wait(NULL); //阻塞式的等待if (ret > 0){printf("成功等待子进程改变状态, ret = %d\n", ret);}while (1){printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());sleep(1);}}
}

在这里插入图片描述
在这里插入图片描述
当子进程运行5s完退出 父进程还在运行 此时子进程处于僵尸状态 当父进程运行了7s 父进程执行了wait()函数 我们可以理解为 父进程被调度到子进程的后面或阻塞队列 直到子进程从僵尸状态改变状态为终止态 父进程才继续运行 从man手册我们可以查到wait()函数成功等待返回值为被终止掉的子进程的PID 没有成功等待则返回-1

2.waitpid

2.1函数的讲解

waitpid(pid, NULL, 0) == wait(NULL);
在这里插入图片描述

返回值

  1. 正常返回 waitpid返回收集到的子进程的PID
  2. 没有已退出的子进程可收集即所有的子进程都还在运行 返回0 当options = WNOHANG
  3. 函数调用出错 返回-1 errno被设置成相应的值以指示错误原因(错误原因如目标子进程不存在)

pid_t pid:

传参数pid = -1 表示父进程要等待任意一个子进程 ==
传参数pid = Pid > 0 等待PID是Pid的进程

int* status:

  1. 输出型参数,由操作系统填充int status = 0; waitpid(pid, &status, 0); 操作系统会根据子进程PCB中的退出码和退出信号,将子进程的退出信息通过status反馈给父进程
  2. 如果传递NULL,表示不关心子进程的退出状态信息。
  3. 程序运行结果有三种 status并不是按照整数整体来使用的 它是按照比特位的方式 将32个比特位进行划分 此文只讲解低16位
  • 实际上为了更方便使用status 即不再是用位运算 OS提供了宏定义 调用即可
    WIFEXITED(status): wait if exited进程是否正常退出 若为正常终止子进程 返回真
    WEXITSTATUS(status): wait exit status获取进程退出码 若WIFEXITED非零==>即子进程正常退出(代码跑完了 不是被信号杀死的) 提取子进程退出码

int options:

默认为0 表示阻塞式等待
options = 1: 非阻塞式等待

  • 为了不再程序中写一些数字 通常用宏来指示特定含义
    如1就可用WNOHANG来指示 wait no hang 表示父进程进行非阻塞式等待
  1. 子进程都在运行 函数返回0 不进行持续等待
  2. 子进程正常退出 函数返回子进程PID
2.2status的理解

在这里插入图片描述
在这里插入图片描述

  1. 已知可以通过查看status来获取子进程的退出码进而得知子进程的运行结果/退出状态信息
  2. 简单来说 进程终止有两种方式 代码跑完和没跑完 代码跑完无论结果正确都叫正常终止 而如果是没跑完就终止即为异常终止 即程序崩溃或异常退出 (我们之前没学过进程 所以一直说程序崩溃或程序退出 其实正确的说法应为进程异常退出或进程崩溃)
  3. 进程崩溃/异常退出 本质是OS通过发信号的方式杀掉了进程 可以通过查看进程收到的信号编号来得知子进程收到了几号信号 如果进程崩溃/异常退出 退出码就没有意义 所以我们不仅要获取退出码 还要通过查看收到的信号编号是0(正常跑完) 1~31(异常/崩溃) 来判断退出码是否有意义
    在这里插入图片描述

OS都能发哪些信号呢? 1~31重点了解 32/33没有 34-64了解即可

在这里插入图片描述

  1. 父进程调用waitpid()函数之前定义一个int变量 调用waitpid()函数时 把这个变量地址传给waitpid()函数
  2. 这个函数不仅会等待子进程改变状态 还会把子进程的退出码和进程收到的信号编号以上图形式填充给你传过来的变量 即status 通过对status的位操作可以查看退出码和信号编号
  3. 程序异常,可能是内部代码有问题,也可能是外力杀掉 (子进程代码是否跑完是不确定的)
2.3代码理解
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int main()
{pid_t id = fork();if (id < 0){perror("fork");exit(1); }else if (id == 0){//子进程int cnt = 5;while (cnt){printf("cnt: %d, 我是子进程, pid: %d, ppid : %d\n", cnt, getpid(), getppid());sleep(1);cnt--;}exit(15);}else{//父进程printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());int status = 0;pid_t ret = waitpid(id, &status, 0); //阻塞式的等待if (ret > 0){   int signal_number = status & 0x7F;int exit_code = (status >> 8) & 0xFF;printf("等待子进程改变状态, ret: %d, 子进程收到的信号编号: %d, 子进程退出码: %d\n", ret, signal_number, exit_code);}}
}

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
信号编号取最低7位: 按位与0000 0000 0000 0000 0000 0000 0111 1111
退出码取次低8位: 按位与0000 0000 0000 0000 0000 0000 1111 1111
跟1按位与: 值不变 是0的是0 是1的是1
跟0按位与: 值全变0

通过可以修改代码 可以看到对应的信号

 子进程死循环 外部 kill -9 子进程pid : 杀死子进程signal_number = 9(SIGKILL:signal kill)exit_code = 0 sn!=0 退出码无意义cnt--;signal_number = 11(SIGSEGV:signal segmentation violation)exit_code = 0 sn!=0 退出码无意义int * p = NULL;*p = 100;signal_number = 8(SIGFPE:signal float point error) exit_code = 0 sn!=0 退出码无意义int a = 10;a /= 0;

3.4学后而思

1.直接用全局变量获取子进程退出码可以吗?如下

#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>int code = 0;
int main()
{pid_t id = fork();if (id < 0){perror("fork");exit(1); }else if (id == 0){//子进程int cnt = 5;while (cnt){printf("cnt: %d, 我是子进程, pid: %d, ppid : %d\n", cnt, getpid(), getppid());sleep(1);cnt--;}code = 15;exit(15);}else{//父进程printf("我是父进程, pid: %d, ppid: %d\n", getpid(), getppid());int status = 0;pid_t ret = waitpid(id, &status, 0); //阻塞式的等待if (ret > 0){   int signal_number = status & 0x7F;int exit_code = (status >> 8) & 0xFF;printf("等待子进程改变状态, ret: %d, 子进程收到的信号编号: %d, 子进程退出码: %d\n", ret, signal_number, exit_code);printf("code: %d\n", code);}}
}
  • 答案是不行. 已知父进程通过wait/waitpid可以拿到子进程的退出结果(退出码+收到的信号)
  • 1. 父进程 子进程均有全局变量code 初始值均为0 当子进程对code修改发生写时拷贝 父进程去访问code时是他自己的code仍为0
  • 2. 即便你显示传一个值把他作为退出码 你怎么直到他的退出码是几? 就是说 退出码是显示子进程的运行结果的 你不知道它结果正确或是发生错误 进而也就不知道他的退出码是几
  • 3. 即便你可以拿到退出码 也拿不到子进程收到的信号

2.进程具有独立性 退出码是子进程的数据 父进程是如何拿到退出码的

僵尸进程: 子进程已死亡等待父进程读取状态
在这里插入图片描述

  1. 僵尸进程的代码和数据已经释放但是PCB还存在 且 task_struct里的int exit_code, exit_signal:字段保留了进程退出时的退出码和退出信号(不仅是僵尸进程的PCB会保留 任何进程退出时都会把退出码和退出信号保留在)int exit_code, exit_signal:
  2. 那么系统调用接口wait/waitpid可以读取子进程的PCB里的int exit_code, exit_signal:把退出码和退出信号以位图形式存在status中 然后父进程就可以再反向获取
  3. 父进程没办法直接获取子进程的信息 但是可以通过调用系统接口来获取

3.对内存泄露的认识

  1. 用户写的C/C++程序中malloc/new的空间当进程终止 即便这些空间有泄露 OS也已经回收
  2. 子进程死亡父进程如果一直不读取子进程的退出状态 那么子进程将一直处于Z状态 子进程的PCB属于内核数据结构 需要OS来释放 如果父进程不管 将影响其他进程

4.对于上文中提到的系统设置的宏

  1. linux是用C语言写的 linux将自己一些系统调用接口二次封装成函数提供使用 除了一些系统调用接口/函数外 还有一些宏定义 比如WNOHANG
  2. 一些运维的程序员会说如这个进程hang住了之类的话 其实就是我们通常说的卡了 可能是在等待某种资源如网络/磁盘等 可能是进程太多了 CPU忙不过来了 这个进程hang住了 说这个进程hang住 就是说它要么在阻塞队列中 要么等待CPU调度

grep -ER 'WNOHANG' /usr/include/

grep -ER是一个Linux命令,用于在文件中递归搜索指定的字符串模式。其中,E表示使用扩展正则表达式,R表示递归搜索子目录。下面是一个例子:

假设我们有一个名为test.txt的文件,内容如下:

hello world
hello grep

我们可以使用grep -ER命令来搜索包含“hello”的行:

grep -ER "hello" test.txt

输出结果为:

test.txt:hello world
test.txt:hello grep

如果我们只想搜索以“h”开头的行,可以使用正则表达式“^h”:

grep -ER "^h" test.txt

输出结果为:

test.txt:hello world
test.txt:hello grep

如果我们想要搜索以“h”开头并且包含“o”的行,可以使用正则表达式“^h.*o”:

grep -ER "^h.*o" test.txt

输出结果为:

test.txt:hello world

5.阻塞等待和非阻塞等待

1先来回顾一下阻塞状态和挂起状态的知识

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

  1. 阻塞等待一般是在内核中阻塞 等待资源就绪去完成自己的工作 而一个进程一直在阻塞即资源一直无法就绪 这个进程的代码和数据就被移到磁盘SWAP分区了 即此进程被切换到挂起状态了
  2. 非阻塞等待: 父进程调用waitpid来等待子进程 如果子进程没有退出 他不像阻塞等待那样一直等着直至父进程被挂起还在等 他会直接返回 这样父进程在子进程运行的这一段时间还可以做其他事情 只不过中途需要再去看看子进程运行状况(下面讲怎么中途查看)
  3. 关于网络部分的代码 大部分时IO类别 阻塞和非阻塞接口非常多
    在这里插入图片描述

对进程等待的认识

  1. 在阻塞式等待下 只有子进程退出的时候,waitpid函数才会进行返回父进程只是挂起 仍然存活
  2. waitpid/wait 让进程退出具有一定的顺序性 将来可以让父进程进行更多的收尾工作.

6.status相关宏加入后的简便写法

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>int main() {pid_t id = fork();if (id < 0) {printf("创建子进程失败!\n");exit(1);}else if (id == 0){printf("我是子进程: pid: %d,我将异常退出!\n", getpid());int* p = NULL;*p = 1; // 引发异常}else {printf("我是父进程: pid: %d,我将耐心地等待子进程!\n", getpid());int status = 0;pid_t ret = waitpid(id, &status, 0);if (ret > 0){// 等待成功//printf("父进程等待成功, 退出码: %d, 退出信号: %d\n", (status>>8)&0xFF, status & 0x7F);if (WIFEXITED(status)){printf("子进程正常退出,退出码: %d\n", WEXITSTATUS(status));}else if (WIFSIGNALED(status)) {printf("子进程异常退出,信号编号: %d\n", WTERMSIG(status));}}}return 0;}

7.非阻塞式等待的实现

当子进程未退出 父进程可以处理其他事务 父进程要处理的事务不是一次就确定好的 可能在日后会有新的事务添加进来 为了封装/降低耦合 可以设置指针数组/回调函数 只需要编写Load函数 就可以增加父进程的任务单

typedef void (*pfunc)(); //函数指针类型std::vector<pfunc> vfunc; //函数指针数组void fun_one()
{printf("临时任务1\n");
}
void fun_two()
{printf("临时任务2\n");
}void Load()
{vfunc.push_back(fun_one);vfunc.push_back(fun_two);
}int main()
{pid_t id = fork();if(id == 0){// 子进程int cnt =  5;while(cnt){printf("我是子进程: %d\n", cnt--);sleep(1);}exit(123); // 123: 测试 无意义}else{int quit = 0;while(!quit){int status = 0;pid_t res = waitpid(-1, &status, WNOHANG); //非阻塞方式等待if(res > 0){//函数调用成功 && 子进程已退出printf("等待子进程退出成功, 退出码: %d\n", WEXITSTATUS(status));quit = 1;}else if( res == 0 ){//函数调用成功 && 子进程未退出printf("子进程未退出,父进程可以处理其他事务\n");if(vfunc.empty()) Load();for(auto func : vfunc){func();}}else{//等待失败printf("函数调用失败!\n");quit = 1;}sleep(1);}}
}

相关文章:

[Linux]进程等待

文章目录 3.进程等待3.1什么是进程等待3.2为什么要进程等待3.3如何进行进程等待?1.wait2.waitpid2.1函数的讲解2.2status的理解2.3代码理解 3.4学后而思1.直接用全局变量获取子进程退出码可以吗?如下2.进程具有独立性 退出码是子进程的数据 父进程是如何拿到退出码的3.对内存…...

Project DESFT 白皮书中文版——应用于普惠金融的可信数字凭证解决方案

1. 概述 Project DESFT 是由 Solv 基金会与 zCloak Network 联合设计孵化&#xff0c;以跨境贸易和金融服务为场景的分布式可信数字凭证解决方案&#xff08;Distributed Trusted Digital Credential Solution&#xff09;&#xff0c;项目获得新加坡金管局&#xff08;Monetar…...

907. 子数组的最小值之和 --力扣 --JAVA

题目 给定一个整数数组 arr&#xff0c;找到 min(b) 的总和&#xff0c;其中 b 的范围为 arr 的每个&#xff08;连续&#xff09;子数组。 由于答案可能很大&#xff0c;因此 返回答案模 10^9 7 。 解题思路 找到以当前值为最小值所能组成的子数组&#xff1b;若存在两个相同…...

3D模型渲染导致电脑太卡怎么办?

在线工具推荐&#xff1a; 三维数字孪生场景工具 - GLTF/GLB在线编辑器 - Three.js AI自动纹理化开发 - YOLO 虚幻合成数据生成器 - 3D模型在线转换 - 3D模型预览图生成服务 1、什么是3D渲染&#xff1f; 3D渲染是指通过计算机图形学技术将三维模型转化为二维图像的过程…...

构建个人代理池:使用GitHub项目proxy_pool的搭建配置及代码接口详解

手把手教你搭建代理IP池&#xff1a; 项目简介&#xff1a; ​ 爬虫代理IP池项目,主要功能为定时采集网上发布的免费代理验证入库&#xff0c;定时验证入库的代理保证代理的可用性&#xff0c;提供API和CLI两种使用方式。同时你也可以扩展代理源以增加代理池IP的质量和数量。…...

Pytorch进阶教学——训练一个图像分类模型(GPU)

目录 1、前言 2、数据集介绍 3、获取数据 4、创建网络 5、训练模型 6、测试模型 6.1、测试整个模型准确率 6.2、测试单张图片 1、前言 编写一个可以分类蚂蚁和蜜蜂图片的模型&#xff0c;使用数据集对卷积神经网络进行训练。训练后的模型可以对蚂蚁或蜜蜂的图片进行…...

Docker Swarm总结+CI/CD Devops、gitlab、sonarqube以及harbor的安装集成配置(3/5)

博主介绍&#xff1a;Java领域优质创作者,博客之星城市赛道TOP20、专注于前端流行技术框架、Java后端技术领域、项目实战运维以及GIS地理信息领域。 &#x1f345;文末获取源码下载地址&#x1f345; &#x1f447;&#x1f3fb; 精彩专栏推荐订阅&#x1f447;&#x1f3fb;…...

Linux:windows 和 Linux 之间文本格式转换

背景 在 Windows 上编辑的文件&#xff0c;放到 Linux 平台&#xff0c;有时会出现奇怪的问题&#xff0c;其中有一个是 ^M 引起的&#xff0c;例如这种错误&#xff1a; /bin/bash^M: bad interpreter 这个问题相信大家也碰到过&#xff0c;原因是 Windows 和 Linux 关于换行的…...

VBA技术资料MF88:测试Excel文件名是否有效

我给VBA的定义&#xff1a;VBA是个人小型自动化处理的有效工具。利用好了&#xff0c;可以大大提高自己的工作效率&#xff0c;而且可以提高数据的准确度。我的教程一共九套&#xff0c;分为初级、中级、高级三大部分。是对VBA的系统讲解&#xff0c;从简单的入门&#xff0c;到…...

u8g2图形库——丝滑菜单制作

目录 一、实物效果展示 二、丝滑菜单实现原理 三、代码开源 1.ui_bmp.h 2.ui.h 3.ui.c 一、实物效果展示 u8g2图形库——丝滑菜单制作 二、丝滑菜单实现原理 int ui_run(short *a,short *a_tag,uint8_t step,uint8_t slow_cnt) //UI滑动效果 {uint8_t temp;…...

Go 异常处理流程

在 Go 语言中&#xff0c;panic、recover 和 defer 是用于处理异常情况的关键字。它们通常一起使用来实现对程序错误的处理和恢复。 1. defer 语句 defer 用于在函数返回之前执行一段代码。被 defer 修饰的语句或函数会在包含 defer 的函数执行完毕后执行。defer 常用于资源清…...

ubuntu20.04安装tensorRT流程梳理

目标&#xff1a;先跑demo&#xff0c;再学习源码 step1, 提前准备好CUDA环境 安装CUDA&#xff0c;cuDNN 注意&#xff0c;CUDA&#xff0c;cuDNN需要去官网下载.run和tar文件安装&#xff0c;否则在下面step4 make命令会报找不到cuda等的错误&#xff0c;具体安装教程网上…...

数字孪生技术:提升UI交互性与个性化设计

随着数字化时代的到来&#xff0c;数字孪生技术正在逐渐改变我们的生活和工作方式。数字孪生是一种复制现实世界系统或实体的技术&#xff0c;通过创建数字模型来模拟现实世界中的各种行为和事件。这种技术不仅为人们提供了一个全新的视角来看待和解决问题&#xff0c;同时也为…...

外包干了5个月,技术退步明显.......

先说一下自己的情况&#xff0c;大专生&#xff0c;18年通过校招进入武汉某软件公司&#xff0c;干了接近4年的功能测试&#xff0c;今年年初&#xff0c;感觉自己不能够在这样下去了&#xff0c;长时间呆在一个舒适的环境会让一个人堕落! 而我已经在一个企业干了四年的功能测…...

嵌入式常见的几种接口

嵌入式开发中&#xff0c;常见的外设通信接口/协议有SPI&#xff0c;I2C&#xff0c;UART三种&#xff0c;本文先分三个部分对SPI&#xff0c;I2C&#xff0c;UART进行介绍&#xff0c;最后对这三种协议进行比较。 1 SPI 1.1 SPI的简介 SPI&#xff08;Serial Peripheral …...

基于SpringBoot+Redis的前后端分离外卖项目-苍穹外卖(七)

分页查询、删除和修改菜品 1. 菜品分页查询1.1 需求分析和设计1.1.1 产品原型1.1.2 接口设计 1.2 代码开发1.2.1 设计DTO类1.2.2 设计VO类1.2.3 Controller层1.2.4 Service层接口1.2.5 Service层实现类1.2.6 Mapper层 1.3 功能测试1.3.2 前后端联调测试 2. 删除菜品2.1 需求分析…...

Grafana采用Nginx反向代理,部分功能报错‘Origin not allowed’ behind proxy

只有部分功能会有这个提示&#xff0c;比如修改密码啥的&#xff0c;网上找了下&#xff0c;官方找到了答案&#xff1a;https://community.grafana.com/t/after-update-to-8-3-5-origin-not-allowed-behind-proxy/60598 有个回复是这样的&#xff1a; tl:dr: try adding prox…...

请大数据把奥威BI分析工具推给每一个财务!

这个财务指标怎么算&#xff1f;那些数据什么时候能拿到&#xff1f;看完报表&#xff0c;发现某部门上个月的支出涨幅过大&#xff0c;想了解原因怎么办&#xff1f;……财务人&#xff0c;你是不是每个月都把时间消耗在这些事情上了&#xff1f;那你可得快接住这个BI大数据分…...

知乎禁止转载的回答怎么复制做笔记?

问题 对于“禁止转载”的回答&#xff0c;右键复制是不行的&#xff0c;ctrl-c也不行&#xff0c;粘贴之后都是当前回答的标题。稍微看了代码&#xff0c;应该是对copy事件进行了处理。不过这样真的有用吗&#xff0c;真是防君子不防小人&#xff0c;只是给收集资料增加了许多…...

pta找鞍点—C语言

7-13 找鞍点 分数 300 全屏浏览题目 切换布局 作者 C课程组 单位 浙江大学 一个矩阵元素的“鞍点”是指该位置上的元素值在该行上最大、在该列上最小。 本题要求编写程序&#xff0c;求一个给定的n阶方阵的鞍点。 输入格式&#xff1a; 输入第一行给出一个正整数n&#xff08;1…...

网络编程(Modbus进阶)

思维导图 Modbus RTU&#xff08;先学一点理论&#xff09; 概念 Modbus RTU 是工业自动化领域 最广泛应用的串行通信协议&#xff0c;由 Modicon 公司&#xff08;现施耐德电气&#xff09;于 1979 年推出。它以 高效率、强健性、易实现的特点成为工业控制系统的通信标准。 包…...

多模态2025:技术路线“神仙打架”,视频生成冲上云霄

文&#xff5c;魏琳华 编&#xff5c;王一粟 一场大会&#xff0c;聚集了中国多模态大模型的“半壁江山”。 智源大会2025为期两天的论坛中&#xff0c;汇集了学界、创业公司和大厂等三方的热门选手&#xff0c;关于多模态的集中讨论达到了前所未有的热度。其中&#xff0c;…...

React Native 导航系统实战(React Navigation)

导航系统实战&#xff08;React Navigation&#xff09; React Navigation 是 React Native 应用中最常用的导航库之一&#xff0c;它提供了多种导航模式&#xff0c;如堆栈导航&#xff08;Stack Navigator&#xff09;、标签导航&#xff08;Tab Navigator&#xff09;和抽屉…...

【android bluetooth 框架分析 04】【bt-framework 层详解 1】【BluetoothProperties介绍】

1. BluetoothProperties介绍 libsysprop/srcs/android/sysprop/BluetoothProperties.sysprop BluetoothProperties.sysprop 是 Android AOSP 中的一种 系统属性定义文件&#xff08;System Property Definition File&#xff09;&#xff0c;用于声明和管理 Bluetooth 模块相…...

Mac软件卸载指南,简单易懂!

刚和Adobe分手&#xff0c;它却总在Library里给你写"回忆录"&#xff1f;卸载的Final Cut Pro像电子幽灵般阴魂不散&#xff1f;总是会有残留文件&#xff0c;别慌&#xff01;这份Mac软件卸载指南&#xff0c;将用最硬核的方式教你"数字分手术"&#xff0…...

C++中string流知识详解和示例

一、概览与类体系 C 提供三种基于内存字符串的流&#xff0c;定义在 <sstream> 中&#xff1a; std::istringstream&#xff1a;输入流&#xff0c;从已有字符串中读取并解析。std::ostringstream&#xff1a;输出流&#xff0c;向内部缓冲区写入内容&#xff0c;最终取…...

九天毕昇深度学习平台 | 如何安装库?

pip install 库名 -i https://pypi.tuna.tsinghua.edu.cn/simple --user 举个例子&#xff1a; 报错 ModuleNotFoundError: No module named torch 那么我需要安装 torch pip install torch -i https://pypi.tuna.tsinghua.edu.cn/simple --user pip install 库名&#x…...

springboot整合VUE之在线教育管理系统简介

可以学习到的技能 学会常用技术栈的使用 独立开发项目 学会前端的开发流程 学会后端的开发流程 学会数据库的设计 学会前后端接口调用方式 学会多模块之间的关联 学会数据的处理 适用人群 在校学生&#xff0c;小白用户&#xff0c;想学习知识的 有点基础&#xff0c;想要通过项…...

Redis:现代应用开发的高效内存数据存储利器

一、Redis的起源与发展 Redis最初由意大利程序员Salvatore Sanfilippo在2009年开发&#xff0c;其初衷是为了满足他自己的一个项目需求&#xff0c;即需要一个高性能的键值存储系统来解决传统数据库在高并发场景下的性能瓶颈。随着项目的开源&#xff0c;Redis凭借其简单易用、…...

PostgreSQL——环境搭建

一、Linux # 安装 PostgreSQL 15 仓库 sudo dnf install -y https://download.postgresql.org/pub/repos/yum/reporpms/EL-$(rpm -E %{rhel})-x86_64/pgdg-redhat-repo-latest.noarch.rpm# 安装之前先确认是否已经存在PostgreSQL rpm -qa | grep postgres# 如果存在&#xff0…...