【信号】信号处理与进程通信:快速上手

news2024/12/24 2:39:46

目录

0. 信号概述

1. 产生信号的方式:

1.1 当用户按某些终端键时,将产生信号。

1.2 硬件异常将产生信号。

1.3 软件异常将产生信号。

1.4 调用kill函数将发送信号。

1.5 运行kill命令将发送信号。

2. 信号的默认(缺省)处理方式

2.1 终止进程:

2.2 缺省出来:

2.3 停止进程:

2.4 让停止的进程恢复运行:

3. 进程收到信号后的处理方式

3.1 执行系统默认动作

3.2 忽略此信号

3.3 执行自定义信号处理函数

4. 常见的信号: 

5. 信号的基本操作

5.1 kill函数

5.2 alarm函数

5.3 setitimer函数(定时器) 

 5.4 raise函数

5.5 abort函数

5.6 pause函数

5.7 signal函数

6. 信号集

6.1 信号集概述

6.2 信号集数据类型

6.3 定义路径:

 6.4 信号集相关的操作主要有以下几个函数:

7. 信号阻塞集(屏蔽集、掩码)

7.1 sigprocmask函数

7.2 sigpending函数

总结:


0. 信号概述

        信号是软件中断,它是在软件层次上对中断机制的一种模拟。

        信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。

        信号是一种异步通信方式。

        进程不必等待信号的到达,进程也不知道信号什么时候到达。

        信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知 用户空间进程发生了哪些系统事件。

        每个信号的名字都以字符 SIG 开头。

        每个信号和一个数字编码相对应,在头文件 signum.h 中,这些信号都被定义为正整数。

信号名定义路径: /usr/include/x86_64-linux-gnu/bits/signum.h (ubuntu12.04)

在 Linux 下,要想查看这些信号和编码的对应关系,可使用命令:kill -l

        信号是由当前系统已经定义好的一些标识,每一个标识都会在特定的场合使用。并且都会对进程有一定的影响,当信号产生时,会让当前信号做出相应的操作。这些信号都是已经定义好的,我们不能自己在去创造,直接使用这些就可以了。

1. 产生信号的方式:

1.1 当用户按某些终端键时,将产生信号。

例如:终端上按“Ctrl+c”组合键通常产生中断信号 SIGINT

           终端上按"Ctrl+\"键通常产 生中断信号 SIGQUIT

           终端上按"Ctrl+z"键通常产生中断信号 SIGSTOP。

1.2 硬件异常将产生信号。

        除数为 0,无效的内存访问等。这些情况通常由硬件检测到,并通知内核,然后内 核产生适当的信号发送给相应的进程。

1.3 软件异常将产生信号。

        当检测到某种软件条件已发生,并将其通知有关进程时,产生信号。

1.4 调用kill函数将发送信号。

        注意:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的 所有者必须是超级用户。

1.5 运行kill命令将发送信号。

        此程序实际上是使用 kill函数来发送信号。也常用此命令终止一个失控的后台进程。

2. 信号的默认(缺省)处理方式

当进程中产生了一个信号,就会让当前进程做出一定的反应。

默认处理进程的方式如下:

2.1 终止进程:

        当信号产生后,当前进程就会立即结束。

2.2 缺省出来:

        当信号产生后,当前进程不做任何处理。

2.3 停止进程:

        当信号产生后,使得当前进程停止。

2.4 让停止的进程恢复运行:

        当信号产生后,停止的进程会恢复执行(后台进程)。

注意:每一个信号只有一个默认的处理方式。

3. 进程收到信号后的处理方式

3.1 执行系统默认动作

        对大多数信号来说,系统默认动作是用来终止该进程。

3.2 忽略此信号

        接收到此信号后没有任何动作。

3.3 执行自定义信号处理函数

        用用户定义的信号处理函数处理该信号。

注意:SIGKILL和SIGSTOP这两个信号只能以默认的处理方式执行,不能忽略,也不能自定义。

4. 常见的信号: 

