当前位置: 首页 > news >正文

linux系统中进程通信之信号

目录

一、信号的概述

信号的本质

信号的来源

一、硬件方式产生的信号(Hardware-Generated Signals)

二、软件方式产生的信号(Software-Generated Signals)

信号的种类,以及处理流程

一、按可靠性分类

1️⃣ 不可靠信号(Non-Reliable Signals)

2️⃣ 可靠信号(Reliable Signals)

二、按时间特性分类

1️⃣ 非实时信号(Non-Real-Time Signals)

2️⃣ 实时信号(Real-Time Signals)

信号列表

键盘产生的信号

一、常见的键盘控制信号

二、信号说明

三、总结对比

信号的处理方式

1️⃣ 默认处理(SIG_DFL)

2️⃣ 忽略信号(SIG_IGN)

3️⃣ 捕获信号(自定义处理函数)

信号通信的流程

阶段一:信号产生(Signal Generation)

阶段二:信号在进程中注册(Signal Registration)

阶段三:信号处理(Signal Handling)

信号通信补充说明

二、发送信号

kill() ------ 给任一进程/进程组发送任一信号

1.头文件

2.函数原型

3.参数说明

4.返回值

kill() 函数与 kill 命令的区别

5.示例代码

raise() ------ 给当前线程发送任一信号

1.头文件

2.函数原型

3.函数参数

4.返回值

5.功能与说明

6.示例代码

alarm() ------ 定时给自身发送SIGALRM信号

1.头文件

2.函数原型

3.函数参数

4.返回值

5.函数功能与说明

6.示例代码

三、等待接收信号

pause()

1.头文件

2.函数原型

3.函数参数

4.返回值

5.功能与说明

6.示例代码

四、处理信号

signal() ------ 设置信号处理

1.头文件

2.函数原型

3.函数参数

4.返回值

5.功能与说明

五、综合示例

触发信号的方式


一、信号的概述

信号的本质

Linux 系统 中,信号(Signal)本质上是一种软中断机制(Software Interrupt),用于通知进程发生了某种异步事件。
它是 软件层面对硬件中断机制的模拟,在概念上,一个进程接收到信号与处理器接收到硬件中断请求非常相似。

信号是 进程间通信(IPC)的一种异步通信机制

  • 进程在执行过程中无需主动等待信号的到来;

  • 信号可能在任何时刻由内核或其他进程异步地发送过来;

  • 被信号打断后,进程会根据预设的处理方式(默认处理、忽略、自定义函数)来应对该事件。

除了基本的事件通知功能外,信号机制还具备一定的控制与管理能力,例如:

  • 可以控制进程的执行状态(终止、暂停、恢复等);

  • 可以实现进程间的事件同步与异常响应。

信号的来源

信号的产生途径多种多样,根据产生条件的不同,可以大体分为 两类:硬件方式软件方式

一、硬件方式产生的信号(Hardware-Generated Signals)

硬件事件在运行过程中触发信号,通常由 内核检测到异常或外部输入事件 所引起。常见情况包括:

  • 1.键盘操作触发

    • 用户在终端输入特定组合键时,终端驱动程序会向前台进程发送信号。

      例如:

      • Ctrl + C → 发送 SIGINT(中断信号),终止前台进程。

      • Ctrl + Z → 发送 SIGTSTP(暂停信号),挂起前台进程。

  • 2.程序运行错误触发

    • 当程序在运行中出现异常或非法操作时,内核会自动向该进程发送信号:

      • 空指针访问、非法内存访问 → SIGSEGV(段错误)

        int *p = NULL;
        *p = 12456;   // 触发段错误,进程被内核终止
        

      • 除以 0 → SIGFPE(算术错误信号)

      • 执行非法指令 → SIGILL


二、软件方式产生的信号(Software-Generated Signals)

