信号与进程(3):信号及其使用

信号及其使用

参考博客

Linux信号的产生和处理

信号及其使用

信号的产生

信号由内核产生,信号的生成与事件的发生相关,事件的发生源有3类:

1、用户

用户在终端上按下某些按键时会产生信号,如**Ctrl+C产生SIGINT信号,Ctrl+\** 产生**SIGQUIT信号,Ctrl+Z**产生SIGTSTP信号等,终端驱动程序通知内核产生信号,发送至相应进程

2、内核

硬件异常产生的信号(如除数为0、无效的存储访问等),通常会由硬件(如CPU)检测到,将其通知给Linux操作系统内核,内核产生相应信号发送给该事件发生时的进程

3、进程

  • 在终端调用**kill**命令可向进程发送任意信号
  • 进程调用**killsigqueue**函数可以发送信号
  • 检测到满足条件时发出信号,如**alarmsettimer设置的定时器超时后产生SIGALRM**信号

信号的种类

使用 kill -l 命令可以查看系统定义的信号列表:

prejudice@prejudice-VirtualBox:~$ kill -l
 1) SIGHUP	 2) SIGINT	 3) SIGQUIT	 4) SIGILL	 5) SIGTRAP
 6) SIGABRT	 7) SIGBUS	 8) SIGFPE	 9) SIGKILL	10) SIGUSR1