信号性质默认处理方式
SIGKILL9当产生这个信号后,当前进程会退出,不能被缺省和捕捉退出进程
SIGSTOP19当产生这个信号后,当前进程会停止,不能缺省和捕捉停止进程
SIGINT2键盘输入ctrl+c时产生信号退出进程
SIGQUIT3键盘输入ctrl+\时产生信号退出进程
SIGSTP20键盘输入ctrl+z时产生信号停止进程
SIGCONT18当产生当前信号后,当前停止的进程会恢复运行停止的进程恢复运行
SIGALRM14当调用alarm函数设置的时间到达时会产生当前信号退出进程
SIGPIPE13当管道破裂时,会产生当前信号退出进程
SIGABRT6当调用abort函数时会产生当前信号退出进程
SIGCHLD17当使用fork创建一个子进程时,如果子进程状态改变(退出),会产生当前信号缺省
SIGUSR110用户自定义信号,不会自动产生,只能使用kill函数或命令给指定的进程发送当前信号缺省
SIGUSR212用户自定义信号,不会自动产生,只能使用kill函数或命令给指定的进程发送当前信号缺省

5. 信号的基本操作

5.1 kill函数

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int signum);
功能:
    给指定进程发送信号。
参数:
    pid:
        pid 的取值有 4 种情况: 
        pid>0: 将信号传送给进程 ID 为 pid 的进程。
        pid=0: 将信号传送给当前进程所在进程组中的所有进程。
        pid=-1: 将信号传送给系统内所有的进程。
        pid<-1: 将信号传给指定进程组的所有进程。这个进程组号等于 pid 的绝对值。
    signum:信号的编号
返回值:
    成功:返回 0
    失败:返回 -1

代码示例: 

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>

int main(int argc, char* argv[])
{
	pid_t pid;
	pid = fork();
	if (pid < 0) {
		perror("fork is error:");
		exit(1);
	}
	else if (pid == 0)//子进程的代码区
	{
		int i = 0;
		for (i = 0; i < 5; i++)
		{
			printf("in son process\n");
			sleep(1);
		}
	}
	else//父进程的代码区
	{
		printf("in father process\n");
		sleep(2);
		printf("kill sub process now \n");
		kill(pid, SIGINT);
	}
	return 0;
}

注意:

        使用 kill 函数发送信号,接收信号进程和发送信号进程的所有者必须相同,或者 发送信号进程的所有者是超级用户。

5.2 alarm函数

#include <unistd.h>
unsigned int alarm(unsigned int seconds);
功能: 
    定时器,闹钟,当设定的时间到达时,会产生SIGALRM信号
参数:
    seconds:设定的秒数
返回值: 
    如果alarm函数之前没有alarm设置,则返回0
    如果有,则返回上一个alarm剩余的时间

代码示例: 

#include <stdio.h>
#include <unistd.h>
int main(int argc, char* argv[])
{
	int seconds = 0;
	seconds = alarm(5);
	printf("seconds = %d\n", seconds);
	sleep(2);
	seconds = alarm(5);
	printf("seconds = %d\n", seconds);
	while (1);
	return 0;
}

5.3 setitimer函数(定时器) 

#include <sys/time.h>
int setitimer(int which, const struct itimerval *new_value, struct itimerval
*old_value);
功能:
    设置定时器(闹钟)。 可代替alarm函数。精度微秒us,可以实现周期定时。
参数:
    which:指定定时方式
        a) 自然定时:ITIMER_REAL → 14)SIGALRM计算自然时间
        b) 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM 只计算进程占用cpu的时间
        c) 运行时计时(用户 + 内核):ITIMER_PROF → 27)SIGPROF计算占用cpu及执行系统调用的时间
    new_value:struct itimerval, 负责设定timeout时间
    struct itimerval {
        struct timerval it_interval; // 闹钟触发周期
        struct timerval it_value; // 闹钟触发时间
    };
    struct timeval {
        long tv_sec; // 秒
        long tv_usec; // 微秒
    }
        itimerval.it_value: 设定第一次执行function所延迟的秒数
        itimerval.it_interval: 设定以后每几秒执行function
    old_value: 存放旧的timeout值,一般指定为NULL
