【Linux 12】进程控制

文章目录

  • 🌈 Ⅰ 进程创建
    • 01. fork 函数介绍
    • 02. 写时拷贝
    • 03. fork 常规用法
    • 04. fork 调用失败的原因
  • 🌈 Ⅱ 进程终止
    • 01. 进程退出场景
    • 02. 常见退出方法
  • 🌈 Ⅲ 进程等待
    • 01. 进程等待必要性
    • 02. 进程等待的方法
      • 2.1 wait 方法
      • 2.2 waitpid 方法
    • 03. 获取子进程状态
      • 3.1 使用位运算获取退出信息
      • 3.2 使用宏获取退出信息
    • 04. 非阻塞轮询访问
  • 🌈 Ⅳ 进程程序替换
    • 01. 替换原理
    • 02. 替换函数
    • 03. 函数解释
    • 04. 命名理解
    • 05. 函数用例
      • 5.1 execl 函数使用示例
      • 5.2 execlp 函数使用示例
      • 5.3 execle 函数使用示例
      • 5.4 execv 函数使用示例
      • 5.5 execvp 函数使用示例
      • 5.6 execvpe 函数使用示例

🌈 Ⅰ 进程创建

01. fork 函数介绍

fork 函数介绍

  • 在 Linux 中可以使用 fork 函数从已经存在的进程中创建新进程
  • 新的进程为子进程,而原进程为父进程。
#include <unistd.h>pid_t fork(void);

fork 的返回值

  • fork 函数有三种返回值
返回值状态说明
返回值 < 0表示子进程创建失败
返回值 = 0表示当前进程为子进程
返回值 > 0该返回值是子进程的 pid,当前进程为父进程,其持有子进程 pid

02. 写时拷贝

1. 什么是写时拷贝

  • 通常情况下,父子进程代码共享,父子进程在不进行写入操作时,其数据也是共享的,当任意一方试图写入时,便会以写时拷贝的方式各自留存一份副本。

在这里插入图片描述

2. 为什么写时拷贝

  1. 创建子进程时,子进程不一定要用到父进程的全部数据,因此不需要直接将父进程的所有数据全部拷贝一份给子进程,而是在要进行修改时再从父进程那拷贝这部分数据即可即可。
  2. 子进程正在尝试对这部分数据进行修改,但是父进程不打算修改这部分共享的数据,因此子进程就必须将旧数据拷贝一份进行修改。

03. fork 常规用法

父子进程执行不同的代码段

  • 判断 fork 的返回值,从而让不同的进程去执行不同的代码段。
#include <unistd.h>
#include <iostream>
#include <sys/types.h>using std::cout;
using std::endl;int main()
{pid_t id = fork();	// 创建进程if (0 == id)    	// 执行子进程部分代码  cout << "I am child process" << endl;  else if(id < 0) 	// 子进程的创建失败了cout << "process creation failure" << endl;else            	// 执行父进程部分代码cout << "I am parent process" << endl;return 0;                               
}

04. fork 调用失败的原因

  1. 系统中进程太多,再 fork 时就会内存不足从而导致进程创建失败。
  2. 实际用户的进程数超过了限制,系统对每个用户能创建的进程数量是有上限的。

🌈 Ⅱ 进程终止

01. 进程退出场景

  1. 代码运行完毕,结果正确。
  2. 代码运行完毕,结果错误。
  3. 代码没执行完,异常终止。

02. 常见退出方法

1. 使用 _exit 函数退出进程

  • 函数原型
    • 该函数是个系统调用。
    • _exit 函数 不支持 刷新缓冲区。
#include <unistd.h>// 用于终止进程,status 表示进程退出时的退出码
void _exit(int status);
  • 函数用例
int main()
{while (true){cout << "I am a process, pid: " << getpid() << endl;_exit(3);}return 0;
}

在这里插入图片描述

2. 使用 exit 函数退出进程

  • 函数原型
    • 该函数本质上是执行了系统调用的 _exit 函数。
    • exit 函数 支持 刷新缓冲区。
