Linux高级编程_29_信号

文章目录

  • 进程间通讯 - 信号
    • 信号
      • 完整的信号周期
      • 信号的编号
      • 信号的产生
      • 发送信号
        • 1 kill 函数(他杀)
          • 作用:
          • 语法:
            • 示例:
        • 2 raise函数(自杀)
          • 作用:
          • 示例:
        • 3 abort函数(自杀)
          • 作用:
          • 语法:
            • 示例:
        • 4 alarm函数(自杀)
          • 只能延迟执行
          • 作用:
          • 语法:
            • 示例:
        • 5 settimer函数(定时器)[扩展]
          • 延迟重复执行
        • 6 pause函数
    • 自定义信号处理
      • 1 signal
        • 作用:
        • 语法:
      • 2 sigaction
        • 作用:
        • 语法:
          • struct sigaction:结构体
      • 可重入函数
        • 概述:
        • 注意:
      • 信号集
        • 概述
      • 自定义信号集函数
      • 信号阻塞集
        • 概述
        • sigprocmask函数
          • 作用:
          • 语法:

进程间通讯 - 信号

中文名:进程间通讯

英文名:IPC

英文全称:Inter Processes Communication

作用:进程间互相传递信息

数据传输:一个进程需要将它的数据发送给另一个进程。
资源共享:多个进程之柯共享同样的资源。
通知事件:一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。
进程控制:有些进程希望完全控制另一个进程的执行(如Debug进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。

分类:

  • 信号
  • 管道
  • 文件共享
  • 网络【socket】

发展史:

  • 最初的UNIX进程间通信
  • SYSTEMV 进程间通信
  • POSIX 进程间通信(PosIX:PortableOperatingSysteminterface 可移植操作系统接口)
  • Socket进程间通信
  • Linux把优势都继承了下来并形成了自己的IPC

Linux进程间通信

在这里插入图片描述

信号

信号是 Linux 进程间通信的最古老的方式

特点:

  • 简单
  • 不能携带大量信息
  • 满足某个特设条件才发出。【也就是不管发 管收】

注意:

  • 1 信号是一种异步通信方式
  • 2 信号是软件中断,它是在软件层次上对中断机制的一种模拟。
  • 3 信号可以导致一个正在运行的进程被另一个正在运行的异步进程中断,转而处理某一个突发事件。
  • 4 进程不必等待信号的到达,进程也不知道信号什么时候到达。
  • 5 信号可以直接进行用户空间进程和内核空间进程的交互,内核进程可以利用它来通知用户空间进程发生哪些系统事件

名词解释

  • 同步:事件、操作或进程是有序的,一个操作必须在另一个操作完成后开始执行
  • 异步:事件、操作或进程是独立的,可以在不等待其他操作完成的情况下开始执行
  • 中断机制:是现代计算机系统中的基本机制之一,完成了对计算机各个事件(如时钟、键盘等)响应工作
  • 硬件中断:是由硬件设备触发的中断,如时钟中断、串口接收中断、外部中断等。

完整的信号周期

  • 发送信号
  • 接收信号
  • 信号的处理(默认、忽略、自定义)
  • 信息的注销

信号的编号

注意:信号不能自定义

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

    信号名定义路径:/usr/include/asm-generic/signal.h

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

    不存在编号为0的信号。

    1-31号信号称之为常规信号(也叫普通信号,标准信号)
    34-64号信号称之为实时信号(自定义信号)

    通过man 7 signal查看信号帮助文档

    注意:9) SIGKILL 和 19) SIGSTOP 信号,不允许忽略和捕捉,只能执行默认动作。(重要)

        SIGQUIT信号由:Ctrl + \触发
        SIGINT信号由:ctrl+c触发

在这里插入图片描述

在这里插入图片描述

注意:信号不能自定义

  • kill -l
  • man 7 signal