11) SIGSEGV	12) SIGUSR2	13) SIGPIPE	14) SIGALRM	15) SIGTERM
16) SIGSTKFLT	17) SIGCHLD	18) SIGCONT	19) SIGSTOP	20) SIGTSTP
21) SIGTTIN	22) SIGTTOU	23) SIGURG	24) SIGXCPU	25) SIGXFSZ
26) SIGVTALRM	27) SIGPROF	28) SIGWINCH	29) SIGIO	30) SIGPWR
31) SIGSYS	34) SIGRTMIN	35) SIGRTMIN+1	36) SIGRTMIN+2	37) SIGRTMIN+3
38) SIGRTMIN+4	39) SIGRTMIN+5	40) SIGRTMIN+6	41) SIGRTMIN+7	42) SIGRTMIN+8
43) SIGRTMIN+9	44) SIGRTMIN+10	45) SIGRTMIN+11	46) SIGRTMIN+12	47) SIGRTMIN+13
48) SIGRTMIN+14	49) SIGRTMIN+15	50) SIGRTMAX-14	51) SIGRTMAX-13	52) SIGRTMAX-12
53) SIGRTMAX-11	54) SIGRTMAX-10	55) SIGRTMAX-9	56) SIGRTMAX-8	57) SIGRTMAX-7
58) SIGRTMAX-6	59) SIGRTMAX-5	60) SIGRTMAX-4	61) SIGRTMAX-3	62) SIGRTMAX-2
63) SIGRTMAX-1	64) SIGRTMAX
  • 1~31为系统信号,有固定含义

  • 34~64为实时信号,可由用户定义,所有实时信号的默认动作是终止进程

  • 详细介绍

    1) SIGHUP
    本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一session内的各个作业, 这时它们与控制终端不再关联。
    
    登录Linux时,系统会分配给登录用户一个终端(Session)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个 Session。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号。这个信号的默认操作为终止进程,因此前台进程组和后台有终端输出的进程就会中止。不过可以捕获这个信号,比如wget能捕获SIGHUP信号,并忽略它,这样就算退出了Linux登录,wget也能继续下载。
    
    此外,对于与终端脱离关系的守护进程,这个信号用于通知它重新读取配置文件。
    
    2) SIGINT
    程序终止(interrupt)信号, 在用户键入INTR字符(通常是Ctrl-C)时发出,用于通知前台进程组终止进程。
    
    3) SIGQUIT
    和SIGINT类似, 但由QUIT字符(通常是Ctrl-\)来控制. 进程在因收到SIGQUIT退出时会产生core文件, 在这个意义上类似于一个程序错误信号。
    
    4) SIGILL
    执行了非法指令. 通常是因为可执行文件本身出现错误, 或者试图执行数据段. 堆栈溢出时也有可能产生这个信号。
    
    5) SIGTRAP
    由断点指令或其它trap指令产生. 由debugger使用。
    
    6) SIGABRT
    调用abort函数生成的信号。
    
    7) SIGBUS
    非法地址, 包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数, 但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或只读存储空间)8) SIGFPE
    在发生致命的算术运算错误时发出. 不仅包括浮点运算错误, 还包括溢出及除数为0等其它所有的算术的错误。
    
    9) SIGKILL
    用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
    
    10) SIGUSR1
    留给用户使用
    
    11) SIGSEGV
    试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
    
    12) SIGUSR2
    留给用户使用
    
    13) SIGPIPE
    管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
    
    14) SIGALRM
    时钟定时信号, 计算的是实际的时间或时钟时间. alarm函数使用该信号.
    
    15) SIGTERM
    程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,我们才会尝试SIGKILL。
    
    17) SIGCHLD
    子进程结束时, 父进程会收到这个信号。
    
    如果父进程没有处理这个信号,也没有等待(wait)子进程,子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)18) SIGCONT
    让一个停止(stopped)的进程继续执行. 本信号不能被阻塞. 可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
    
    19) SIGSTOP
    停止(stopped)进程的执行. 注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行. 本信号不能被阻塞, 处理或忽略.
    
    20) SIGTSTP
    停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z)发出这个信号
    
    21) SIGTTIN
    当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号. 缺省时这些进程会停止执行.
    
    22) SIGTTOU
    类似于SIGTTIN, 但在写终端(或修改终端模式)时收到.
    
    23) SIGURG
    有”紧急”数据或out-of-band数据到达socket时产生.
    
    24) SIGXCPU
    超过CPU时间资源限制. 这个限制可以由getrlimit/setrlimit来读取/改变。
    
    25) SIGXFSZ
    当进程企图扩大文件以至于超过文件大小资源限制。
    
    26) SIGVTALRM
    虚拟时钟信号. 类似于SIGALRM, 但是计算的是该进程占用的CPU时间.
    
    27) SIGPROF
    类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间.
    
    28) SIGWINCH
    窗口大小改变时发出.
    
    29) SIGIO
    文件描述符准备就绪, 可以开始进行输入/输出操作.
    
    30) SIGPWR
    Power failure
    
    31) SIGSYS
    非法的系统调用。
    
    在以上列出的信号中,程序不可捕获、阻塞或忽略的信号有:SIGKILL,SIGSTOP
    不能恢复至默认动作的信号有:SIGILL,SIGTRAP
    默认会导致进程流产的信号有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
    默认会导致进程退出的信号有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
    默认会导致进程停止的信号有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
    默认进程忽略的信号有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
    

对信号的响应

  1. 捕获信号
    为要捕获的信号指定信号处理函数,信号发生时自动调用该函数,在函数内部实现该信号处理
  2. 忽略信号
    多数信号可使用该方式,但**SIGKILLSIGSTOP信号不可忽略,同时也不可被捕获和阻塞**
    忽略某些硬件异常产生的信号(除0)会产生不可预测行为
  3. 按系统默认方式处理
    大部分信号默认操作是终止进程,所有实时信号的默认动作是终止进程

信号处理

在这里插入图片描述

内核在接收到信号后,未必会马上对信号进行处理,而是选择在适当的时机,例如在发生中断、发生异常或系统调用返回时,以及在将控制权切换至进程之际,处理所接收的信号

但对于用户进程,进程在接收到信号后,会暂停代码的执行,并保存当前的运行环境,转而执行信号处理程序,待信号处理结束后,才恢复中断点的运行环境,按正常流程继续执行

信号的捕获

signal()函数

#include <signal.h>