返回值:
    成功:0
    失败:-1

代码示例: 

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>

void myfunc(int sig)
{
	printf("hello\n");
}
int main(int argc, char* argv[])
{
	struct itimerval new_value;
	//定时周期
	new_value.it_interval.tv_sec = 1;
	new_value.it_interval.tv_usec = 0;
	//第一次触发的时间
	new_value.it_value.tv_sec = 2;
	new_value.it_value.tv_usec = 0;
	signal(SIGALRM, myfunc); //信号处理
	setitimer(ITIMER_REAL, &new_value, NULL); //定时器设置
	while (1);
	return 0;
}

 5.4 raise函数

#include <signal.h>
int raise(int signum);
功能:
    给调用进程本身送一个信号。
参数:
    signum:信号的编号。
返回值:
    成功:返回 0
    失败:返回 非0

raise(sig) <==> kill(getpid(),sig)

代码示例: 

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>

int main(int argc, char const* grgv[]) 
{
	int num = 0;
	while (1) {
		printf("hello world\n");
		sleep(1);
		num++;

		//循环执行5秒后,进程退出
		if (num == 5) {
			raise(SIGINT);
			//kill(getpid(), SIGINT);
		}
	}

	return 0;
}

5.5 abort函数

#include <stdlib.h>
void abort(void);
功能:
    向进程发送一个 SIGABRT 信号,默认情况下进程会退出
参数:
    无
返回值:
    无

注意:

        即使 SIGABRT 信号被加入阻塞集,一旦进程调用了 abort 函数,进程也还是会被终止,且在终止前会刷新缓冲区,关文件描述符。

代码示例: 

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>

int main(int argc, char const* grgv[]) 
{
	int num = 0;
	while (1) {
		printf("hello world\n");
		sleep(1);
		num++;

		//循环执行5秒后,进程退出
		if (num == 5) {
			abort();
		}
	}

	return 0;
}

5.6 pause函数

#include <unistd.h>
int pause(void);
功能:
    将调用进程挂起直至捕捉到信号为止。这个函数通常用于判断信号是否已到。
参数:
    无
返回值:
    直到捕获到信号,pause 函数才返回-1,且 errno 被设置成 EINTR。

代码示例: 

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>

int main(int argc, char* argv[])
{
	pid_t pid;
	pid = fork();
	if (pid < 0) {
		perror("fail to fork");
		exit(1);
	}
	else if (pid > 0)//父进程的代码区
	{
		printf("in father process\n");
		pause();
	}
	else//子进程的代码区
	{
		printf("in son process\n");
		sleep(3);
		kill(getpid(), SIGINT);
	}
	return 0;
}

5.7 signal函数

#include <signal.h>
void (*signal(int sig,void(*func)(int)))(int)
    typedef void (*sighandler_t)(int);
    sighandler_t signal(int signum,sighandler_t handler);
功能: 
    当进程中产生某一个信号时,对当前信号进行处理
参数: 
    sig:指定要处理的信号
    handler:处理方式
        SIG_INT 当信号产生时,以缺省(忽略)方式处理
        SIG_DFL 当信号产生时,以当前信号默认的方式处理
        void handler(int sig):当信号产生时,通过信号处理函数自定义方式处理,
                                函数名可以随便写,参数表示当前的信号
返回值:
    成功:第一次返回 NULL,下一次返回此信号上一次注册的信号处理函数的地址。如果需要使用此返回
值,必须在前面先声明此函数指针的类型。
    失败:SIG_ERR

代码示例: 

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>

void handler(int sig);

int main(int argc, char* argv[])
{
	//以默认的方式处理信号
#if 0
	if (signal(SIGINT, SIG_DFL) == SIG_ERR) {
		perror("fail to signal");
		exit(1);
	}
	if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) {
		perror("fail to signal");
		exit(1);
	}