>通过  kill -l 查看
  		每个信号的名字都以字符 SIG 开头。
  		每个信号和一个数字编码相对应,在头文件ignal.h中,这些信号都被定义为正整数。
  		信号名定义路径:/usr/include/asm-generic/signal.h	

> 不存在编号为0的信号。
	1-31号信号称之为常规信号(也叫普通信号,标准信号)
	34-64号信号称之为实时信号(自定义信号)
通过man	7	signal	查看信号帮助文档  
注意:
	9)SIGKILL
		和
	19)SIGSTOP信号,
	不允许忽略和捕捉,只能执行默认动作。(重要)
SIGQUIT 信号由:Ctr]+\触发
SIGINT 	信号由:ctrl +c触发

信号的产生

	1, 当用户按某些终端键时,将产生信号。 终端上按“Ctrl+c”组合键通常产生中断信号 SIGINT 终端上按“Ctrl+\”键通常产生中断信号 SIGQUIT 终端上按“Ctrl+z”键通常产生中断信号 SIGSTOP 等。

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

    3,软件异常将产生信号。 当检测到某种软件条件已发生(如:定时器 alarm),并将其通知有关进程时,产生信号。

    4,调用系统函数(如:kill、raise、abort)将发送信号。
注意:接收信号进程和发送信号进程的所有者必须相同,或发送信号进程的所有者必须是超级用户。

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

发送信号

1 kill 函数(他杀)
作用:

给指定进程发送指定信号(不一定杀死),他杀

语法:
>	头文件:
			#include <sys/types.h>
			#include <signal.h>
函数
	int kill(pid_t pid, int sig);
参数:
	pid:取值有4种情况:
					pid〉O:将信号传送给进程 ID 为 pid 的进程。
					pid=0:将信号传送给当前进程所在的进程组中的所有进程。
					pid=-1:将信号传送给系统内所有的进程。
					pid〈-1:将信号传给指定进程组的所有进程。这个进程组号等于pid的绝对值。				
	sig:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过命令ki1]-1("]”为字母)进行相应查看。不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。
	
> 返回值:
			成功:0
			失败:-1
示例:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    int pid = fork();
    if (pid == 0)
    {
        printf("子进程开始\n");
        while (1);
        printf("子进程end\n");
    }
    else if (pid > 0)
    {
        printf("当前进程开始\n");
        printf("3秒后发送信号给子进程\n");
        sleep(3);
        // 给子进程发送 SIGINT 信号
        kill(pid, SIGINT);
        printf("当前进程已经发送了信号\n");
        int id = wait(NULL);
        printf("进程%d被回收了\n",id);
    }
    return 0;
}

在这里插入图片描述

2 raise函数(自杀)

#include <signal.h>

作用:

自己给自己发信号

语法:

函数
	int raise(int sig);
参数:
	sig:信号编号
返回值:
		成功:0
		失败:非 0
注意:
		该函数等价与kill(getpid(),sig);
示例:
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <wait.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    printf("当前进程开始 \n");
    //发送停止信号
    raise(SIGSTOP);
    while (1)
    printf("当前进程结束 \n");
    return 0;
}

在这里插入图片描述

3 abort函数(自杀)
作用:

给自己发送异常终止信号 6) SIGABRT,并产生 core 文件,等价于 kill(getpid(), SIGABRT)

core文件(了解)

core就是内核
core文件会包含了程序运行时的内存,寄存器状态,堆栈指针,内存管理信息还有各种
函数调用堆栈信息等,我们可以理解为是程序工作当前状态存储生成第一个文件,许多
的程序出错的时候都会产生一个core文件,通过工具分析这个文件,我们可以定位到程
序异常退出的时候对应的堆栈调用等信息,找出问题所在并进行及时解决。
语法:
所需头
	#include <stdlib.h>
函数
	void abort(void);