软件层面可以通过系统调用或命令来产生信号,这些信号通常由进程主动触发系统函数自动触发

  • 命令触发

    • 通过命令行工具向进程发送信号,例如:

      kill -9 1234     # 向 PID 为 1234 的进程发送 SIGKILL 信号
      

      (内部实际上是调用了 kill() 系统调用。)

  • 函数触发

    • 程序中可以通过以下函数主动发送信号:

      kill(pid_t pid, int sig);       // 向指定进程发送信号
      raise(int sig);                 // 向当前进程发送信号
      sigqueue(pid_t pid, int sig, const union sigval value); // 带参数发送信号
      

    • 示例:

      kill(getpid(), SIGINT);   // 向自己发送中断信号
      

信号的种类,以及处理流程

Linux 系统中的信号可以从不同角度进行分类,主要包括 按可靠性分类按时间特性分类


一、按可靠性分类

1️⃣ 不可靠信号(Non-Reliable Signals)
  • 早期 UNIX 系统 设计较为简单,信号机制存在缺陷:
    当同一种信号在尚未处理完时再次到达,后续信号可能被丢失。

  • 因此,这类可能丢失的信号被称为 “不可靠信号”

  • 在 Linux 中,信号编号 1~31 的传统信号属于不可靠信号。

特点:

  • 不支持信号排队(同类型信号可能被覆盖)

  • 信号值固定,语义预定义(例如 SIGINT、SIGKILL 等)


2️⃣ 可靠信号(Reliable Signals)
  • 为解决信号丢失问题,Linux 在原有信号机制基础上扩展了一组新的信号,称为 可靠信号

  • 这类信号支持 信号排队机制
    当一个信号正在被处理时,如果再次收到同类信号,会进入信号队列中,等待前一个信号处理完成后依次处理。

  • 因此,不会发生信号丢失的情况。

范围:

  • 可靠信号编号从 SIGRTMIN ~ SIGRTMAX(一般为 34~64)。

特点:

  • 支持排队(不会丢失)

  • 可附带参数(通过 sigqueue() 发送)

  • 信号含义可由用户自定义


二、按时间特性分类

1️⃣ 非实时信号(Non-Real-Time Signals)
  • 非实时信号即早期的 不可靠信号

  • 每个信号都有固定含义,系统预定义行为,例如:

    • SIGINT:中断进程

    • SIGKILL:强制结束进程

  • 不支持排队机制,如果在信号处理期间收到相同信号,后者会被忽略。

总结:
非实时信号的特点是:

“固定含义,不支持排队,可能丢失”


2️⃣ 实时信号(Real-Time Signals)
  • 实时信号是 POSIX 标准 新增的信号类型,属于可靠信号的一种

  • 允许用户自定义信号编号及其处理逻辑。

  • 具有 排队优先级 特性:

    • 多个相同信号可以依次排队执行;

    • 编号越小的实时信号优先级越高;

    • 可以使用 sigqueue() 附带数据发送。

总结:
实时信号的特点是:

“可自定义,支持排队,不丢失,具备优先级”

信号列表