/* Set the handler for the signal SIG to HANDLER, returning the old
   handler, or SIG_ERR on error.
   By default `signal' has the BSD semantic.  */
extern __sighandler_t signal (int __sig, __sighandler_t __handler)
     __THROW;

/* Type of a signal handler.  */
typedef void (*__sighandler_t) (int);
  • **signal根据sig信号编号设置信号处理函数,信号到达时就会执行handler**指定的函数
  • 若**handler不是函数指针,则必须为SIG_IGN(忽略该信号)或SIG_DFL**(执行默认操作)
  • signal()函数执行成功返回以前的信号处理函数指针,错误返回SIG_ERR(-1)

示例:

#include <iostream>
#include <signal.h>
#include <unistd.h>
using namespace std;

void func(int sig)
{
    cout << endl << sig << " signal is captured!" << endl;
    signal(SIGINT, SIG_DFL);
}

int main()
{
    signal(SIGINT, func);
    int i = 0;
    while (true)
    {
        cout << "第" << i + 1 << "只猪;" << endl;
        ++i;
        sleep(1);
    }
    return 0;
}

打印输出:

prejudice@prejudice-VirtualBox:~/Cplus_learning/bin$ ./signal 
第1只猪;
第2只猪;
第3只猪;
第4只猪;
^C
2 signal is captured!5只猪;
第6只猪;
第7只猪;
第8只猪;
^C

疑问:

  • **sig**参数如何产生
  • func()函数不会**阻塞**main()函数的执行吗

信号的屏蔽

信号屏蔽就是临时阻塞信号被发送至某个进程,它包含一个被阻塞的信号集。通过操作信号集,可增加和删除需要阻塞的信号。信号屏蔽与信号忽略不同,当进程屏蔽某个信号时,内核将不发送该信号至屏蔽它的进程,直至该信号的屏蔽被解除;而对于信号忽略,内核将被忽略的信号发送至进程,只是进程对被忽略的信号不进行处理

POSIX.1 标准定义了数据类型**sigest_t可以存放一个信号集(由多个信号构成的集合),并且定义了5个处理信号集的函数。在Linux中,包含signal.h头文件即可引用sigest_t**和这5个信号集处理函数

#define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))
typedef struct
{
  unsigned long int __val[_SIGSET_NWORDS];
} __sigset_t;

#include <signal.h>
/* Clear all signals from SET.  */
extern int sigemptyset (sigset_t *__set) __THROW __nonnull ((1));

/* Set all signals in SET.  */
extern int sigfillset (sigset_t *__set) __THROW __nonnull ((1));

/* Add SIGNO to SET.  */
extern int sigaddset (sigset_t *__set, int __signo) __THROW __nonnull ((1));

/* Remove SIGNO from SET.  */
extern int sigdelset (sigset_t *__set, int __signo) __THROW __nonnull ((1));

/* Return 1 if SIGNO is in SET, 0 if not.  */
extern int sigismember (const sigset_t *__set, int __signo)
     __THROW __nonnull ((1));
  • **sigemptyset**信号集__set排除所有信号
  • **sigfillset**信号集__set包括所有信号

sigaction()函数

linux中sigaction函数详解

/* Get and/or set the action for signal SIG.  */
extern int sigaction (int __sig, const struct sigaction *__restrict __act,
		      struct sigaction *__restrict __oact) __THROW;
  • sig是准备捕获或忽略的信号
  • act是将要设置得到信号处理动作
  • oldact是取回原先的信号处理动作
  • 成功返回0,失败返回1,并设置errno变量
struct sigaction 
{
    void (*sa_handler)(int);
    void (*sa_sigaction)(int, siginfo_t *, void *);
    sigset_t sa_mask;
    int sa_flags;
    void (*sa_restorer)(void);
}

如果act指针非空,则表示要修改sig信号的处理动作。如果 oldact指针非空,则系统在其中返回该信号的原先动作

  • **sa_handler**此参数和signal()的参数handler相同,代表新的信号处理函数
  • sa_mask 用来设置在处理该信号时暂时将sa_mask 指定的信号集搁置

