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

Linux课程六课---Linux进程控制

作者前言 ✨✨✨✨✨✨​ 作者介绍 作者id老秦包你会 简单介绍喜欢学习C语言、C和python等编程语言是一位爱分享的博主有兴趣的小可爱可以来互讨 个人主页::小小页面gitee页面:秦大大 一个爱分享的小博主 欢迎小可爱们前来借鉴进程控制**作者前言**内核Kernel写时拷贝进程终止进程退出场景进程退出方法main函数的返回值退出码_exit和exit进程信号进程等待进程等待的方式waitwaitpid子进程的status父进程的阻塞和非阻塞进程程序替换父进程和子进程执行不同分支的原理替换函数execlexeclpexecv含e的函数内核Kernel定义内核是操作系统的核心部分负责管理系统资源如 CPU、内存、设备和文件系统。它是操作系统与硬件之间的桥梁。功能进程管理创建、调度和终止进程。内存管理分配和回收内存管理虚拟内存。设备管理与硬件设备进行通信提供设备驱动程序。系统调用提供用户程序与内核之间的接口。类型内核可以是单内核Monolithic Kernel或微内核Microkernel。单内核将所有功能集成在一起而微内核则将功能模块化。写时拷贝通常父子代码共享父子再不写入时数据也是共享的当任意一方试图写入便以写时拷贝的方式各自一份副本。应用于物理内存。如图未修改的时候父进程和子进程的对应的内容时相同的权限读取也是相同的。当子进程进行修改就会在页表进行映射由于子进程修改的内容是正确的页表对应的映射权限有错操作系统就会介入在物理内存进行写时拷贝然后更新对应的映射关系权限也会修改。写时拷贝的好处1.提高内存利用率2. 申请地址后拷贝数据过来可以防止很大程度上出现问题。进程终止进程退出场景代码运行完毕结果正确代码运行完毕结果不正确代码异常终止进程退出方法main函数的返回值退出码main函数的返回值是0表示进程正常结束不为0表示进程不是正常结束。这个main函数的返回值就是进程的退出码 是让bash看的可以使用如下命令进行查看最近一次进程的退出码echo $?这个进程退出码会写入到进程的PCB里面的当再一次执行这个命令就会返回0查看的上一个echo这个进程的退出码这些退出码是需要计算机获取的但是我们是不知道是否执行成功所以就会有了把退出码转换成对应的信息描述。需要使用到strerron函数来进行查看对应的的信息#includestdlib.h #includestdio.h #includeassert.h #includestring.h int main() { for(int i 0; i 200; i) { printf(%d: %s\n, i , strerror(i)); } return 0; }如图:虽然我们可以知道main函数的返回码但是我们在写其他的函数的时候也是希望在调用函数的的时候查看对应的情况调用函数我们想知道的函数的执行结果函数的执行情况前面的我们知道C语言的文件的fopen函数如果打开失败我们可以使用errno这个全局变量来获取对应的错误码信息。在C语言中errno 是一个全局变量用于指示最近一次系统调用或库函数调用的错误类型。它是一个整型变量定义在 errno.h 头文件中。使用 errno 可以帮助程序员了解错误的具体原因从而采取相应的处理措施。#includeiostream #includestdio.h #includeerrno.h #includeassert.h #includestring.h using namespace std; int main(int argc, char* argv[], char *env[]) { FILE* file fopen(1.txt,r); if(file NULL) printf(%s, strerror(errno)); return 1; fclose(file); return 0; }上述的就是进程退出场景的1和2情况而3的情况发生后进程的退出码也就不那么重要了。_exit和exit进程信号一般进程异常常常是收到了信号在写代码的时候main函数结束往往表示进程结束而其他的函数结束往往表示函数调用结束。一般情况下前面我们写代码的时候有时候会因为访问野指针而崩溃这是因为进程收到了异常信号。我看可以使用kill -l查看对应的命令参数如图我们可以写一段代码然后运行起来然后给这个进程发送一个信号这里使用的kill -8includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h int main() { while(1) { printf(getpid: %d\n, getpid()); sleep(1); } return 0; }效果如下可以说明不同的信号代表不同的异常信息。除了使用kill命令进行进程的终止还可以exit函数终止进程。这个函数里面的参数status表示的是进程的退出状态退出码#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h int main() { while(1) { printf(getpid: %d\n, getpid()); sleep(1); exit(3);//exit终止进程的退出,退出码为3 } return 0; }如图进程运行一下就终止了退出码为3exit和_exit的区别:exit的代码#includeiostream #includestdio.h #includeerrno.h #includeassert.h #includestdlib.h #includestring.h #includeunistd.h using namespace std; void fun() { printf(开始运行);//没有\n不会刷新缓冲区 } void funend() { printf(运行结束\n); } int main(int argc ,char* argv[], char* env[]) { fun(); _exit(1); funend(); return 0; }可以看到,在没有设置sleep的时候,会输出字符出来当把exit换成_exit的时候就会发现,运行结果如下:没有输出字符出来至于_exit这个函数的使用,和exit的使用的是一模一样的,但是有一点不一样的就是,exit函数比_exit函数多了一步,刷新缓冲区.缓冲区是不在操作系统里的。这个缓冲区叫做库缓冲区exit是c语言提供的而_exit是系统提供的.所以可以说exit底层调用的就是_exit这个系统调用由此我们可以总结出任意进程的执行情况由两个数字进行表示一个是进程的信号值一个是退出码进程的终止是操作系统操作,进程的创建和销毁都是操作系统进行操作的进程的创建首先创建对应的PCB和 进程地址空间页表然后操作系统把数据加载到内存上然后进行映射进程终止创建的地址全部释放掉保留PCB等待父进程进行释放进程等待进程等待必要性之前讲过子进程退出父进程如果不管不顾就可能造成‘僵尸进程’的问题进而造成内存泄漏。另外进程一旦变成僵尸状态那就刀枪不入“杀人不眨眼”的kill -9 也无能为力因为谁也没有办法杀死一个已经死去的进程。最后父进程派给子进程的任务完成的如何我们需要知道。如子进程运行完成结果对还是不对或者是否正常退出。父进程通过进程等待的方式回收子进程资源获取子进程退出信息。一个进程的结束会保留对应进程的PCB然后等待父进程进行释放进程等待的原因父进程通过wait方式回收子进程的资源通过wait方式获取到子进程的退出信息进程等待的方式wait使用这个函数需要导入#include sys/types.h #include sys/wait.h pid_t wait(int *status);status: 一个指向整数的指针用于存储子进程的退出状态。如果不需要状态信息可以传入NULL我们只需传入一个整数地址然后就会在父进程进行把子进程的退出码返回到这个整数上覆盖。返回值成功时返回终止的子进程的PID失败时返回-1并设置errno下面写一段代码进行验证#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { pid_t id fork(); //子进程运行 if(id 0) { int cnt 6; while(cnt) { printf(I is child pid: %d, ppid: %d\n,getpid(), getppid()); sleep(1); cnt--; } exit(0); } //父进程等待10秒 sleep(10); //父进程阻塞等待 pid_t rid wait(NULL); if(rid 0) { printf(wait succes, rid:%d\n,rid); } sleep(3); return 0; }当运行成功后查看对应的进程运行情况就会发现父进程等待到子进程运行完成进程运行到对应时间就会把子进程的僵尸状态释放掉。如图waitpidpid_t waitpid(pid_t pid, int *status, int options);参数说明pid:如果 pid 为 -1表示等待任意子进程等待任一个子进程。与wait等效。如果 pid 为具体的进程 ID表示等待该特定子进程。如果 pid 为 0表示等待与调用进程同一进程组中的任意子进程。如果 pid 为大于 0 的值表示等待该特定子进程。status:指向整数的指针用于存储子进程的退出状态。如果不需要状态信息可以传递NULL。options:可以是 0 或者其他选项如 WNOHANG非阻塞等待等。WNOHANG: 若pid指定的子进程没有结束则waitpid()函数返回0不予以等待。若正常结束则返回该子进程的ID。返回值当正常返回的时候waitpid返回收集到的子进程的进程ID如果设置了选项WNOHANG,而调用中waitpid发现没有已退出的子进程可收集,则返回0如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在代码如下#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { pid_t id fork();//如果是父进程是具体数字子进程就是0 if(id 0) { int i 0; while(i 5) { printf(child pid: %d, father pid: %d\n, getpid(), getppid()); i; sleep(1); } exit(1);//结束进程,退出码为1 printf(chila process end\n); } //父进程进行等待 int status 0;//设置子进程的退出状态这个不是退出码和信号 printf(当前的进程的pid:%d\n, id); pid_t childId waitpid(id, status,0); printf(父进程释放完毕,释放的子进程id:%d, 子进程的状态码: %d\n, childId, status); sleep(10); return 0; }效果如下子进程的status当我们查看对应的子进程的退出码的时候就会发现 子进程的退出码为啥不是exit设置的1呢 为啥是256原因如下一个整形的大小是32bitstatus不能简单的当作整形来看待可以当作位图来看待具体细节如下图只研究status低16比特位因为在Linux中一个整形的大小是32位我们代码中的exit设置为1所以会在16位bit后面补上八位的二进制写法如下子进程没有收到信号就会补全后面的信号编码格式组合起来就会变成一个整数也就是status这个整数如图当我们把代码修改为如下#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { pid_t id fork();//如果是父进程是具体数字子进程就是0 if(id 0) { int i 0; while(i 5) { printf(child pid: %d, father pid: %d\n, getpid(), getppid()); i; sleep(1); } exit(1);//结束进程,退出码为1 printf(chila process end\n); } //父进程进行等待 int status 0;//设置子进程的状态码 printf(当前的进程的pid:%d\n, id); pid_t childId waitpid(id, status,0); if(childId 0) { printf(父进程释放完毕,释放的子进程id:%d, 子进程的状态码: %d, 进程的退出码:%d, 进程的信号:%d\n, childId, status, (status8)0xFFFFFF,status0x7F); sleep(10); } return 0; }我们发送向子进程发送一个8的信号就会观看到对应的信号码我们下个获取对应的信息除了可以手动计算在C语言中也提供对应的宏直接获取到对应的的退出码以及进程是否为正常终止:WIFEXITED(status): 若为正常终止子进程返回的状态则为真。查看进程是否是正常 出WEXITSTATUS(status): 若WIFEXITED非零提取子进程退出码。查看进程的退出码疑问1为啥我们不在父进程中使用全局变量来控制子进程的状态码和信号码而是使用waitpid来获取对应的状态码因为虽然我们使用fork创建子进程子进程会继承对应的父进程的全局变量但是当父进程或者子进程对这些全局变量进行修改修改的进程就会发生写时拷贝进程之间互不干扰对应的的进程地址空间是通过页面进行映射到对应的物理内存哪个进程修改全局变量都不会影响其他进程所以就会使用waitpid传入status这个参数获取对应的子进程的状态码。父进程的阻塞和非阻塞阻塞在使用waitpid的时候会发现有一个参数就是options当设置为0的时候父进程会等待子进程执行结束这一过程父进程会进行阻塞等待只会调用调用一次系统调用。非阻塞如果设置为宏WNOHANG如果子进程还没结束就会让父进程获取子进程的错误信息不管子进程是否运行正常都会返回错误的信息让父进程继续执行不会进行阻塞等待然后循环调用waitpid进行访问对应的的子进程的状态直到子进程运行结束期间会进行多次的调用系统调用。父进程可以继续执行其他任务同时定期检查子进程状态。如图代码如下#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h typedef void(*fun_t)(); #define NUM 10 fun_t task[NUM]; void taskLog() { printf(this is tasklog\n); } void taskNet() { printf(this is taskNet\n); } void initTask() { task[0] taskLog; task[1] taskNet; task[2] NULL; } void funtionPrint() { initTask(); printf(任务开始\n); for(int i 0; task[i];i) { printf(%p\n, task[i]); (*task[i])(); } } int main() { //创建子进程 pid_t id fork(); if(id0) { int elemet 20; while(elemet 6) { printf(child pid: %d father pid: %d\n, getpid(), getppid()); sleep(1); elemet--; } printf(child ended\n); exit(1); } //父进程开始操作 // int status 0; printf(father state\n); while(1) { pid_t childId waitpid(id, status, WNOHANG); printf(child status: %d, child exit code %d, childe exit sigend: %d\n, status, WEXITSTATUS(status), (status0x7F)); printf(当前获取到子进程的id为%d, 需要等待到的子进程id:%d\n, childId, id); sleep(1); funtionPrint(); if(childId id) break; } return 0; }只需把任务写入循环中父进程在访问后不会进行阻塞就是执行自己的任务。进程程序替换原理如下如上图前面我们知道一个进程的基本工作原理进程创建首先会创建对应的PCB进程拥有对应的进程地址空间通过页表映射到对应的物理内存执行对应的内容而进程程序替换就是在可执行程序加载到内存这一步进行相关的操作把新程序的内容覆盖原理的物理空间的内容这样就达到了一个程序可以运行不一样的程序的内容。这个过程不会创建新的PCB父进程和子进程执行不同分支的原理前面我们使用fork创建子进程就会好奇进程的工作原理就是需要通过页表映射到物理内存父进程的页表和子进程的页表相同可以找到对应的内容但是如果有代码分支比如只有子进程才能执行的代码这块是怎么让父进程无法执行的呢这是因为子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调用exec前后该进程的id并未改变。替换函数#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[]);execlint execl(const char *path, const char *arg, ...);参数说明path 表示对应的文件路径arg表示运行的方式前面我们知道在Linux中的命令都是一个个对应的的程序都是在/usr/bin/中只不过是添加到对应的环境变量 比如ls -a中ls、-a就相当于参数arg /usr/bin/ls就是path。返回参数 execl运行失败就会返回-1成功不会返回下面写一个ls的代码#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { printf(start process\n); execl(/usr/bin/ls, ls, -a, -l, NULL); printf(process end\n); return 0; }需要注意的是execl函数的最后一个参数一定是NULL不是字符效果如下可以看出运行成功后执行的是ls执行的功能但是当执行完execl函数后后面的代码就不运行了。因为把ls程序的代码替换进去了导致本该运行execl函数的后面的代码被ls的代码覆盖了。如果execl函数执行失败就会执行后面的代码进程替换工作本质就是程序加载 创建一个进程首先会先创建对应的的PCB地址空间页表然后再把程序加载到内存多进程版本上面的代码是在父进程进行我们可以创建一个子进程然后让子进程进行执行这些代码#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { pid_t id fork(); if(id 0) { printf(start process\n); printf(child pid: %d , ppid:%d\n, getpid(), getppid()); sleep(20); execl(/usr/bin/ls, ls, -a, -l, NULL); printf(process end\n); } int status 0; pid_t childId waitpid(id, status, 0); if(childId 0) printf(child exit status: %d, exit code:%d exit signal: %d\n, status, WEXITSTATUS(status), status0x7f); return 0; }这里通过子进程去进行程序替换为啥子进程不会影响父进程呢 因为为了保护进程的独立性子进程在进行程序替换就是把程序加载到内存进行覆盖会进行写时拷贝进而不会影响父进程。execlp小知识pPATH不用告诉程序在哪里系统替换过程会在PATH中去寻找int execlp(const char *file, const char *arg, ...);代码如下#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { pid_t pid fork(); if(pid 0) { printf(I am is child, pid:%d\n, getpid()); execlp(ls, ls, -a,-l, NULL); printf(child end\n); } int stat 0; pid_t childPid waitpid(pid, stat, 0); printf(child pid: %d\n, childPid); return 0; }效果execvint execv(const char *path, char *const argv[]);这个函数的传参不像前面的函数传参一样这里第二个参数和我们前面的main函数的第二个参数是一样的 mainint argc char* const argv[]; 需要我们自己构建对于的argv,重要的 一点就是NULL结尾(不是字符).所以我们可以大致理解就是l:就是列表传参v:就是vector传参#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main() { pid_t pid fork(); if(pid 0) { char* argv[] { (char*)ls, (char*)-a, (char*)-l, NULL }; printf(child end , child pid: %d\n, getpid()); execv(/usr/bin/ls, argv); } int stata 0; pid_t childPid waitpid(pid, stata, 0); printf(child pid is %d, childPid); printf(father end\n); return 0; }效果如下:含e的函数int execve(const char *path, char *const argv[], char *const envp[]); int execle(const char *path, const char *arg, ...,char *const envp[]); int execvpe(const char *file, char *const argv[], char *const envp[]);e跟环境变量有关如图这些就是环境变量的PYTH就是其中的一个下面我们以execle为例我们自己写一个程序然后通过进程程序替。在Linux中.cc 或者.cpp或者.cxx都是C的后缀名.cc#includeiostream #includestdio.h using namespace std; int main(int argc, char* argv[]) { for(int i 0; i argc; i) { cout argv[i] endl; } std::cout 我是程序 std::endl; return 0; }.cpp#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main(int argc, char* argv[]) { pid_t pid fork(); if(pid 0) { printf(我是子进程开始\n); execle(./mytest, ./mytest, -a, -d, -c, NULL); } int stat 0; pid_t pidChlid waitpid(pid, stat, 0); printf(父进程结束, 等待的子进程为:%d\n, pidChlid); return 0; }结果如下:在Linux下写的任何语言都可以进行相关的进程程序替换,因为这些语言运行起来就是一个个进程,其他语言的调用比如shell脚本,bash为命令行, mytest.sh为命令行参数,书写如下:execle(/usr/bin/bash, bash, mytest.sh, NULL);没有传入env运行成功的上面我们发现, execle函数,子进程是没有传入一个对应的的环境变量表的,为啥会运行成功呢?这是因为在进程地址空间中有一段空间是存放命令行参数环境变量的,创建子进程,会复制父进程的进程地址空间, 而进程替换是不会替换环境变量的数据的,还要一个因素就是在mytest.cc中是不涉及环境变量的运行起来是成功的,在此,如果我们需要子进程在继承父进程的环境变量的基础上增加新的环境,可以使用函数putenv来进行增加,如果想要从0开始可以自己构建例如:char *const envp[] {PATH/bin:/usr/bin, TERMconsole, NULL};.c#includestdlib.h #includestdio.h #includeassert.h #includestring.h #includeunistd.h #includesys/types.h #includesys/wait.h int main(int argc, char* argv[], char* env[]) { pid_t pid fork(); if(pid 0) { printf(我是子进程开始\n); //for(int i 0; env[i]; i) //{ // printf(%s\n, env[i]); //} printf(开始程序替\n); execle(./mytest, ./mytest, -a, -d, -c, NULL, env); } int stat 0; pid_t pidChlid waitpid(pid, stat, 0); printf(父进程结束, 等待的子进程为:%d\n, pidChlid); return 0; }.cc#includeiostream #includestdio.h #includeunistd.h using namespace std; int main(int argc, char* argv[]) { extern char ** environ; for(int i 0; environ[i]; i) { cout environ[i] endl; } std::cout 我是程序 std::endl; return 0; }

