『 Linux 』信号的捕捉及部分子问题

文章目录

    • 信号的捕捉
    • sigaction函数
    • 未决信号集的置零时机
    • 信号处理过程的阻塞
    • 可重入函数
    • volatile 关键字
    • SIGCHLD 信号


信号的捕捉

请添加图片描述

该图为基于信号处理为用户自定义动作的图解;

  • 信号的捕捉

    当一个信号被递达时,如果该信号的处理动作是用户自定义的函数(如int sighandler(int))时就会调用这个函数,该步骤被称为捕捉信号;

  • 用户程序注册新号处理函数

    用户程序中注册了SIGQUIT信号的处理函数sighandler;

  • 切换到内核态

    当前正在执行main函数时发生了中断或是异常,这导致程序从用户态切换到内核态(如图中标注的12);

  • 内核态处理信号

    内核在处理完中断或异常之后,在返回用户态的main函数前检测到有信号SIGQUIT递达(图中标注2);

  • 独立控制流程

    sighandlermain函数使用不同的堆栈空间,之间没有调用和被调用的关系,是两个独立的控制流程(图中标注4);

  • 返回用户态

    sighandler函数返回后通过执行的特殊的系统调用sys_sigreturn再次进入内核(图中标注4);

  • 恢复主流程

    如果没有新的信号被注册,这次返回用户态时将恢复main函数中断前的上下文继续执行(图中标注5);


sigaction函数

请添加图片描述

sigaction()函数与signal()函数相同,功能都为捕捉信号;

NAMEsigaction - examine and change a signal actionSYNOPSIS#include <signal.h>int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);Feature Test Macro Requirements for glibc (see feature_test_macros(7)):sigaction(): _POSIX_C_SOURCE >= 1 || _XOPEN_SOURCE || _POSIX_SOURCEsiginfo_t: _POSIX_C_SOURCE >= 199309LRETURN VALUEsigaction() returns 0 on success; on error, -1 is returned, and errno is set to indicate the error.

该函数的具体功能用于检查和改变指定型号的处理方式;

它允许程序定义或更改信号处理函数,也可以获取当前信号处理函数的信息;

函数调用成功时返回0,调用失败时返回-1并设置errno;

参数如下:

  • int signum

    表示传入一个int类型的参数,该参数表明需要传入的信号编号;

  • const struct sigaction *act

    传入一个struct sigaction *参数,其中加了const作修饰表示是一个传入型参数;

    该参数用于指定新的信号处理动作,其结构体包含以下成员:

    struct sigaction {void (*sa_handler)(int);      // 信号处理函数指针void (*sa_sigaction)(int, siginfo_t *, void *); // 另一个信号处理函数指针,用于接收额外信息sigset_t sa_mask;             // 在信号处理期间需要阻塞的信号集int sa_flags;                 // 影响信号处理行为的标志位void (*sa_restorer)(void);    // 不常用的字段,一般设置为NULL
    };
    

    在处理普通信号时只需要关注void (*sa_handler)(int)成员与sigset_t sa_masksigset_t sa_mask成员即可;

    • void (*sa_handler)(int)

      该成员为一个函数指针类型,用于指向信号处理函数,如SIG_IGN,SIG_DFL或是用户自定义函数void userhandler(int);

    • sigset_t sa_mask

      该成员为一个sigset_t类型的数据,其中sigset_t类型为操作系统封装的一个位图结构用于依靠该结构对信号集进行操作;

      当一个进程在处理一个信号的时候会将对应的信号添加到其信号屏蔽字(阻塞信号集)中以避免信号方法重复调用;

      设置该成员可以使得进程在处理一个信号时同时将多个信号添加至信号屏蔽字中;

    其余成员可默认设为0;

  • struct sigaction *oldact

    该参数类型与act类型相同,不被const修饰为一个输出型参数;

    该参数用于保存之前的信号处理动作,如果不需要保存旧的信号处理则传递nullptr;

