Unix进程

文章目录

  • 命令行参数
  • 进程终止
    • 正常结束
    • 异常终止
    • exit和_exit
      • atexit
  • 环境变量
    • 环境变量性质
    • 环境表
    • shell中操作环境变量
      • 查看环境变量
      • 设置环境变量
    • 环境变量接口
      • 获取环境变量
      • 设置环境变量
    • 环境变量的继承性
  • 进程资源
    • shell命令查看进程的资源限制
  • 进程关系
    • 进程标识
    • 进程组
    • 会话
    • 控制终端
      • 控制终端ID
      • 前后台进程切换命令
      • jobs
      • bg
      • fg
    • 精灵进程(守护进程)
    • 闲逛进程
  • 进程创建
    • fork与写时复制
  • 进程等待
    • wait、waitpid
      • statloc参数详解
    • 僵尸进程与孤儿进程
  • 进程替换
    • exec函数解析
    • exec用户ID问题

命令行参数

ISO C标准的main函数具有2个参数,分别是argc和argv,它们标识==命令行参数的个数和命令行参数数组==,所谓命令行参数,就是通过命令行启动一个程序时所传入的参数。内核启动一个程序时调用一个特殊的例程——程序入口点进行命令行参数和环境变量的初始化,随后调用main函数执行用户逻辑。

./a.out 这是执行一个可执行程序最简单的方式,命令行参数个数为1,参数即./a.out

./a.out xxx yyy 命令行参数个数为3,参数分别为./a.out xxx yyy

命令行参数需要以空格作为分割符,shell会以空格为单位解析命令行参数。当程序运行时,它可以选择读取命令行参数进行特定的任务处理。

//简单加法器的实现
#include <stdio.h>
#include <stdlib.h>
int main(int argc,char* argv[]){
    if(argc<3){printf("请至少输入2个操作数\n");return 0;}
    int res=0;
    for(int i=1;i<argc;++i){ //注意从下标为1处开始读取
        res+=atoi(argv[i]);
    }
    printf("求和的结果是%d\n",res);
    return 0;
}

运行效果展示image-20241113191304256

ISO C标准规定argv[argc]必须为NULL标识结束,因此for(int i=1;i<argc;++i) <==> for(int i=1;argv[i]!=NULL;++i)

进程终止

程序入口点在调用main函数后等待main函数返回,并进行结果处理。在用户源代码中通过main函数return来表示进程的结束。其实想要让一个进程结束的方式不只有在main函数中return。Unix环境下一共有8种进程终止的方式,其中5种是正常结束,3种是异常终止

正常结束

  1. main返回
  2. exit调用
  3. _exit调用
  4. 最后一个线程返回
  5. 最后一个线程pthread_exit调用

异常终止

  1. 调用abort
  2. 收到默认处理动作为终止进程的信号
  3. 最后一个线程对取消请求做出相应

本文只讨论正常结束的前3种情况,请注意main函数返回和exit调用是等价的

exit和_exit

exit是ISO C标准提供的一个接口,在程序的任意处调用exit函数都会导致进程结束;_exit是POSIX标准提供的一个接口,它是一个系统调用,会直接进入内核,在程序的任意处调用也会导致进程结束。

exit是对_exit的封装,相较于其的区别在于exit在进入内核之前会先调用一系列终止处理程序,其中包括刷新所有的C标准缓冲区。

exit和_exit都有一个整型类型的参数用于标识进程退出状态,宏常量EXIT_SUCCESS(扩展到0)标识进程正常结束且结果正确,EXIT_FAILURE(扩展到1)标识进程正常结束但结果错误

int exit(int status);
int _exit(int status);

atexit

可以通过atexit函数来自定义终止处理程序,执行exit时会按照atexit注册的函数逆向逐个调用。atexit必须传入一个无参数无返回值的函数

int atexit(void (*func)());
#include <stdio.h>
#include <stdlib.h>
void my_handler1(){printf("my_handler1\n");}
void my_handler2(){printf("my_handler2\n");}
void my_handler3(){printf("my_handler3\n");}
int main(){
    atexit(my_handler1);
    atexit(my_handler2);
    atexit(my_handler3);
    return 0;
}/*调用次序3->2->1*/

image-20241113195953789

环境变量

环境变量是操作系统层面的一种机制,用于向运行中的进程提供配置信息。它们是一组动态的键值对(key=value),可以在程序启动时或运行过程中被读取,从而影响程序的行为

环境变量性质