注意:
	1,使用abort函数结束进程,进程结束前会刷新缓冲区并关闭所有的文件描述符(0/1/22,即使 SIGABRT 信号被加入阻塞集,一旦进程调用了 abort 函数,进程也还是会被终止
示例:
#include <stdio.h>
#include <stdlib.h>
//  abort() 给自己发送异常终止信号
int main(int argc, char const *argv[])
{
    printf("当前进程开始 \n");
    abort();
    while (1)
    printf("当前进程结束 \n");
    return 0;
}

4 alarm函数(自杀)
只能延迟执行

#include <unistd.h>

作用:
>
设置定时器(闹钟)。在指定时间后(单位秒),内核会给当前进程发送 14)SIGALRM 信号。进程收到该信号,默认动作终止。每个进程都有且只有唯一的一个定时器。
	
	取消定时器 alarm(0),返回旧闹钟余下秒数。
语法:
函数
	unsigned int alarm(unsigned int seconds);
参数:
	seconds:时间,单位秒
返回值:
		若以前没有设置过定时器,或设置的定时器已超时,返回0;
		否则返回定时器剩余的秒数,并重新设定定时器

注意:

定时,与进程状态无关(即自然定时法)!就绪、运行、挂起(阻塞、暂停)、终止、僵尸……无论进程处于何种状态,alarm 都计时
		不阻塞当前进程
示例:
#include <stdio.h>
#include <unistd.h>
//    alarm函数(自杀)
int main(int argc, char const *argv[])
{
    printf("当前进程开始 \n");
    alarm(5);  //每过五秒就自杀 不阻塞进程 也就不妨碍下面进程的执行
    int i = 0;
    while (1)
    {
        sleep(1);
        i++;
        printf("已经过去了%d秒\n", i);
    }

    printf("当前进程结束 \n");
    return 0;
}

在这里插入图片描述

示例2: 设置了 0 就是取消定时

因为死循环 就会一直执行 不会发出杀死进程的信号

#include <stdio.h>
#include <unistd.h>
//    alarm函数(自杀)
int main(int argc, char const *argv[])
{
    printf("当前进程开始 \n");
    alarm(5);  //每过五秒就自杀 不阻塞进程 也就不妨碍下面进程的执行
    alarm(0); // 0 代表取消
    int i = 0;
    while (1)
    {
        sleep(1);
        i++;
        printf("已经过去了%d秒\n", i);
    }

    printf("当前进程结束 \n");
    return 0;
}

在这里插入图片描述

5 settimer函数(定时器)[扩展]
延迟重复执行

等价 kill+sleep 【开发实际 会选用 kill + sleep】

settimer 开发有延迟重复执行就用

alarm 开发需要延迟执行就用它

但是 kill + sleep 其实就够用

作用: 设置定时器(闹钟)。 可代替 alarm 函数。精度微秒 us,可以实现周期定时。

语法:

> 所需头文件
			#include <sys/time.h>
> 函数
int setitimer(int which, const struct itimerval *new_value, structitimerval *old_value);

>参数:
	which:指定定时器类型,可以是以下三个值之一:
			a) 自然定时:ITIMER_REAL → 14)SIGALRM 计算自然时间
			b) 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM 只计算进程占用 cpu 的时间
			c) 运行时计时(用户+内核):ITIMER_PROF → 27)SIGPROF 计算占用 cpu及执行系统调用的时间

	new_value:一个指向struct itimerval结构体的指针,用于指定新的定时器值(启动时间和间隔时间)。

	old_value:一个指向struct itimerval结构体的指针,用于获取旧的定时器值,即之前设置的定时器值。如果不需要获取旧的定时器值,则可以传入NULL>注意:
	设置的value一定要全部设置值,因为结构体成员变量默认值为随机数

在这里插入图片描述

struct itimerval 结构体

struct itimerval 
{
	struct timerval it_interval; // 间隔时间
	struct timerval it_value; // 延迟时间
};

struct timerval结果体

struct timeval
{
		long tv_sec; // 秒
		long tv_usec; // 微秒
};

示例

