【genius_platform软件平台开发】第九十七讲:linux设备驱动中信号(signal函数)的异步通知机制

news2025/2/25 21:28:33

1. 信号的异步通知

  • 意思是:一旦设备就绪,则主动通知应用程序,这样应用程序根本就不需要查询设备状态,这一点非常类似于硬件上“中断”的概念,比较准确的称谓是“信号驱动的异步I/O”。信号是在软件层次上对中断机制的一种模拟,在原理上,一个进程收到一个信号与处理器收到一个中断请求可以说是一样的。信号是异步的,一个进程不必通过任何操作来等待信号的到达,事实上,进程也不知道信号到底什么时候到达。
  • 在这里插入图片描述

2. 信号的特点

  • 一个进程收到一个信号与处理器收到一个中断请求可以说是一样的(软中断)。
  • 信号是异步的,一个进程不必通过任何操作来等待信号的到达。
  • 信号是内核定义的,当相应的事件产生时,内核发射相应的信号。

3. 信号头文件和函数

3.1 头文件:

#include <signal.h>

3.2 signal()函数:

typedef void (*__sighandler_t) (int);
__sighandler_t signal (int __sig, __sighandler_t __handler);
  • 第一个参数是要捕捉的信号(linux下查看信号:kill -l,9号SIGKILL信号不能被捕捉 )
  • 第二个参数表示我们要对信号进行的处理方式。

4. 信号的处理

进程可以通过3种方式来响应一个信号

4.1 忽略信号(SIG_IGN)

  • 即对信号不做任何处理,其中,有两个信号不能被忽略:SIGKLL-9SIGSTOP-19。接收到了,但是什么都不做。
#include <stdio.h>
#include <signal.h>
 
int main()
{
	// 忽略SIGINT信号
    signal(SIGINT, SIG_IGN);
    while(1)
    {
        printf("hello\n");
        sleep(1);
    }
    return 0;
}
  • 对SIGINT信号进行忽略,所以按下Ctrl+c后进程不会停止,若要停止,可用Ctrl+(SIGQUIT);

3.2 默认操作

  • 对每种信号都规定了默认操作,如 kill -l 查看到的命令
#include <stdio.h>
#include <signal.h>
 
int main()
{
	//SIGINT默认操作
    signal(SIGINT, SIG_DFL);
    while(1)
    {
        printf("hello\n");
        sleep(1);
    }
    return 0;
}
  • 对SIGINT信号进行默认动作处理,当按下Ctrl+c后进程会终止。

3.3 捕捉信号

  • 定义信号处理函数,当信号发生时,执行相应的处理函数。提供一个信号处理函数,要求内核在处理该信号时切换到用户态执行这个处理函数,这个方式称为捕捉(catch)一个信号。

3.3.1 信号注册函数signal()

#include <signal.h>
#include <stdio.h> 
//typedef void (*sighandler_t)(int);//定义sighandler_t为void *(int)无返回值的函数指针类型
//sighandler_t signal(int signum, sighandler_t handler);//第二个参数是函数指针类型,目的是传入函数地址。函数名相当于指针,存放着指向函数空间的地址。
void handler(int signum)
{
        printf("signum is:%d\n",signum);
        printf("don't quit\n");
}
 
int main()
{
        signal(SIGINT,handler);//信号处理函数的注册,当Ctrl+C信号出现,内核调用handler函数处理 ,也可以用宏(SIG_IGN)忽略        
        while(1);
 
        return 0;
}
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <error.h>
#include <stdlib.h>
 
void handler_sign(int sign)
{
    printf("catch a signal:%d\n", sign);
}
 
int main()
{
	//SIGINT定义信号处理函数handler_sign
    signal(SIGINT, handler_sign);
    while(1)
    {
        printf("hello\n");
        sleep(1);
    }
    return 0;
}
  • 执行程序时,按下Ctrl+c后,接收到信号,执行handler_sign函数,打印信息。

3.3.2 kill函数和system函数发送信号

  • 通过kill发送信号给其他进程:
#include <signal.h>
#include <stdio.h>
#include <sys/types.h>
 