当要更改信号动作时,如果act参数的sa_handler 指向一个信号捕获函数(不是常数SIG_IGN或SIG_DFL),则sa_mask字段说明了一个信号集。在调用信号捕获函数之前,该信号集被加入进程的信号屏蔽字中。仅当从信号捕获函数(也称“信号处理程序”)中返回时才将进程的信号屏蔽字恢复为原先值。这样,在调用信号处理程序时就能阻塞某些信号在信号处理程序被调用时,系统建立的新信号屏蔽字会自动包括正被递送的信号。因此保证了在处理一个给定的信号时,如果这种信号再次发生,那么它会被阻塞到对前一个信号的处理结束为止。系统在同一种信号产生多次的情况下,通常并不将它们排队,所以如果在某种信号被阻塞时,它产生了5次,那么对这种信号解除阻塞后,其信号处理函数通常只会被调用一次

sigaction结构的**sa_flags**字段用于设置对信号进行处理的可选项,常用选项及其含义如下:

  • SA_NOCLDSTOP:子进程停止时不产生SIGCHLD信号
  • SA_RESETHAND:在信号处理函数入口处将把此信号的处理方式重置为SIG_DFL
  • SA_RESTART:重启可中断的函数而不是给出EINTR错误
  • SA_NODEFER:捕获到信号时不将它添加到信号屏蔽字当中,即不自动阻塞当前捕获到的信号

推荐使用SA_RESTART的理由是,程序中使用的许多系统调用都是可中断的,当接到一个信号时,它们将返回一个错误并设置errno,以此表明函数是因为一个信号而返回的。在设置了SA_RESTART的情况下,信号处理函数执行完后被中断的系统调用将被重启

示例:

#include <unistd.h>
#include <signal.h>
#include <iostream>
using namespace std;

void show_handler(int sig)
{
    cout << endl << sig << " signal is captured!" << endl;
    for (int i = 0; i < 5; i++)
    {
        cout << "第" << i+1 << "只猪;" <<endl;
        sleep(1);
    }
}

int main(void)
{
    int i = 0;
    struct sigaction act;
    sigemptyset(&act.sa_mask);
    act.sa_handler = show_handler;      //设置中断函数
    sigaddset(&act.sa_mask, SIGQUIT);   //屏蔽“ctrl+\",中断后处理
    act.sa_flags = SA_RESTART;      //中断后重启main函数        
    // act.sa_flags = 0;                      

    sigaction(SIGINT, &act, 0);
    while (true)
    {
        cout << i+1 << "只羊;" << endl;
        sleep(1);
        i++;
    }
}

运行输出:

prejudice@prejudice-VirtualBox:~/Cplus_learning/bin$ ./sigaction 
1只羊;
2只羊;
3只羊;
^C     //捕获中断信号,执行中断函数
2 signal is captured!1只猪;
第2只猪;
第3只猪;
^C第4只猪;
^C第5只猪;   //多次捕获相当于一次

2 signal is captured!1只猪;
第2只猪;
第3只猪;
第4只猪;
第5只猪;
4只羊;   //sa_flags设置为SA_RESTART中断函数执行完后继续执行主函数
5只羊;
6只羊;
^C
2 signal is captured!1只猪;
第2只猪;
第3只猪;
^\第4只猪;   //"ctrl+\"被阻塞,中断函数结束后执行该信号动作
^C第5只猪;

2 signal is captured!1只猪;
第2只猪;
第3只猪;
第4只猪;
第5只猪;
退出 (核心已转储)   //执行"ctrl+\"

💡 signal()函数因为不同版本的Linux都不同,且性能也不如sigaction()函数,因此尽量不用

The  behavior  of  signal()  varies across UNIX versions, and has also varied historically
       across different versions of Linux.  Avoid its use: use sigaction(2) instead.