#endif

	//以忽略的方式处理信号
#if 0
	if (signal(SIGINT, SIG_IGN) == SIG_ERR) {
		perror("fail to siganl");
		exit(1);
	}
	if (signal(SIGQUIT, SIG_IGN) == SIG_ERR) {
		perror("fail to signal");
		exit(1);
	}
#endif

	//以用户自定义方式处理信号
#if 0
	if (signal(SIGINT, handler) == SIG_ERR) {
		perror("fail to signal");
		exit(1);
	}
	if (signal(SIGQUIT, handler) == SIG_ERR) {
		perror("fail to signal");
		exit(1);
	}
#endif 

	while (1) {
		printf("hello world\n");
		sleep(1);
	}
	void handler(int sig) {
		if (sig == SIGINT) {
			printf("SIGINT正在处理\n");
		}
		if (sig == SIGQUIT) {
			printf("SIGQUIT正在处理\n");
		}
	}
	return 0;
}

返回值:

#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#include<unistd.h>
#include<sys/types.h>

void handler(int sig);
void* ret_handler;
void handler(int sig) {
	printf("******************************\n");
	printf("nihao beijing\n");
	printf("welcome to hangzhou\n");
	printf("******************************\n");

	if (signal(SIGINT, ret_handler) == SIG_ERR) {
		perror("fail to signal");
		exit(1);
	}
}
int main(int argc, char* argv[])
{
	if ((ret_handler = signal(SIGINT, handler)) == SIG_ERR){
		perror("fail to siganl");
		exit(1);
	}
	while (1) {
		printf("hello world\n");
		sleep(1);
	}

	return 0;
}

6. 信号集

6.1 信号集概述

        一个用户进程常常需要对多个信号做出处理。为了方便对多个信号进行处理,在Linux系统中引入了信号集。

        信号集是用来表示多个信号的数据类型。

6.2 信号集数据类型

        sigset_t

6.3 定义路径:

        /usr/include/x86_64-linux-gnu/bits/sigset.h   (ubuntu12.04)

 6.4 信号集相关的操作主要有以下几个函数:

  • sigemptyset
  • sigfillset
  • sigismember
  • sigaddset
  • sigdelset
1----sigemptyset()
#include <signal.h>
int sigemptyset(sigset_t *set);
功能: 
    初始化由 set 指向的信号集,清除其中所有的信号即初始化一个空信号集。
参数: 
    set:信号集标识的地址,以后操作此信号集,对 set 进行操作就可以了。
返回值: 
    成功:返回 0
    失败:返回 -1

2----sigfillset()
#include <signal.h>
int sigfillset(sigset_t *set);
功能: 
    初始化信号集合 set, 将信号集合设置为所有信号的集合。
参数: 
    信号集标识的地址,以后操作此信号集,对 set 进行操作就可以了。
返回值:
    成功:返回 0
    失败:返回 -1

3----sigismember()
#include <signal.h>
int sigismember(const sigset_t *set,int signum);
功能: 
    查询 signum 标识的信号是否在信号集合 set 之中。
参数: 
    set:信号集标识符号的地址。
    signum:信号的编号。
返回值: 
    在信号集中返回 1,不在信号集中返回 0
    错误:返回 -1

4----sigaddset()
#include <signal.h>
int sigaddset(sigset_t *set, int signum);
功能: 
    将信号 signum 加入到信号集合 set 之中。
参数: 
    set:信号集标识的地址。
    signum:信号的编号。
返回值: 
    成功:返回 0
    失败:返回 -1

5----sigdelset()
#include <signal.h>
int sigdelset(sigset_t *set, int signum);
功能: 
    将 signum 所标识的信号从信号集合 set 中删除。
参数: 
    set:信号集标识的地址。
    signum:信号的编号。
返回值: 
    成功:返回 0
    失败:返回 -1

代码案例: 

#include<stdio.h>
#include<signal.h>