int main(int argc,int **argv[])
{
        int signum;
        int pid;
        char cmd[128] ={0};
 
        signum = atoi(argv[1]);//字符转换成整形
        pid = atoi(argv[2]);
 
        printf("signum:%d,pid:%d\n",signum,pid);
 
		/*发送指令*/
        /*方法一*/
        /*
        kill(pid,signum);//执行命令
        printf("send kill\n");      
        */
        /*方法二*/
        sprintf(cmd,"kill -%d %d",signum,pid);//构建字符串
        system(cmd);//创建子进程执行
        printf("system work\n");
        return 0;
}

3.3.3 信号注册函数2 sigaction()

  • 信号安装函数sigaction(int signum,const struct sigaction *act,struct sigaction *oldact)的第二个参数是一个指向sigaction结构的指针(结构体名称与函数名一样,千万别弄混淆了)。在结构sigaction的实例中,指定了对特定信号的处理,信号所传递的信息,信号处理函数执行过程中应屏蔽掉哪些函数等。当然,此指针也可以为NULL,进程会以默认方式处理信号。以下就简单介绍一下sigaction结构以及一般的用法。

  • 对于内核头文件而言,struct sigaction 结构体定义在kernel/include/asm/signal.h,此头文件又被kernel/include/linux/signal.h包含。

  • 对于用户空间的头文件而言,struct sigaction定义在 /usr/include/bits/sigaction.h,此头文件又被/usr/include/signal.h包含,所以应用程序中如果用到此结构,只要#include <signal.h>即可。注意内核中的定义和应用程序中的定义是不一样的内核空间的sigaction结构只支持函数类型为__sighandler_t的信号处理函数,不能处理信号传递的额外信息。具体定义如下:

struct sigaction
{
         __sighandler_t sa_handler;
        unsigned long sa_flags;
        void (*sa_restorer)(void);
        sigset_t sa_mask;   /* mask last for extensibility */
};

sa_handler的原型是一个参数为int,返回类型为void的函数指针。参数即为信号值,所以信号不能传递除信号值之外的任何信息;
sa_sigaction的原型是一个带三个参数,类型分别为intstruct siginfo *void *,返回类型为void的函数指针。第一个参数为信号值;第二个参数是一个指向struct siginfo结构的指针,此结构中包含信号携带的数据值,第三个参数没有使用。
sa_mask指定在信号处理程序执行过程中,哪些信号应当被阻塞。默认当前信号本身被阻塞。
sa_flags包含了许多标志位,比较重要的一个是SA_SIGINFO,当设定了该标志位时,表示信号附带的参数可以传递到信号处理函数中。即使sa_sigaction指定信号处理函数,如果不设置SA_SIGINFO,信号处理函数同样不能得到信号传递过来的数据,在信号处理函数中对这些信息的访问都将导致段错误。

#include <signal.h>
void sig_handler_with_arg(int sig,siginfo_t *sig_info,void *unused){……}
    
int main(int argc,char **argv)
{
         struct sigaction sig_act;
         ……
         sigemptyset(&sig_act.sa_mask);
         sig_act.sa_sigaction=sig_handler_with_arg;
         sig_act.sa_flags=SA_SIGINFO;

          ……
}
  • 如果你的应用程序只需要接收信号,而不需要接收额外信息,那你需要的设置的是sa_handler,而不是sa_sigaction,你的程序可能类似下面的代码:
#include <signal.h>
 void sig_handler(int sig){……}

 int main(int argc,char **argv)
 {
          struct sigaction sig_act;
          ……
          sigemptyset(&sig_act.sa_mask);
          sig_act.sa_handler=sig_handler;
          sig_act.sa_flags=0; 
           ……
  }
  • 可以携带信息,发信号用sigqueque(),注册信号函数用sigaction()
  • 注册信号处理函数:
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
 