全局性:环境变量通常是全局的,意味着它们可以被任何进程访问,除非被明确地限制在一个特定的进程中。
动态性:环境变量可以在程序运行期间被更改,这使得程序可以根据环境的变化调整其行为。
继承性:当一个进程创建子进程时,子进程会继承父进程的环境变量副本,除非另有指定。

环境表

每一个进程都会收到一张环境表,它是一个字符指针数组,以NULL代表结束,全局变量environ指向环境表

image-20241114090609831

我们可以通过遍历environ所管理的环境表来获取所有的环境变量

#define _GNU_SOURCE
#include <unistd.h>
#include <stdio.h>
int main(){
    for(int i=0;environ[i]!=NULL;++i){
        printf("%s\n",environ[i]);
    }
    return 0;
}

部分Unix系统对main函数做出了扩展,允许传入第三个参数标识环境表(Linux就支持)

int main(int agrc,char* argv[],char* env[]);

也可以通过遍历env来获取所有的环境变量

#include <stdio.h>
int main(int argc,char* argv[],char* env[]){
    for(int i=0;env[i]!=NULL;++i){
        printf("%s\n",env[i]);
    }
    return 0;
}

2种方式效果等价

shell中操作环境变量

以Linux为例

查看环境变量

  1. echo ${KEY} ::=查看指定的环境变量值
  2. env\printenv ::=查看所有的环境变量值

设置环境变量

  1. export {KEY}={VALUE} ::=设置会话级别的环境变量,当会话关闭之后失效

环境变量接口

获取环境变量

char* getenv(const char* key);

如果存在环境变量key则返回对应的值,否则返回一个空指针

设置环境变量

int putenv(const char* kv);
int setenv(const char* key,const char* value,int mode);
int unsetenv(const char* name);
  • putenv无则新增,有则更新
  • setenv根据mode决定对已存在的环境变量的处理方式。非0则更新,0则保留
  • unsetenv删除环境变量

进程所设置的环境变量只对当前进程或者其子进程有效,并不影响其他进程

环境变量的继承性

putenv可以被子进程所继承,在CGI机制中往往父进程可以通过设置环境变量的方式向子进程传递必要的参数。

//环境变量继承性实验
int main(){
    putenv("OPEARND1=1000");
    putenv("OPEARND2=1000");
    pid_t pid=fork(); //表示创建一个子进程
    if(pid==0){
        printf("计算结果%d\n",atoi(getenv("OPEARND1"))+atoi(getenv("OPEARND2")));
    }
    else{
        printf("父进程设置环境变量完毕\n");
    }
    return 0;
}

进程资源

任何一个进程都具有一定的资源限制,例如占用CPU的最大时间,数据段的最大长度,文件最大字节数,打开文件上限等等,有些业务逻辑需要受限至于进程的资源限制,这时候查询和修改进程资源限制就显得很有必要。XSI扩展了2个接口用于查询和修改进程资源限制

#include <sys/time.h>
#include <sys/resource.h>
int getrlimit(int resource,struct rlimit* rlptr);
int setrlimit(int resource,const struct rlimit* rlptr);

typedef long unsigned int rlim_t;

struct rlimit{

​ rlim_t rlim_cur; //称为软限制,标识当前进程的限制值

​ rlim_t rlim_max; //称为硬限制,标识当前进程限制值的最大值

};

对于进程资源的修改操作并不是随意进行的,它必须遵守如下规则

  • root权限进程可以提高硬限制
  • 普通进程可以灵活调整软限制(不可超过硬限制) 和 降低硬限制(不可逆,调低了就回不去了)

eg

/*这是一个普通进程*/
#include <sys/time.h>
#include <sys/resource.h>
#include <stdio.h>
int main(){
	struct rlimit rl;
	getrlimit(RLIMIT_CORE,&rl); //获取当前进程核心转储文件最大值
	printf("soft rlimit_core:%lu\n",rl.rlim_cur);
    printf("hard rlimit_core:%lu\n",rl.rlim_max);
    
	getrlimit(RLIMIT_CPU,&rl); //获取当前进程占用CPU时间最大值
	printf("soft rlimit_cpu:%lu\n",rl.rlim_cur);
    printf("hard rlimit_core:%lu\n",rl.rlim_max);
    
    rl.rlim_cur=100;
    setrlimit(RLIMIT_CPU,&rl); //ok
    rl.rlim_max=101;
    setrlimit(RLIMIT_CPU,&rl); //ok 普通进程可以降低硬限制
/*-----------------------------------------------------------------------*/
    rl.rlim_cur=200;
    setrlimit(RLIMIT_CPU,&rl); //error 软限制不可大于硬限制
    rl.rlim_max=200;
    setrlimit(RLIMIT_CPU,&rl); //error 普通进程不可抬高硬限制(之前降低过调不回去)
	return 0;
}

