C语言进程的相关操作
C语言进程的相关操作
进程简介
- 每个进程都有一个非负整数形式到的唯一编号,即PID(Process Identification,进程标识)
- PID在任何时刻都是唯一的,但是可以重用,当进程终止并被回收以后,其PID就可以为其他进程使用
- 进程的PID由系统内核根据延迟重用算法生成,以确保新进程的PID不同于最近终止进程到的PID
- 其中0号进程,叫做交换进程,系统内核中的一部分,所有进程的根进程,磁盘上没有它的可执行文件
- 1号进程是init进程,在系统自举过程结束时由调度进程创建,读写与系统相关的初始化文件,引导系统至一个特定状态,以超级用户特权运行的普通进程,永不终止
- 除去调度进程以外,系统中的每个进程都有一个唯一的父进程,对任何一个子进程而言,其父进程的PID即是它的PPID
- 下面这些函数都包含在
unistd.h
头文件中 pid_t getpid(void);
返回调用进程的PIDpid_t getppid(void);
返回调用进程的父进程的PIDuid_t getuid(void);
返回调用进程的实际用户IDgid_t getgid(void);
返回调用进程的实际组IDuid_t geteuid(void);
返回调用进程的有效用户IDgid_t getegid(void);
返回调用进程的有效组ID
创建子进程
-
创建子进程的函数包含在
unistd.h
头文件中 -
fork
函数pid_t fork(void);
- 功能:创建调用进程的子进程
- 返回值:失败返回-1,成功情况下返回的变量在父进程中是PID,在子进程中是0
- 可以通过这个返回值来执行父进程和子进程
- 当系统中的总的线程数达到了上限,或者用户的总进程达到了上限,
fork
函数会失败。
-
创建子进程示例代码
#include <stdio.h> #include <unistd.h>int main(void) {printf("haha\n");// 创建子进程int pid = fork();printf("heihei\n");return 0; }/* haha heihei heihei */
父子进程间的关系
-
以下是父子进程中数据相关
copy
的示例图
-
验证上图
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <time.h>int global = 100; // 父进程全局变量->数据区int main(void) { int local = 200; // 父进程局部变量->栈区 int *heap = malloc(sizeof(int)); // 动态分配内存->堆区 *heap = 3;printf("父进程第一次打印: PID->%d %p->%d %p->%d %p->%d\n", getpid(), &global, global, &local, local, heap, *heap); // 创建子进程 pid_t pid = fork(); if(pid == 0) { // 子进程操作,数据会从父进程copy一份过来,这里执行++操作 printf("子进程打印: PID->%d PPID->%d %p->%d %p->%d %p->%d\n", getpid(), getppid(), &global, ++global, &local, ++local, heap, ++*heap); return 0; } sleep(1); // 这里等1s,让子进程++ printf("父进程第二次打印: PID->%d %p->%d %p->%d %p->%d\n", getpid(), &global, global, &local, local, heap, *heap);return 0; }/* 父进程第一次打印: PID->1674604 0x5577e1acc010->100 0x7ffd4d4bfaa8->200 0x5577e23422a0->3 子进程打印: PID->1674605 PPID->1674604 0x5577e1acc010->101 0x7ffd4d4bfaa8->201 0x5577e23422a0->4 父进程第二次打印: PID->1674604 0x5577e1acc010->100 0x7ffd4d4bfaa8->200 0x5577e23422a0->3这里的父进程和子进程地址一样是虚拟地址里面一样,因为每个进程都有一个独立的虚拟地址池,相互不影响的 发现子进程跟父进程互相不影响,验证了上图的案例 */
-
父子进程操作文件,其实是共享一个文件表项的
-
验证上图
#include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> #include <time.h>int main(void) {// 父进程打开文件int fd = open("./test.txt", O_WRONLY | O_CREAT | O_TRUNC, 0664);if(fd == -1){perror("open");return -1;}// 父进程写入数据char *data = "hello bhlu!";if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 创建子进程pid_t pid = fork();if(pid == 0){// 子进程修改文件读写位置if(lseek(fd, -5, SEEK_END) == -1){perror("lseek");return -1;}return 0;}// 再次插入数据,验证子进程修改的读写位置是否生效sleep(1); // 先等1s,让子进程执行完data = "linux\n";if(write(fd, data, strlen(data)) == -1){perror("write");return -1;}// 关闭文件close(fd);return 0; }/* cat test.txt hello linux 发现是修改成功的,说明上图是对的,子进程和父进程共用一个文件表项 */
进程的终止
以下内容只是简单的介绍进程的终止,以便理解
-
进程的终止分为两种
-
正常终止:分为三种情况
-
main函数中正常返回
-
使用
exit
函数终止:exit
函数可以在任何函数中执行令进程结束,return
语句只有在main
函数中执行才能令进程结束#include <stdlib.h>void exit(int status); /* 功能: 令进程终止 参数: status 进程的退出码,相当于main函数的返回值 无返回值 *//* exit函数在终止前会做以下几件收尾工作 1. 调用实现通过atexit或on_exit函数注册的函数退出函数 2. 冲刷并关闭所有仍处于打开状态的标准I/O流 3. 删除所有通过tmpfile函数创建的临时文件 4. 执行_exit(status); 使用exit函数令进程终止,通常使用EXIT_SUCCESS和EXIT_FAILUR两个宏 EXIT_SUCCESS -> 1; EXIT_FAILUR -> 0; */
-
调用
_exit/_Exit
函数令进程终止// _exit函数 #include <unistd.h>void _exit(int status); /* 参数: status 进程的退出码,相当于main函数的返回值 无返回值 */// _Exit函数 #include <stdlib.h>void _Exit(int status); /* 参数: status 进程的退出码,相当于main函数的返回值 无返回值 *//* _exit函数在终止前会做以下几件收尾工作 1. 关闭所有仍处于打开状态的文件描述符 2. 将调用进程的所有子进程托付过init进程 3. 向调用进程的父进程发送SIGCHLD(7)信号 4. 令调用进程终止运行,将status的低八位作为退出码保存在其终止状态中 */
-
-
异常终止
-
进程执行了系统认为具有危险性的操作时,或者系统本身发生故障或意外,内核会向进程发送特定的信号
SIGILL(4) -> 进程试图执行非法指令 SIGBUS(7) -> 硬件或对齐错误 SIGEPE(8) -> 浮点异常 SIGSEGV(11) -> 无效内存访问 SIGPWR(30) -> 系统供电不足
-
人为触发信号
SIGINT(2) -> Ctrl+c SIGQUIT(3) -> Ctrl+\ SIGKILL(9) -> 不能被捕获或忽略的进程终止信号 SIGTERM(15) -> 可以被捕获或忽略的进程终止编号
-
向进程自己发送信号
#include <stdlib.h>void abort(void); /* 功能: 想进城发送SIGABRT(6)信号,该信号默认情况下可以使进程结束 无返回值 */
-
-
在使用
exit
函数或main
函数正常退出时,如果注册了atexit
或on_exit
,那就会触发退出函数,以下是示例代码#include <stdio.h> #include <stdlib.h>void func(void) {exit(6); }void goto1(void) {printf("goto1\n"); }void goto2(int status, void *arg) {printf("status = %d\n", status);printf("arg = %s\n", (char *)arg); }int main(void) {atexit(goto1); // 退出之前执行goto1on_exit(goto2, "heihei"); // 退出之前执行goto2,可以传参func();return 0; }/* 相当于钩子函数,在退出之前执行,可以进行一些回收操作 status = 6 arg = heihei goto1 */
回收子进程
- 如果不回收子进程的话,会导致有很多僵尸进程的存在,从而消耗更多的系统资源。
- 父进程需要等待子进程到的终止,以继续后续工作
- 父进程需要了解子进程终止的原因,是正常终止,还是异常终止
阻塞回收
-
wait
函数是用于回收子进程的一个函数,它使用的是阻塞回收,使用它必须包含sys/wait.h
头文件 -
wait
函数-
pid_t wait(int *status);
-
功能:等待和回收任意子进程
-
参数:
status
用于输出子进程的终止状态,可置NULL
-
补充:可以使用以下工具宏分析子进程的终止状态
if(WIFEXITED(status))// 真printf("正常终止: 进程退出码是%d\n", WEXITSTATUS(status)); else// 假printf("异常终止: 终止进程的信号是%d\n", WTERMSIG(status));// 下面跟上面判断条件相反 if(WIFSIGNALED(status))// 真printf("异常终止: 终止进程的信号是%d\n", WTERMSIG(status)); else// 假printf("正常终止: 进程退出码是%d\n", WEXITSTATUS(status));
-
-
返回值:成功返回回收的子进程PID,失败返回-1
-
-
-
简单代码示例
// 子进程的回收 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/wait.h>int main(void) {// 创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());// sleep(5);// exit(3);// _exit(5);// return 2;// abort(); // 向进程发送信号异常结束// 以下两句会造成内存无效访问,会返回11char *p = NULL;*p = 123;}// 父进程等待回收子进程printf("%d进程: 我是父进程!\n", getpid());int s; // 用来输出所回收的子进程终止状态pid_t childpid = wait(&s);if(childpid == -1){perror("wait");return -1;}printf("父进程回收了%d进程的僵尸!\n", childpid);// 根据返回值判断子进程是否是正常结束if(WIFEXITED(s))printf("正常结束: %d\n", WEXITSTATUS(s));elseprintf("异常结束: %d\n", WTERMSIG(s));return 0; }
-
以下代码是一个循环创建5个进程,然后父进程挨个回收
#include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <errno.h>int main(void) {printf("%d进程: 我是父进程!\n--------------------------\n", getpid());sleep(1);// 创建子进程for(int i = 0; i < 5; i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());sleep(i+1);return i+1;}}// 父进程操作: 回收子进程while(1){int s; // 用户接收子进程的终止状态pid_t childpid = wait(&s);if(childpid == -1){if(errno == ECHILD){printf("没有子进程可以回收了!\n");break;}else{perror("wait");return -1;}}// 判断子进程的终止状态if(WIFEXITED(s))printf("正常结束: %d\n", WEXITSTATUS(s));elseprintf("异常终止: %d\n", WTERMSIG(s));}return 0; }
非阻塞回收
-
waitpid
函数一般用于非阻塞回收子进程,还可以回收特定子进程,使用这个函数需要引用sys/wait.h
头文件 -
waitpid
函数pid_t waitpid(pid_t pid, int *status, int options);
- 功能:等待并回收任意或特定子进程
- 参数
pid
:取-1等待并回收任意子进程,相当于wait
函数,>0等待回收特定子进程status
:用于输出子进程的终止状态,可置NULL
option
:0代表阻塞模式,WNOHANG
代表非阻塞模式,如果等待的进程还在运行,则返回0
- 返回值:成功返回回收子进程的PID或者0,失败返回-1
-
以下是使用非阻塞回收的方法回收子进程
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/wait.h> #include <errno.h>int main(void) {printf("%d进程: 我是父进程!\n-------------------------\n", getpid());// 创建子进程for(int i = 0; i < 5; i++){pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){printf("%d进程: 我是子进程!\n", getpid());sleep(i+1);// 三种效果if(i == 3){abort();}else if(i == 4){char *p = NULL;*p = 123;}else{return i+1;}}}// 父进程回收子进程sleep(1);while(1){int s; // 用于保存进程的终止状态pid_t childpid = waitpid(-1, &s, WNOHANG); // 这里使用的是非阻塞模式if(childpid == -1){// 报错或者没有子进程了if(errno == ECHILD){printf("没有子进程了!\n");break;}else{perror("waitpid");return -1;}}else if(childpid == 0){// 子进程还在运行printf("子进程在运行,无法回收,先睡会!\n");sleep(2);}else{// 回收成功并判断是否正常终止printf("%d子进程回收成功!\n", childpid);if(WIFEXITED(s))printf("%d进程正常终止, 进程退出码: %d\n\n", childpid, WEXITSTATUS(s));elseprintf("%d进程异常终止, 终止进程信号: %d\n\n", childpid, WTERMSIG(s));}}return 0; }/* 代码执行效果 1761797进程: 我是父进程! ------------------------- 1761798进程: 我是子进程! 1761799进程: 我是子进程! 1761800进程: 我是子进程! 1761801进程: 我是子进程! 1761802进程: 我是子进程! 子进程在运行,无法回收,先睡会! 1761798子进程回收成功! 1761798进程正常终止, 进程退出码: 11761799子进程回收成功! 1761799进程正常终止, 进程退出码: 2子进程在运行,无法回收,先睡会! 1761800子进程回收成功! 1761800进程正常终止, 进程退出码: 31761801子进程回收成功! 1761801进程异常终止, 终止进程信号: 6子进程在运行,无法回收,先睡会! 1761802子进程回收成功! 1761802进程异常终止, 终止进程信号: 11没有子进程了! */
补充
- 实际情况下,无论进程是正常终止还是异常终止,都会通过系统内核向其父进程发送一个SIGCHLD(17)信号,我们可以提供一个针对该信号的处理函数,在信号处理函数中异步的方式回收子进程,这样不仅流程简单,回收效率还高,僵尸进程的存活时间也会很短。
创建新进程
与fork函数不同,这里使用的exec
函数是创建一个新的进程,新进程会取代调用自身的进程,新进程覆盖之前的进程地址空间,进程的PID不会改变。
-
exec
不是一个函数,而是一堆函数,功能一样,用法相似 -
#include <unistd.h>
-
int execl(const char *path, const char *arg, ...);
execl("/bin/ls", "ls", "-a", "-l", NULL); /* path使用的是路径名 使用NULL作为arg的结尾 失败返回-1,成功不返回 */
-
int execlp(const char *file, const char *arg, ...);
execlp("ls", "ls", "-a", "-l", NULL); /* file使用的是文件名,会从环境变量中一个个的找 使用NULL作为arg的结尾 失败返回-1,成功不返回 */
-
int execle(const char *path, const char *arg, ..., char *const envp[]);
char *envp[] = {"NAME=bhlu", "AGE=25", NULL}; execle("/usr/bin/env", "env", NULL, envp); /* 比excel多一个envp,用于设置环境变量,它设置什么,新进程的环境变量就只有什么 失败返回-1,成功不返回 环境变量输出:NAME=bhluAGE=25 */
-
int execv(const char *path, char *const argv[]);
char *argv[] = {"ls", "-a", "-l", NULL}; execv("/bin/ls", argv); /* execv系列使用的都是字符指针数组,字符数组是以NULL结尾 失败返回-1,成功不返回 */
-
int execvp(const char *file, char *const argv[]);
char *argv[] = {"ls", "-a", "-l", NULL}; execvp("ls", argv); /* 跟execv差不多,就第一个参数是文件名 失败返回-1,成功不返回 */
-
int execve(const char *path, char *const argv[], char *const envp[]);
char *argv[] = {"env", NULL}; char *envp[] = {"NAME=bhlu", "AGE=25", NULL}; execve("/usr/bin/env", argv, envp); /* 跟execle函数差不多,就是这里的第二个参数是字符指针数组 失败返回-1,成功不返回 */
-
-
后缀不同,代码的含义也不同
l
:即list
,新进程的命令以字符指针列表形式传入,列表以空指针结束p
:即path
:第一个参数,不包含/
,就根据PATH
环境变量搜索文件e
:即environment
:设定环境变量,不指定则从调用进程复制v
:即vector
:新进程的命令行参数以字符指针数组的形式传入,数组以空指针结束- 实际底层最后使用的都是
execve
函数
-
使用
exec
函数基本会将原进程的所有信号、属性、数据等都丢失或者恢复初识状态,只有PID
、PPID
、UID
等会被继承下来。 -
一般都会先创建一个子进程,然后在子进程中使用
exec
函数,以下是相关示例#include <stdio.h> #include <unistd.h>int main(void) {// 创建子进程pid_t pid = fork();if(pid == -1){perror("fork");return -1;}// 子进程相关操作if(pid == 0){char *argv[] = {"env", NULL};if(execvp("/bin/env", argv) == -1){perror("execvp");return -1;}}// 父进程操作printf("父进程PID: %d\n", getpid());return 0; }
system
-
下面介绍的是
c
语言执行shell
命令的函数 -
#include <stdlib.h>
int system(const char *command);
- 功能:执行
shell
命令 - 参数:
shell
命令,如果参数取NULL
,返回非0表示Shell
可用,返回0表示不可用 - 返回值:成功返回
command
进程的终止状态, 失败返回-1
- 功能:执行
-
代码实例
#include <stdio.h> #include <stdlib.h> #include <unistd.h>int main(void) {int s = system("echo $PATH");if(s == -1){perror("system");return -1;}printf("父进程PID: %d\n", getpid());return 0; }
-
system
函数内部调用了vfork
、exec
和waitpid
等函数,而且它是标准库函数,可以跨平台使用- 如果调用
vfork
或waitpid
函数出错,则返回-1 - 如果调用
exec
函数出错,则在子进程中执行exit(127)
- 如果都成功,会从
waitpid
获取command
进程的终止状态
- 如果调用
相关文章:

C语言进程的相关操作
C语言进程的相关操作 进程简介 每个进程都有一个非负整数形式到的唯一编号,即PID(Process Identification,进程标识)PID在任何时刻都是唯一的,但是可以重用,当进程终止并被回收以后,其PID就可…...
数据结构学习系列之链式栈
链式栈:即:栈的链式存储结构;分析:为了提高程序的运算效率,应采用头插法和头删法;进栈: int push_link_stack(stack_t *link_stack,int data) {if(NULL link_stack){printf("入参合理性检…...
too many session files in /var/tmp
Linux中Too many open files 问题分析和解决_e929: too many viminfo temp files-CSDN博客...
【7.0】打开未知来源安装应用
默认打开未知来源安装应用 frameworks\base\packages\SettingsProvider\res\values\defaults.xml <bool name"def_install_non_market_apps">false</bool>...

安装ipfs-swarm-key-gen
安装ipfs-swarm-key-gen Linux安装go解释器安装ipfs-swarm-key-gen Linux安装go解释器 https://blog.csdn.net/omaidb/article/details/133180749 安装ipfs-swarm-key-gen # 编译ipfs-swarm-key-gen二进制文件 go get -u github.com/Kubuxu/go-ipfs-swarm-key-gen/ipfs-swarm…...
BASH shell脚本篇5——文件处理
这篇文章介绍下BASH shell中的文件处理。之前有介绍过shell的其它命令,请参考: BASH shell脚本篇1——基本命令 BASH shell脚本篇2——条件命令 BASH shell脚本篇3——字符串处理 BASH shell脚本篇4——函数 在Bash Shell脚本中,可以使用…...

ElementUI之首页导航及左侧菜单(模拟实现)
目录 编辑 前言 一、mockjs简介 1. 什么是mockjs 2. mockjs的用途 3. 运用mockjs的优势 二、安装与配置mockjs 1. 安装mockjs 2. 引入mockjs 2.1 dev.env.js 2.2 prod.env.js 2.3 main.js 三、mockjs的使用 1. 将资源中的mock文件夹复制到src目录下 2. 点击登…...

Java开源工具库使用之Lombok
文章目录 前言一、常用注解1.1 AllArgsConstructor/NoArgsConstructor/RequiredArgsConstructor1.2 Builder1.3 Data1.4 EqualsAndHashCode1.5 Getter/Setter1.6 Slf4j/Log4j/Log4j2/Log1.7 ToString 二、踩坑2.1 Getter/Setter 方法名不一样2.2 Builder 不会生成无参构造方法2…...

uboot启动流程涉及reset函数
一. uboot启动流程中函数 之前了解了uboot链接脚本文件 u-boot.lds。 从 u-boot.lds 中我们已经知道了入口点是 arch/arm/lib/vectors.S 文件中的 _start。 本文了解 一下,uboot启动过程中涉及的 reset 函数。本文继上一篇文章学习,地址如下ÿ…...

端口被占用怎么解决
第一步:WinR 打开命令提示符,输入netstat -ano|findstr 端口号 找到占用端口的进程 第二步: 杀死使用该端口的进程,输入taskkill /t /f /im 进程号( !!!注意是进程号,不…...
python reportlab 生成多页pdf
多页 from reportlab.pdfgen import canvas from reportlab.platypus import (SimpleDocTemplate, Paragraph, PageBreak, Image, Spacer, Table, TableStyle) from reportlab.lib.enums import TA_LEFT, TA_RIGHT, TA_CENTER, TA_JUSTIFY from reportlab.lib.styles import P…...

word 多级目录的问题
一、多级标题自动编号 --> 制表符 -> 空格 网址: 【Word技巧】2 标题自动编号——将多级列表链接到样式 - YouTube 二、多级列表 --> 正规形式编号 网址:Word 教学 - 定框架:文档格式与多级标题! - YouTube 三、目…...

python使用mitmproxy和mitmdump抓包之拦截和修改包(四)
我认为mitmproxy最强大的地方,就是mitmdump可以结合python代理,灵活拦截和处理数据包。 首先,mitmdump的路径如下:(使用pip3 install mitmproxy安装的情况,参考我的文章python使用mitmproxy和mitmdump抓包…...
邓俊辉《数据结构》→ “2.6.5 二分查找(版本A)”之“成功查找长度”递推式推导
【问题描述】 邓俊辉的《数据结构(C语言版)(第3版)》(ISBN:9787302330646)中,开始于第48页的“2.6.5 二分查找(版本A)”内容在第50页详述了“成功查找长度”的…...

Linux文件查找,别名,用户组综合练习
1.文件查看: 查看/etc/passwd文件的第5行 [rootserver ~]# head -5 /etc/passwd root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin adm:x:3:4:adm:/var/adm:/sbin/nologin lp:x:4:7:lp:/var/spool/lpd:/sbin/nologi…...

【MATLAB第77期】基于MATLAB代理模型算法的降维/特征排序/数据处理回归/分类问题MATLAB代码实现【更新中】
【MATLAB第77期】基于MATLAB代理模型算法的降维/特征排序/数据处理回归/分类问题MATLAB代码实现 本文介绍基于libsvm代理模型算法的特征排序方法合集,包括: 1.基于每个特征预测精度进行排序(libsvm代理模型) 2.基于相关系数corr的…...

第三章 图标辅助元素的定制
第三章 图标辅助元素的定制 1.认识图表常用的辅助元素 图表的辅助元素是指除了根据数据绘制的图形之外的元素,常用的辅助元素包括坐标轴、标题、图例、网格、参考线、参考区域、注释文本和表格,它们都可以对图形进行补充说明。 上图中图表常用辅…...

【前端】ECMAScript6从入门到进阶
【前端】ECMAScript6从入门到进阶 1.ES6简介及环境搭建 1.1.ECMAScript 6简介 (1)ECMAScript 6是什么 ECMAScript 6.0(以下简称 ES6)是 JavaScript 语言的下一代标准,已经在2015年6月正式发布了。它的目标ÿ…...

Android Shape设置背景
设置背景时,经常这样 android:background“drawable/xxx” 。如果是纯色图片,可以考虑用 shape 替代。 shape 相比图片,减少资源占用,缩减APK体积。 开始使用。 <?xml version"1.0" encoding"utf-8"?…...

什么是GraphQL?它与传统的REST API有什么不同?
聚沙成塔每天进步一点点 ⭐ 专栏简介⭐ 什么是GraphQL?⭐ 与传统的REST API 的不同⭐ 写在最后 ⭐ 专栏简介 前端入门之旅:探索Web开发的奇妙世界 欢迎来到前端入门之旅!感兴趣的可以订阅本专栏哦!这个专栏是为那些对Web开发感兴趣…...
【磁盘】每天掌握一个Linux命令 - iostat
目录 【磁盘】每天掌握一个Linux命令 - iostat工具概述安装方式核心功能基础用法进阶操作实战案例面试题场景生产场景 注意事项 【磁盘】每天掌握一个Linux命令 - iostat 工具概述 iostat(I/O Statistics)是Linux系统下用于监视系统输入输出设备和CPU使…...

cf2117E
原题链接:https://codeforces.com/contest/2117/problem/E 题目背景: 给定两个数组a,b,可以执行多次以下操作:选择 i (1 < i < n - 1),并设置 或,也可以在执行上述操作前执行一次删除任意 和 。求…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
生成 Git SSH 证书
🔑 1. 生成 SSH 密钥对 在终端(Windows 使用 Git Bash,Mac/Linux 使用 Terminal)执行命令: ssh-keygen -t rsa -b 4096 -C "your_emailexample.com" 参数说明: -t rsa&#x…...

Cloudflare 从 Nginx 到 Pingora:性能、效率与安全的全面升级
在互联网的快速发展中,高性能、高效率和高安全性的网络服务成为了各大互联网基础设施提供商的核心追求。Cloudflare 作为全球领先的互联网安全和基础设施公司,近期做出了一个重大技术决策:弃用长期使用的 Nginx,转而采用其内部开发…...

算法:模拟
1.替换所有的问号 1576. 替换所有的问号 - 力扣(LeetCode) 遍历字符串:通过外层循环逐一检查每个字符。遇到 ? 时处理: 内层循环遍历小写字母(a 到 z)。对每个字母检查是否满足: 与…...

人工智能(大型语言模型 LLMs)对不同学科的影响以及由此产生的新学习方式
今天是关于AI如何在教学中增强学生的学习体验,我把重要信息标红了。人文学科的价值被低估了 ⬇️ 转型与必要性 人工智能正在深刻地改变教育,这并非炒作,而是已经发生的巨大变革。教育机构和教育者不能忽视它,试图简单地禁止学生使…...

push [特殊字符] present
push 🆚 present 前言present和dismiss特点代码演示 push和pop特点代码演示 前言 在 iOS 开发中,push 和 present 是两种不同的视图控制器切换方式,它们有着显著的区别。 present和dismiss 特点 在当前控制器上方新建视图层级需要手动调用…...

Chrome 浏览器前端与客户端双向通信实战
Chrome 前端(即页面 JS / Web UI)与客户端(C 后端)的交互机制,是 Chromium 架构中非常核心的一环。下面我将按常见场景,从通道、流程、技术栈几个角度做一套完整的分析,特别适合你这种在分析和改…...

Android写一个捕获全局异常的工具类
项目开发和实际运行过程中难免会遇到异常发生,系统提供了一个可以捕获全局异常的工具Uncaughtexceptionhandler,它是Thread的子类(就是package java.lang;里线程的Thread)。本文将利用它将设备信息、报错信息以及错误的发生时间都…...