控制欲过强的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.mzph.cn/diannao/49254.shtml

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

相关文章

使用GoAccess进行Web日志可视化

运行网站的挑战之一是了解您的 Web 服务器正在做什么。虽然各种监控应用程序可以在您的服务器以高负载或页面响应缓慢运行时提醒您&#xff0c;但要完全了解正在发生的事情&#xff0c;唯一的方法是查看 Web 日志。阅读日志数据页面并了解正在发生的事情可能需要花费大量时间。…

C++的UI框架和开源项目介绍

文章目录 1.QT2.wxWidgets3.Dear ImGui 1.QT QT的开源项目&#xff1a;QGIS&#xff08;地理信息系统&#xff09; https://github.com/qgis/QGIS?tabreadme-ov-file 2.wxWidgets wxWidgets的开源项目&#xff1a;filezilla https://svn.filezilla-project.org/svn/ wxWidg…

环形链表的相关证明

141. 环形链表 - 力扣&#xff08;LeetCode&#xff09; 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使…

基于MobileNetv2的垃圾分类函数式自动微分-昇思25天打卡

基于MobileNetv2的垃圾分类 本文档主要介绍垃圾分类代码开发的方法。通过读取本地图像数据作为输入&#xff0c;对图像中的垃圾物体进行检测&#xff0c;并且将检测结果图片保存到文件中。 1、实验目的 了解熟悉垃圾分类应用代码的编写&#xff08;Python语言&#xff09;&a…

推荐推荐两款免费的WIN PE工具,很好用两款免费的WIN PE工具,很好用

上次推荐了三款WIN PE工具​&#xff1a;推荐3款装机必不可少的PE工具箱&#xff0c;全是宝藏工具&#xff0c;千万不要错过-CSDN博客 今天继续推荐两款WIN PE工具。 FirPE FirPE是一款系统预安装环境&#xff08;Windows PE&#xff09;&#xff0c;它具有简约、易操作等特点…

《SeTformer Is What You Need for Vision and Language》

会议&#xff1a;AAAI 年份&#xff1a;2024 论文&#xff1a;DDAE: Towards Deep Dynamic Vision BERT Pretraining - AMinerhttps://www.aminer.cn/pub/6602613613fb2c6cf6c387c2/ddae-towards-deep-dynamic-vision-bert-pretraining 摘要 这篇论文介绍了一种新型的变换器…

如何将 M.2 HAT+ 与 Raspberry Pi 5 一起使用?

树莓派 M.2 HAT M Key 可以让您连接 M.2 外围设备,如 NVMe 硬盘和其他 PCIe 配件,到树莓派 5 的 PCIe 接口。 M.2 HAT 转接板可以把树莓派 5 上的 PCIe 连接器转换为单个 M.2 M key 边缘连接器。您可以连接任何使用 2230 或 2242 尺寸的设备。M.2 HAT 最大可提供 3A 的电源输出…

Superset 4.0.1导出csv数据中文乱码问题解决

Apache Superset 是一个开源的数据探索和可视化平台,专门用于创建交互式数据报表和仪表盘。它具有强大的数据集成和可视化能力,广泛用于数据分析和商业智能领域。 Superset详细介绍详见 报表系统之Superset-CSDN博客 Superset 导出CSV 默认编码为utf-8,在导出包含中文的文…

jenkins替换配置文件

1.点击首页的【Manage Jenkins】-【Manage Plugins】&#xff0c;在选项【Available plugins】安装 Config File Provider Plugin &#xff0c;安装后重启jenkins 2.安装完成后会有这个图标&#xff0c;点进去 3.点击新建&#xff0c;选择自定义&#xff0c;填入要替换的文件…

深入浅出理解 C 语言中的 qsort 函数

目录 引言 一、什么是qsort 二、函数原型 1.qsort函数 2.比较函数 三、qsort函数使用示例 1.使用qsort排序整形数据 2.使用qsort排序结构数据 总结 引言 在编程中&#xff0c;排序是一个常见且重要的操作。C 语言标准库提供了一系列排序函数&#xff0c;其中 qsort 函…

