控制欲过强的Linux小进程

news2024/11/22 15:31:28

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

哈哈哈哈开个玩笑,这篇就主要讲讲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.coloradmin.cn/o/1937607.html

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

相关文章

C++ 类和对象 构造函数(下)

一 初始化列表&#xff1a; 1.1 构造函数体赋值&#xff1a; 在C中&#xff0c;构造函数用于创建对象并赋予其初始值。通常&#xff0c;我们可以在构造函数体内对成员变量进行赋值&#xff1a; class Date { public:Date(int year, int month, int day) {_year year;_mont…

常见的数据分析用例 —— 信用卡交易欺诈检测

文章目录 引言数据集分析1. 读入数据并快速浏览2.计算欺诈交易占数据集中交易总数的百分比3. 类别不平衡对模型的影响3.1 总体思路&#xff08;1&#xff09;数据的划分&#xff08;2&#xff09;训练模型&#xff08;3&#xff09;测试模型&#xff08;4&#xff09;解决不平衡…

知迪科技发布了全新软件产品

近日&#xff0c;知迪科技发布了全新软件产品——Vehicle Bus Tool-Trace Version免费版。该软件产品能高效的离线分析汽车总线数据&#xff0c;并拥有一大亮点功能&#xff1a;Ethernet通信离线文件基于ARXML文件的信号级解析&#xff0c;具体操作如下&#xff1a; 1、新建一…

git修改提交姓名

git config --global user.name “新用户名” git config --global user.email “新邮箱地址” 修改提交的用户名 git config --global user.name “yu***”

java转义文本中的HTML字符为安全的字

java转义文本中的HTML字符为安全的字 &#xff0c;以下字符被转义&#xff1a;替换为 (&apos; doesnt work in HTML4) " 替换为 &quot; & 替换为 &amp; < 替换为 < > 替换为 >1.先添加hutool依赖到pom <dependency><groupId>cn…

Docker安装笔记

1. Mac安装Docker 1.1 Docker安装包下载 1.1.1 阿里云 对于10.10.3以下的用户 推荐使用 对于10.10.3以上的用户 推荐使用 1.1.2 官网下载 系统和芯片选择适合自己的安装包 1.2 镜像加速 【推荐】阿里镜像 登陆后&#xff0c;左侧菜单选中镜像加速器就可以看到你的专属地…

阿尔泰科技利用485模块搭建自动灌溉系统实现远程控制

自动灌溉系统又叫土壤墒情监控系统&#xff0c;土壤墒情监控系统主要实现固定站无人值守情况下的土壤墒情数据的自动采集和无线传输&#xff0c;数据在监控中心自动接收入库&#xff1b;可以实现24小时连续在线监控并将监控数据通过有线、无线等传输方式实时传输到监控中心生成…

怎样在 PostgreSQL 中实现数据的异地备份?

&#x1f345;关注博主&#x1f397;️ 带你畅游技术世界&#xff0c;不错过每一次成长机会&#xff01;&#x1f4da;领书&#xff1a;PostgreSQL 入门到精通.pdf 文章目录 怎样在 PostgreSQL 中实现数据的异地备份&#xff1f;一、异地备份的重要性二、PostgreSQL 中的备份方…

Docker的安装【虚拟机】

Docker的安装【虚拟机】 准备环节【可跳过】 # 进入yum.repos.d目录&#xff0c;保留base.repo 和 CentOs-Base.repo&#xff0c;其余删除 cd /etc/yum.repos.d/ ----------------------------------------------------- # 现将base.repo 和 CentOs-Base.repo 存放至bak包中 …

SQL执行流程、SQL执行计划、SQL优化

select查询语句 select查询语句中join连接是如何工作的&#xff1f; 1、INNER JOIN 返回两个表中的匹配行。 2、LEFT JOIN 返回左表中的所有记录以及右表中的匹配记录。 3、RIGHT JOIN 返回右表中的所有记录以及左表中的匹配记录。 4、FULL OUTER JOIN 返回左侧或右侧表中有匹…

深度洞察市场需求,Vatee万腾平台定制化解决方案引领潮流