信号名数值英文名称默认行为说明
SIGHUP1Hang Up终止进程控制终端挂起或控制进程终止(终端关闭)
SIGINT2Interrupt终止进程键盘中断(Ctrl + C)
SIGQUIT3Quit终止并产生 core dump键盘退出(Ctrl + \)
SIGILL4Illegal Instruction终止并产生 core dump非法指令(执行了错误的机器码)
SIGTRAP5Trace Trap终止并产生 core dump调试断点或陷阱
SIGABRT6Abort终止并产生 core dump调用 abort() 函数触发
SIGBUS7Bus Error终止并产生 core dump总线错误(内存访问未对齐)
SIGFPE8Floating Point Exception终止并产生 core dump浮点异常(除以零等)
SIGKILL9Kill立即终止强制杀死进程,不能被捕获或忽略
SIGUSR110User-defined Signal 1终止进程用户自定义信号1
SIGSEGV11Segmentation Violation终止并产生 core dump非法内存访问(段错误)
SIGUSR212User-defined Signal 2终止进程用户自定义信号2
SIGPIPE13Broken Pipe终止进程向无读端的管道写数据
SIGALRM14Alarm Clock终止进程定时器信号(来自 alarm()
SIGTERM15Terminate终止进程请求进程正常终止(可捕获)
SIGSTKFLT16Stack Fault终止进程协处理器栈错误(很少使用)
SIGCHLD17Child Status Changed忽略子进程结束或状态改变时发送给父进程
SIGCONT18Continue继续运行恢复被暂停的进程(不能被阻塞)
SIGSTOP19Stop暂停进程暂停进程(不可捕获或忽略)
SIGTSTP20Terminal Stop暂停进程键盘暂停(Ctrl + Z)
SIGTTIN21Background Read暂停进程后台进程尝试从终端读输入
SIGTTOU22Background Write暂停进程后台进程尝试往终端写输出
SIGURG23Urgent Condition忽略套接字紧急(out-of-band)数据到达
SIGXCPU24CPU Time Limit Exceeded终止并产生 core dump超出 CPU 时间限制
SIGXFSZ25File Size Limit Exceeded终止并产生 core dump超出文件大小限制
SIGVTALRM26Virtual Alarm Clock终止进程虚拟时间定时器信号
SIGPROF27Profiling Timer Expired终止进程统计定时器信号
SIGWINCH28Window Change忽略终端窗口大小改变
SIGIO29I/O Possible终止进程异步 I/O 事件(可读可写)
SIGPWR30Power Failure终止进程电源故障(UPS事件)
SIGSYS31Bad System Call终止并产生 core dump错误的系统调用
SIGRTMIN ~ SIGRTMAX34~64Real-Time Signals默认终止实时信号(可自定义,用于线程或实时通信)

键盘产生的信号

在 Linux 系统中,用户通过 键盘组合键 可以向 前台进程组(Foreground Process Group) 发送特定信号。
这些信号由 终端驱动程序(Terminal Driver) 捕获并传递给相关进程,用于中断、暂停或终止当前运行的程序。


一、常见的键盘控制信号

键盘操作产生的信号作用对象默认处理行为说明
Ctrl + CSIGINT前台进程组中的所有进程终止进程常用于终止正在运行的程序(Interrupt)
Ctrl + ZSIGTSTP前台进程组中的所有进程挂起进程暂停当前进程(Stop),可用 fgbg 恢复
Ctrl + \SIGQUIT前台进程组中的所有进程终止进程并生成 core 文件常用于调试,程序异常退出时产生 core dump
Ctrl + D(非信号)EOF输入结束关闭标准输入流并非信号,而是表示输入流结束(End Of File)

二、信号说明

  • 1.Ctrl + C → SIGINT(中断信号)

    • 向前台进程组发送中断信号。

    • 默认行为:终止所有前台进程。

    • 可通过 signal(SIGINT, handler) 捕获并自定义处理。

    • 示例:

      ./a.out
      ^C        # 用户按下 Ctrl+C,进程被终止
      

  • Ctrl + Z → SIGTSTP(暂停信号)

    • 向前台进程组发送暂停信号。

    • 默认行为:挂起(暂停)进程,可通过 fg 恢复至前台或 bg 放入后台运行。

    • 示例:

      ./a.out
      ^Z
      [1]+  Stopped   ./a.out
      

  • Ctrl + \ → SIGQUIT(退出信号)

    • 向前台进程组发送退出信号。

    • 默认行为:终止进程并生成 core dump 文件,用于调试程序异常。

    • 常用于调试运行时错误或崩溃。


三、总结对比

操作键信号名称信号编号默认动作是否可捕获说明
Ctrl + CSIGINT2终止进程✅ 可捕获常用的中断信号
Ctrl + ZSIGTSTP20暂停进程✅ 可捕获可用 fg/bg 恢复
Ctrl + \SIGQUIT3终止并生成 core✅ 可捕获用于调试
Ctrl + DEOF(非信号)输入结束表示输入流结束

信号的处理方式

在 Linux 系统中,每个信号都可以被进程以不同方式处理,主要分为 三类


1️⃣ 默认处理(SIG_DFL)

  • 含义:使用系统为该信号预定义的默认动作。

  • 特点

    • 对大多数信号,默认动作是 终止进程

    • 对少数信号,默认动作可能是 忽略信号停止进程

  • 示例

#include <signal.h>
#include <stdio.h>int main() {signal(SIGINT, SIG_DFL); // Ctrl+C 将按默认方式终止进程while(1);                // 无限循环等待信号return 0;
}