shell命令查看进程的资源限制

prlimit --pid={PROCESS_ID} --output=DESCRIPTION,RESOURCE,SOFT,HARD

进程关系

进程标识

内核需要管理每一个进程,需要通过一个唯一标识符来确定进程。进程的唯一标识符为称为==进程ID(PID),进程ID是一个非负整数,同一时刻不会存在进程ID相同的2个进程;每一个进程都有唯一的父进程,通过父进程ID(PPID)==所指示*(0号进程没有父进程,它是系统启动的第一个进程,负责进程调度,属于内核级进程)*;除此之外,一个进程还有许多标识信息,例如真实用户ID(UID),有效用户ID(EID),真实组ID(GID),有效组ID(EGID)。并且都具有相应的系统调用可以用于获取调用进程的相关标识信息。

#include <unistd.h>
pid_t getpid();
pid_t getppid();
pid_t getuid();
pid_t geteuid();
pid_t getgid();
pid_t getegid();

//
pid_t getresuid();
pid_t getresgid();  //获取保留的用户ID和组ID,这2个接口不具备可移植性,但是Linux中有提供

进程组

多个进程可以构成一个进程组,通过进程组可以高效管理多个进程。每一个进程组都有一个组长进程,组长进程的进程ID等于进程组组ID==(PID=GID)==。在shell中可以通过管道连接符同时启动多个进程,这些进程会被归置为一组。

pid_t getpgrp(); //获取GID

进程组的生命周期随进程组中最后结束的进程,即只要进程组中存在一个进程,进程组就会存在。非组长进程可以加入别的进程组或者创建一个进程组自己成为组长。

int setpgid(pid_t pid,pid_t pgid);
//一个进程可以调用setpgid加入或创建一个进程组
//pid一般传入0标识进程自身

会话

image-20241114194210636

多个进程组成一个会话。进程可以通过调用setsid来创建一个新的会话,但是这个进程不能是组长进程(组长进程调用setsid会失败);如果调用成功,则调用进程会成为新会话里的唯一进程(会话首进程),会话也有标识即会话ID(SID),会话ID等于会话首进程ID

int setsid();
pid_t getsid(pid_t pid); 

getsid返回的是目标进程的进程组ID,而非会话ID,并且getsid函数作用域被限制在会话中,即一个进程通过getsid获得其他会话的进程组信息会失败

控制终端

一个会话可以具有一个控制终端,通过xshell远程登录Linux时其实就是建立一个带有控制终端的会话,用户可以通过控制终端与主机进行交互

带有控制终端的会话中进程组可以被划分为前台进程组后台进程组。其中前台进程组有且仅有一个,键盘发送信号就是发给前台进程组的(CTRL+C会强制结束当前会话的所有前台进程)

image-20241114194906877

一个非组长进程调用setsid创建新会话后默认没有控制终端。

控制终端ID

ps命令中有一个字段为TPGID,即控制终端ID,一般TPGID都不固定的。我们可以通过TPGID来判断一个进程是否属于前台进程,对于前台进程它的TPGID是非负的,而对于后台进程其TPGID为-1

前后台进程切换命令

jobs

jobs命令可以查看==当前会话==的后台进程信息,结果以组为单位呈现

image-20241114200106061

bg

bg命令可以将==一组==被CTRL+Z暂停的前台进程挂到后台继续运行

bg %{jobs_no}

fg

fg命令可以将==一组==后台进程挂到前台继续运行

fg %{jobs_no}

精灵进程(守护进程)

精灵进程也称为守护进程,是一种长时间运行的后台进程。它们通常在系统启动时启动,并持续运行直到系统关闭。精灵进程不与用户直接交互,而是执行特定的服务或任务。
特点

  1. 没有控制终端,通常在后台运行
  2. 生命周期长,通常从系统启动到关闭一直存在
  3. 通常作为系统服务运行,提供网络服务、定时任务等功能
  4. 创建时会调用fork()和setsid()等函数,确保自己独立于启动它的终端
  5. 通常会将当前工作目录更改为根目录,避免占用挂载的文件系统
  6. 设置文件权限掩码,确保创建的文件具有适当的权限
  7. 关闭不需要的文件描述符,避免占用系统资源