#include <stdio.h>
#include <signal.h>
#include <sys/time.h>
extern void fun01();
int main(int argc, char const *argv[])
{
//注意  因为 是结构体 所以成员变量初始值 是随机数  使用时 全部初始化 
   signal(SIGALRM,fun01);
   struct itimerval val;
   val.it_value.tv_sec=3;    // 设置 延迟时间为3s
   val.it_value.tv_usec=0;
   val.it_interval.tv_sec=1.5; // 设置 间隔时间为1s
   val.it_interval.tv_usec=0;
   setitimer(ITIMER_REAL,&val,NULL);
   while(1);
   return 0;
}
void fun01()
{
   printf("收到信号\n");
   // _exit(0); 
}

在这里插入图片描述

6 pause函数

作用:捕捉信号,会阻塞当前进程

语法: #include <unistd.h>

	int pause(void);
返回值:
    	直到捕获到信号
    pause返回 -1 并且errno 被设置为 EINTR

示例:

#include <stdio.h>
#include <unistd.h>
// pause()函数  会阻塞进程
int main(int argc, char const *argv[])
{
    printf("开始咯\n");
    pause();
    printf("开始咯\n");
    return 0;
}

在这里插入图片描述

自定义信号处理

信号处理:

  • 默认 SIG_DEF
  • 忽略 SIG_IGN
  • 自定义 函数名

注意:

SIGSTOP 与 SIGKILL 这两个信号 不能或略 不能自定义 只能默认

1 signal

作用:

​ 可以用于进程间通讯

语法:

# include <signal.h>

函数:
	sighandler_t signal(int signum, sighandler_t handler);
参数:
	signum:信号的编号,这里可以填数字编号,也可以填信号的宏定义,可以通过
命令 
	kill - l("l" 为字母)进行相应查看。

>handler : 取值有 3 种情况:
						SIG_IGN:忽略该信号
						SIG_DFL:执行系统默认动作
						信号处理函数名:自定义信号处理函数
				//定义的一个函数指针
				typedef void(*sighandler_t)(int);
返回值:
	成功:第一次返回 NULL,下一次返回此信号上一次注册的信号处理函数的地址。如果需要使用此返回值,必须在前面先声明此函数指针的类型。
失败:
	返回 SIG_ERR
  • 通过 kill -l 查看

    • 每个信号的名字都以字符 SIG 开头。
    • 每个信号和一个数字编码相对应,在头文件ignal.h中,这些信号都被定义为正整数。
    • 信号名定义路径:/usr/include/asm-generic/signal.h
  • 不存在编号为0的信号。

    • 1-31号信号称之为常规信号(也叫普通信号,标准信号)
    • 34-64号信号称之为实时信号(自定义信号)

在这里插入图片描述

示例1:

注意: sigCode 返回的是 信号编号

#include <stdio.h>
#include <signal.h>
void fun(int sigCode)
{
    printf("sigCode = %d\n", sigCode);
    printf("sigCode 是用来获取信号编号的");
    printf("你好,这是只有按下 CTRL+c 后显示的函数内容\n");
}
int main(int argc, char const *argv[])
{
    // SIGINT  键盘ctrl+c 就触发信号
    // signal(SIGINT, SIG_DFL); // 这是默认处理   也就是停止进程

    // signal(SIGINT,SIG_IGN); //SIG_IGN 忽略

    // 绑定信号
    //  当按下 CTRL + c 就去执行函数  fun
    signal(SIGINT, fun);
    while (1);
    return 0;
}

在这里插入图片描述

示例2:

#include <stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include <wait.h>
#include <signal.h>
int tag = 1;
void fun()
{
    tag = 0;
}
void fun02()
{
    printf("子进程结束了\n");
}
int main(int argc, char const *argv[])
{
    int pid = fork();
    if (pid == 0)
    {
        signal(SIGUSR1, fun);
        for (int i = 0; i < 100 && tag; i++)
        {
            sleep(1);
            printf("已下载:%d%%\n", i + 1);
        }
        printf("结束下载\n");
        kill(getpgrp(), SIGUSR2);
    }
    else if (pid > 0)
    {
        signal(SIGUSR2, fun02);
        printf("5秒后停止下载\n");
        sleep(5);
        kill(pid, SIGUSR1);
        wait(NULL);
    }
    return 0;
}