#include <stdlib.h>// 用于终止进程,status 表示进程退出时的退出码
void exit(int status);
  • 函数用例
    • 在代码的任何地方调用 exit 函数都表示退出进程。
int main()
{while (true){cout << "I am a process, pid: " << getpid() << endl;exit(2);}return 0;
}

在这里插入图片描述

3. 使用 return 退出进程

  • 执行 return n 等同于执行 exit(n),因为调用 main 的运行时函数会将 main 函数的返回值当做 exit 函数的参数。
  • return 返回的值为 0 则表示进程执行成功,反之则表示进程执行失败。且 0 之外的不同数字能够表示进程执行失败的不同原因。
#include <cstdio>
#include <cstdlib>
#include <unistd.h>int main()
{return 3;
}
  • 使用 echo $? 能够查看最近一次执行的进程的退出码。

在这里插入图片描述

🌈 Ⅲ 进程等待

01. 进程等待必要性

为什么要进程等待

  • 子进程在退出时,父进程如果对其不管不问,就会造成僵尸问题,从而造成内存泄漏。
  • 进程如果进入了僵尸状态,就没人能够将其干掉,无法杀死一个已经死去的进程。
  • 父进程需要知道派发给子进程的任务完成得如何,结果是否正确,是否正常退出等。
  • 因此父进程需要通过进程等待得方式,去回收子进程所占用得资源,并且获取子进程退出的相关信息。

进程等待能做什么

  1. 父进程能够通过 wait 方法,回收子进程的资源 (必然)。
  2. 父进程能够通过 wait 方法,获取子进程的退出信息 (退出码、退出信号) (可选)。

02. 进程等待的方法

2.1 wait 方法

wait 函数原型

#include <sys/wait.h>
#include <sys/types.h>pid_t wait (int *status);	// 输出型参数,能通过该参数获取子进程退出结果,默认置为空即可

wait 函数功能

  • 父进程阻塞等待任意一个子进程,子进程不退则父进程不退。
  • 该函数能够回收子进程资源,以及获取子进程的 pid。

wait 函数返回值

  • 返回值 > 0:返回值是所等待的子进程的 pid。
  • 返回值 < 0:等待失败。

函数用例

  • 演示父进程回收子进程资源
#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>using std::cout;
using std::endl;int main()
{// 创建子进程pid_t id = fork();// 子进程执行自己的代码if (0 == id) {for (size_t i = 0; i < 5; i++){cout << "child process is running, pid: " << getpid() << ", ppid: " << getppid() << endl;sleep(1);}cout << "子进程准备退出,马上变僵尸" << endl;exit(0);}cout << "父进程休眠" << endl;sleep(8);cout << "父进程开始回收僵尸进程" << endl;// 父进程阻塞等待任意子进程pid_t rid = wait(nullptr);// 等待成功,rid 是子进程的 pidif (rid > 0)cout << "wait success, rid: " << rid << endl;cout << "父进程回收僵尸进程成功" << endl;sleep(1);return 0;
}

在这里插入图片描述

2.2 waitpid 方法

waitpid 函数原型

#include <sys/wait.h>
#include <sys/types.h>pid_t waitpid (pid_t pid, int *status, int options);

waitpid 函数参数

  • pid_t pid
    • pid 为 -1 时,等待任意一个子进程,功能等同 wait。
    • pid > 0 时,指定具体想要等待的那个进程。
  • int *status:输出型参数,能通过该参数获取子进程退出结果,默认置为空即可
  • int options:指定父进程的等待方式,为 0 则让父进程进行阻塞等待,非 0 则进行非 阻塞等待。

waitpid 函数功能

  • 回收子进程资源,解决僵尸问题的同时,还能够获取子进程退出信息

waitpid 函数返回值

  • 返回值 > 0:返回值是所等待的子进程的 pid。
  • 返回值 < 0:等待失败。

函数用例