华为IoTDA解码插件报告错误:The decoding result is empty.data

前面的博文讲过&#xff0c;在使用Neuron上传数据到华为IoTDA的时候没有使用华为的物模型进行解析&#xff0c;因为两者的数据格式不同。具体的说Neuron上传的格式是 {"node": "RS485", "group": "Data", "timestamp": 172…

CSS画边框线带有渐变线和流光边框实例

流光边框css流光边框动画效果_哔哩哔哩_bilibili流光边框css流光边框动画效果_哔哩哔哩_bilibili纯CSS写一个动态流水灯边框的效果&#xff5e;_哔哩哔哩_bilibili荧光边框CSS 动画发光渐变边框特效_哔哩哔哩_bilibili [data-v-25d37a3a] .flow-dialog-custom {background-col…

xhs全参

声明 本文章中所有内容仅供学习交流&#xff0c;抓包内容、敏感网址、数据接口均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff0c;若有侵权&#xff0c;请联系我立即删除&#xff01; 目标网站 aHR0cHM6Ly93d…

【线性代数】矩阵变换

一些特殊的矩阵 一&#xff0c;对角矩阵 1&#xff0c;什么是对角矩阵 表示将矩阵进行伸缩&#xff08;反射&#xff09;变换&#xff0c;仅沿坐标轴方向伸缩&#xff08;反射&#xff09;变换。 2&#xff0c;对角矩阵可分解为多个F1矩阵&#xff0c;如下&#xff1a; 二&a…

.NET C# 配置 Options

.NET C# 配置 Options 使用 options 模式可以带来许多好处&#xff0c;包括清晰的配置管理、类型安全、易于测试和灵活性。但在使用过程中&#xff0c;也需要注意配置复杂性、性能开销和依赖框架等问题。通过合理设计和使用&#xff0c;可以充分发挥 options 模式的优势&#…

Vue.js 2 项目实战(五):水果购物车

前言 Vue.js 是一个用于构建用户界面的渐进式 JavaScript 框架。它的设计目标是通过采用易于上手的结构和强大的功能&#xff0c;使前端开发变得更加简便和高效。以下是 Vue.js 的一些关键特性和优点&#xff1a; 核心特性 声明式渲染 Vue.js 使用声明式语法来描述用户界面&a…

MybatisPlus的使用与详细讲解

今天我们来讲解一下Mybatis的升级版&#xff0c;就是MybatisPlus. MybatisPlus是如何获取实现CRUD的数据库表信息的&#xff1f; 默认以类名驼峰转下划线作为表名 默认把名为id的字段作为主键 默认把变量名驼峰转下划线作为表的字段名 1.MybatisPlus中比较常见的注解 TableN…

宠物空气净化器哪款除臭效果好?质量好的养狗空气净化器排名

作为一个宠物家电小博主&#xff0c;炎炎夏日&#xff0c;家中的宠物给你带来的不仅仅是温暖的陪伴&#xff0c;还有那挥之不去的宠物异味。普通空气净化器虽然能够应对一般的空气净化需求&#xff0c;但对于养猫家庭特有的挑战&#xff0c;如宠物毛发、皮屑和异味等&#xff0…

mysql中的索引和分区

目录 1.编写目的 2.索引 2.1 创建方法 2.2 最佳适用 2.3 索引相关语句 3.分区 3.1 创建方法 3.2 最佳适用 Welcome to Code Blocks blog 本篇文章主要介绍了 [Mysql中的分区和索引] ❤博主广交技术好友&#xff0c;喜欢文章的可以关注一下❤ 1.编写目的 在MySQL中&…

JAVA中的输入输出流

FileInputStream、FileOutputStream&#xff08;字节流&#xff09; 字节输入流InputStream主要方法&#xff1a; read() &#xff1a;从此输入流中读取一个数据字节。 read(byte[] b) &#xff1a;从此输入流中将最多 b.length 个字节的数据读入一个 byte 数组中。 read(b…