void sighandler(int signo) {// 自定义处理动作printf("sighandler get a signal: %d\n", signo);
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

该例子未使用可以同时阻塞多个信号的特性;

运行结果为:

$ ./mysignal 
I am a Process ,id : 29791
^Csighandler get a signal: 2
I am a Process ,id : 29791
^Csighandler get a signal: 2
I am a Process ,id : 29791
^\Quit

当使用Ctrl + C对进程发送SIGINT信号时被捕捉而后执行自定义动作;


未决信号集的置零时机

请添加图片描述

当信号被处理后未决信号集对应位置应置零从而表示该信号已经被处理完毕为递达状态;

实际上未决信号集的置零时机为执行信号处理前,即先将未决信号集对应位置置零再执行处理信号的函数;

以上文代码为基础进行修改:

void PrintPending() {sigset_t pending;sigemptyset(&pending);sigpending(&pending);for (int i = 31; i > 0; --i) {if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;
}void sighandler(int signo) {// 自定义处理动作PrintPending();printf("sighandler get a signal: %d\n", signo);
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

添加了PrintPending()函数用于打印整张Pending位图;

运行结果为:

$ ./mysignal 
I am a Process ,id : 29858
^C0000000000000000000000000000000
sighandler get a signal: 2
I am a Process ,id : 29858
^C0000000000000000000000000000000
sighandler get a signal: 2
I am a Process ,id : 29858
^C0000000000000000000000000000000

当进行信号处理并对Pending位图进行打印时并未出现SIGINT位置变为1的情况;

这意味着实际上在进行信号处理函数前对应的Pending位图已经被置零;


信号处理过程的阻塞

请添加图片描述

当一个信号在被进行处理时将会把正在处理的信号添加进信号屏蔽字以阻塞下一个相同的信号;

本质上是防止同一个信号处理函数被重复调用;

void PrintPending() {sigset_t pending;sigemptyset(&pending);sigpending(&pending);for (int i = 31; i > 0; --i) {if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;
}void sighandler(int signo) {// 自定义处理动作//   PrintPending();printf("sighandler get a signal: %d\n", signo);while (1) {PrintPending();cout << endl;sleep(1);}
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

该程序中当进程捕获到一个信号时将会调用用户自定义动作进行信号处理;

用户自定义动作为打印对应捕捉到的信号而后无限循环打印pending表以验证相同信号在第二次注册时是否会被阻塞保留其未决状态;

运行并在另一个窗口中使用kill -signo <pid>命令向进程发送SIGINT信号,结果为:

$ ./mysignal 
I am a Process ,id : 29921
sighandler get a signal: 20000000000000000000000000000000000000000000000000000000000001000000000000000000000000000000100000000000000000000000000000010^\Quit

当第一次注册SIGINT信号时将执行用户自定义动作,即先打印sighandler get a signal: 2再循环打印pending表;

一开始的pendingSIGINT信号处已经置零表示正在对该信号进行处理;

当再次注册SIGINT信号时由于上一个相同信号未被处理完成,处于未递达状态,相同的信号被阻塞,停留在未决信号集中;

可调用sigaction()函数并设置struct sigaction *act成员中的sigset_t sa_mask位图以能够在进行一个信号的处理时阻塞多个信号;

void PrintPending() {sigset_t pending;sigemptyset(&pending);sigpending(&pending);for (int i = 31; i > 0; --i) {if (sigismember(&pending, i))cout << "1";elsecout << "0";}cout << endl;
}void sighandler(int signo) {// 自定义处理动作//   PrintPending();printf("sighandler get a signal: %d\n", signo);while (1) {PrintPending();cout << endl;sleep(1);}
}int main() {struct sigaction act, oldact;memset(&act, 0, sizeof(act));  // 利用memset()初始化结构体memset(&act, 0, sizeof(oldact));sigset_t mask;sigemptyset(&mask);// 使用sigaddset()设置信号集sigaddset(&mask, 3);sigaddset(&mask, 4);sigaddset(&mask, 1);sigaddset(&mask, 5);act.sa_mask = mask;  // 将设置好的信号集进行赋值从而设置处理时阻塞act.sa_handler = sighandler;  // 为成员赋值 设置自定义处理动作int n = sigaction(SIGINT, &act, &oldact);  // 函数调用while (!n) {                               // 循环打印cout << "I am a Process ,id : " << getpid() << endl;sleep(1);}return 0;
}

该函数中定义了一个sigset_t类型的mask,并用sigaddset()将信号1,3,4,5分别加入了信号集,并将该sigset_t类型赋值给sa_mask成员,使其阻塞多个信号;

运行程序并在另一个终端中使用kill命令分别以2,1,2,3,4,5的顺序依次注册信号并观察结果;

对应的结果为:

$ ./mysignal 
I am a Process ,id : 30083
I am a Process ,id : 30083
sighandler get a signal: 2
00000000000000000000000000000100000000000000000000000000000011000000000000000000000000000011100000000000000000000000000011110000000000000000000000000011111

结果为首先处理2号信号SIGINT并进入死循环打印pending表;

向进程再次发送1-5号信号其都被阻塞至未决信号集中;


可重入函数

请添加图片描述

当一个进程在调用一个函数,在函数执行过程中接收到了一个信号发生了中断;

而信号的处理中又需要调用一次该函数,若是出现了数据错误或是数据不一致等错误问题则称该函数为不可重入函数,反之则称为可重入函数;

以该图为例;

全局环境中存在一个链表并对其进行一次头插操作,其头插操作的核心代码为:

	/* 伪代码 */
insert(...){// ...NodeA->next = head;head = &NodeA;// ...
}

即将新头插的节点的next指针指向head所指向的节点;

head重新指向新插入的头结点;

若是在插入过程中,即执行完了NodeA -> next = head;后进程接收到了一个信号并对信号进行处理;

而在信号的处理中需要调用insert()函数进行头插而再次执行该函数时这个过程被称为 “重入” ,即相同函数重复进入;

当执行主控制流时执行insert()函数,在调用insert()时接收信号并处理型号,在处理信号中再次调用了一次insert();

流程图如下:

结果为main()函数和sighandler()先后向链表中插入两个头结点;

而最后只有一个节点被真正插入链表中,使得另一个节点出现 节点丢失 的内存泄漏问题;

这意味着该insert()函数为一个不可重入函数;

可重入函数 , 不可重入函数 都只为一个函数的特点;

如果一个函数符合以下条件之一的则是不可重入函数:

  • 调用了mallocfree

    malloc也是用全局链表来管理堆的;

  • 调用了标准I/O库函数

    标准I/O库的很多实现都以不可重入的方式使用全局数据堆;


volatile 关键字

请添加图片描述

编译器在编译时可以将代码进行优化;

对应的使用不同优化级别使编译时对代码进行优化,常见的有-O0,-O1,-O2,-O3等选项,其中-O0为默认优化,即表示不进行优化;

volatile关键字是用来修饰一个变量以防止编译器过度优化;

int flag = 1;void sighandler(int signo) {printf("sighandler get a signal: %d\n", signo);flag = 0;printf("falg : %d\n", flag);
}int main() {signal(SIGINT, sighandler);while (flag);cout << "process quit sucess" << endl;
}

在这段代码中定义了一个全局变量,且设置了对SIGINT信号的捕获;

在主控制流main函数中使用whileflag为条件进行循环,当条件为真时循环,条件为假时跳出循环;

当捕捉到SIGINT信号时对该信号进行处理,处理方案为使用自定义动作,为将全局变量flag设为0,即条件为假并对当前flag进行一次打印;

使用g++-O3选项进行编译并进行编译优化;

g++ -o mysignal mysignal.cc -g -O3 -Wall -std=c++11

运行该程序并向该程序发送2号信号SIGINT;

$ ./mysignal 
^Csighandler get a signal: 2
falg : 0
^Csighandler get a signal: 2
falg : 0
^Csighandler get a signal: 2
falg : 0

从结果看出,即使注册了SIGINT信号且执行了自定义动作将全局变量flag设为了0但进程仍不退出;

本质原因是由于进行了优化后,其flag参数将被存放至寄存器当中,而main()函数中的循环条件始终以寄存器中的flag进行条件判断,修改的flag确实内存中的flag;

两者出现了隔离,寄存器不会向内存再去读取flag变量而是由于while循环不停判断寄存器中的变量从而使得进程无法正常退出;

该行为即为编译器的一种编译过度优化;

可通过使用volatile关键字修饰来放置过度优化行为;

int flag修改为volatile int flag;

重新使用-O3选项编译并运行,并使用Ctrl + C向进程发送一个2号信号SIGINT信号;

$ ./mysignal 
^Csighandler get a signal: 2
falg : 0
process quit sucess
$ 

结果为使用Ctrl + C发送2号信号SIGINT时进程被终止;


SIGCHLD 信号

请添加图片描述

当一个子进程退出时将会为其父进程发送一个信号,该信号为17号信号SIGCHLD;

该信号默认行为SIG_DFL为忽略;

可在父进程中调用signal()接口捕捉SIGCHLD函数进行验证;

void sighandler(int signo) {sleep(2);printf("parent process catch a signal: %d\n", signo);cout << endl;waitpid(-1, nullptr, WNOHANG); // 不考虑获取进程退出信息 参数2设置为nullptr
}int main() {signal(SIGCHLD, sighandler);pid_t id = fork();if (id == 0) {// childint cnt = 2;while (cnt--) {printf("I am child process,the PID is %d\n", getpid());cout << endl;sleep(1);}cout << "child process quit...." << endl<<endl;;exit(-1);}// parentwhile (1) {printf("I am parent process,the PID is %d\n", getpid());cout << endl;sleep(1);}return 0;
}

在父进程中使用signal()设置捕获17号信号SIGCHLD信号,并设置自定义动作为调用waitpid(-1,nullptr,WNOHANG)以非阻式来等待子进程退出;

fork()创建子进程,子进程在2s后退出进程并向父进程发送17号信号SIGCHLD;

当父进程获取到子进程所发的信号时将该信号进行捕获,而后调用自定义动作对已经僵尸的子进程进行等待处理;

运行程序并在另一个窗口使用shell脚本:

$ while :; do ps axj | head -1 && ps axj | grep mysignal | grep -v grep ; echo "----------------------------" ; sleep 1 ; done

观察父子进程的状态;

运行结果为:

$ ./mysignal 
I am parent process,the PID is 32131I am child process,the PID is 32132I am parent process,the PID is 32131I am child process,the PID is 32132I am parent process,the PID is 32131child process quit....parent process catch a signal: 17I am parent process,the PID is 32131I am parent process,the PID is 32131

2s过后子进程退出,父进程捕获到17号信号并对子进程进行waitpid()清理;

另一个会话显示的结果为:

 PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 Z+    1001   0:00 [mysignal] <defunct>
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
32131 32132 32131 29111 pts/0    32131 Z+    1001   0:00 [mysignal] <defunct>
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32131 32131 29111 pts/0    32131 S+    1001   0:00 ./mysignal
----------------------------
^C

子进程僵尸了2s后被父进程回收;

若是不需要子进程的退出信息时父进程可对子进程发出的信号使用signal()进行忽略从而避免子进程停留在僵尸;

以上述代码为基础进行修改:

int main() {
//   signal(SIGCHLD, sighandler);signal(SIGCHLD, SIG_IGN);pid_t id = fork();if (id == 0) {// childint cnt = 2;while (cnt--) {printf("I am child process,the PID is %d\n", getpid());cout << endl;sleep(1);}cout << "child process quit...." << endl<<endl;;exit(-1);}// parentwhile (1) {printf("I am parent process,the PID is %d\n", getpid());cout << endl;sleep(1);}return 0;
}

对应结果为:

 # 程序所在会话$ ./mysignal 
I am parent process,the PID is 32213I am child process,the PID is 32214I am parent process,the PID is 32213I am child process,the PID is 32214I am parent process,the PID is 32213child process quit....I am parent process,the PID is 32213I am parent process,the PID is 32213^C# 脚本所在会话PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
32213 32214 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
32213 32214 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------PPID   PID  PGID   SID TTY      TPGID STAT   UID   TIME COMMAND
29111 32213 32213 29111 pts/0    32213 S+    1001   0:00 ./mysignal
----------------------------

结果表示子进程退出时直接退出,其父进程并未接收到子进程的SIGCHLD而是直接将其进行忽略处理;

shell脚本所在会话显示子进程并未在僵尸状态下进行停留而是直接退出;

  • SIGCHLD信号的忽略行为

    SIGCHLD的默认动作是忽略与对SIGCHLD信号进行忽略是两种概念;

    第一种为默认动作实际上是调用signal(SIGCHLD,SIG_DFL),而其对应的行为为忽略;

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

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

相关文章

钉钉 钉钉打卡 钉钉定位 2024 免费试用 保用

打卡助手定位 如图&#xff0c;表示开启成功&#xff0c;软件已定位到钉钉打卡位置。 测试显示&#xff0c;高德地图位置已成功修改。 开启助手定位后&#xff0c;观察效果&#xff0c;打卡按钮由无法打卡变为可打卡状态&#xff0c;照片还显示打卡地点。 伙伴们担心作弊行为会…

Prometheus Pushgateway 协议上报观测云最佳实践

Pushgateway 介绍 Pushgateway 是 Prometheus 生态系统中的一个组件&#xff0c;主要用于解决 Prometheus 默认的 pull&#xff08;拉取&#xff09;模式在某些情况下无法获取数据的问题&#xff0c;如监控源位于防火墙之后&#xff0c;Prometheus 无法穿透防火墙&#xff0c;…

嵌入式C++、Raspberry Pi、LoRa和Wi-Fi技术、TensorFlow、ROS/ROS2:农业巡检数据导航机器人设计流程(代码示例)

随着科技的不断进步&#xff0c;农业领域也在逐渐向智能化发展。农业巡检机器人作为农业智能化的重要组成部分&#xff0c;能够自动化地监测农作物生长状况&#xff0c;提高农业管理的效率和精确度。本文将介绍一个基于Raspberry Pi和NVIDIA Jetson的农业巡检机器人&#xff0c…

3U/6U CPCI 系统核心模块,可应用于电力、轨道交通等领域

一、3U CPCI核心模块 提供CPCI系列产品&#xff0c;具有&#xff0c;高性能&#xff0c;应用扩展灵活&#xff0c;X86软件兼容&#xff0c;无风扇设计&#xff0c;宽工作温度范围&#xff0c;长嵌入式生命周期等特点&#xff0c;满足不同客户的需求。 产品特点 Intel Pentium …

微信Android一面凉经(2024)

微信Android一面凉经(2024) 笔者作为一名双非二本毕业7年老Android, 最近面试了不少公司, 目前已告一段落, 整理一下各家的面试问题, 打算陆续发布出来, 供有缘人参考。今天给大家带来的是《微信Android一面凉经(2024)》。 面试职位: 微信-客户端开发工程师-基础功能(广州) And…

力扣高频SQL 50 题(基础版)第四题

文章目录 力扣高频SQL 50 题&#xff08;基础版&#xff09;第四题584.寻找用户推荐人题目说明思路分析实现过程准备数据实现方式结果截图 力扣高频SQL 50 题&#xff08;基础版&#xff09;第四题 584.寻找用户推荐人 题目说明 表: Customer -------------------- | Colu…

【Code Complete2】Note-1 [启发式编程、管理复杂度、隐藏设计]

【Code Complete2】_Note-1 [启发式编程、管理复杂度、隐藏设计] 文章目录 【Code Complete2】_Note-1 [启发式编程、管理复杂度、隐藏设计]启发式编程管理复杂度隐藏设计--减少“改动所影响的代码量” 启发式编程 ​ **设计是一个启发的过程&#xff0c;充满了不确定性&#…

Golang | Leetcode Golang题解之第283题移动零

题目&#xff1a; 题解&#xff1a; func moveZeroes(nums []int) {left, right, n : 0, 0, len(nums)for right < n {if nums[right] ! 0 {nums[left], nums[right] nums[right], nums[left]left}right} }

Anaconda +Pytorch安装教程

Anaconda Pytorch安装教程 Anaconda安装 小土堆的安装教程&#xff1a;PyTorch深度学习快速入门教程&#xff08;绝对通俗易懂&#xff01;&#xff09;【小土堆】_哔哩哔哩_bilibili Free Download | Anaconda 这里自己选一个文件夹安装即可 第一个红框表示添加图标到开始菜…

pytorch-scheduler(调度器)

scheduler简介 scheduler(调度器)是一种用于调整优化算法中学习率的机制。学习率是控制模型参数更新幅度的关键超参数,而调度器根据预定的策略在训练过程中动态地调整学习率。 优化器负责根据损失函数的梯度更新模型的参数,而调度器则负责调整优化过程中使用的特定参数,通…

【C++笔试强训】day03

简写单词 思路 首先把第一个字符&#xff08;转大写&#xff09;加到答案t中。 遍历字符串s的同时&#xff0c;如果遇到了空格&#xff0c;就把空格后面的字符&#xff08;转大写&#xff09;加到t中。 代码 #include <bits/stdc.h>using namespace std;int main() {…

The conversion of the nvarchar value ‘10033121171441‘ overflowed an int column

The conversion of the nvarchar value ‘10033121171441‘ overflowed an int column 目录 The conversion of the nvarchar value ‘10033121171441‘ overflowed an int column 【常见模块错误】 【解决方案】 欢迎来到英杰社区https://bbs.csdn.net/topics/617804998 欢迎…

Linux冯诺依曼体系、操作系统、进程概念、进程状态、进程切换

个人主页&#xff1a;仍有未知等待探索-CSDN博客 专题分栏&#xff1a;Linux 目录 一、冯诺依曼体系结构 二、操作系统 1、概念 2、为什么要有操作系统&#xff1f; 3、理解操作系统 1.管理的本质 2.管理的概念 3.操作系统结构图 4.为什么要有操作系统&#xff1f; 三…

扁平化Tree组件Vue3过渡动画

本小节在巧用Vue3 composition api的计算属性实现扁平化tree连线 的基础上实现动画效果。 看下目前的实现&#xff0c;展开折叠比较生硬&#xff0c;正好我们用vue提供的过渡动画特性来优化用户体验。 模板应用动画 在for循环渲染外面应用过渡组标签&#xff0c;对每个节点的…

【全面介绍Pip换源】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【QT】UDP

目录 核心API 示例&#xff1a;回显服务器 服务器端编写&#xff1a; 第一步&#xff1a;创建出socket对象 第二步&#xff1a; 连接信号槽 第三步&#xff1a;绑定端口号 第四步&#xff1a;编写信号槽所绑定方法 第五步&#xff1a;编写第四步中处理请求的方法 客户端…

Linxu系统:hwclock命令

1、命令详解&#xff1a; hwclock命令用于显示与设定硬件时钟。它是一种访问硬件时钟的工具&#xff0c;可以显示当前时间&#xff0c;将硬件时钟设置为指定的时间&#xff0c;将硬件时钟设置为系统时间&#xff0c;以及从硬件时钟设置系统时间。您还可以定期运行hwlock以插入或…

C++迈向精通:STL-iterator_traits迭代器类型萃取解析

STL-iterator_traits迭代器类型萃取解析 源码 在阅读STL源码的时候遇到了这样的一行代码&#xff1a; 通过ctags跳转到对应的定义区域&#xff1a; 下面还有两个特化版本&#xff1a; 根据英文释义&#xff0c;发现模板中需要传入的是一个迭代器类型&#xff0c;在上面找到源…

Platform Designer 自定义IP(用于纯RTL设计)

在开始菜单找到Quartus Prime工具&#xff0c;点击并打开。 点击Quartus菜单File——New&#xff1a; 选择Verilog HDL File&#xff0c;点击OK&#xff1a; 这是新建的.v文件如下&#xff1a; 在新建的.v文件中键入如下Verilog代码&#xff1a; module mux2x1( //模块的开头…

第二证券:特斯拉Robotaxi将于10月发布 煤炭高景气有望延续

煤炭高景气有望延续 2022年以来随着国内煤炭稳价政策逐步落地&#xff0c;动力煤价格逐步回归&#xff0c;特别自2023年下半年以来&#xff0c;国内动力煤价格坚持高位窄幅轰动。炼焦煤方面&#xff0c;2021年国内炼焦煤价格总体亦出现宽幅轰动走势&#xff0c;2022年以来国内…