2️⃣ 忽略信号(SIG_IGN)

  • 含义:进程 忽略该信号,收到信号时不做任何处理,相当于屏蔽信号。

  • 特点

    • 对于无法忽略的信号(如 SIGKILL, SIGSTOP),此方式无效;

    • 常用于忽略临时无关紧要的信号。

  • 示例

#include <signal.h>
#include <stdio.h>int main() {signal(SIGINT, SIG_IGN); // 忽略 Ctrl+C 信号while(1);                // 无限循环,按 Ctrl+C 不会终止程序return 0;
}


3️⃣ 捕获信号(自定义处理函数)

  • 含义:进程为信号注册一个 自定义处理函数(类似中断服务函数),信号到来时执行该函数。

  • 特点

    • 可以执行用户定义的逻辑,例如打印消息、清理资源、计数等;

    • 捕获信号的函数原型:

      void handler(int signum);
      

  • 示例

#include <signal.h>
#include <stdio.h>void sig_handler(int signum) {printf("收到信号 %d\n", signum);
}int main() {signal(SIGINT, sig_handler); // 注册自定义处理函数while(1);                    // 无限循环,按 Ctrl+C 时不会终止return 0;
}

信号通信的流程

一个完整的信号生命周期可以分为 三个阶段,涉及 四个关键事件


阶段一:信号产生(Signal Generation)

  • 发生位置:内核(Kernel)

  • 触发方式

    • 硬件触发:如空指针访问、非法指令、键盘组合键(Ctrl+C)

    • 软件触发:如 kill()raise()sigqueue()

  • 说明
    内核负责生成信号,并将其标记到目标进程的信号位图中,等待进程处理。


阶段二:信号在进程中注册(Signal Registration)

  • 发生位置:用户进程(User Process)

  • 操作

    • 进程通过 signal()sigaction() 注册信号处理函数

    • 也可以选择忽略信号 (SIG_IGN) 或使用默认动作 (SIG_DFL)

  • 说明

    • 用户进程不能直接给其他用户进程发送信号,必须通过 内核

    • 用户空间只负责告诉内核如何处理信号


阶段三:信号处理(Signal Handling)

  • 发生位置:用户进程

  • 操作

    • 当进程在用户态运行时,内核检查是否有待处理信号

    • 若有,则暂停当前执行,调用注册的处理函数执行信号响应逻辑

    • 信号处理完成后,恢复原来的程序执行


信号通信补充说明

  • 用户进程不能直接向其他用户进程发送信号

    • 必须通过内核转发

    • 例子:

      进程A --> 内核 --> 发送信号 --> 进程B
  • 用户空间不具备发送信号的能力

    • 进程只能将信号请求提交给内核,由内核完成实际发送

二、发送信号

kill() ------ 给任一进程/进程组发送任一信号

1.头文件

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

2.函数原型

int kill(pid_t pid, int sig);

3.参数说明

参数名类型说明
pidpid_t

指定要发送信号的目标进程或进程组:

pid > 0:向指定 PID 的进程发送信号。

pid = 0:向调用进程所在的进程组的所有进程发送信号。

pid = -1:向系统上所有有权限的进程发送信号。