#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>using std::cout;
using std::endl;int main()
{// 创建子进程pid_t id = fork();// 子进程if (0 == id){for (size_t i = 0; i < 5; i++){cout << "child process is running, pid: "<< getpid() << ", ppid: " << getppid() << endl;sleep(1);}cout << "子进程准备退出,马上变僵尸" << endl;exit(1);}cout << "父进程休眠" << endl;sleep(8);cout << "父进程开始回收僵尸进程" << endl;// 用来获取子进程的退出信息 (退出码 + 退出信号)int status;// 父进程阻塞等待由 id 指定的子进程pid_t rid = waitpid(id, &status, 0);// 等待成功,rid 是子进程的 pidif (rid > 0){cout << "等待成功, 子进程 pid: " << rid<< " 子进程退出码: " << status << endl;}sleep(1);return 0;
}

在这里插入图片描述

  • 这里子进程的退出码之所以不是 1 而是 256 的原因是变量 status 装着退出码和退出信号两部分信息。
  • 在 status 中,只有低 16 位用于存储 退出码 + 退出信号。
    • 正常退出时,这 16 位的高 8 位存储退出码,低 8 位默认全部存 0。
    • 异常退出时,这 16 位的高 8 位不使用,低 7 位 存储终止信号,还有 1 位是 core dump 标志位。
  • 当前子进程没有出异常,因此为 1 的退出码在内存中是这样存储的 0000 0001 0000 0000,因此按照整形的方式打印出的结果就是 256 了。

在这里插入图片描述

03. 获取子进程状态

3.1 使用位运算获取退出信息

  1. 获取子进程退出码:将获取的状态码右移 8 位再和 0xFF 相与即可,(status >> 8) & 0xFF
  2. 获取子进程退出信号:将获取的状态码和 0x7F 相与即可,status & 0x7F
// 1. 使用位运算获取退出信息
#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>using std::cout;
using std::endl;int main()
{// 创建子进程pid_t id = fork();// 子进程if (0 == id){for (size_t i = 0; i < 5; i++){cout << "child process is running, pid: "<< getpid() << ", ppid: " << getppid() << endl;sleep(1);}cout << "子进程准备退出,马上变僵尸" << endl;exit(1);}cout << "父进程休眠" << endl;sleep(8);cout << "父进程开始回收僵尸进程" << endl;// 用来获取子进程的退出信息 (退出码 + 退出信号)int status;// 父进程阻塞等待由 id 指定的子进程pid_t rid = waitpid(id, &status, 0);// 等待成功,rid 是子进程的 pidif (rid > 0){cout << "等待成功, 子进程 pid: " << rid<< " 子进程退出信号: " << (status & 0x7F)<< " 子进程退出码: " << ((status >> 8) & 0xFF) << endl;}sleep(1);return 0;
}

在这里插入图片描述

3.2 使用宏获取退出信息

  1. WIFEXITED(status):如果子进程是正常退出,则该宏的值位真。(用以查看进程是否是正常退出)
  2. WEXITSTATUS(status):如果 WIFEXITED 的值为真,则提取子进程退出码。(用以查看进程的退出码)
// 2. 使用宏获取退出信息
#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>using std::cout;
using std::endl;int main()
{// 创建子进程pid_t id = fork();// 子进程if (0 == id){for (size_t i = 0; i < 5; i++){cout << "child process is running, pid: "<< getpid() << ", ppid: " << getppid() << endl;sleep(1);}cout << "子进程准备退出,马上变僵尸" << endl;exit(1);}cout << "父进程休眠" << endl;sleep(8);cout << "父进程开始回收僵尸进程" << endl;// 用来获取子进程的退出信息 (退出码 + 退出信号)int status;// 父进程阻塞等待由 id 指定的子进程pid_t rid = waitpid(id, &status, 0);// 等待成功,rid 是子进程的 pidif (rid > 0){// 子进程是正常退出的, 用户只需要关心退出码即可if (WIFEXITED(status)) {cout << "等待成功, 子进程 pid: " << rid<< " 子进程退出码: " << WEXITSTATUS(status) << endl;}}sleep(1);return 0;
}

在这里插入图片描述