2 sigaction

作用:

​ 检查或修改指定信号的设置(或同时执行这两种操作)。

语法:

#include <signal.h>

> 函数:
		int sigaction(int signum, const struct sigaction *act, struct sigaction* oldact);
> 参数:
	signum:要操作的信号。
			act: 要设置的对信号的新处理方式(传入参数)。
			oldact:原来对信号的处理方式(传出参数)。
> 注意:
		如果act指针非空,则要改变指定信号的处理方式(设置),如果oldact指针非空,则系统将此前指定信号的处理方式存入oldact。
> 返回值:
		成功:0
		失败:-1
struct sigaction:结构体
struct sigaction
{
    void (*sa_handler)(int);                           // 旧的信号处理函数指针 
    void (*sa_sigaction)(int, siginfo_t *, void *); // 新的信号处理函数指针
    sigset_t sa_mask;          // 信号阻塞集
    int sa_flags;              // 信号处理的方式
    void (*sa_restorer)(void); // 已弃用
};
/*
    1,sa_handler,sa_sigaction:信号处理函数指针,和signal()里的函数指针用法一样,应根据情况给 sa_sigaction、sa_handler 两者之一赋值,其取值如下:
        a SIGIGN:忽略该信号
        b SIGDFL:执行系统默认动作
        c 处理函数名:自定义信号处理函数
    2,sa_mask:信号阻塞集,在信号处理函数执行过程中,临时屏蔽指定的信号。
    3,sa_flags:用于指定信号处理的行为,通常设置为 0,表使用默认属性。它可以是以下值的“按位或”组合:
            SA_RESTART:使被信号打断的系统调用自动重新发起(已经废弃)
            SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD信号。
            SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
            SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
            SA_RESETHAND:信号处理之后重新设置为默认的处理方式
            SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理
函数
        void(*sa_sigaction)(int signum, siginfo_t *info, void *context);
参数说明:
    signum:信号的编号。
            info:记录信号发送进程信息的结构体。
            context:可以赋给指向ucontext_t类型的一个对象的指针,以引用在传递信号时被中断的接收进程或线程的上下文。
*/
#include <stdio.h>
#include <signal.h>
void fun()
{
    printf("已处理\n");
}
int main(int argc, char const *argv[])
{
    struct sigaction mys;
    mys.sa_handler = fun;
    sigaction(SIGINT, &mys, NULL);
    while (1);
    return 0;
}

可重入函数

概述:
一种特殊的函数
多个进程执行同一个函数,多个进程之间互不干扰,该函数为可重入函数
注意:
1、不使用或返回 静态的数据、全局变量(除非用信号量互斥)。
2、不调用动态内存分配、释放的函数。
3、不调用任何不可重入的函数(如标准 I/O 函数)

系统提供的可重入函数

在这里插入图片描述

信号集

概述
> 在PCB中有两个非常重要的信号集。一个称之为“阻塞信号集”,另一个称之为“未决信号集”。 这两个信号集都是内核使用位图机制来实现的。但操作系统不允许我们直接对其进行位操作。而需自定义另外一个集合,借助信号集操作函数来对 PCB 中的这两个信号集进行修改

> 信号阻塞集:将某些信号加入 集合,对他们设置屏蔽,当屏蔽 x 信号后,再收到该信号,该信号的处理将暂缓。

> 未决信号集:未被处理的信号集合。

在这里插入图片描述

自定义信号集函数

所需头文件
#include <signal.h>