v
int main(int argc, char* argv[])
{
	//创建一个信号集
	sigset_t set;
	int ret = 0;
	//初始化一个空的信号集
	sigemptyset(&set);
	//判断SIGINT信号是否在信号集中
	ret = sigismember(&set, SIGINT);
	if (ret == 0) {
		printf("SIGINT is not a member of sigprocmask\n ret=%d\n", ret);
	}
	//将指定的信号添加到信号集中
	sigaddset(&set, SIGINT);
	sigaddset(&set, SIGQUIT);

	ret = sigismember(&set, SIGINT);
	ir(ret == 1) {
		printf("SIGINT is a member of sigprocmask\n ret=%d\n", ret);
	}
	return 0;
}

7. 信号阻塞集(屏蔽集、掩码)

        每个进程都有一个阻塞集,它用来描述哪些信号递送到该进程的时候被阻塞(在信 号发生时记住它,直到进程准备好时再将信号通知进程)。

         所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻 塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。

7.1 sigprocmask函数

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
功能: 
    检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由 set 指定,而原先的信号阻塞集合由 oldset 保存。
参数: 
    how:信号阻塞集合的修改方法。
        SIG_BLOCK:向信号阻塞集合中添加 set 信号集
        SIG_UNBLOCK:从信号阻塞集合中删除 set 集合
        SIG_SETMASK:将信号阻塞集合设为 set 集合
    set:要操作的信号集地址。
    oldset:保存原先信号集地址。
    注:若 set 为 NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到oldset 中。
返回值: 
    成功:返回 0
    失败:返回 -1

代码示例: 

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>

int main(int argc, char* argv[])
{
	int i = 0;
	//创建一个信号集
	sigset_t set;
	sigemptyset(&set);
	sigaddset(&set, SIGINT);
	while(1){
		//将set信号集添加到信号阻塞集中
		sigprocmask(SIG_BLOCK, &set, NULL);
		for (i = 0; i < 5; i++) {
			printf("SIGINT signal is blocked\n");
			sleep(1);
		}
		//将set信号集从信号阻塞集中删除
		sigprocmask(SIG_UNBLOCK, &set, NULL);
		for (i = 0; i < 5; i++) {
			printf("SIGINT signal unblocked\n");
			sleep(1);
		}
	}
	return 0;
}

7.2 sigpending函数

#include <signal.h>
int sigpending(sigset_t *set);
功能:
    读取当前进程的未决信号集
参数:
    set:未决信号集
返回值:
    成功:0
    失败:-1

代码示例: 

#include<stdio.h>
#include<signal.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
	// 自定义信号集
	sigset_t myset, old;
	sigemptyset(&myset);// 清空 -》 0
	// 添加要阻塞的信号
	sigaddset(&myset, SIGINT);
	sigaddset(&myset, SIGQUIT);
	sigaddset(&myset, SIGKILL);
	// 自定义信号集设置到内核中的阻塞信号集
	sigprocmask(SIG_BLOCK, &myset, &old);
	sigset_t pend;
	int i = 0;
	while (1)
	{
		// 读内核中的未决信号集的状态
		sigpending(&pend);
		for (int i = 1; i < 32; ++i)
		{
			if (sigismember(&pend, i))
			{
				printf("1");
			}
			else if (sigismember(&pend, i) == 0)
			{
				printf("0");
			}
		}
		printf("\n");
		sleep(1);
		i++;
		// 10s之后解除阻塞
		if (i > 10)
		{
			// sigprocmask(SIG_UNBLOCK, &myset, NULL);
			sigprocmask(SIG_SETMASK, &old, NULL);
		}
	}
	return 0;
}

总结:

        信号机制是操作系统中用于处理异步事件的一种强大工具,它提供了一种处理程序中非预期事件(如硬件错误、特定的用户交互等)的方法。理解和有效使用信号机制,可以帮助我们编写更健壮、更稳定的程序。总的来说,信号是进程通信的重要方式之一,它的理解和掌握对于系统编程人员至关重要。

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

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