pid < -1:向进程组 ID = -pid 的所有进程发送信号。

sigint

要发送的信号编号,例如:

SIGKILL(9) 强制终止进程。

SIGTERM(15) 请求终止(可被捕获)。

SIGSTOP 暂停进程。

SIGCONT 继续被暂停的进程。

4.返回值

返回值含义
0信号发送成功
-1发送失败(常见错误:进程不存在或无权限)

常见错误号:

  • ESRCH:指定的进程不存在;

  • EPERM:没有权限向该进程发送信号。

kill() 函数与 kill 命令的区别

对比项kill() 函数kill 命令
使用位置C 语言程序中调用在终端命令行中使用
参数顺序kill(pid, sig)先进程,后信号kill -sig pid先信号,后进程
示例kill(1234, 9);kill -9 1234

5.示例代码

利用 kill() 函数结束进程

思路

  1. 第一个终端运行目标程序(例如 ./pidtest)。

  2. 第二个终端运行本程序,通过命令行参数发送信号结束目标进程。

#include <stdio.h>
#include <unistd.h>
#include <signal.h>
#include <sys/types.h>
#include <stdlib.h>/*
使用方式:./a.out -9 1234
表示向 PID=1234 的进程发送 SIGKILL 信号
*/int main(int argc, char *argv[])
{pid_t pid;int sig;if (argc != 3) {printf("用法: %s -sig pid\n", argv[0]);printf("示例: %s -9 1234\n", argv[0]);return -1;}// 将 "-9" 转换为信号编号 9sig = atoi(argv[1]) * (-1);pid = (pid_t)atoi(argv[2]);if (kill(pid, sig) == 0) {printf("成功向进程 %d 发送信号 %d\n", pid, sig);} else {perror("kill 发送失败");}return 0;
}

现象:

1.

2.

raise() ------ 给当前线程发送任一信号

1.头文件

#include <signal.h>

2.函数原型

int raise(int sig);

3.函数参数

  • sig:要发送的信号编号(例如 SIGINTSIGTERMSIGKILL 等)。

表示将该信号发送给调用本函数的进程本身
常用信号:

信号宏数值含义
SIGINT2中断信号(如 Ctrl + C)
SIGTERM15终止信号(可捕获、可处理)
SIGKILL9强制杀死(不可捕获、不可忽略)
SIGSEGV11段错误
SIGABRT6调用 abort() 产生的信号

4.返回值

  • 成功:返回 0

  • 失败:返回非 0

5.功能与说明

raise() 函数用于向当前进程发送信号
可以理解为:

“自己给自己发一个信号。”

相当于:

kill(getpid(), sig);

6.示例代码

#include <stdio.h>
#include <signal.h>void handler(int sig) {printf("捕获到信号 %d\n", sig);
}int main(void) {// 注册信号处理函数signal(SIGINT, handler);printf("程序开始运行...\n");// 发送信号给自己raise(SIGINT);printf("程序正常结束。\n");return 0;
}

现象:

alarm() ------ 定时给自身发送SIGALRM信号

1.头文件

#include <unistd.h>

2.函数原型

unsigned int alarm(unsigned int seconds);

3.函数参数

seconds:指定 多少秒后 向当前进程发送 SIGALRM 信号。

  • seconds = 0:取消之前设置的定时器(不再发送信号)。

  • 若非 0:在指定秒数后系统自动给本进程发送 SIGALRM 信号。

4.返回值

返回值为:

  • 0:表示之前没有设置定时器;

  • 非 0:返回 上一次定时器剩余的秒数

5.函数功能与说明

  • alarm() 用于设置一个定时信号(定时器)。

  • 当设置的时间到达后,系统自动向当前进程发送 SIGALRM 信号。

  • 可以配合 signal() 使用来自定义信号处理函数。

  • 一个进程同一时间只能有一个 alarm 定时器。
    再次调用会覆盖上一次设置。