信号的发送

信号发送的关键是让系统知道该向哪个进程发送信号,以及发送什么信号。只要明确了这两点,就可以很方便地发送信号了

进程除了从用户和内核接收信号外,还可以接收其他进程发送的信号

Linux内核提供的发送信号的应用编程接口主要有**killraisesigqueuealarmsettimerabort**等

kill()函数

Linux 下的KILL函数的用法

#include <sys/types.h>
#include <signal.h>

/* Send signal SIG to process number PID.  If PID is zero,
   send SIG to all processes in the current process's process group.
   If PID is < -1, send SIG to all processes in process group - PID.  */
#ifdef __USE_POSIX
extern int kill (__pid_t __pid, int __sig) __THROW;
#endif /* Use POSIX.  */

成功则返回0, 出错则返回-1

_**pid参数**有以下4种情况:

  • pid > 0: 将该信号发送给进程ID为pid的进程
  • pid == 0: 将该信号发送给与发送进程属于同一进程组的所有进程(不包括内核进程和init进程). 此时, 发送进程必须具有向这些进程发送信号的权限
  • pid < 0: 将该信号发给其进程组ID等于pid绝对值的所有进程(不包括内核进程和init进程). 此时, 发送进程必须具有向这些进程发送信号的权限
  • pid == -1: 将该信号发送给发送进程有权限向它们发送信号的系统上的所有进程.(不包括内核进程和init进程)

_sig参数

POSIX.1将编号为0的信号定义为空信号. 如果_sig参数是0, 则kill仍执行正常的错误检查, 但不发送信号. 这被用来确定一个进程是否存在

示例:

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

int main(void)
{
    pid_t childpid;
    int status;
    int retval;

    childpid = fork();
    if (-1 == childpid)
    {
        perror("fork()");
        exit(EXIT_FAILURE);
    }
    else if (0 == childpid)
    {
        puts("In child process");
        sleep(100); //让子进程睡眠,看看父进程的行为
        exit(EXIT_SUCCESS);
    }
    else
    {
        if (0 == (waitpid(childpid, &status, WNOHANG)))
        {
            retval = kill(childpid, SIGKILL);

            if (retval)
            {
                puts("kill failed.");
                perror("kill");
                waitpid(childpid, &status, 0);
            }
            else
            {
                printf("%d killed\n", childpid);
            }
        }
    }

    exit(EXIT_SUCCESS);
}

运行输出:

prejudice@prejudice-VirtualBox:~/Cplus_learning/bin$ ./kill 
7446 killed

在确信fork调用成功后,子进程睡眠100秒,然后退出

同时父进程在子进程上调用waitpid函数,但使用了WNOHANG选项,所以调用waitpid后立即返回

父进程接着杀死子进程,如果kill执行失败,返回-1,否则返回0

如果kill执行失败,父进程第二次调用waitpid,保证他在子进程退出后再停止执行,否则父进程显示一条成功消息后退出

raise()函数

#include <sys/types.h>
#include <signal.h>

/* Raise signal SIG, i.e., send SIG to yourself.  */
extern int raise (int __sig) __THROW;

成功则返回0, 出错则返回-1

raise函数是通过kill实现

raise(sig)
等价于
kill(getpid(),sig)
  • kill和raise是用来发送信号的
  • kill把信号发送给进程或进程组
  • raise把信号发送给(进程)自身

示例:

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

void signal_catchfunc(int);

int main()
{
    int ret;

    signal(SIGINT, signal_catchfunc);

    printf("开始生成一个信号\n");
    ret = raise(SIGINT);
    if (ret != 0)
    {
        printf("错误,不能生成SIGINT信号\n");
        exit(0);
    }

    printf("退出....\n");
    return 0;
}

void signal_catchfunc(int signal)
{
    printf("捕获信号:%d\n", signal);
}

运行输出:

prejudice@prejudice-VirtualBox:~/Cplus_learning/bin$ ./raise 
开始生成一个信号
捕获信号:2
退出....

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

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