闲逛进程

闲逛进程也称为空闲进程或调度进程,是操作系统中的一个特殊进程。当系统中没有其他可运行的进程时,调度器会将CPU的控制权交给闲逛进程。闲逛进程的主要目的是确保CPU不会空转,从而防止CPU进入不受控制的状态。
特点

  1. 具有最低的优先级,只有当系统中没有其他可运行的进程时才会被调度
  2. 主要任务是让CPU保持忙碌状态,即使这种“忙碌”实际上是空闲的
  3. 通常执行一个简单的循环,使CPU进入低功耗状态
  4. 不消耗实际的计算资源,只是占用了CPU的时间片
  5. 在多处理器系统中,每个CPU核心通常都有一个对应的闲逛进程

进程创建

fork与写时复制

一个进程可以通过fork创建子进程,子进程会继承父进程的进程组ID(即子进程和父进程属于同一个进程组),内核会为fork生成的子进程生成一份父进程的地址空间快照,并且在子进程不进行数据修改操作时,子进程与父进程共享同一块物理内存;如果子进程对数据发生修改操作,则内核会建立被修改的数据的副本以确保子进程不会影响父进程,这种机制成为写时复制技术

值得注意的是,子进程会继承父进程的文件描述符表(重定位信息也会得到继承),并且==与父进程共享一个文件表项----->共享文件偏移量==,这意味着如果父子进程同时往一个文件中写入数据可能出现数据错乱的情况;但这个特性也有其优势,如果子进程向标准输出打印了一串信息后退出,假设父进程等待子进程结束,那么它能很自然得(不需要复杂的控制)将一些子进程的退出状态输出到标准输出上(父进程的输出信息一定是在子进程打印的信息之后的)

image-20241114202248187

进程等待

当一个进程正常或异常终止时,内核向其父进程发送SIGCHID信号,告诉父进程其子进程已经结束。父进程需要在合适的时机调用系统API获取子进程的退出信息并回收已经结束的子进程资源(子进程的大部分资源已经被内核回收,但是内核仍会保留子进程的进程控制块信息,因为这里面存储了子进程的退出信息)。标准为我们提供了wait和waitpid两个接口用于进程等待,进程调用wait和waitpid函数默认陷入阻塞直到子进程返回。

wait、waitpid

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

***wait:***阻塞等待任意子进程,如果不关心子进程的退出信息,参数写NULL,否则子进程的退出信息会被写入到statloc所指向的空间。

**waitpid:参数statloc与wait一致,默认为阻塞等待,如果将options参数设置为WNOHANG则为非阻塞等待。

waitpid的pid参数不同的值具有不同的意义

  1. pid==-1,等待任意子进程(如果options=0则退化为wait)
  2. pid>0,等待进程id等于pid的子进程
  3. pid==0,等待组ID为调用进程组ID的任意子进程
  4. pid<0,等待组ID为pid绝对值的任意子进程

statloc参数详解

statloc保存了子进程的退出状态,按照比特位划分成不同的段,每一个段具有不同的意义(正常退出码,异常结束信号,core文件是否生成)。标准提供了一组宏函数用来从statloc中获取相应的信息。

image-20241114204815202

image-20241114204636150

僵尸进程与孤儿进程

如果父进程始终不对子进程进行回收操作,那么子进程便会一直占用进程ID号,内核也会一直保留其进程控制块,但子进程不会占用CPU资源,此时的子进程处于一种僵死状态(Z),称为==僵尸进程==,僵尸进程应该被避免,因为它会造成内存资源的浪费。一种解决僵尸进程的方式是杀死父进程,让僵尸进程称为孤儿进程(如果父进程先于子进程结束,则子进程变成孤儿进程,但会很快被1号进程所领养),让1号进程称为其父进程,1号进程一定会调用进程等待函数回收子进程的资源。

进程替换

大部分情况下,fork函数创建新的子进程后,子进程会调用exec家族函数以执行另个程序。当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main 函数开始执行。调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。

exec会继承如下属性(重点关注框出来的)

image-20241114212035951

exec函数解析

image-20241114210623696

请注意以filename为参数的exec函数遵循如下规则:

  1. filename如果是绝对路径等价于pathname
  2. filename仅是一个文件名则从环境变量PATH和当前目录查找可执行文件

exec用户ID问题