6.示例代码

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void sig_handler(int sig) {printf("收到信号 %d:定时器时间到!\n", sig);
}int main(void) {// 注册 SIGALRM 信号处理函数signal(SIGALRM, sig_handler);printf("设置定时器 5 秒...\n");alarm(5);  // 5秒后产生SIGALRM信号// 主进程持续运行,等待信号for (int i = 1; i <= 10; i++) {printf("第 %d 秒\n", i);sleep(1);}return 0;
}

现象:

三、等待接收信号

pause()

1.头文件

#include <unistd.h>

2.函数原型

int pause(void);

3.函数参数

  • 无参数

4.返回值

情况返回值说明
正常情况下不返回程序被挂起(等待信号时不会返回)
被信号唤醒后-1函数被中断,返回 -1,并将 errno 设为 EINTR

5.功能与说明

  • pause() 使进程进入睡眠状态,直到接收到一个信号。

  • 当信号被捕获并执行完信号处理函数后,pause() 才会返回。

  • 常用于等待定时信号或外部信号。

  • 通常和 alarm()signal() 搭配使用。

6.示例代码

#include <stdio.h>
#include <unistd.h>
#include <signal.h>void sig_handler(int sig) {printf("收到信号 %d:唤醒进程!\n", sig);
}int main(void) {// 注册信号处理函数signal(SIGALRM, sig_handler);printf("设置定时器为 5 秒...\n");alarm(5);  // 5秒后触发SIGALRM信号printf("进程进入休眠,等待信号...\n");pause();   // 暂停执行,直到收到信号printf("pause() 返回,程序继续运行。\n");return 0;
}

现象:

四、处理信号

signal() ------ 设置信号处理

1.头文件

#include <signal.h>

2.函数原型

void (*signal(int signum, void (*handler)(int)))(int);

虽然原型比较复杂,但可以理解为:

signal(信号编号, 信号处理函数);

3.函数参数

参数名类型说明
int signum信号编号指定要处理的信号编号(如 SIGINT, SIGALRM, SIGTERM 等)。
sighandler_t handler信号处理方式设置该信号的处理方法,常见三种:

三种处理方式如下:

处理方式含义
SIG_DFL对该信号采取 默认的处理方式
SIG_IGN忽略 该信号(不做任何反应)
自定义函数名(如 sig_handler指定自定义的信号处理函数(函数声明格式如下)

⚠️ 信号处理函数声明格式:

void sigX_handler(int arg);

当捕获到信号时,系统会自动调用此函数,并将信号编号作为参数传入。

4.返回值

  • 成功:返回先前的信号处理函数指针;

  • 失败:返回 SIG_ERR

5.功能与说明

signal() 函数用于设置当前进程对指定信号的处理方式
当进程接收到信号 signum 时,系统根据 handler 指定的方式进行处理:

  • 若设置为 SIG_DFL → 使用系统默认处理;

  • 若设置为 SIG_IGN → 忽略信号;

  • 若设置为自定义函数 → 调用该处理函数。

五、综合示例

综合案例1:0~5s发送系统信号2 忽略  5~10秒发送信号2 执行处理函数的内容  10秒后执行信号默认功能

#include <signal.h>
#include <stdio.h>
#include <unistd.h>void sigint_handler(int signo) {printf("捕获到信号 SIGINT(Ctrl+C),执行自定义处理函数!\n");
}int main(void) {printf("程序开始运行...\r\n");printf("0-5s忽略SIGINT信号\r\n");signal(SIGINT, SIG_IGN);for(int i=0; i<5; i++) {printf("time is %ds\r\n", i);sleep(1);}printf("5-10s执行自定义处理函数\r\n");signal(SIGINT, sigint_handler);for(int i=5; i<10; i++) {printf("time is %ds\r\n", i);sleep(1);}printf("10s后执行信号默认功能\r\n");signal(SIGINT, SIG_DFL);int i=10;while (1) {printf("time is %ds\r\n", i++);sleep(1);}}