函数
	int sigemptyset(sigset_t *set); //将 set 集合置空
	int sigfillset(sigset_t *set)//将所有信号加入 set 集合
	int sigaddset(sigset_t *set, int signo); //将 signo 信号加入到 set合
	int sigdelset(sigset_t *set, int signo); //从 set 集合中移除 signo信号
	int sigismember(const sigset_t *set, int signo); //判断信号是否存在于集合中

信号阻塞集

概述
>	信号阻塞集也称信号屏蔽集、信号掩码。
> 	每个进程都有一个阻塞集,创建子进程时子进程将继承父进程的阻塞集。信号阻塞集用来描述哪些信号递送到该进程的时候被阻塞(在信号发生时记住它,直到进程准备好时再将信号通知进程)。 所谓阻塞并不是禁止传送信号, 而是暂缓信号的传送。若将被阻塞的信号从信号阻塞集中删除,且对应的信号在被阻塞时发生了,进程将会收到相应的信号。

> 	我们可以通过 sigprocmask() 修改当前的信号阻塞集来改变信号的阻塞情况。
sigprocmask函数
作用:

检查或修改信号阻塞集,根据 how 指定的方法对进程的阻塞集合进行修改,新的信号阻塞集由 set 指定,而原先的信号阻

塞集合由 oldset 保存。

语法:

所需头文件

#include <signal.h>

函数
	int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数
	how:信号阻塞集合的修改方法,3种情况:
		SIG_BLOCK:添加
				向信号阻塞集合中添加set信号集,新的信号掩码是set和旧信号掩码的并集.相当于mask=mask|set。
		SIG_UNBLOCK:删除
				从信号阻塞集合中删除set信号集,从当前信号掩码中去除set中的信号.相当于mask=mask&~set。
		SIG_SETMASK:替换	
				将信号阻塞集合设为set信号集,相当于原来信号阻塞集的内容清空,然后按照set中的信号重新设置信号阻塞集。相当于mask=set。
>	set:要操作的信号集地址。若set为NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到oldset中
>	oldset:保存原先信号阻塞集地址
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
//   进程的加锁  与解锁
void fun()
{
    printf("已处理\n");
}
int main(int argc, char const *argv[])
{
    signal(SIGINT, fun);
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    sigprocmask(SIG_BLOCK, &set, NULL);  //设置阻塞
    printf("3秒后移除对SIGINT的阻塞\n");
    sleep(3);
    sigprocmask(SIG_UNBLOCK, &set, NULL);   //解除阻塞
    while (1);
    return 0;
}
	SIG_BLOCK:添加
			向信号阻塞集合中添加set信号集,新的信号掩码是set和旧信号掩码的并集.相当于mask=mask|set。
	SIG_UNBLOCK:删除
			从信号阻塞集合中删除set信号集,从当前信号掩码中去除set中的信号.相当于mask=mask&~set。
	SIG_SETMASK:替换	
			将信号阻塞集合设为set信号集,相当于原来信号阻塞集的内容清空,然后按照set中的信号重新设置信号阻塞集。相当于mask=set。
set:要操作的信号集地址。若set为NULL,则不改变信号阻塞集合,函数只把当前信号阻塞集合保存到oldset中
oldset:保存原先信号阻塞集地址
#include <stdio.h>
#include <signal.h>
#include <sys/types.h>
#include <unistd.h>
//   进程的加锁  与解锁
void fun()
{
    printf("已处理\n");
}
int main(int argc, char const *argv[])
{
    signal(SIGINT, fun);
    sigset_t set;
    sigemptyset(&set);
    sigaddset(&set, SIGINT);
    sigprocmask(SIG_BLOCK, &set, NULL);  //设置阻塞
    printf("3秒后移除对SIGINT的阻塞\n");
    sleep(3);
    sigprocmask(SIG_UNBLOCK, &set, NULL);   //解除阻塞
    while (1);
    return 0;
}

在这里插入图片描述

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

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

苏州 数字化科技展厅展馆-「世岩科技」一站式服务商