编写程序时应该遵守==最小权限原则,进程在运行时对应的权限应该为当前能正常处理任务的最低权限==。最小权限原则可以避免进程因为权限过高而进行恶意读写的行为。exec所执行的可执行文件所需要的最小权限可能低于在调用exec之前的权限,为了能够降低权限,建议将目标可执行文件设置用户ID为,保证exec在进程替换后有效用户ID是可执行文件的用户ID。

image-20241114220125728

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

c# onnx 调用yolo v11进行目标检测

先上图&#xff0c;支持图片&#xff0c;视频检测 FormYoloV11.cs using Microsoft.ML.OnnxRuntime; using Microsoft.ML.OnnxRuntime.Tensors; using OpenCvSharp; using OpenCvSharp.Dnn; using System; using System.Collections.Generic; using System.Diagnostics; usin…

【多语言】每种语言打印helloworld,编译为exe会占多大空间

文章目录 背景c语言 53KBc 53KBgo 1.8Mdart 4.6Mpython未测试nodejs未测试rust未测试java未测试cmd || bash || powershell 未测试other 背景 各个版本的helloworld&#xff0c;纯属闲的, 环境如下: - win10 - mingw: gcc8.1.0 - go1.21 - dart3.5.4c语言 53KB gcc main.c -…

前端搭建低代码平台,微前端如何选型?

目录 背景 一、微前端是什么&#xff1f; 二、三大特性 三、现有微前端解决方案 1、iframe 2、Web Components 3、ESM 4、EMP 5、Fronts 6、无界&#xff08;文档&#xff09; 7、qiankun 四、我们选择的方案 引入qiankun并使用&#xff08;src外层作为主应用&#xff09; 主应…

CVE-2024-2961漏洞的简单学习

简单介绍 PHP利用glibc iconv()中的一个缓冲区溢出漏洞&#xff0c;实现将文件读取提升为任意命令执行漏洞 在php读取文件的时候可以使用 php://filter伪协议利用 iconv 函数, 从而可以利用该漏洞进行 RCE 漏洞的利用场景 PHP的所有标准文件读取操作都受到了影响&#xff1…

InternVL 多模态模型部署微调实践

