控制欲过强的Linux小进程

控制欲强?视奸?普通人那才叫视奸,您是皇帝,天下大事无一逃过您的耳目,您想看什么就看什么,臣怀疑他在朋友圈私养兵士,囤积枪甲,蓄意谋反,图谋皇位啊!

哈哈哈哈开个玩笑,这篇就主要讲讲Linux进程的控制吧~
fork( )
由于fork()之前也说过啦(从已存在进程中创建一个新进程:新进程为子进程,原进程为父进程),所以下面主要讲内核的操作,进程调用fork,当控制转移到内核中的fork代码后,内核做:
1.分配新的内存块和内核数据结构给子进程
2.将父进程部分数据结构内容拷贝至子进程
3.添加子进程到系统进程列表当中
4.fork返回,开始调度器调度

当一个进程调用fork之后,就有两个二进制代码相同的进程。而且它们都运行到相同的地方。但每个进程都将可以 开始它们自己的旅程:
#include <unistd.h>
#include<stdio.h>
int main(void)
{pid_t pid;printf("Before: pid is %d\n", getpid());if ((pid = fork()) == -1)perror("fork()"), exit(1);printf("After:pid is %d, fork return %d\n", getpid(), pid);sleep(1);return 0;
}

先来下个定义:
进程=内核的相关管理数据结构(task_struct + mm_struct + 页表)+ 代码和数据
已知fork函数的返回值是这样的:
子进程返回0
父进程返回子进程的pid
那为什么捏?
原因其实也很简单,爹得知道儿子名,杀掉他啊等待他啊,爹总要知道的(为了方便父进程对紫禁城进行标识,进而进行管理)
进程具有独立性就在于紫禁城代码数据和父进程共享,但因为写时拷贝又不影响父进程
fork常规用法
一个父进程希望复制自己,使父子进程同时执行不同的代码段(父进程等待客户端请求,生成子 进程来处理请求)
一个进程要执行一个不同的程序(子进程从fork返回后,调用exec函数)
fork调用失败原因
系统中有太多的进程
实际用户的进程数超过了限制
进程终止
终止是在做什么
进程终止就是在释放曾经的代码和数据所占据的空间,也是在释放内核数据结构(task_struct,当进程状态是Z就要释放对应PCB)
终止三种情况
先来看两段代码:
#include<stdio.h>
#include<unistd.h>int main()
{printf("hello world!\n");return 0;
}
#include<stdio.h>
#include<unistd.h>int main()
{printf("hello world!\n");return 100;
}
只有返回值不一样对吧,对?取内容会发现也不一样:

echo是内建命令,打印的都是bash内部的变量数据
?:父进程bash获取到的最近一个紫禁城的退出码 (0:成功,!0:失败)
退出码存在意义:告诉关心方(父进程)任务完成如何
因为成功的退出码就是0,而!0有很多,所以不同!0值一方面表示失败,一方面还表示失败的原因
可以这样打印下错误信息:
#include<stdio.h>
#include<unistd.h>
#include<string.h>int main()
{int errcode = 0;for (errcode = 0; errcode <= 255; errcode++){printf("%d:%s\n", errcode, strerror(errcode));}return 0;
}
那么父进程知道紫禁城退出码因为点撒捏?
因为:!要知道紫禁城退出情况,正常退出了嘛,错误了嘛,错哪了呀,,,
错误码可以自己设定:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
int Div(int x, int y)
{if (0 == y){return -1;}else{return x / y;}
}
int main()
{printf("%d\n",Div(-1,1));return 0;
}
但是这样没法判断是y==0导致返回错误码-1,还是本来的计算结果就是-1
所以可以这样改:
#include<stdio.h>
#include<unistd.h>
#include<string.h>//自定义枚举常量
enum
{Success = 0,Div_Zero,Mod_Zero,
};int exit_code = Success;int Div(int x, int y)
{if (0 == y){exit_code = Div_Zero;return -1;}else{return x / y;}
}
int main()
{printf("%d\n", Div(-1, 1));return exit_code;
}
还可以接着写接口补充错误信息:
#include<stdio.h>
#include<unistd.h>
#include<string.h>//自定义枚举常量
enum
{Success = 0,Div_Zero,Mod_Zero,
};int exit_code = Success;const char* CodeToErrString(int code)
{switch (code){case Success:return "Success";case Div_Zero:return "div zero!";case Mod_Zero:return "mod zero!";default:return "unknow error!";}
}int Div(int x, int y)
{if (0 == y){exit_code = Div_Zero;return -1;}else{return x / y;}
}
int main()
{printf("%d\n", Div(-1, 1));printf("%s\n", CodeToErrString(exit_code));return exit_code;
}
来看看进程终止的三种情况吧:
1.代码跑完,结果正确
2.代码跑完,结果不正确(正确与否可通过进程退出码决定)
3.代码执行时,出现了异常,提前退出了(系统&&自定义退出码)
什么是崩溃?
就是编译运行的时候,操作系统发现你的进程做了不该做的是事,于是OS杀掉了你的进程
那异常了退出码还有意义吗(肯定没有啊,作弊拿到60和作弊拿到100被抓没区别)
进程出现了异常,本质是因为进程收到了OS发给进程的信号
比如说,来上一份妇孺皆知的代码:
#include<stdio.h>
#include<unistd.h>
int main()
{while (1){printf("I am a process,pid:%d\n", getpid());}return 0;
}
这进程能一直运行下去,但是我们可以通过kill的方式干掉它:
kill -9 pid;
这进程没有出现异常,但是由于进程收到了OS发给进程的信号,所以进程不得不终止
再来一瓶野指针:
#include<stdio.h>
#include<unistd.h>
int main()
{int* p = NULL;while (1){printf("I am a process,pid:%d\n", getpid());sleep(1);*p = 100; //看好了小登中登老登,这是故意哒!}return 0;
}
在Linux中运行这段代码会发现出现段错误:Segmentation fault:

不嘻嘻 ,段错误,OS提前终止进程
我们通过观察进程退出的时候退出信号是多少就可以判断我们的进程为何异常了
判断流程:
1.先确认是否异常
2.不是异常就是代码跑完了,直接看退出码
衡量一个进程退出,只需要两个数字:退出码,退出信号
进程退出时会把退出码和退出信号写入PCB(方便父进程知道)
如何进行终止
main函数return就表示进程终止啦(非main函数return,代表函数结束)
代码调用exit函数(头文件为stdlib.h)
exit(0); //里面数字是return数
还有个东西叫_exit( )
和exit的区别就是,它在程序结束的时候并不会冲刷缓冲区
缓冲区必定在_exit()之上

exit在调用_exit前还做了其他工作:
1. 执行用户通过 atexit或on_exit定义的清理函数
2. 关闭所有打开的流,所有的缓存数据均被写入
3. 调用_exit
除了exit,return是一种更常见的退出进程方法。执行return n等同于执行exit(n)(调用main的运行时函数会将main的返回值当做 exit的参数)
进程等待
是什么
任何子进程在退出的情况下,一般必须要被父进程进行等待
为什么捏?
你想奥,如果进程在退出时,父进程不管不顾,退出进程,状态将会变成Z(僵尸状态),发生内存泄漏(进程一旦变成僵尸状态,那就刀枪不入,“杀人不眨眼”的kill -9 也无能为力,因为谁也没有办法杀死一个已经死去的进程,就像是永远没办法叫醒一个装睡的人)
1.父进程通过等待,解决紫禁城退出的僵尸问题,回收系统资源(一定要考虑的)
2.获取紫禁城的退出信息知道紫禁城是什么原因退出的(可选功能)
怎么办
要来看两个可爱的函数:wait、waitpid
wait:
返回值:等待成功时,紫禁城pid
参数:等待任意一个紫禁城退出(是输出型的参数,获取紫禁城退出状态,不关心可以置NULL)
pid_t wait(int* status);

上代码!
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt = 5;while (cnt){printf("I am child process,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit ...\n");exit(0);}sleep(3);pid_t rid = wait(NULL);if (rid > 0){printf("wait success,rid:%d\n", rid);}sleep(3);printf("father quit\n");return 0;
}
一遍运行一边开监控脚本看看怎么个事:
while :; do ps ajx | head -1 && ps ajx | grep myprocess | grep -v grep; sleep 1;done
可以看到紫禁城在被父进程回收前是处于僵尸状态的:


父进程在等待时候也没干其他事,只是等
给大家看看单核处理器小猫:

polo tiu ~,橘域网链接已断开
如果紫禁城没有退出,父进程其实一直在进行阻塞等待
紫禁城本身就是软件,父进程本质是在等待某种软件条件就绪
阻塞等待?
怎么个事?
等待硬件or软件,本质都是数据结构对象
来康康waitpid:

关于这个就改一下就好:
pid_t rid = waitpid(-1, NULL, 0);
作用和上面的也一样(-1是在等任意一个的意思),等待紫禁城,等待到了哪个就返回哪个,那样的还准备俩函数干啥,别着急,这样就能等待特定的了,我是在等,可我在等的只是你:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt = 5;while (cnt){printf("I am child process,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit ...\n");exit(0);}sleep(3);pid_t rid = waitpid(id, NULL, 0);if (rid > 0){printf("wait success,rid:%d\n", rid);}sleep(3);printf("father quit\n");return 0;
}
也是可能失败的(但基本上不会失败):
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt = 5;while (cnt){printf("I am child process,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit ...\n");exit(0);}sleep(3);pid_t rid = waitpid(id+1, NULL, 0);if (rid > 0){printf("wait success,rid:%d\n", rid);}else{printf("wait failed\n");}sleep(3);printf("father quit\n");return 0;
}
再来回看这个函数:
pid_t waitpid(pid_t pid, int status, int options);
返回值:
当正常返回的时候waitpid返回收集到的子进程的进程ID(等待成功,紫禁城退出
父进程回收成功)
若返回值为0,那证明检测成功,但紫禁城并未退出,需要再次进行等待
若设置了选项WNOHANG,调用中waitpid发现没有已退出的子进程可收集,则返回0
如果调用中出错,则返回-1,这时errno会被设置成相应的值以指示错误所在
参数:
Pid:
Pid=-1:等待任一个子进程,与wait等效
Pid>0:等待其进程ID与pid相等的子进程
Status:
WIFEXITED(status): 若为正常终止子进程返回的状态,则为真(查看进程是否是正常退出)
WEXITSTATUS(status): 若WIFEXITED非零,提取子进程退出码(查看进程的退出码)
options:
WNOHANG: 若pid指定的子进程没有结束,则waitpid()函数返回0,不予以等待
若正常结束,则返回该子进程的ID
若子进程已经退出,调用wait/waitpid时,
wait/waitpid会立即返回,并且释放资源,获得子进程退出信息
若在任意时刻调用wait/waitpid,子进程存在且正常运行,则进程可能阻塞
若不存在该子进程,则立即出错返回

退出信息就退出码和退出信号啦,可是Status只有一个数哎(别猜了人家有特殊格式,可以当做位图看待,图中表示比特位):

退出码:0~255(最多就那么多)
这样可以直接看:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt = 5;while (cnt){printf("I am child process,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit ...\n");exit(49);}sleep(3);int status = 0;pid_t rid = waitpid(id, &status , 0);if (rid > 0){printf("wait success,rid:%d\n", rid);}else{printf("wait failed\n");}sleep(3);printf("father quit,status:%d,child quit code:%d,child quit signal:%d\n",status,(status>>8)&0xFF, status & 0x7F);return 0;
}
退出后会发现是正常退出的:

上面的宏和这个位操作差不多,使用的话就是(结果是紫禁城退出码,想知道退出信号就自己去按位与去):
if(WIFEXITED(status))
很好,正和我意
那假如紫禁城死循环怎么办?
看看不就知道了:
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt = 5;while (1){printf("I am child process,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}
}int main()
{printf("I am father,pid:%d,ppid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit ...\n");exit(123);}sleep(3);int status = 0;pid_t rid = waitpid(id, &status , 0);if (rid > 0){printf("wait success,rid:%d\n", rid);}else{printf("wait failed\n");}sleep(3);printf("father quit,status:%d,child quit code:%d,child qiut signal:%d\n",status,(status>>8)&0xFF, status & 0x7F);return 0;
}