相关文章

springboot+mp自动生成没有实体类

mybatisX版本冲突问题 一开始我的MyBatisX版本是1.6.1-3,使用mybatis-plus一直不能正常生成实体类 将MyBatisX的版本换成了1.5.7就可以了 MyBatisX版本更换 1.将原有的MyBatisX卸载后重新安装一个新的版本 2.选择一个合适的版本,这里我选的是1.5.7 下载完成后自己选择一个…

【隧道篇 / WAN优化】(7.4) ❀ 02. WAN优化的作用 ❀ FortiGate 防火墙

【简介】看了上一篇文章&#xff0c;相信大家都知道了在防火墙上启动WAN优化的方法&#xff0c;但是WAN优化到底能做什么&#xff1f;相信有很多人想了解。 什么是WAN优化 现在有许多企业通过集中应用程序或在云中提供应用程序来降低成本并整合资源。应用程序在本地局域网内都能…

python多标签图像分类的图片相册共享交流系统vue+django

建立图片共享系统&#xff0c;进一步提高用户对图片共享信息的查询。帮助用户和管理员提高工作效率&#xff0c;实现信息查询的自动化。使用本系统可以轻松快捷的为用户提供他们想要得到的图片共享信息。 根据本系统的基本设计思路&#xff0c;本系统在设计方面前台采用了pytho…

springboot 引入第三方bean

如何进行第三方bean的定义 参数进行自动装配

CH32V 系列 MCU IAP 使用函数形式通过传参形式灵活指定APP跳转地址

参考: CH32V 系列 MCU IAP 升级跳转方法 CH32V103 的 IAP 问题&#xff08;跳转及中断向量表重定位&#xff09; 1. 沁恒的RISC-V内核MCU的IAP跳转示例程序简要分析 沁恒的RISC-V内核的MCU比如CH32V203、CH32V307等系列的EVT包中IAP升级的示例程序中都是通过使能软件中断之后&…

先进制造业数字化转型,为什么基于传统存储无法完成?

本文是 XSKY 智能存储方案助力先进制造数字化转型系列文章中的第一篇&#xff0c;重点分享先进制造行业数字化转型过程中&#xff0c;对于数据存储的需求&#xff0c;以及为何传统存储架构无法很好满足这些需求。 随着智能制造的发展&#xff0c;自动化、信息化、智能化等技术…

linux命令——fdisk分区

在linux中&#xff0c;一切皆文件&#xff0c;硬盘设备在系统中也以文件形式存在&#xff0c;使用fdisk命令可以查看硬盘分区信息 并非硬盘转速越快&#xff0c;文件读取速率越高&#xff0c;有一个文件存储密度的概念

数据合规官认证证书CCRC-DCO使用设计和默认数据保护处理个人数据

快来了解隐私保护工程实践&#xff01;合法原则是关键&#xff0c;一起守护数据安全&#xff01; 隐私保护工程实践需要遵循合法原则&#xff0c;控制者必须确保处理个人数据有明确的法律依据。在设计和默认数据保护中&#xff0c;相关性、差异化、特定目的、必要性和自主权是合…

在ATECLOUD测试平台测试新能源车内连接器

在测试车内连接器的温度时&#xff0c;需要用到直流电源和温度巡检仪&#xff0c;通过温度巡检仪采集连接器工作时的温度。由于用户在测试时会用到多台直流电源和温度巡检仪&#xff0c;并且型号不一样。因此&#xff0c;在用ATECLOUD测试连接器温度时&#xff0c;技术工程师需…

框架漏洞RCE-1

一、前提 1、命令执行漏洞&#xff1a;直接调用操作系统命令。攻击者构造恶意命令&#xff0c;将命令拼接到正常的输入中&#xff0c;达到恶意攻击的目的。 (1)、常见命令执行函数 PHP&#xff1a;exec、shell_exec、system、passthru、popen、proc_open、反引号等 ASP.N…