相关文章:

Linux课程六课---Linux进程控制

作者前言 🎂 ✨✨✨✨✨✨🍧🍧🍧🍧🍧🍧🍧🎂 ​🎂 作者介绍: 🎂🎂 🎂 🎉🎉&#x1f389…...

华为OD机试双机位C卷-乘坐保密电梯 (Py/Java/C/C++/Js/Go)

乘坐保密电梯 华为OD机试双机位C卷 华为OD上机考试双机位C卷 100分题型 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 题目描述 有一座保密大楼,你从0楼到达指定楼层m,必须这样的规则乘坐电梯: 给定一个数字序列,每…...

double-conversion开发者必备:测试用例编写与验证完全指南

double-conversion开发者必备:测试用例编写与验证完全指南 【免费下载链接】double-conversion Efficient binary-decimal and decimal-binary conversion routines for IEEE doubles. 项目地址: https://gitcode.com/gh_mirrors/do/double-conversion doubl…...

Gatt社区贡献指南:如何参与开源项目并提交PR

Gatt社区贡献指南:如何参与开源项目并提交PR 【免费下载链接】gatt Gatt is a Go package for building Bluetooth Low Energy peripherals 项目地址: https://gitcode.com/gh_mirrors/ga/gatt Gatt是一个用于构建蓝牙低功耗(BLE)外设…...