04. 非阻塞轮询访问

  • 父进程在阻塞等待子进程时,父进程这时候什么事的做不了,只能等待子进程退出才能去做自己的事,效率太低,非阻塞轮询访问就因此出现。

1. 非阻塞轮询访问

  • 父进程每隔一段时间就执行一次系统调用,判断子进程是否退出。
  • 如果子进程没有退出,则父进程子继续执行自己的任务。
  • 如果子进程已经退出,则父进程回收子进程的资源及退出信息。

2. 如何使用非阻塞等待

  • 将 waitpid 函数的第三个参数改成非 0 值即可,一般是用 WNOHANG 宏作为参数。
pid_t rid = waitpid(id, &status, WNOHANG);

3. 非阻塞轮询访问实例

// 非阻塞轮询访问
#include <iostream>
#include <sys/wait.h>
#include <sys/types.h>using std::cout;
using std::endl;int main()
{// 创建子进程pid_t id = fork();// 子进程if (0 == id){for (size_t i = 0; i < 5; i++){cout << "子进程正在运行, pid: "<< getpid() << ", ppid: " << getppid() << endl;sleep(1);}exit(1);}// 用来获取子进程的退出信息 (退出码 + 退出信号)int status = 0;while (true){// 父进程以非阻塞状态等待子进程退出pid_t rid = waitpid(id, &status, WNOHANG);if (rid > 0)        // 等待成功, 子进程已经退出{cout << "等待成功, 子进程 pid: " << rid<< " 子进程退出码: " << WEXITSTATUS(status) << endl;break;          // 不需要再执行循环等待了}else if (0 == rid)  // 等待成功,子进程还没退出, 父进程可以执行其他任务{cout << "子进程还未退出, 父进程执行其他任务" << endl;// ... 父进程在等待子进程退出期间要执行的任务} else                // 等待失败{perror("waitpid");break;}sleep(1);           // 父进程每隔 1 秒查询一次子进程是否退出}sleep(1);return 0;
}

在这里插入图片描述

🌈 Ⅳ 进程程序替换

01. 替换原理

1. 什么是程序替换

  • 在用 fork 创建子进程之后,子进程执行的是和父进程相同的程序 (但是有可能执行的是不同的代码分支),这样创建子进程就没多大意义了。
  • 如果创建的子进程想执行其他程序的代码,需要调用 exec 系列函数执行其他程序,这种操作被称之为程序替换
  • 当进程调用 exec 系列函数时,该进程的用户空间代码和数据会完成被新的程序所替换,从一个新的程序启动例程看i是执行。
  • 调用 exec 系列函数不会创建新进程,因此在调用 exec 系列函数的前后该进程的 pid 不变。

02. 替换函数

  • exec 系列函数总共有 6 种,都是以 exec 开头的,这些函数统称为 exec 函数。
#include <unistd.h>int execl  (const char *path, const char *arg, ...);
int execlp (const char *file, const char *arg, ...);
int execle (const char *path, const char *arg, ..., char *const envp[]);
int execv  (const char *path, char *const argv[]);
int execvp (const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[], char *const envp[]);

03. 函数解释

1. execl 函数