目录 0 什么是MLLM 1 开发机创建与使用 2 LMDeploy部署 2.1 环境配置 2.2 LMDeploy基本用法介绍 2.3 网页应用部署体验 3 XTuner微调实践 3.1 环境配置 3.2.配置文件参数解读 3.3 开始微调 4.体验模型美食鉴赏能力 0 什么是MLLM 多模态大语言模型 ( Multimodal Larg…

干货分享之Python爬虫与代理

嗨伙伴们&#xff0c;今天是干货分享哦&#xff0c;可千万不要错过。今天小蝌蚪教大家使用phthon时学会巧妙借用代理ip来更好地完成任务。 让我们先了解一下为什么说咱们要用爬虫代理ip呢&#xff0c;那是因为很多网站为了防止有人过度爬取数据&#xff0c;对自身资源造成损害…

鸿蒙学习生态应用开发能力全景图-赋能套件(1)

文章目录 赋能套件鸿蒙生态应用开发能力全景图 赋能套件 鸿蒙生态白皮书: 全面阐释了鸿蒙生态下应用开发核心理念、关键能力以及创新体验,旨在帮助开发者快速、准确、全面的了解鸿蒙开发套件给开发者提供的能力全景和未来的愿景。 视频课程: 基于真实的开发场景,提供向导式…

netcat工具安装和使用

netcat是一个功能强大的网络实用工具&#xff0c;可以从命令⾏跨⽹络读取和写⼊数据。 netcat是为Nmap项⽬编写的&#xff0c;是⽬前分散的Netcat版本系列的经典。 它旨在成为可靠的后端⼯具&#xff0c;可⽴即为其他应⽤程序和⽤户提供⽹络连接。 一&#xff0c;下载安装 1&a…

【PHP】ThinkPHP基础

下载composer ComposerA Dependency Manager for PHPhttps://getcomposer.org/ 安装composer 查看composer是否安装 composer composer --version 安装 ThinkPHP6 如果你是第一次安装的话&#xff0c;首次安装咱们需要打开控制台&#xff1a; 进入后再通过命令,在命令行下面&a…

【HarmonyOS】应用实现读取剪切板内容(安全控件和自读取)

【HarmonyOS】应用实现读取粘贴板内容(安全控件和自读取) 前言 三方应用 读取系统剪切板是比较常见的功能。可以实现功能入口的快捷激活跳转&#xff0c;以及用户粘贴操作的简化&#xff0c;增强用户的体验感。 但是在用户日渐注重隐私的今天&#xff0c;系统对于剪切板权限的…

飞牛云fnOS本地部署WordPress个人网站并一键发布公网远程访问

文章目录 前言1. Docker下载源设置2. Docker下载WordPress3. Docker部署Mysql数据库4. WordPress 参数设置5. 飞牛云安装Cpolar工具6. 固定Cpolar公网地址7. 修改WordPress配置文件8. 公网域名访问WordPress 前言 本文旨在详细介绍如何在飞牛云NAS上利用Docker部署WordPress&a…

解析安卓镜像包和提取DTB文件的操作日志

概述 想查看一下安卓的镜像包里都存了什么内容 步骤 使用RKDevTool_v3.15对RK3528_DC_HK1_RBOX_K8_Multi_WIFI_13_20230915.2153.img解包 路径: 高级(Advancing) > 固件(firmware) > 解包(unpacking)得到\Output\Android\Image boot.imguboot.imgsuper.img 处理boot.…

LeetCode 热题100(八)【二叉树】(3)

目录 8.11二叉树展开为链表&#xff08;中等&#xff09; 8.12从前序与中序遍历序列构造二叉树&#xff08;中等&#xff09; 8.13路径总和III&#xff08;中等&#xff09; 8.14二叉树的最近公共祖先&#xff08;中等&#xff09; 8.15二叉树中的最大路径和&#xff08;困…

FPGA实现PCIE3.0视频采集转SDI输出,基于XDMA+GS2971架构,提供工程源码和技术支持

目录 1、前言工程概述免责声明 2、相关方案推荐我已有的PCIE方案本博已有的 SDI 编解码方案本博客方案的PCIE2.0版本 3、PCIE基础知识扫描4、工程详细设计方案工程设计原理框图电脑端视频QT上位机XDMA配置及使用XDMA中断模块FDMA图像缓存Native视频时序生成RGB转BT1120SDI转HDM…

纽约大学:指导LLM提出澄清性问题

&#x1f4d6;标题&#xff1a;Modeling Future Conversation Turns to Teach LLMs to Ask Clarifying Questions &#x1f310;来源&#xff1a;arXiv, 2410.13788 &#x1f31f;摘要 &#x1f538;大型语言模型&#xff08;LLM&#xff09;必须经常对高度模糊的用户请求做出…

STM32F1学习——I2C通信

一、I2C通信一带多 在学习通信的时候&#xff0c;我们常会听到串口通信。但串口通信只限定两个设备之间&#xff0c;如果有多个设备&#xff0c;通信的两个设备就要连接上&#xff0c;接线复杂。所以有了总线式通信&#xff0c;在一条总线上可以连接多个设备&#xff0c;这些根…

当你想要conda安装遇到UnavailableInvalidChannel: HTTP 404 NOT FOUND for channel的问题

想要装个虚拟环境&#xff0c;结果遇到404。 看了第一个GitHub帖子中的一句话 UnavailableInvalidChannel: The channel is not accessible or is invalid. Navigator not launching. Issue #9473 conda/conda GitHub 想说那我就把这个not found的channel删掉吧&#xff…

Jmeter中的前置处理器(一)

前置处理器 1--JSR223 PreProcessor 功能特点 自定义数据处理&#xff1a;使用脚本语言处理请求数据&#xff0c;实现高度定制化的数据处理和生成。动态数据生成&#xff1a;在请求发送前生成动态数据&#xff0c;如随机数、时间戳等。变量设置&#xff1a;设置和修改 JMeter…

2023年高校大数据挑战赛A题中文文本纠错求解全过程文档及程序

2023年高校大数据挑战赛 A题 中文文本纠错 原题再现&#xff1a; 中文文本纠错的任务主要是针对中文文本中出现的错误进行检测和纠正&#xff0c;属于人工智能自然语言处理的研究子方向。中文文本纠错通常使用的场景有政务公文、裁判文书、新闻出版等&#xff0c;中文文本纠错…

使用CNN进行验证码识别:深度学习与图像预处理教程

验证码&#xff08;CAPTCHA&#xff09;广泛用于区分人类和自动化程序&#xff08;如机器人&#xff09;&#xff0c;通常由扭曲的字母、数字或符号组成。为了实现验证码的自动识别&#xff0c;深度学习尤其是卷积神经网络&#xff08;CNN&#xff09;非常有效。本文将带你一起…