struct sigaction
 {
   void       (*sa_handler)(int); //信号处理程序
   void       (*sa_sigaction)(int, siginfo_t *, void *); //信号处理程序,能够接受额外数据和sigqueue配合使用
   sigset_t   sa_mask;//阻塞关键字的信号集,在调用捕捉函数之前,把信号添加到信号阻塞字,信号捕捉函数返回之前恢复为原先的值。
   int        sa_flags;//SA_SIGINFO表示能够接受数据
 };
 
  • 关于void(sa_sigaction)(int, siginfo_t, void );处理函数来说还需要有一些说明。void 是接收到信号所携带的额外数据;而struct siginfo这个结构体主要适用于记录接收信号的一些相关信息。
 siginfo_t {
               int      si_signo;    /* Signal number */
               int      si_errno;    /* An errno value */
               int      si_code;     /* Signal code */
               int      si_trapno;   /* Trap number that caused
                                        hardware-generated signal
                                        (unused on most architectures) */
               pid_t    si_pid;      /* Sending process ID */
               uid_t    si_uid;      /* Real user ID of sending process */
               int      si_status;   /* Exit value or signal */
               clock_t  si_utime;    /* User time consumed */
               clock_t  si_stime;    /* System time consumed */
               sigval_t si_value;    /* Signal value */
               int      si_int;      /* POSIX.1b signal */
               void    *si_ptr;      /* POSIX.1b signal */
               int      si_overrun;  /* Timer overrun count; POSIX.1b timers */
               int      si_timerid;  /* Timer ID; POSIX.1b timers */
               void    *si_addr;     /* Memory location which caused fault */
               int      si_band;     /* Band event */
               int      si_fd;       /* File descriptor */
}

发送信号,并携带信息

int sigqueue(pid_t pid, int sig, const union sigval value);
union sigval {
   int   sival_int;
   void *sival_ptr;
 };

例程

/*信号处理函数注册*/
 
#include <signal.h>
#include <stdio.h>
 
 //int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
 
/* struct sigaction {
               void     (*sa_handler)(int);
               void     (*sa_sigaction)(int, siginfo_t *, void *);//与上一个2选1,此可携带信息
               sigset_t   sa_mask;
               int        sa_flags;
               void     (*sa_restorer)(void);
};*/
 
void handle(int signum, siginfo_t *info, void *content)
{
        printf("signum:%d\n",signum);
        if(content != NULL)
        {
                printf("si_pid:%d\n",info->si_pid);
                printf("si_int:%d\n",info->si_int);
                printf("sival_int: %d\n",info->si_value.sival_int);//读取结构体里面的由sigqueue函数发送的联>
合体si_value里面的数据
        }
}
 
int main()
{
        printf("pid:%d\n",getpid());
        struct sigaction act;
        act.sa_sigaction = handle;//绑定操作函数
        act.sa_flags = SA_SIGINFO;//表示能接受数据
 
        sigaction(SIGUSR1, &act, NULL);//注册信号函数,接受该信号后采取结构体act里面的操作,第三个参数做数据备
份,不用则NULL      
        while(1);
        return 0;
}


/*信号发送*/
 
#include <signal.h>
#include <stdio.h>
 
//int sigqueue(pid_t pid, int sig, const union sigval value);
/* union sigval {
               int   sival_int;
               void *sival_ptr;
           };*/
 
int main(int argc,int **argv)
{
        int pid;
        int signum;
        signum = atoi(argv[1]);
        pid = atoi(argv[2]);
 
        union sigval value;
        value.sival_int = 10;
 
        sigqueue(pid, signum, value);
        printf("pid:%d,done\n",getpid());
        return 0;
}
 

5. Linux下信号的定义:

  • 前31个为传统UNIX支持的信号,是不可靠信号(非实时的)后33个是后来扩充的,是可靠信号(实时信号)。不可靠信号和可靠信号的区别在于前者不支持排队,可能会造成信号丢失,而后者不会。