相关文章

用for循环----输出0-999的水仙数

输出0-999的水仙数 水仙算法&#xff1a;一个数它各位的立方和&#xff0c; 例如&#xff1a;1531*1*15*5*53*3*3 提示&#xff1a;for循环&#xff0c;求余数(%)取整&#xff08;/&#xff09;运算符。 答案&#xff1a; 代码如下&#xff1a; #include <stdio.h> …

物理备份xtrabackup

物理备份xtrabackup Xtrabackup 是一个开源的免费的热备工具&#xff0c;在 Xtrabackup 包中主要有 Xtrabackup 和innobackupex 两个工具。其中Xtrabackup 只能备份 InnoDB 和 XtraDB 两种引擎; innobackupex则是封装了Xtrabackup&#xff0c;同时增加了备份MyISAM引擎的功能。…

STM32开发——智能小车(循迹、避障、测速)

目录 1.循迹小车 1.1CubeMX配置 1.2函数代码 2.避障小车 3.小车测速 1.循迹小车 需求&#xff1a;用左右轮实现PWM调速、红外传感器获取道路信息改变方向。 左边红外D0——PB12 右边红外D0——PB13 1.1CubeMX配置 1.2函数代码 motor.c代码 #include "gpio.h"…

TCP三挥四握

TCP三挥四握 TCP最关键的三个步骤&#xff1a;建立连接、数据传输、释放连接&#xff0c;这里的三次握手实现的是服务端和客户端建立连接&#xff1b;四次握手实现的是服务端和客户端释放连接。 三次握手&#xff1a; 建立数据连接 TCP连接需要三次握手的原因&#xff1a; 三次…

【Free】基于主从博弈的主动配电网阻塞管理

目录 1 主要内容 程序亮点 2 部分代码 3 程序结果 4 下载链接 1 主要内容 《基于主从博弈的主动配电网阻塞管理》文献介绍&#xff1a;主要采用一种配电网节点边际电价统一出清的主从博弈双层调度框架。上层框架解决用户在负荷聚合商引导下的用电成本最小化问题&#xff0…

力扣题库刷题笔记18--四数之和

1、题目如下&#xff1a; 2、个人Python代码实现&#xff1a; 首先看到这题就会想到两种方式&#xff0c;一是四层循环暴力破解&#xff0c;二就是基于前面的三数之和外面加加一层嵌套。 先看一下暴力破解&#xff1a; 这里可以看到&#xff0c;当数据量足够大时&#xff0c;依…

Openresty原理概念篇(十)为什么 lua-resty-core 性能更高一些

一 为什么 lua-resty-core 性能更高一些 ① 回顾 lua-resty-core和lua-nginx-module各自都有哪些API? --> 看下面两个仓库的文档 lua-nginx-module lua-resty-core 下面&#xff1a;一起看下Lua C API和FFI 的实现有何不同之处,这样可以对它们的性能有个直观认识…

14. python从入门到精通——GUI编程

目录 常用的GUI框架 wxPython&#xff1a;比较常用 PyQt6&#xff1a;比较常用 Kivy Flexx Tkinter 安装PyQt5 要开发PyQt5程序需要安装三个模块&#xff1a; 安装命令&#xff1a; 安装 安装命令&#xff1a; window安装&#xff1a; PyCharm软件安装&#xff1a…

Android跳转具体应用权限管理,三种方式

背景&#xff1a;Android越来越安全合规&#xff0c;在应用里&#xff0c;需要给用户所有可选择和取消的明确方式。 比如&#xff1a;设置了权限&#xff0c;也要给用户关闭权限的入口。被要求在应用的设置里&#xff0c;提供权限管理入口。 解决方式有三&#xff1a; 方式一…

ansible实训-Day2(ansible基本问题及部署安装)