如何通过eluceo iCal 2创建重复事件与例外日期?

如何通过eluceo iCal 2创建重复事件与例外日期? 【免费下载链接】iCal iCal-creator for PHP 项目地址: https://gitcode.com/gh_mirrors/ic/iCal eluceo iCal 2是一款强大的PHP iCal创建工具,能够帮助开发者轻松生成符合iCalendar标准的日历文件…...

如何将Bramses‘ Highly Opinionated Vault 2023与外部工具集成:Readwise、Alfred与Zotero协同工作流

如何将Bramses Highly Opinionated Vault 2023与外部工具集成:Readwise、Alfred与Zotero协同工作流 【免费下载链接】bramses-highly-opinionated-vault-2023 A highly opinionated, fully featured Obsidian vault that can get you from Zero to Zettelkasten lic…...

TextAttack实战教程:用5行代码实现BERT模型的对抗性攻击

TextAttack实战教程:用5行代码实现BERT模型的对抗性攻击 【免费下载链接】TextAttack TextAttack 🐙 is a Python framework for adversarial attacks, data augmentation, and model training in NLP https://textattack.readthedocs.io/en/master/ 项…...

打造响应式导航:laravel-menu与Bootstrap 3/5的完美结合方案

打造响应式导航:laravel-menu与Bootstrap 3/5的完美结合方案 【免费下载链接】laravel-menu A quick way to create menus in Laravel 项目地址: https://gitcode.com/gh_mirrors/la/laravel-menu laravel-menu是一款专为Laravel框架设计的菜单创建工具&…...