数字化科技展厅展馆设计施工是一个综合性强、技术要求高的项目&#xff0c;涉及到众多方面的要点。以下是对数字化科技展厅展馆设计施工要点的详细分析&#xff1a; 一、明确目标与定位 在设计之初&#xff0c;必须明确展厅的目标和定位。这包括确定展厅的主题、目标受众、展…

详解正确创建好SpringBoot项目后但是找不到Maven的问题

目录 问题 解决步骤&#xff1a; 找到File->Project Structure... 设置SDK 设置SDKs 问题 刚刚在使用IDEA专业版创建好SpringBoot项目后&#xff0c;发现上方导航栏的运行按钮是灰色的&#xff0c;而且左侧导航栏的pom.xml的图标颜色也不是正常的&#xff0c;与此同时我…

PIKACHU | PIKACHU 靶场 XSS 后台配置

关注这个靶场的其他相关笔记&#xff1a;PIKACHU —— 靶场笔记合集-CSDN博客 PIKACHU 自带了一个 XSS 平台&#xff0c;可以辅助我们完成 XSS 攻击&#xff0c;但是该后台需要配置数据库以后才能使用。本教程&#xff0c;就是教大家如何配置 PIKACHU XSS 平台的。 PIKACHU XS…

在线教育的未来:SpringBoot技术实现

1系统概述 1.1 研究背景 随着计算机技术的发展以及计算机网络的逐渐普及&#xff0c;互联网成为人们查找信息的重要场所&#xff0c;二十一世纪是信息的时代&#xff0c;所以信息的管理显得特别重要。因此&#xff0c;使用计算机来管理微服务在线教育系统的相关信息成为必然。开…

Hadoop大数据入门——Hive-SQL语法大全

Hive SQL 语法大全 基于语法描述说明 CREATE DATABASE [IF NOT EXISTS] db_name [LOCATION] path; SELECT expr, ... FROM tbl ORDER BY col_name [ASC | DESC] (A | B | C)如上语法&#xff0c;在语法描述中出现&#xff1a; []&#xff0c;表示可选&#xff0c;如上[LOCATI…

基于深度学习的乳腺癌分类识别与诊断系统

温馨提示&#xff1a;文末有 CSDN 平台官方提供的学长 QQ 名片 :) 1. 项目简介 乳腺癌是全球最常见的癌症之一&#xff0c;早期诊断对于治疗效果至关重要。近年来&#xff0c;深度学习技术在医学图像分析领域取得了显著进展&#xff0c;能够从大量的医学影像数据中自动学习和提…

【Android 14源码分析】WMS-窗口显示-第一步:addWindow

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…

网络安全概述:从认知到实践

一、定义 网络安全&#xff0c;即致力于保护网络系统所涵盖的硬件、软件以及各类数据&#xff0c;切实保障其免遭破坏、泄露或者篡改等不良情形的发生。 二、重要性 个人层面&#xff1a;着重于守护个人隐私以及财产安全&#xff0c;为个人在网络世界中的各项活动提供坚实的保…

Redis篇(Redis原理 - 数据结构)(持续更新迭代)

目录 一、动态字符串 二、intset 三、Dict 1. 简介 2. Dict的扩容 3. Dict的rehash 4. 知识小结 四、ZipList 1. 简介 2. ZipListEntry 3. Encoding编码 五、ZipList的连锁更新问题 六、QuickList 七、SkipList 八、RedisObject 1. 什么是 redisObject 2. Redi…

使用JavaScript写一个网页端的四则运算器