在当今这个日新月异的商业环境中&#xff0c;深刻理解并精准把握市场需求&#xff0c;已成为企业生存与发展的核心要义。Vatee万腾平台&#xff0c;作为行业内的佼佼者&#xff0c;凭借其卓越的定制化解决方案能力&#xff0c;正引领着市场潮流&#xff0c;为企业客户量身打造数…

【BUG】已解决:AttributeError: ‘DataFrame‘ object has no attribute ‘append‘

已解决&#xff1a;AttributeError: ‘DataFrame‘ object has no attribute ‘append‘ 目录 已解决&#xff1a;AttributeError: ‘DataFrame‘ object has no attribute ‘append‘ 【常见模块错误】 错误原因&#xff1a; 解决办法&#xff1a; 欢迎来到英杰社区https:/…

Linux—KVM虚拟化中(虚拟机克隆,快照,还原,删除)等应用实例

&#x1f3e1;作者主页&#xff1a;点击&#xff01; &#x1f427;Linux基础知识(初学)&#xff1a;点击&#xff01; &#x1f427;Linux高级管理防护和群集专栏&#xff1a;点击&#xff01; &#x1f510;Linux中firewalld防火墙&#xff1a;点击&#xff01; ⏰️创作…

【数据结构初阶】顺序表三道经典算法题(详解+图例)

Hello&#xff01;很高兴又见到你了~~~ 看看今天要学点什么来充实大脑吧—— 目录 1、移除元素 【思路图解】 【总结】 2、删除有序数组中的重复项 【思路图解】 【总结】 3、合并两个有序数组 【思路图解】 【总结】 至此结束&#xff0c;Show Time&#xff01; 1、…

加拿大上市药品查询-加拿大药品数据库

在加拿大&#xff0c;药品的安全性、有效性和质量是受到严格监管的。根据《食品药品法案》的规定&#xff0c;所有药品制造商必须提供充分的科学证据&#xff0c;证明其产品的安全性和有效性。为此&#xff0c;加拿大卫生部建立了一个全面的药品数据库 &#xff08;DPD) &#…

Websocket自动消息回复服务端工具

点击下载《Websocket自动消息回复服务端工具》 1. 前言 在进行Websocket开发时&#xff0c;前端小伙伴通常是和后端开发人员同步进行项目开发&#xff0c;经常会遇到后端开发人员接口还没开发完&#xff0c;也没有可以调试的环境&#xff0c;只能按照接口文档进行“脑回路开发…

深入探究理解大型语言模型参数和内存需求

概述 大型语言模型 取得了显著进步。GPT-4、谷歌的 Gemini 和 Claude 3 等模型在功能和应用方面树立了新标准。这些模型不仅增强了文本生成和翻译&#xff0c;还在多模态处理方面开辟了新天地&#xff0c;将文本、图像、音频和视频输入结合起来&#xff0c;提供更全面的 AI 解…

昇思25天学习打卡营第20天|Diffusion扩散模型

Mindspore框架利用扩散模型DDPM生成高分辨率图像&#xff08;生成高保真图像项目实践&#xff09; Mindspore框架利用扩散模型DDPM生成高分辨率图像|&#xff08;一&#xff09;关于denoising diffusion probabilistic model &#xff08;DDPM&#xff09;模型Mindspore框架利…

GitHub CLI 发布 2.53.0

gh 是 GitHub 官方的命令行客户端工具&#xff0c;在此推荐给每一位在参与、想参与到 GitHub 上开源项目的小伙伴。体验异常舒适&#xff01; 常用的命令包括有&#xff1a; 登陆认证 gh auth login克隆代码仓库 gh repo clone linuxsuren/api-testing创建 Fork 仓库 gh repo f…

Java面试八股之Redis Stream的实现原理及应用场景

Redis Stream的实现原理及应用场景 Redis Stream是一种在Redis 5.0版本中引入的数据结构&#xff0c;它主要用于实现高效的消息队列服务。下面我将详细解释其实现原理以及一些常见的应用场景。 实现原理 1. 结构组成&#xff1a; - Redis Stream由一个或多个消息组成&#xf…