Matic Network合约升级机制解析:Proxy模式与Governance如何确保系统可扩展性

Matic Network合约升级机制解析:Proxy模式与Governance如何确保系统可扩展性 【免费下载链接】contracts Smart contracts comprising the business logic of the Matic Network 项目地址: https://gitcode.com/gh_mirrors/con/contracts Matic Network作为高…...

Clojure开发者的Python之旅:从语法差异到实战技巧

Clojure开发者的Python之旅:从语法差异到实战技巧 【免费下载链接】libpython-clj Python bindings for Clojure 项目地址: https://gitcode.com/gh_mirrors/li/libpython-clj 作为一名Clojure开发者,当你需要与Python生态系统交互时,…...

DuckieTV自定义界面教程:从主题切换到快捷键设置,打造你的专属追剧工具

DuckieTV自定义界面教程:从主题切换到快捷键设置,打造你的专属追剧工具 【免费下载链接】DuckieTV A web application built with AngularJS to track your favorite tv-shows with semi-automagic torrent integration 项目地址: https://gitcode.com…...

dbblog常见问题解决:从安装到运行的15个实用技巧

dbblog常见问题解决:从安装到运行的15个实用技巧 【免费下载链接】dbblog 基于SpringBoot2.xVue2.xElementUIIviewElasticsearchRabbitMQRedisShiro的多模块前后端分离的博客项目 项目地址: https://gitcode.com/gh_mirrors/db/dbblog dbblog是一个基于Sprin…...