拉普拉斯丨独家冠名2024年度ATPV技术分论坛,助力产业科技持续创新

为了进一步促进行业技术交流&#xff0c;推进光伏行业发展及标准建设的进程&#xff0c;针对高效电池&#xff0c;领跑组件&#xff0c;新产品认证及应用等技术专题及国内外光伏标准的最新进程&#xff0c;由中国绿色供应链联盟光伏专委会&#xff08;ECOPV&#xff09;指导的2…

Java 运行的底层原理

Java是一种跨平台的编程语言&#xff0c;其底层原理涉及到了多个方面&#xff0c;包括Java虚拟机&#xff08;JVM&#xff09;、字节码、类加载机制、垃圾回收器等。让我们逐一深入了解Java运行的底层原理。 1. Java虚拟机&#xff08;JVM&#xff09; Java虚拟机是Java程序运…

Java代码基础算法练习-年龄问题-2024.05.07

数学家维纳智力早熟&#xff0c;11岁就上了大学。一次&#xff0c;他参加某个重要会议&#xff0c;年轻的脸孔引人注目。于是有人询问他的年龄&#xff0c;他回答说&#xff1a;“我年龄的立方是个4位数。我年龄的4次方是个6位数。这10 个数字正好包含了从0到9这10个数字&#…

Mybatis报错sql injection violation, syntax error: TODO :IDENTIFIER

今天被这个报错搞了一下午 <select id"getMmZxZffs" resultType"cn.vetech.asms.pay.service.freepwdpay.vo.ZxZffsQueryVO" parameterType"cn.vetech.asms.pay.service.freepwdpay.dto.ZxZffsQueryDTO">select t.A skFs,t.B,t.C skFsm…

建筑物健康监测:振弦式应变计的应用

随着科技的进步和建筑安全意识的提高&#xff0c;对建筑物健康状况的监测变得日益重要。振弦式应变计作为一种高精度、高灵敏度的测量工具&#xff0c;已经在建筑物健康监测中得到了广泛应用。本文将探讨如何使用振弦式应变计进行建筑物的健康监测&#xff0c;并分享一些最佳实…

安卓手机原生运行 ARM Ubuntu 24.04 桌面版(一)

本篇文章&#xff0c;聊一聊尝试让安卓手机原生运行 Ubuntu&#xff0c;尤其是运行官方未发布过的 ARM 架构的 Ubuntu 24.04 桌面版本。 写在前面 最近的几篇文章&#xff0c;都包含了比较多的实操内容、需要反复的复现验证&#xff0c;以及大量的调试过程&#xff0c;为了不…

Linux——综合实验

要求 按照上面的架构部署一个简单的web节点所有的服务器使用DNS服务器作为自己的DNS服务器 就是/etc/reslov.conf 中nameserver的值必须是途中dns服务器的地址所有的数据库都是用mysql应用 nfs共享导出在客户端(web服务器上)使用autofs在自动挂载&#xff0c;或者写入/etc/fsta…

使用 Gitea 进行私有 Git 仓库管理

在本文中&#xff0c;我们将介绍如何使用 Gitea 搭建并管理私有 Git 仓库。Gitea 是一个轻量级的 Git 服务&#xff0c;提供了类似于 GitHub 的功能&#xff0c;适合个人和小团队使用。我们将通过以下步骤来完成搭建和配置 Gitea 服务器。 步骤一&#xff1a;安装 Gitea 首先…

沉浸式的三维立体3D产品互动展示,有哪些优势?如何实现?

沉浸式的三维立体3D产品互动展示&#xff0c;可以720度旋转缩放查看产品的每一个细节&#xff0c;具有很强的交互性和逼真性&#xff0c;从品牌营销的层面来分析&#xff0c;具有以下优势&#xff1a; 首先&#xff0c;3D产品展示能够带来生动、真实的视觉冲击效果。 随着消费…
最新文章