一、前言 该篇是对ansible实训第二天内容的归纳总结&#xff0c;主要包括ansible的一些基本问题以及ansible的部署安装。 二、理论部分 Q1&#xff1a;什么是ansible Ansible是一种自动化IT工具&#xff0c;它可以帮助管理和自动化IT基础架构。使用Ansible&#xff0c;管理员…

神仙级编程神器,吹爆!

Visual Studio 编程领域公认的“最强IDE”&#xff0c;Visual Studio是目前最流行的Windows平台应用程序的集成开发环境&#xff0c;提供了高级开发工具、调试功能、数据库功能和创新功能&#xff0c;帮助在各种平台上快速创建当前最先进的应用程序&#xff0c;开发新的程序。 …

界面控件DevExpress ASP.NET中文 - 如何自定义编辑表单运行时布局?

在DevExpress ASP.NET控件v19.2版本中就针对ASP. NET WebForms和MVC平台的ASP. NET GridView和CardView控件添加了一个主要增强功能。 DevExpress ASP.NET v23.1正式版下载(Q技术交流&#xff1a;523159565&#xff09; 当您使用预定义的或 自定义的编辑表单时&#xff0c;经…

网易兑换礼包码分析

🍋前言 由于C站版权太多,所有的爬虫相关均为记录,不做深入! 接到了一个项目大概是电商老板想弄一个自动化工具,方便自己处理买家买的兑换码,一个一个的兑换有点累人,代码写到一半才发现由于自己的原因分析错了,刚刚开始我以为他的验证码是在html上的,就像这样 <…

SpringBoot02:运行原理初探

目录 一、运行原理探究 1、pom.xml文件 1.1、父依赖 1.2、启动器Spring-boot-starter 2、主启动类 2.1、默认的主启动类&#xff1a; 2.2、分析主启动类注解&#xff1a;SpringBootApplication 1、Target(ElementType.TYPE) 2、Retention(RetentionPolicy.RUNTIME) 3…

【C语言初阶(3)】循环语句:for 循环

文章目录 1. 语法结构2. for 语句的执行流程3. for 循环中的 break 和 continue3.1 for 循环中的 break3.2 for 循环中的 continue 4. for 循环语句的循环控制变量5. for 循环的变种5.1 for( ; ; )5.2. for 循环的嵌套5.3 使用多个变量控制循环 6. for 循环笔试题 1. 语法结构 …

【读书笔记】《数据结构C语言版》

目录 第一章 线性表 第二章 栈和队列 第三章 字符串 第四章 广义表 第五章 树 第六章 图 第七章 查找 第八章 内排序 第一章 线性表 一个线性表是n个数据元素的优先序列线性表可分为顺序存储结构&#xff08;数组&#xff09;和链式存储结构&#xff08;链表&#xff…

安科瑞WHD智能型温湿度控制器

安科瑞WHD智能型温湿度控制器 安科瑞 崔丽洁

前端Vue自定义发送短信验证码弹框popup 实现剩余秒数计数 重发短信验证码

前端Vue自定义发送短信验证码弹框popup 实现剩余秒数计数 重发短信验证码&#xff0c; 请访问uni-app插件市场地址&#xff1a;https://ext.dcloud.net.cn/plugin?id13207 效果图如下&#xff1a; 实现代码如下: # cc-codeDialog #### 使用方法 使用方法 <!-- show:是…

【uniapp】uniapp反向代理解决跨域问题

背景介绍 前段时间&#xff0c;在拿uniapp开发的时候&#xff0c;出现了跨域问题&#xff0c;按理说跨域应该由后端解决&#xff0c;但既然咱前端可以上&#xff0c;我想就上了&#xff08;顺手装个13&#xff09; 什么是跨域 出于浏览器的同源策略&#xff0c;在请求时&…

React 简单实现 v-if和v-show的元素控制效果

react中并没有直接的想模板引擎那样的命令可以直接控制元素展示 但是 我们了解了 v-if和v-show之后 还是大有文章的 我们在 项目的 src下创建 components 文件夹 创建dom.jsx 编写代码如下 import React from "react" export default class dom extends React.Comp…