eslint-plugin-jest完全指南:如何用ESLint提升Jest测试代码质量

eslint-plugin-jest完全指南:如何用ESLint提升Jest测试代码质量 【免费下载链接】eslint-plugin-jest ESLint plugin for Jest 项目地址: https://gitcode.com/gh_mirrors/es/eslint-plugin-jest eslint-plugin-jest是一款专为Jest测试框架设计的ESLint插件…...

从0到1搭建Jest测试环境:eslint-plugin-jest助力代码质量保障

从0到1搭建Jest测试环境:eslint-plugin-jest助力代码质量保障 【免费下载链接】eslint-plugin-jest ESLint plugin for Jest 项目地址: https://gitcode.com/gh_mirrors/es/eslint-plugin-jest 在现代JavaScript开发中,高质量的测试是保障代码可…...

pyproj性能优化指南:提升地理空间数据处理效率的5个方法

pyproj性能优化指南:提升地理空间数据处理效率的5个方法 【免费下载链接】pyproj Python interface to PROJ (cartographic projections and coordinate transformations library) 项目地址: https://gitcode.com/gh_mirrors/py/pyproj pyproj作为Python接口…...

终极指南:如何使用JsonSchema数据生成自动创建符合规范的测试数据

终极指南:如何使用JsonSchema数据生成自动创建符合规范的测试数据 【免费下载链接】json-everything System.Text.Json-based support for all of your JSON needs. 项目地址: https://gitcode.com/gh_mirrors/jso/json-everything JsonSchema数据生成是json…...

