控制欲过强的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

父进程就完成了在轮询检测时还做其他事 

就这些捏,到目前为止说的差不多啦,再会啦~

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

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

相关文章

正则表达式(Ⅰ)——基本匹配

学习练习建议 正则表达式用途非常广泛&#xff0c;各种语言中都能见到它的身影&#xff08;js&#xff0c;java&#xff0c;mysql等&#xff09; 正则表达式可以快读校验/生成/替换符合要求的模式的字符串&#xff0c;而且语法通俗易懂&#xff0c;所以应用广泛 学习链接&am…

git clone加速

gitte克隆法 1.复制链接 2.gitte新建仓库 3.导入 并将原来的目标的url输入 4.从gitte导入 git clone https://gitee.com/gsci-panda/infini-lm2

排序系列 之 快速排序

&#xff01;&#xff01;&#xff01;排序仅针对于数组哦本次排序是按照升序来的哦代码后边有图解哦 介绍 快速排序英文名为Quick Sort 基本思路 快速排序采用的是分治思想&#xff0c;即在一个无序的序列中选取一个任意的基准元素base&#xff0c;利用base将待排序的序列分…

(01)Unity使用在线AI大模型(使用百度千帆服务)

目录 一、概要 二、环境说明 三、申请百度千帆Key 四、使用千帆大模型 四、给大模型套壳 一、概要 在Unity中使用在线大模型分为两篇发布&#xff0c;此篇文档为在Python中使用千帆大模型&#xff0c;整体实现逻辑是&#xff1a;在Python中接入大模型—>发布为可传参的…

算法日记day 12(栈实现队列|队列实现栈|有效的括号)

队列是先进先出的&#xff0c;就像排队一样&#xff0c;谁在前谁先获得服务 栈是一种先进后出的数据结构 一、用栈实现队列 题目&#xff1a; 请你仅使用两个栈实现先入先出队列。队列应当支持一般队列支持的所有操作&#xff08;push、pop、peek、empty&#xff09;&#xf…

uniapp上传功能用uni-file-picker实现

文章目录 html代码功能实现css样式代码 html代码 <uni-file-pickerselect"onFileSelected"cancel"onFilePickerCancel"limit"1"class"weightPage-upload-but"file-mediatype"image"></uni-file-picker><imag…

飞睿智能UWB Tag蓝牙防丢器标签,宠物安全新升级,5cm精准定位测距不迷路

宠物早已成为许多家庭不可或缺的一员&#xff0c;它们用无条件的爱温暖着我们的心房&#xff0c;陪伴我们度过每一个平凡而温馨的日子。然而&#xff0c;随着宠物活动范围的扩大和外界环境的复杂多变&#xff0c;宠物走失的风险也随之增加。每一次出门遛弯&#xff0c;都像是心…

视频压缩文件太大了怎么缩小?怎么压缩视频大小?视频压缩方法:10个!(宝藏)

视频压缩文件太大了怎么缩小&#xff1f;让我看看是谁下班之后不是一手刷手机短视频&#xff0c;顺便葛优躺在沙发上的&#xff1f;互联网发展到现在&#xff0c;视频已成为我们生活中不可或缺的一部分。不管是视频录制还是视频缓存&#xff0c;视频文件体积越来越庞大&#xf…

微信小程序canvas 使用案例(一)