目录 style(内联样式表部分) body部分 html script 总的代码 网页演示 style(内联样式表部分) <style>body {font-family: Arial, sans-serif;display: flex;justify-content: center;align-items: center;height: 100vh;background-color: #f0f0f0;}.calculator {…

Python开发环境配置(mac M2)

1. 前言 作为一名程序员&#xff0c;工作中需要使用Python进行编程&#xff0c;甚至因为项目需要还得是不同版本的Python如何手动管理多个版本的Python&#xff0c;如何给Pycharm&#xff08;IDE&#xff09;配置对应的interpreter等&#xff0c;都成为一个 “不熟练工” 的难…

使用百度文心智能体创建多风格表情包设计助手

文章目录 一、智能定制&#xff0c;个性飞扬二、多元风格&#xff0c;创意无限 百度文心智能体平台为你开启。百度文心智能体平台&#xff0c;创建属于自己的智能体应用。百度文心智能体平台是百度旗下的智能AI平台&#xff0c;集成了先进的自然语言处理技术和人工智能技术&…

基于SpringBoot实现QQ邮箱发送短信功能 | 免费短信服务

开发学习过程中有个短信发送功能&#xff0c;阿里云腾讯云等等都要money&#xff0c;听说qq邮箱可以实现免费发送邮箱的功能&#xff08;短信发送的平替&#xff09;&#xff0c;就用这个来实现&#xff01;&#xff01;&#xff01;【找了好多好多方法才成功的啊啊啊啊&#x…

【Linux】进程第三弹(虚拟地址空间)

目录 现象 底层原因 数据不发生修改 数据修改 小总结 地址空间本质 为什么要有地址空间 现象 来看代码&#xff1a; #include <stdio.h> #include <unistd.h> #include <sys/types.h>int val 50;int main() {printf("father process is running…

标准正态分布的数据 tensorflow 实现正态分布图,python 编程,数据分析和人工智能

import tensorflow as tf import matplotlib.pyplot as plt # 设置随机种子以获得可重复的结果 tf.random.set_seed(42) # 生成正态分布的数据 # mean0 和 stddev1 表示生成标准正态分布的数据 # shape(1000,) 表示生成1000个数据点 data tf.random.normal(mean0, stddev1, …

【python】追加写入excel

输出文件运行前&#xff08;有两张表&#xff0c;“表1”和“Sheet1”&#xff09;&#xff1a; 目录 一&#xff1a;写入单表&#xff08;删除所有旧工作表&#xff0c;写入新表&#xff09;二&#xff1a;写入多表&#xff08;删除所有旧工作表&#xff0c;写入新表&#x…

HTML5实现唐朝服饰网站模板源码

文章目录 1.设计来源1.1 网站首页-界面效果1.2 唐装演变-界面效果1.3 唐装配色-界面效果1.4 唐装花纹-界面效果1.5 唐装文化-界面效果 2.效果和源码2.1 动态效果2.2 源代码 源码下载万套模板&#xff0c;程序开发&#xff0c;在线开发&#xff0c;在线沟通 作者&#xff1a;xcL…

[C++][第三方库][Websocket]详细讲解

目录 1.Websocket 协议1.介绍2.原理简介 2.Websocketpp1.介绍2.安装 3.常用接口4.使用 1.Websocket 协议 1.介绍 WebSocket是从HTML5开始支持的一种网页端和服务端保持长连接的消息推送机制产生原因&#xff1a; 传统的web程序都是属于"一问一答"的形式 即客户端给…

Python 语言学习——应用1.2 数字图像处理(第二节,变换)

目录 1.基础知识 1.图像几何变换概念 2.图像几何变换方式 3.插值运算 4.几何变换步骤 2.各类变换 1.位置变换 2.形状变换 3.代数运算 3.实战演练 1.基础知识 1.图像几何变换概念 在图像处理过程中&#xff0c;为了观测需要&#xff0c;常常需要对 图像进行几何变换&am…

如何创建免费版本的ABP分离模块?

由于ABP最近官方大改革&#xff0c;我们打开ABP.IO 官方会发现通过Cli创建模板的时候不能创建Trered类型的了 就是创建一个分层的解决方案&#xff0c;其中Web和Http API层在物理上是分开的。如果不勾选&#xff0c;则创建一个分层的解决方案&#xff0c;它不那么复杂&#xf…