LaTeXML常见问题解答:从入门到精通的避坑指南

LaTeXML常见问题解答:从入门到精通的避坑指南 【免费下载链接】LaTeXML LaTeXML: a TeX and LaTeX to XML/HTML/ePub/MathML translator. 项目地址: https://gitcode.com/gh_mirrors/la/LaTeXML LaTeXML是一款强大的TeX/LaTeX到XML/HTML/ePub/MathML转换器&a…...

Lilith窗口管理器实战:终端模拟器与文件管理器使用教程

Lilith窗口管理器实战:终端模拟器与文件管理器使用教程 【免费下载链接】lilith x86-64 os made in crystal 项目地址: https://gitcode.com/gh_mirrors/li/lilith Lilith是一款基于Crystal语言开发的x86-64操作系统,其内置的窗口管理器提供了简洁…...

DeepGTAV v2:将GTA V转变为视觉自动驾驶研究环境的终极指南

DeepGTAV v2:将GTA V转变为视觉自动驾驶研究环境的终极指南 【免费下载链接】DeepGTAV 项目地址: https://gitcode.com/gh_mirrors/dee/DeepGTAV DeepGTAV v2是一款强大的GTA V插件,能够将这款流行的开放世界游戏转变为功能完备的视觉自动驾驶研…...