信号id名称含义备注
1SIGHUP终端关闭会发送进程结束
2SIGINTctrl + c 终止当前进程进程结束
3SIGQUITctrl + \ 停止当前进程进程结束
9SIGKILL杀死进程进程结束
12SIGUSR2用户自定信号
14SIGALRM闹钟信号
19SIGSTOP进程暂停
20SIGTSTPctrl+z 挂起进程转换成后台进程
信号id名称含义备注
1SIGHUP本信号在用户终端连接(正常或非正常)结束时发出, 通常是在终端的控制进程结束时, 通知同一会话控制内各个作业, 这时它们与控制终端不再关联。用户,系统会分配给登录用户一个Session(终端)。在这个终端运行的所有程序,包括前台进程组和后台进程组,一般都属于这个Session(终端)。当用户退出Linux登录时,前台进程组和后台有对终端输出的进程将会收到SIGHUP信号
2SIGINT程序终止(interrupt)、中断信号, 在用户键入INTR字符,通常命令行Ctrl C时发出,用于通知前台进程组终止进程。
3SIGQUITSIGQUIT是其控制终端发送到进程,当用户请求的过程中执行核心转储的信号。但由QUIT字符(通常命令行Ctrl \)来控制,进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号。
4SIGILL执行了非法指令, 通常是因为可执行文件本身出现错误,或者试图执行数据段, 堆栈溢出时也有可能产生这个信号。
5SIGTRAP断点指令或其它trap指令产生,由debugger使用
6SIGABRT调用abort函数生成的信号,程序异常结束,进程终止并产生core文件
7SIGBUS非法地址,包括内存地址对齐(alignment)出错。比如访问一个四个字长的整数,但其地址不是4的倍数。它与SIGSEGV的区别在于后者是由于对合法存储地址的非法访问触发的(如访问不属于自己存储空间或
8SIGFPE出现了致命的算术运算错误时发出,不仅包括浮点运算错误,还包括溢出及除数为0等其它所有的算术的错误。
9SIGKILL用来立即结束程序的运行,本信号不能被阻塞、处理和忽略。如果管理员发现某个进程终止不了,可尝试发送这个信号。
10SIGUSR1用户自定义信号,终止进程
11SIGSEGV试图访问未分配给自己的内存, 或试图往没有写权限的内存地址写数据.
12SIGUSR2用户自定义信号,终止进程
13SIGPIPE管道破裂。这个信号通常在进程间通信产生,比如采用FIFO(管道)通信的两个进程,读管道没打开或者意外终止就往管道写,写进程会收到SIGPIPE信号。此外用Socket通信的两个进程,写进程在写Socket的时候,读进程已经终止。
14SIGALRM有setitimer产生时钟定时信号,计算的是实际的时间或时钟时间, alarm函数使用该信号
15SIGTERM程序结束(terminate)信号,与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出,shell命令kill缺省产生这个信号。如果进程终止不了,才会用SIGKILL。
16SIGSTKFLT协处理器堆栈错误信号
17SIGCHLD子进程结束时, 父进程会收到这个信号,如果父进程没有处理这个信号也没有等待(wait)子进程子进程虽然终止,但是还会在内核进程表中占有表项,这时的子进程称为僵尸进程。这种情况我们应该避免(父进程或者忽略SIGCHILD信号,或者捕捉它,或者wait它派生的子进程,或者父进程先终止,这时子进程的终止自动由init进程来接管)。
18SIGCONT让一个停止(stopped)的进程继续执行, 本信号不能被阻塞,可以用一个handler来让程序在由stopped状态变为继续执行时完成特定的工作. 例如, 重新显示提示符
19SIGSTOP提供给管理员暂停进程的特权, 所以不能忽略和重定义。停止(stopped)进程的执行, 注意它和terminate以及interrupt的区别:该进程还未结束,只是暂停执行,本信号不能被阻塞, 处理或忽略。
20SIGTSTP停止进程的运行, 但该信号可以被处理和忽略. 用户键入SUSP字符时(通常是Ctrl-Z),向前台进程组发送SIGTSTP信号以暂停进程(默认动作), 该信号可以被忽略和重定义。
21SIGTTIN当后台作业要从用户终端读数据时, 该作业中的所有进程会收到SIGTTIN信号,缺省时这些进程会停止执行。
22SIGTTOU类似于SIGTTIN, 但在写终端(或修改终端模式)时收到。
23SIGURG有"紧急"数据或out-of-band数据到达socket时产生。
24SIGXCPU超过CPU时间资源限制,这个限制可以由getrlimit/setrlimit来读取/改变。
25SIGXFSZ当进程企图扩大文件以至于超过文件大小资源限制。
26SIGVTALRM虚拟时钟信号, 类似于SIGALRM, 但是计算的是该进程占用的CPU时间。
27SIGPROF类似于SIGALRM/SIGVTALRM, 但包括该进程用的CPU时间以及系统调用的时间。
28SIGWINCH窗口大小改变时发出。
29SIGIO文件描述符准备就绪,可以开始进行输入/输出操作。
30SIGPWR电源失效/再启动信号
31SIGSYS非法的系统调用。
34SIGRTMIN
35SIGRTMIN+1
36SIGRTMIN+2
37SIGRTMIN+3
38SIGRTMIN+4
39SIGRTMIN+5
40SIGRTMIN+6
41SIGRTMIN+7
42SIGRTMIN+8
43SIGRTMIN+9
44SIGRTMIN+10
45SIGRTMIN+11
46SIGRTMIN+12
47SIGRTMIN+13
48SIGRTMIN+14
49SIGRTMIN+15
50SIGRTMAX-14
51SIGRTMAX-13
52SIGRTMAX-12
53SIGRTMAX-11
54SIGRTMAX-10
55SIGRTMAX-9
56SIGRTMAX-8
57SIGRTMAX-7
58SIGRTMAX-6
59SIGRTMAX-5
60SIGRTMAX-4
61SIGRTMAX-3
62SIGRTMAX-2
63SIGRTMAX-1
64SIGRTMAX
  • pl2ps_intr.h
/*
 * @Author: fliu
 */

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

#define FRAME_SIZE ((648*360*2 + 1024)*5)
#define DEVICE_NAME "/dev/pl_irq_drv"

void init_env(int fd, __sighandler_t handler, pid_t pid);
  • pl2ps_intr.c
 /*
 * @Author: fliu
 */


#include "pl2ps_intr.h"

void init_env(int fd, __sighandler_t handler, pid_t pid)
{
		int flags = 0;
        //https://blog.csdn.net/xiadeliang1111/article/details/84348006
        //https://blog.csdn.net/qq_41936794/article/details/105273059
        // 实现异步通知机制,用户程序涉及2项工作,指定一个进程作为文件的“属主(owner)”.
        // 当应用程序使用fcntl系统调用执行F_SETOWN命令时,属主进程的进程ID号就被保存在filp->f_owner中.
        // 这一步是必需的,目的是告诉内核将信号发给谁,也就是发给哪个进程.
        // 然后为了真正启动异步通知机制,用户程序还必须在设备中设置FASYNC标志,这通过fcntl的F_SETFL命令完成的,
        // 文件打开时,FASYNC标志被默认为是清除的.,每当FASYNC标志改变时,驱动程序中的fasync()函数将得以执行。
        // 执行完这两个步骤之后,内核就可以在新数据到达时通过调用kill_fasync()函数请求发送一个SIGIO信号给应用层,
        // 该信号被发送到存放在filp->f_owner中的进程(如果是负值就是进程组)
		signal(SIGIO, handler);

		fcntl(fd, F_SETOWN, pid);		
		flags = fcntl(fd, F_GETFL);		
		fcntl(fd, F_SETFL, flags | FASYNC);	
}
  • demo_app.c
/*
 * @Author: your name
 */

#include "pl2ps_intr.h"

static int fd = 0;	

static void sigio_signal_func(int signum)
{
	int err = 0;
	char buf[FRAME_SIZE + 1];
	FILE* fp = NULL;

	printf("-------------in void sigio_signal_func. \n");

#if 1

	err = read(fd, buf, FRAME_SIZE);

	if(err < 0) {
		printf("read device error! \n");
	} else {
		printf("sigio signal! \n");
	}

#endif

}

int main(int argc, char *argv[])
{
	int flags = 0;
	char *filename;

	if (argc != 2) {

		printf("Error Usage!\r\n");
		return -1;

	}
	
	filename = argv[1];
	fd = open(filename, O_RDWR);

	if (fd < 0) {
		printf("Can't open file %s\n", filename);
		return -1;
	}

	init_env(fd, sigio_signal_func, getpid());

	while(1) {

		sleep(2);

	}

	close(fd);

	return 0;
}

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

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

相关文章

创新升级!深度学习算法注入机器视觉应用,助力工业领域智慧生产

随着人工智能在算力、算法、大数据等方面的技术突破&#xff0c;基于神经网络的深度学习算法在各行各业加速渗透。而制造业作为AI融合创新的主要场景之一&#xff0c;在机器视觉技术的加持下&#xff0c;持续赋能生产线的自动化、智能化升级&#xff0c;赋予工业生产“感知”能…

【Java se】为什么大部分小数在计算机中是不精确的

目录 一、什么是小数 二、浮点数在计算机中的转换规则 &#xff08;1&#xff09;整数部分转换步骤 &#xff08;2&#xff09; 小数部分转换步骤 三、使用代码说明 四、手动转换小数 一、什么是小数 在计算机中&#xff0c;也称小数为浮点数&#xff0c;因为在使用科学计…

程序员的炫技代码写法

程序员的炫技代码写法 程序员&#xff0c;这个职业总是让人感到神秘而又充满魅力。他们手中的代码常常充满了令人惊叹的炫技操作&#xff0c;让人不禁感叹他们的技术能力之高。在这篇博客中&#xff0c;我想和大家分享一些我所知道的程序员的炫技代码写法。 一、代码美感——…

什么是死锁?互斥锁进入死锁怎么解决?

1.死锁是什么&#xff1f; &#xff08;1&#xff09;死锁是一组相互竞争资源的线程因为他们之间得到互相等待导致“永久“阻塞的现象&#xff1b; &#xff08;你等我 我等你 你不放我也不放 就导致“永久“阻塞的现象&#xff09; &#xff08;2&#xff09;死锁是指两个或…

mysql索引possible_keys,key问题。

explain中有两个字段possible_keys&#xff0c;key。 possible_keys&#xff1a;表示可能用到的索引。 key&#xff1a;实际使用到的索引。 为什么会有单独的两列&#xff1f; 你的where条件中如果使用到了索引列字段&#xff0c;那么possible_keys会列出索引字段对应的索引…

【区块链】科普:零知识证明、ZKRollup 与 zkEVM

译者语:2023 年将迎来 zkEVM 大发展,让我们看看有哪些项目在进行 zkEVM 的研究,zkEVM 可以做什么 原文链接: https://blog.pontem.network/zk-proofs-301-zksync-other-zkevm-b28641dc8565这篇文章让我们解释一下zkEVM是如何工作的,为什么它们是如此重要的创新。 长话短说…

基于html+css的图展示113

准备项目 项目开发工具 Visual Studio Code 1.44.2 版本: 1.44.2 提交: ff915844119ce9485abfe8aa9076ec76b5300ddd 日期: 2020-04-16T16:36:23.138Z Electron: 7.1.11 Chrome: 78.0.3904.130 Node.js: 12.8.1 V8: 7.8.279.23-electron.0 OS: Windows_NT x64 10.0.19044 项目…

云下自建Grafana获取阿里云监控数据

前言 官方文档&#xff0c;我看了要付费的 https://help.aliyun.com/document_detail/313316.html?spma2c4g.217924.0.i2 grafana:8.3.5 插件名称&#xff1a;aliyun-cms-services grafana安装&#xff1a; https://blog.csdn.net/qq_44379042/article/details/121852310 …

Activiti 工作流 学习笔记

环境信息: IntelliJ IDEA 2021.3.3 (Ultimate Edition) JDK 1.8 Activiti 7 文章目录 Activiti 7一、了解工作流1.1 什么是工作流1.2 工作流引擎1.3 常见的工作流引擎1.4 Activiti 概述1.4.1 Activiti介绍1.4.2 建模语言BPMN1.4.3 Activiti使用流程1.4.4 流程设计工具1.4.5 下载…

全排列II

1题目 给定一个可包含重复数字的序列 nums &#xff0c;按任意顺序 返回所有不重复的全排列。 示例 1&#xff1a; 输入&#xff1a;nums [1,1,2] 输出&#xff1a; [[1,1,2],[1,2,1],[2,1,1]]示例 2&#xff1a; 输入&#xff1a;nums [1,2,3] 输出&#xff1a;[[1,2,3],…

OpenGauss MOT 实现技术研究

目录 一、概述 二、技术细节和OpenGauss中的实现 1、内存表在内存中的组织 2、事务并发控制算法 3、检查点算法 这篇文档写于2022年6月份&#xff0c;今天打算发到网上&#xff0c;重读时发现可能opengauss mot现在的代码已经有所改变&#xff0c;文中有些代码分支可能已经…

高效远程控制另一台电脑的3种方法,提升工作效率!

如何从我的电脑控制另一台电脑&#xff1f; “我妈妈的电脑出了问题&#xff0c;我需要帮她修理下&#xff0c;但是我不能亲自去进行故障排除。我应该如何从我的电脑远程控制另一台电脑&#xff0c;并提供远程支持&#xff1f;” 如何远程控制另一台电脑&#xff1f;&a…

基于SpringBoot+Vue的学生综合测评系统设计与实现

博主介绍&#xff1a; 大家好&#xff0c;我是一名在Java圈混迹十余年的程序员&#xff0c;精通Java编程语言&#xff0c;同时也熟练掌握微信小程序、Python和Android等技术&#xff0c;能够为大家提供全方位的技术支持和交流。 我擅长在JavaWeb、SSH、SSM、SpringBoot等框架下…

http缓存详解

为什么要缓存 通过http协议在客户端和服务端建立连接需要消耗时间&#xff0c;重复访问同一个资源&#xff0c;增加访问服务器数据资源的成本&#xff0c;因此&#xff0c;利用浏览器的缓存机制重用以前获取的数据来优化性能 1、 减少了网络延迟&#xff0c;加快了页面响应速度…

分析全志Tina打包流程,并在buildroot上来实现打包生成tina镜像,支持PhoenixSuit烧写

本文转载自全志在线&#xff1a;https://bbs.aw-ol.com/topic/1532/ 步骤简述 首先记录下官方 tina-sdk 打包的log输出。 bookvirtual-machine:~/D1s-Core/tina-d1-h$ pack ---- PACK_CHIP sun20iw1p1 PACK_PLATFORM tina PACK_BOARD d1-h-nezha PACK_KE…

Vue.js 中的指令和组件详解

Vue.js 中的指令和组件详解 在 Vue.js 中&#xff0c;指令和组件是两个非常重要的概念&#xff0c;它们都可以用来扩展 Vue.js 的功能&#xff0c;但它们之间有一些不同之处。本文将对 Vue.js 中的指令和组件进行详细的介绍&#xff0c;并附上相关的代码示例。 指令 在 Vue.j…

华为OD机试真题 Java 实现【找最小数】【2023 B卷 100分】,附详细解题思路

一、题目描述 给一个正整数num1&#xff0c;计算出新正整数num2&#xff0c;num2为num1中移除N位数字后的结果&#xff0c;需要使得num2的值最小。 二、输入描述 输入的第一行为一个字符串&#xff0c;字符串由0~9字符组成&#xff0c;记录正整数num1&#xff0c;num1的长度…

2.1 网络io、io多路复用select/poll/epoll、基于事件驱动的reactor

目录 一、网络IO请求二、一请求一线程三、IO多路复用——select的通俗理解1、select函数2、accpet函数3、recv函数 四、IO多路复用——poll五、IO多路复用——epoll1、epoll_create2、epoll_ctl3、epoll_wait4、epoll_event5、边缘触发和水平触发&#xff09; 五、区别对比1、s…

axios、跨域与JSONP、防抖和节流

文章目录 一、axios1、什么是axios2、axios发起GET请求3、axios发起POST请求4、直接使用axios发起请求 二、跨域与JSONP1、了解同源策略和跨域2、JSONP&#xff08;1&#xff09;实现一个简单的JSONP&#xff08;2&#xff09;JSONP的缺点&#xff08;3&#xff09;jQuery中的J…

Wwise内存问题

1&#xff09;Wwise内存问题 ​2&#xff09;安卓平台特效显示不一致的问题 3&#xff09;多个矩形小方块组成的地形接缝处有黑线问题 这是第339篇UWA技术知识分享的推送&#xff0c;精选了UWA社区的热门话题&#xff0c;涵盖了UWA问答、社区帖子等技术知识点&#xff0c;助力大…