当然是爹一直等了,,,把紫禁城干掉回收
如果紫禁城异常怎么办?
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include<sys/types.h>
#include<sys/wait.h>void ChildRun()
{int cnt = 5;int* p = NULL;while (cnt){printf("I am child process,pid:%d,ppid:%d\n", getpid(), getppid());sleep(1);cnt--;}*p = 10;
}int main()
{printf("I am father,pid:%d,ppid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit ...\n");exit(123);}sleep(3);int status = 0;pid_t rid = waitpid(id, &status , 0);if (rid > 0){printf("wait success,rid:%d\n", rid);}else{printf("wait failed\n");}sleep(3);printf("father quit,status:%d,child quit code:%d,child qiut signal:%d\n",status,(status>>8)&0xFF, status & 0x7F);return 0;
}
也看看:

可以发现直接挂了,退出信息告诉程序猿:赶紧回去查查你的代码,有bug!!!
如果紫禁城没有退出,父进程在进行执行waitpid进行等待(等待某种条件发生,只不过如今的条件恰好是紫禁城退出),阻塞等待(进程阻塞,父进程什么事都没干)
但是现在我们使用的大部分进程都是阻塞板的,WNOHANG选项就是非阻塞等待,如果一直hang住什么都做不了,我们把这种情况叫做服务器宕机
讲个小故事来阐述这个故事吧:
从前有一只学生名为燃燃子,她舍友是个学霸叫挽鸢(超级厉害,什么都会的那种,你问她要课堂笔记没有一个是不记录的,平时都不逃课),有天燃燃子给挽鸢说,宝宝下午C语言要考试了,给我画个重点呗,考完咱俩出去吃好吃的我请客,挽鸢欣然答应,但是挽鸢当时正在学cpp的一本书,就问燃燃子能不能等她半小时,她学完就干,燃燃子一听说那好吧,你先忙,燃燃子在等待挽鸢的过程中,一会开局王者,一会刷会视频号,一会拿出书装样子看看,过了差不多半小时,燃燃子给挽鸢打电话,问她好了没,挽鸢说还有两分钟就好(怎么可能),等待是周而复始的,但燃燃子在等待挽鸢的过程中还做了其他事,所以这是非阻塞等待,打电话的过程是函数调用(调用的本质是在检测挽鸢的状态),燃燃子和挽鸢说话的过程是函数传参,挽鸢告诉燃燃子自己还需要一会的过程就是函数返回值