掌握ScalaTest Matchers:让断言代码更简洁、更可读

掌握ScalaTest Matchers:让断言代码更简洁、更可读 【免费下载链接】scalatest A testing tool for Scala and Java developers 项目地址: https://gitcode.com/gh_mirrors/sc/scalatest ScalaTest Matchers是一款专为Scala和Java开发者设计的测试工具&#…...

@react-native-menu/menu深入剖析:从源码看跨平台菜单组件的实现原理

react-native-menu/menu深入剖析:从源码看跨平台菜单组件的实现原理 【免费下载链接】menu UIMenu Component for React Native 项目地址: https://gitcode.com/gh_mirrors/men/menu React Native开发中,跨平台UI组件的实现一直是开发者关注的重点…...

curriculum项目源码分析:深入理解Elixir模块设计与实现

curriculum项目源码分析:深入理解Elixir模块设计与实现 【免费下载链接】curriculum 项目地址: https://gitcode.com/gh_mirrors/curricu/curriculum curriculum项目是一个基于Elixir语言的开源学习资源库,通过丰富的示例项目和练习展示了Elixir…...

wsl自动识别和附加串口

使用连接串口的程序链接:https://pan.baidu.com/s/1-nCDuv8nuGTD7jlUOaSQQQ?pwdqlql将连接程序直接下载放在桌面,程序1mb不到,不需要担心占用内存。1.先打开vscode进入wsl环境,然后插上esp32设备。2.然后进入程序,寻找带有JTAG字…...