现象:

综合案例2:可以调用对应的信号处理函数,可以触发多个信号,例如:2信号 3信号 4信号 调用同一个信号处理函数。

#include <signal.h>
#include <stdio.h>
#include <unistd.h>// 通用信号处理函数
void signal_handler(int signo)
{switch (signo){case SIGINT:   printf("捕获到信号 2 (SIGINT):键盘中断信号 Ctrl+C\n");break;case SIGQUIT:  printf("捕获到信号 3 (SIGQUIT):键盘退出信号 Ctrl+\\\n");break;case SIGILL:  printf("捕获到信号 4 (SIGILL):非法指令信号\n");break;default:printf("捕获到其他信号:%d\n", signo);break;}
}int main(void)
{printf("综合案例2:多个信号共用同一个处理函数\n");printf("请尝试按 Ctrl+C、Ctrl+\\ 或使用 kill 命令发送信号\n");// 注册信号(2、3、4 都调用同一个处理函数)signal(SIGINT,  signal_handler);   // 信号 2signal(SIGQUIT, signal_handler);   // 信号 3signal(SIGILL,  signal_handler);   // 信号 4// 主循环while (1){printf("程序正在运行中... PID = %d\n", getpid());sleep(3);}return 0;
}

触发信号的方式

信号编号信号名称触发方式
2SIGINT终端按 Ctrl+C
3SIGQUIT终端按 Ctrl+\
4SIGILLkill -4 <pid> 命令发送(模拟非法指令)

现象:

综合案例3:每隔5秒钟,输出一下当前的时间。

思路:需要使用定时器alarm + signal对应的信号处理函数

#include <signal.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>// 通用信号处理函数
void signal_handler(int signo)
{// 每次触发时输出系统时间system("date");// 再次启动定时器,实现周期触发alarm(5);
}int main(void){printf("程序PID:%d\n", getpid());int i=1;// 注册SIGALRM信号signal(SIGALRM, signal_handler);// 启动第一次定时alarm(5);// 主进程持续运行,等待信号触发while (1){printf("%ds\r\n",i++);sleep(1);}return 0;}

现象:

http://www.dtcms.com/a/516169.html

相关文章:

  • 求数字1-10的阶乘
  • 如何使用最简单的get请求融合众多AI API,包括ChatGPT、Grok等
  • 链表的概念和单向链表的实现
  • 2013年下半年试题二:论企业应用系统的分层架构风格
  • U 盘写写保护解决方法
  • 简约手机网站源码兴宁电子商务网站建设
  • 教程网站搭建wordpress二次元风格
  • 02-Vue 插值
  • 【NebulaGraph】Nebula Importer使用
  • 不同形态组织镊在口腔临床的适配性选择
  • 深入理解进程、线程与协程
  • 用IIS自带FTP功能搭一个FTP!
  • 一种简单的Yolov8 onnx模型类别标签获取的方法
  • 用哪个网站做首页好做网站哪里最便宜
  • ROS1+Vscode
  • Ubuntu22.04 中搭建基于 Qemu 的内核(驱动)开发环境
  • JMETER+ANT接口自动化测试环境搭建实战讲解
  • 告别“大力金刚指”:晶尊微触摸芯片让电梯按键一触即灵
  • HTML教程
  • 基于Qt Quick的图像标注与标注数据管理工具
  • vscode搭建python项目隔离的虚拟环境
  • 模版网站有源代码吗wordpress栏目对应模板
  • 海阳市城建设局网站网页价格表
  • 网站建设客户分析调查表wordpress打不开页面
  • JAVA算法练习题day50
  • xss-labs pass-10
  • ArcMap批量修改字段的属性值
  • 龙虎榜——20251022
  • 03-RAG Agent-集成百炼知识库(Spring AI Alibaba)
  • 基于DEIM模型的声纳图像目标检测系统设计与实现