故事拉长,燃燃子在挽鸢的帮助下顺利考过了C语言考试,燃燃子狂喜,但是先别急着高兴,过两天考操作系统了(燃燃子:我嘞个骚刚,操作系统是啥啊),于是燃燃子顺理成章找到挽鸢,哎嘿能不能再帮我划个操作系统重点,这两天饭我包了,挽鸢说OK啊,但是挽鸢当时在学Linux网络编程,就问燃燃子能不能等她一会,她还没看完,但是燃燃子觉得来回打电话有点麻烦,就和挽鸢说你不用挂电话,就把手机放旁边,好了直接叫我就好,这个时候燃燃子墨墨听着电话那头的无尽的翻书声,只是沉默着,她什么也没干,这个时候燃燃子在进行的是阻塞等待(同时状态不就绪就不返回),这时路过一只笙宝,看燃燃子啥也不干就在那扒着手机听听听,于是笙宝过去问:“干啥呢干在这坐着”,燃燃子也不理,过会笙宝自讨没趣走了,那燃燃子为何要进行这样的苦等呢?有很多种可能,可能单纯就是想等着,还有可能是挽鸢比较受欢迎,不太容易约到(但是阻塞等待在现实中不太能存在吧,应该),waitpid检测紫禁城状态变化的
当我们采用非阻塞等待的时候,一般要加上循环,直到检测到紫禁城退出,我们把这种方案叫做非阻塞轮询方案
而阻塞等待优点也很明显了,就是简单可靠,但非阻塞时父进程可以做其他的事
各有千秋
写段非阻塞轮询的代码吧:
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>void ChildRun()
{int cnt = 5;while (cnt){printf("I am child process,pid:%d,ppid:%d,cnt:%d\n", getpid(), getppid(), cnt);cnt--;sleep(1);}
}int main()
{printf("I am father process,pid:%d,pid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit\n");exit(123);}while (1){int status = 0;pid_t rid = waitpid(id, &status, WNOHANG); //进行非阻塞等待if (rid == 0){printf("child is running, father check next time!\n");//DoOtherThing();}else if (rid > 0){if (WIFEXITED(status)){printf("child quit success,child exit code:%d\n", WEXITSTATUS(status));}else{printf("child quit unnormal!\n");}break;}else{printf("waitpid failed!\n");break;}}return 0;
}
刚说在父进程等待的时候还可以做其他事,下面来举个栗子:基于函数指针级别的对父进程完成任务进行解耦
myprocess.c
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>
#include"task.h"typedef void(*func_t)();#define N 3
func_t tasks[N] = { NULL };void LoadTask()
{tasks[0] = Printlog;tasks[1] = Download;tasks[2] = MysqlDataSync;
}void HanderTask()
{for (int i = 0; i < N; i++){tasks[i]();}
}void DoOtherThing()
{HanderTask();
}void ChildRun()
{int cnt = 5;while (cnt){printf("I am child process,pid:%d,ppid:%d,cnt:%d\n", getpid(), getppid(), cnt);cnt--;sleep(1);}
}int main()
{printf("I am father process,pid:%d,pid:%d\n", getpid(), getppid());pid_t id = fork();if (id == 0){ChildRun();printf("child quit\n");exit(123);}LoadTask();while (1){int status = 0;pid_t rid = waitpid(id, &status, WNOHANG); //进行非阻塞等待if (rid == 0){printf("child is running, father check next time!\n");//DoOtherThing();}else if (rid > 0){if (WIFEXITED(status)){printf("child quit success,child exit code:%d\n", WEXITSTATUS(status));}else{printf("child quit unnormal!\n");}break;}else{printf("waitpid failed!\n");break;}}return 0;
}
task.h
#pragma once
#include<stdio.h>
#include<string.h>
#include<sys/types.h>
#include<sys/wait.h>
#include<stdlib.h>
#include<unistd.h>void Printlog();
void Download();
void MysqlDataSync();
task.c
#include"task.h"void Printlog()
{printf("begin Printlog...\n");
}void Download()
{printf("begin Download...\n");
}void MysqlDataSync()
{printf("begin MysqlDataSync...\n");
}
makefile
myprocess:myprocess.c task.c
gcc - o $@ $ ^
.PHONT:clean
clean:rm -f myprocess
父进程就完成了在轮询检测时还做其他事
就这些捏,到目前为止说的差不多啦,再会啦~
相关文章:
控制欲过强的Linux小进程
控制欲强?视奸?普通人那才叫视奸,您是皇帝,天下大事无一逃过您的耳目,您想看什么就看什么,臣怀疑他在朋友圈私养兵士,囤积枪甲,蓄意谋反,图谋皇位啊! 哈哈哈哈开个玩笑&…...
探讨元宇宙和VR虚拟现实之间的区别
在数字时代,人们对虚拟现实的兴趣与日俱增。在虚拟现实技术的推动下,出现了两个概念:元宇宙和VR虚拟现实。虽然这两个概念都与虚拟现实有关,但它们有着不同的特点和用途。在本文中,我们将探讨元宇宙和VR虚拟现实之间的…...
Docker Desktop安装
0 Preface/Foreward 1 安装 1.1 运行docker安装包 安装完Docker Desktop后,运行Docker Desktop,出现WSL 2安装不完整情况,具体情况如下: 解决方法:旧版 WSL 的手动安装步骤 | Microsoft Learn 也可以直接下载新的安…...
《Towards Black-Box Membership Inference Attack for Diffusion Models》论文笔记
《Towards Black-Box Membership Inference Attack for Diffusion Models》 Abstract 识别艺术品是否用于训练扩散模型的挑战,重点是人工智能生成的艺术品中的成员推断攻击——copyright protection不需要访问内部模型组件的新型黑盒攻击方法展示了在评估 DALL-E …...
vscode调试nextjs前端后端程序、nextjs api接口
最近有一个项目使用了nextjs框架,并且使用nextjs同时实现了前后端,由于之前前后端都是分离的,前端的调试可以通过在代码种添加debugger或者直接在浏览器中打断点实现,现在想调试后端接口,前面的方式就不适用了。故研究…...
《SeTformer Is What You Need for Vision and Language》
会议:AAAI 年份:2024 论文:DDAE: Towards Deep Dynamic Vision BERT Pretraining - AMinerhttps://www.aminer.cn/pub/6602613613fb2c6cf6c387c2/ddae-towards-deep-dynamic-vision-bert-pretraining 摘要 这篇论文介绍了一种新型的变换器…...
[保姆级教程]uniapp安装使用uViewUI教程
文章目录 创建 UniApp 项目下载uView UI下载安装方式步骤 1: 安装 uView UI步骤 2: 查看uView UI是否下载成功步骤 3: 引入 uView 主 JS 库步骤 4: 引入 uView 的全局 SCSS 主题文件步骤 5: 引入 uView 基础样式步骤 6: 配置 easycom 组件模式注意事项 NPM方式步骤 1: 安装 uVi…...
网络安全法规对企业做等保有哪些具体规定?
网络安全法规对企业做等保的具体规定 根据《中华人民共和国网络安全法》,企业作为网络运营者,需要履行网络安全等级保护制度的相关义务,确保网络安全和数据保护。具体规定包括: 网络安全等级保护制度:企业应根据网络安…...
Java开发中超好用Orika属性映射工具
Orika属性映射工具 引入pom依赖 <dependency><groupId>ma.glasnost.orika</groupId><artifactId>orika-core</artifactId><version>1.5.4</version></dependency>上干货 封装的工具类:OriUtilsimport ma.glasnost.orika.Map…...
qt初入门8:下拉框,输入框模糊查询,提示简单了解 (借助QCompleter)
实现一个简单的模糊查询的逻辑,输入框能提示相关项。 主要借助qt的QCompleter 类( Qt 框架中提供的一个用于自动补全和模糊搜索的类),结合一些控件,比如QComboBox和QLineEdit,实现模糊查询的功能。 1&…...
【qt】VS中如何配置Qt环境
https://download.qt.io/official_releases/vsaddin/ 首先需要下载一下vsaddin,上面的是下载的网站. 下载的时候可能会出现下图的情况 说明你下的vsaddin和您的VS版本不匹配,所以你可以多下几个其他版本的vsAddin,一般都是和你VS版本相匹配的才可以,如Vs2022,那就试试vsaddin2…...
对于相同网段的IP,部分无法ping通问题
现象1:在Linux上执行 ping 192.168.1.232,无法ping通 分析1:使用ifconfig查询,联网使用eth0口,只能上网192.168.10.xx网段,需要增加网段 解决方法:使用ip addr 查询,本身只具备10网…...
Unity发布XR中用于worldbuilding的全新电子书
通过身临其境的虚拟领域开始旅程,在维度之间传送,或将数字奇迹与现实世界融合——虚拟现实(VR)和混合现实(MR)的千万种可能性将邀请创作者把他们的想象力带入生活。 Unity发布的最新版综合指南将帮助有抱负的创作者和经验丰富的开发者深入研究和理解构建…...
Vue3相比于Vue2进行了哪些更新
1、响应式原理 vue2 vue2中采用 defineProperty 来劫持整个对象,然后进行深度遍历所有属性,给每个属性添加getter和setter,结合发布订阅模式实现响应式。 存在的问题: 检测不到对象属性的添加和删除数组API方法无法监听到需要对…...
Unity UGUI 之 Slider
本文仅作学习笔记与交流,不作任何商业用途 本文包括但不限于unity官方手册,唐老狮,麦扣教程知识,引用会标记,如有不足还请斧正 1.Slider是什么 滑块,由三部分组成:背景 填充条 手柄 填充条就是…...
这7款高效爬虫工具软件,非常实用!
在当今数据驱动的时代,自动化爬虫工具和软件成为了许多企业和个人获取数据的重要手段。这里会介绍6款功能强大、操作简便的自动化爬虫工具,用好了可以更高效地进行数据采集。 1. 八爪鱼采集器 八爪鱼是一款功能强大的桌面端爬虫软件,主打可…...
【OJ】二叉树相关OJ题
✨✨欢迎大家来到Celia的博客✨✨ 🎉🎉创作不易,请点赞关注,多多支持哦🎉🎉 所属专栏:OJ题 个人主页:Celias blog~ 目录 编辑 单值二叉树 题目描述 OJ-单值二叉树 解题思路 …...
Blender中保存透明图片
在Blender中保存透明图片,主要是通过在渲染设置中调整背景透明度,并选择合适的文件格式来保存图像。以下是一个详细的步骤指南: 一、设置渲染属性 打开Blender并加载你想要渲染的模型。在右侧的属性编辑器中,找到并点击“渲染属…...
MySQL之索引优化
1、在进行查询时,索引列不能是表达式的一部分,也不能是函数的参数,否则无法使用索引 例如下面的查询不能使用 actor_id 列的索引: #这是错误的 SELECT actor_id FROM sakila.actor WHERE actor_id 1 5; 优化方式:…...
Spring Boot 与 Amazon S3:快速上传与下载文件的完整指南
概要 在将 Spring Boot 更新到 3 系列时,由于 javax 需要被替换为 jakarta,因此原先依赖于 javax 的 spring-cloud-starter-aws1 将无法使用(虽然在我本地环境中仍然可以正常工作)。为了确保兼容性,我将依赖关系更改为…...
基于FPGA的PID算法学习———实现PID比例控制算法
基于FPGA的PID算法学习 前言一、PID算法分析二、PID仿真分析1. PID代码2.PI代码3.P代码4.顶层5.测试文件6.仿真波形 总结 前言 学习内容:参考网站: PID算法控制 PID即:Proportional(比例)、Integral(积分&…...
《Qt C++ 与 OpenCV:解锁视频播放程序设计的奥秘》
引言:探索视频播放程序设计之旅 在当今数字化时代,多媒体应用已渗透到我们生活的方方面面,从日常的视频娱乐到专业的视频监控、视频会议系统,视频播放程序作为多媒体应用的核心组成部分,扮演着至关重要的角色。无论是在个人电脑、移动设备还是智能电视等平台上,用户都期望…...
家政维修平台实战20:权限设计
目录 1 获取工人信息2 搭建工人入口3 权限判断总结 目前我们已经搭建好了基础的用户体系,主要是分成几个表,用户表我们是记录用户的基础信息,包括手机、昵称、头像。而工人和员工各有各的表。那么就有一个问题,不同的角色…...
基础测试工具使用经验
背景 vtune,perf, nsight system等基础测试工具,都是用过的,但是没有记录,都逐渐忘了。所以写这篇博客总结记录一下,只要以后发现新的用法,就记得来编辑补充一下 perf 比较基础的用法: 先改这…...
江苏艾立泰跨国资源接力:废料变黄金的绿色供应链革命
在华东塑料包装行业面临限塑令深度调整的背景下,江苏艾立泰以一场跨国资源接力的创新实践,重新定义了绿色供应链的边界。 跨国回收网络:废料变黄金的全球棋局 艾立泰在欧洲、东南亚建立再生塑料回收点,将海外废弃包装箱通过标准…...
Python爬虫(二):爬虫完整流程
爬虫完整流程详解(7大核心步骤实战技巧) 一、爬虫完整工作流程 以下是爬虫开发的完整流程,我将结合具体技术点和实战经验展开说明: 1. 目标分析与前期准备 网站技术分析: 使用浏览器开发者工具(F12&…...
回溯算法学习
一、电话号码的字母组合 import java.util.ArrayList; import java.util.List;import javax.management.loading.PrivateClassLoader;public class letterCombinations {private static final String[] KEYPAD {"", //0"", //1"abc", //2"…...
Windows安装Miniconda
一、下载 https://www.anaconda.com/download/success 二、安装 三、配置镜像源 Anaconda/Miniconda pip 配置清华镜像源_anaconda配置清华源-CSDN博客 四、常用操作命令 Anaconda/Miniconda 基本操作命令_miniconda创建环境命令-CSDN博客...
探索Selenium:自动化测试的神奇钥匙
目录 一、Selenium 是什么1.1 定义与概念1.2 发展历程1.3 功能概述 二、Selenium 工作原理剖析2.1 架构组成2.2 工作流程2.3 通信机制 三、Selenium 的优势3.1 跨浏览器与平台支持3.2 丰富的语言支持3.3 强大的社区支持 四、Selenium 的应用场景4.1 Web 应用自动化测试4.2 数据…...
Ubuntu系统多网卡多相机IP设置方法
目录 1、硬件情况 2、如何设置网卡和相机IP 2.1 万兆网卡连接交换机,交换机再连相机 2.1.1 网卡设置 2.1.2 相机设置 2.3 万兆网卡直连相机 1、硬件情况 2个网卡n个相机 电脑系统信息,系统版本:Ubuntu22.04.5 LTS;内核版本…...