eblog搜索引擎架构:RabbitMQ+Elasticsearch实现高效全文检索

eblog搜索引擎架构:RabbitMQElasticsearch实现高效全文检索 【免费下载链接】eblog eblog是一个基于Springboot2.1.2开发的博客学习项目,为了让项目融合更多的知识点,达到学习目的,编写了详细的从0到1开发文档。主要学习包括&…...

DC-TTS与Tacotron性能对比:为什么卷积网络训练速度更快?

DC-TTS与Tacotron性能对比:为什么卷积网络训练速度更快? 【免费下载链接】dc_tts A TensorFlow Implementation of DC-TTS: yet another text-to-speech model 项目地址: https://gitcode.com/gh_mirrors/dc/dc_tts 在文本转语音(TTS&…...

华为OD机试双机位C卷-转盘寿司(C/C++/Py/Java/Js/Go)

转盘寿司 华为OD机试双机位C卷真题目录点击查看: 华为OD机试双机位C卷真题题库目录|机考题库 + 算法考点详解 华为OD机试双机位C卷 华为OD上机考试双机位C卷 100分题型 题目描述 寿司店周年庆,正在举办[优惠活动]回馈新老客户。 寿司转盘上总共有 n 盘寿司,prices[i] 是第…...

媒体会话API在Syntax Podcast网站中的集成与应用

媒体会话API在Syntax Podcast网站中的集成与应用 【免费下载链接】website Syntax Podcast Website 项目地址: https://gitcode.com/gh_mirrors/website2/website Syntax Podcast网站作为专业的前端开发播客平台,为了提升用户的音频体验,巧妙地集…...

Surya flatten命令:一键合并Solidity源代码的简单方法

Surya flatten命令:一键合并Solidity源代码的简单方法 【免费下载链接】surya A set of utilities for exploring Solidity contracts 项目地址: https://gitcode.com/gh_mirrors/sur/surya Surya是一个强大的Solidity智能合约工具集,而flatten命…...

终极 Meshtastic-Android 新手教程:从安装到发送第一条 mesh 消息的完整步骤

终极 Meshtastic-Android 新手教程:从安装到发送第一条 mesh 消息的完整步骤 【免费下载链接】Meshtastic-Android Android application for Meshtastic 项目地址: https://gitcode.com/gh_mirrors/me/Meshtastic-Android Meshtastic-Android 是一款强大的 m…...

如何为Thor机械臂设计自定义末端执行器?创意案例分享

如何为Thor机械臂设计自定义末端执行器?创意案例分享 【免费下载链接】Thor DIY 3D Printable Robotic Arm 项目地址: https://gitcode.com/gh_mirrors/thor10/Thor Thor机械臂是一款开源的DIY 3D打印机器人手臂项目,允许用户根据需求定制各种功能…...