int execl (					// 替换失败时返回 -1,成功时没有返回值const char *path, 		// 进程要执行的程序的所在路径const char *arg, ...);	// 参数列表,表示如何执行该程序 (命令行怎么写参数就怎么传), 以 NULL 结尾

2. execlp 函数

int execlp (const char *file, 		// 要执行的程序名,无需提供程序路径,会自动去 PATH 环境变量中查找const char *arg, ...);	// 参数列表,表示如何执行该程序 (命令行怎么写参数就怎么传)

3. execle 函数

int execle (const char *path, 		// 进程要执行的程序的所在路径const char *arg, ..., 	// 参数列表,表示如何执行该程序 (命令行怎么写参数就怎么传)char *const envp[]);	// 自己提供环境变量给子进程

4. execv 函数

int execv (const char *path, 		// 进程要执行的程序的所在路径char *const argv[]);	// 该参数是个指针数组,用以存储参数,表示如何执行该程序

5. execvp 函数

int execvp (const char *file, 		// 要执行的程序的程序名,无需提供程序路径char *const argv[]);	// 参数数组,表示如何执行该程序

6. execvpe 函数

int execvpe(const char *file, 		// 要执行的程序的程序名,无需提供程序路径char *const argv[], 	// 参数数组,表示如何执行该程序char *const envp[]);	// 自己提供环境变量给子进程

04. 命名理解

解释 exec 之外的每个字母所表示的含义

  • l (list) : 表示参数采用列表。
  • v (vector) : 表示参数用数组。
  • p (path) : 有 p 表示会自动搜索环境变量 PATH。
  • e (env) : 表示需要用户自己维护环境变量。
函数名参数格式是否带路径是否使用当前环境变量
execl列表否,需要自己提供程序路径
execlp列表
execle列表否,需要自己提供程序路径否,需要自己配置环境变量
execv数组否,需要自己提供程序路径
execvp数组
execve数组否,需要自己提供程序路径否,需要自己配置环境变量

05. 函数用例

5.1 execl 函数使用示例

  • 让当前进程以 ls -a -l 的方式执行 /usr/bin/ls 路径所指定的程序。
int main()
{cout << "程序替换开始" << endl;// 当前进程以 ls -a -l 的方式跑去执行 /usr/bin 目录下的 ls 程序execl("/usr/bin/ls", "ls", "-a", "-l", nullptr);cout << "程序替换结束" << endl;return 0;
}

在这里插入图片描述

5.2 execlp 函数使用示例

  • 用第一个参数作为要执行的程序的程序名,无需自己补全程序路径。
int main()
{pid_t id = fork();// 由子进程去进程程序替换if (0 == id){cout << "程序替换开始" << endl;// 以 ls -a -l 的方式执行 ls 程序,无需自己补全程序路径execlp("ls", "ls", "-a", "-l", nullptr);cout << "程序替换结束" << endl;exit(1);}// 父进程阻塞等待子进程pid_t rid = waitpid(id, nullptr, 0);if (rid > 0)cout << "等待成功" << endl;return 0;
}

在这里插入图片描述

5.3 execle 函数使用示例

// 演示 execle 函数
int main()
{// 定义环境变量char *const env[] = { (char*)"hello=world", (char*)"PAHT=/" };pid_t id = fork();// 由子进程去进程程序替换if (0 == id){   cout << "程序替换开始" << endl;// 以 mytest 的方式执行 当前目录下的 mytest.exe 文件execle("./mytest.exe", "mytest", NULL, env);}pid_t rid = waitpid(id, nullptr, 0);if (rid > 0)cout << "等待成功" << endl;return 0;
}

5.4 execv 函数使用示例

// 演示 execv 函数
int main()
{pid_t id = fork();// 由子进程去进程程序替换if (0 == id){   // 存储参数的指针数组char *argv[] = { (char*)"ls", (char*)"-a", (char*)"-l" };cout << "程序替换开始" << endl;// 以 ls -a -l 的方式执行指定路径下的程序,execv("/usr/bin/ls", argv);}pid_t rid = waitpid(id, nullptr, 0);if (rid > 0)cout << "等待成功" << endl;return 0;
}

在这里插入图片描述

5.5 execvp 函数使用示例

// 演示 execvp 函数
int main()
{cout << "演示 execvp 函数" << endl;pid_t id = fork();// 由子进程去进程程序替换if (0 == id){   // 存储参数的指针数组char *argv[] = { (char*)"ls", (char*)"-a", (char*)"-l" };cout << "程序替换开始" << endl;// 以 ls -a -l 的方式执行 ls 程序,无需指定程序所在路径execvp("ls", argv);}pid_t rid = waitpid(id, nullptr, 0);if (rid > 0)cout << "等待成功" << endl;return 0;
}

在这里插入图片描述

5.6 execvpe 函数使用示例

// 演示 execve 函数
int main()
{// 定义环境变量char *const env[] = { (char*)"hello=world", (char*)"PAHT=/" };pid_t id = fork();// 由子进程去进程程序替换if (0 == id){   // 存储参数的指针数组char *argv[] = { "mytest" };cout << "程序替换开始" << endl;// 以 mytest 的方式执行 当前目录下的 mytest.exe 文件execve("./mytest.exe", argv, env);}pid_t rid = waitpid(id, nullptr, 0);if (rid > 0)cout << "等待成功" << endl;return 0;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/854453.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

关于禁止word的无用插入模式

这是我的word版本号 点击左上角文件选项 找到左侧最下方的选项 点击高级 把这两个叉掉

第二十篇——去除噪音:如何获得更多更准确的信息?

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 噪音的原理&#xff0c;换一个维度来看就会很清晰了&#xff1b;通俗易懂…

element-ui将组件默认语言改为中文

在main.js中加入以下代码即可 // 引入 Element Plus 及其样式 import ElementPlus from element-plus import element-plus/dist/index.css// 引入中文语言包 import zhCn from element-plus/es/locale/lang/zh-cn// 使用 Element Plus 并设置语言为中文 app.use(ElementPlus,…

04 远程访问及控制

1、SSH远程管理 SSH是一种安全通道协议&#xff0c;主要用来实现字符界面的远程登录、远程复制等功能。 SSH协议对通信双方的数据传输进行了加密处理&#xff08;包括用户登陆时输入得用户口令&#xff09;。 终端&#xff1a;接收用户的指令 TTY终端不能远程&#xff0c;它…

数据预处理之基于预测的(线性,ARIMA)异常值检测#matlab

基于密度的LOF异常值检测可见上篇文章。以下介绍基于预测的异常值检测&#xff1a; 1.基于预测的异常值检测方法 基于预测的异常值检测方法&#xff0c;特别是结合线性回归和ARIMA&#xff08;自回归积分滑动平均模型&#xff09;模型&#xff0c;是数据分析中常用的技术。这…

【自动驾驶】ROS小车系统介绍

文章目录 小车组成轮式运动底盘的组成轮式运动底盘的分类轮式机器人的控制方式感知传感器ROS决策主控ROS介绍ROS的坐标系ROS的单位机器人电气连接变压模块运动底盘的电气连接ROS主控与传感器的电气连接运动底盘基本组成电池电机控制器与驱动器控制器与运动底盘状态数据&#xf…

深度学习 --- stanford cs231学习笔记四(神经网络的几大重要组成部分)

训练神经网络1 1&#xff0c;激活函数&#xff08;activation functions&#xff09; 激活函数是神经网络之于线性分类器的最大进步&#xff0c;最大贡献&#xff0c;即&#xff0c;引入了非线性。 1&#xff0c;1 Sigmoid sigmoid函数的性质&#xff1a; 结合指数函数的图像可…

OpenGL3.3_C++_Windows(12)

demo演示 demo演示 模板stencil测试 OpenGL颜色缓冲区是用于存储渲染图像的颜色数据的内存区域&#xff0c;在每个新的渲染迭代&#xff0c;我们都将屏幕颜色清理glClearColor&#xff08;&#xff09;为我们指定的颜色&#xff0c;然后同时清除glClear()颜色缓冲区&#xff0…

《骑行健身:“柳叶刀”研究揭示的健康与经济双赢策略》

在这个物价飞涨、经济压力日益加重的时代&#xff0c;普通人如何在不增加额外负担的情况下提升生活质量&#xff1f;《柳叶刀》的最新研究为我们揭开了一个意想不到的秘密&#xff1a;坚持健身&#xff0c;尤其是骑行&#xff0c;竟等同于每年为自己赚取了一笔不小的财富。这一…

C++链表相关内容温习回顾——移除链表元素

本文主要对之前学过的C链表相关内容进行温习回顾&#xff0c;并以 移除链表元素 为例&#xff0c;进行应用。 关于链表的基础理论可见&#xff1a;链表理论基础 应用示例&#xff1a;LeetCode 203 移除链表元素 https://leetcode.cn/problems/remove-linked-list-elements/ 0、…

旋转的六边形

【题目描述】 输入一个整数n&#xff0c;绘制出n个不断旋转的六边形&#xff0c;如图1所示。 图1 旋转的六边形图形 【要求】 -绘制速度设为最快&#xff0c;画笔粗细为3。 -六边形每次旋转10度&#xff0c;边长增加10%。 【分析】 这是一个同心正六边&#xff0c;六边形边…

spring:深入理解@EnableAspectJAutoProxy

Hi~&#xff01;这里是奋斗的小羊&#xff0c;很荣幸您能阅读我的文章&#xff0c;诚请评论指点&#xff0c;欢迎欢迎 ~~ &#x1f4a5;&#x1f4a5;个人主页&#xff1a;奋斗的小羊 &#x1f4a5;&#x1f4a5;所属专栏&#xff1a;C语言 &#x1f680;本系列文章为个人学习…

Airtest 使用指南

Airtest 介绍 准备工作 AirtestIDE 安装与启动: https://airtest.doc.io.netease.com/IDEdocs/getting_started/AirtestIDE_install/ 电脑端的准备工作完成后,对于手机端只需要打开允许USB调试,当首次运行时会提示安装PocoService,同意即可。 界面介绍

微信登录过程分析

文章目录 1、微信登录过程分析2、身份认证实现方案&#xff1a;3、AOP回顾3.1、AOP底层 1、微信登录过程分析 2、身份认证实现方案&#xff1a; 网关过滤器&#xff1a;gateway网关GlobalFilter自定义过滤器&#xff0c;拦截经过网关的所有请求SpringMVC拦截器&#xff1a;代码…

现货白银实时交易平台的成长阶段 你出在哪个阶段?

很多人喜欢在现货白银平台上做模拟交易&#xff0c;因为他们认为现货白银实时交易平台上交易太痛苦了&#xff0c;不光随时会面临风险&#xff0c;而且还可能让自己出现大的亏损。如果投资者认为痛苦&#xff0c;那笔者觉得投资者不妨将在现货白银实时交易平台上做交易&#xf…

0 简单的图像分类

本文主要针对交通标识图片进行分类&#xff0c;包含62类&#xff0c;这个就是当前科大讯飞比赛&#xff0c;目前准确率在0.94左右&#xff0c;难点如下&#xff1a; 1 类别不均衡&#xff0c;有得种类图片2百多&#xff0c;有个只有10个不到&#xff1b; 2 像素大小不同&…

滑动窗口(LeeCode209题,以JS为例)

什么是滑动窗口&#xff1f; 滑动窗口是算法中一种非常有用的技术&#xff0c;特别是在处理数据序列或数组时。它的核心思想是维护一个固定大小的窗口&#xff0c;这个窗口在数据序列上滑动&#xff0c;以便于在窗口内的元素上进行操作或计算。滑动窗口技术通常用于解决与数据…

对 2024 年美赛选题的建议

对2024年美赛选题的建议包括&#xff1a; 1. 深入探讨当下全球面临的重大问题和挑战&#xff1a;鉴于美赛通常聚焦于全球性议题&#xff0c;如气候变化、可持续发展、数据分析等&#xff0c;参赛学生应关注这些议题&#xff0c;并深入研究相关数据与背景信息&#xff0c;以提出…

单片机建立自己的库文件(4)

文章目录 前言一、新建自己的外设文件夹1.新建外设文件夹&#xff0c;做项目好项目文件管理2.将之前写的.c .h 文件添加到文件夹中 二、在软件中添加项目 .c文件2.1 编译工程保证没问题2. 修改项目列表下的名称 三、在软件项目中添加 .h文件路径四、实际使用测试总结 前言 提示…

sheng的学习笔记-AI-集成学习(adaboost,bagging,随机森林)

ai目录&#xff1a;sheng的学习笔记-AI目录-CSDN博客 目录​​​​​​​ 集成学习 什么是集成学习 集成学习一般结构&#xff1a; 示意图 弱学习器 经典算法 Boosting 什么是boosting 方法图 AdaBoost 算法 AdaBoost示意图 流程解析&#xff1a; 错误分类率error…