一、cavans 对象获取、上线文创建 1.wxml <!-- canvas.wxml --><canvas type"2d" id"myCanvas"></canvas> 2.js /*** 生命周期函数--监听页面加载*/onLoad(options) {const query wx.createSelectorQuery()query.select(#myCanvas).f…

DICOM CT\MR片子免费在线查看工具;python pydicom包加载查看;mayavi 3d查看

DICOM CT\MR片子免费在线查看工具 参考&#xff1a; https://zhuanlan.zhihu.com/p/668804209 dicom格式&#xff1a; DICOM&#xff08;Digital Imaging and Communications in Medicine&#xff09;是医学数字成像和通信的标准。它定义了医学图像&#xff08;如CT、MRI、X…

.net6 core Worker Service项目,使用Exchange Web Services (EWS) 分页获取电子邮件收件箱列表,邮件信息字段

Program.cs 安装包&#xff1a;Microsoft.AspNetCore.Hosting.WindowsServices、Microsoft.Extensions.Hosting、Microsoft.Extensions.Hosting.WindowsServices、Microsoft.Extensions.Logging.Log4Net.AspNetCore 新建Configs/log4net.config using Com.Chinahorn.Exchange.W…

【人工智能】机器学习 -- 决策树(乳腺肿瘤数)

目录 一、使用Python开发工具&#xff0c;运行对iris数据进行分类的例子程序dtree.py&#xff0c;熟悉sklearn机器实习开源库。 二、登录https://archive-beta.ics.uci.edu/ 三、使用sklearn机器学习开源库&#xff0c;使用决策树对breast-cancer-wisconsin.data进行分类。 …

docker自建rustdesk-server远程桌面

rustdesk简介 RustDesk 是一款可以平替 TeamViewer 的开源软件&#xff0c;旨在提供安全便捷的自建方案。 RustDesk 是一款功能齐全的远程桌面应用&#xff0c;具有以下特性&#xff1a; 支持 Windows、macOS、Linux、iOS、Android、Web 等多个平台。支持 VP8 / VP9 / AV1 …

JMeter使用手册

安装 下载地址 https://jmeter.apache.org/download_jmeter.cgi 下载后解压到win的文件夹中 打开JMeter的bin文件夹&#xff0c;双击这个jar就启动了JMeter 启动 出现这样的界面 基本使用 添加变量 这个变量在使用的时候可以被引用 创建线程组 所有的请求都得基于…

Harbor系列之1:介绍、架构及工作流程说明

Harbor介绍、架构及工作流程说明 Harbor 是一个用于存储、签名和扫描内容的企业级容器镜像注册表项目。由 VMware 开发并于 2016 年开源。Harbor 提供了一些关键特性&#xff0c;使其成为企业使用的理想选择。 1. Harbor 介绍 1.1 什么是 Harbor Harbor 是一个开源的云原生…

Spring-Boot基础--yaml

目录 Spring-Boot配置文件 注意&#xff1a; YAML简介 YAML基础语法 YAML:数据格式 YAML文件读取配置内容 逐个注入 批量注入 ConfigurationProperties 和value的区别 Spring-Boot配置文件 Spring-Boot中不用编写.xml文件&#xff0c;但是spring-Boot中还是存在.prope…

基于GTX的64B66B编码的自定义接收模块(高速收发器二十二)

点击进入高速收发器系列文章导航界面 1、自定义PHY顶层模块 前文设计了64B66B自定义PHY的发送模块&#xff0c;本文完成自定义PHY剩余的模块的设计&#xff0c;整体设计框图如下所示。 其中phy_tx是自定义PHY的发送数据模块&#xff0c;scrambler是加扰模块&#xff0c;rx_slip…

多口适配器,给您的生活增添便利

随着科技的快速发展&#xff0c;我们的生活已离不开各种各样的电子设备&#xff0c;智能手机、平板电脑、智能手表、无线耳机……它们共同构建了我们丰富多彩的数字生活。然而&#xff0c;面对众多设备的充电需求&#xff0c;传统的单一充电口已难以满足现代人的使用习惯。在这…

使用JWT双令牌机制进行接口请求鉴权

在前后端分离的开发过程中&#xff0c;前端发起请求&#xff0c;调用后端接口&#xff0c;后端在接收请求时&#xff0c;首先需要对收到的请求鉴权&#xff0c;在这种情况先我们可以采用JWT机制来鉴权。 JWT有两种机制&#xff0c;单令牌机制和双令牌机制。 单令牌机制服务端…

IDEA的详细设置

《IDEA破解、配置、使用技巧与实战教程》系列文章目录 第一章 IDEA破解与HelloWorld的实战编写 第二章 IDEA的详细设置 第三章 IDEA的工程与模块管理 第四章 IDEA的常见代码模板的使用 第五章 IDEA中常用的快捷键 第六章 IDEA的断点调试&#xff08;Debug&#xff09; 第七章 …