Linux 进程信号学习笔记:从概念到实操

一、Linux 信号基本概念

1.1 生活角度理解信号

我们可以把进程比作等待快递的人,信号就像快递:

  • 识别信号:就像我们知道快递来了该 怎么处理,进程对信号的识别是内核程序员预先编写的内置特性,即使信号没产生,进程也知道该如何处理。
  • 信号延迟处理:比如正在打游戏时收到快递通知,会等游戏结束再去取,进程收到信号后,若在执行优先级更高的任务,会在合适的时候处理信号。
  • 信号记录:从收到快递通知到取到快递的这段时间,我们会记住有快递要取,进程收到信号后,在未处理前也会暂时记录信号。
  • 信号处理方式:处理快递有打开使用(默认动作)、送给他人(自定义动作)、扔在一边(忽略)三种方式,进程处理信号也有默认、自定义、忽略三种方式,自定义处理信号也叫信号捕捉。

1.2 技术角度理解信号

信号是进程之间事件异步通知的一种方式,属于软中断。比如在 Shell 下启动一个前台进程,当我们按下Ctrl+C,会产生一个硬件中断,被操作系统获取后解释成SIGINT(2 号信号)发送给前台进程,前台进程收到信号后会退出,这就是信号的实际应用。

1.3 查看信号

在 Linux 系统中,我们可以通过相关命令查看信号,每个信号都有对应的编号和宏定义名称,这些宏定义可在signal.h中找到。例如:

  • SIGINT(2 号信号):来自键盘的中断信号。
  • SIGQUIT(3 号信号):来自键盘的退出信号,会生成 core dump 文件。
  • SIGKILL(9 号信号):杀死进程的信号,无法被捕捉和忽略。

我们也可以通过man 7 signal命令查看每个信号的产生条件和默认处理动作,部分常见信号如下表:

SignalStandardActionComment
SIGABRTP1990Core来自 abort (3) 的中止信号
SIGALRMP1990Term来自 alarm (2) 的定时器信号
SIGBUSP2001Core总线错误(不良内存访问)
SIGCHLDP1990Ign子进程停止或终止
SIGINTP1990Term来自键盘的中断

二、信号产生的一般方式

2.1 通过终端按键产生信号

  • Ctrl+C(SIGINT,2 号信号):发送中断信号,默认终止前台进程。
    • 示例:编写一个简单的循环程序,运行后按下Ctrl+C,进程会退出。
#include <iostream>
#include <unistd.h>
int main() {while (true) {std::cout << "I am a process, I am waiting signal!" << std::endl;sleep(1);}return 0;
}

编译运行:g++ sig.cc -o sig && ./sig,按下Ctrl+C,进程终止

  • Ctrl+\(SIGQUIT,3 号信号):发送退出信号,默认终止进程并生成 core dump 文件,用于事后调试。
    • 示例:修改上述程序,捕捉SIGQUIT信号。
#include <iostream>
#include <unistd.h>
#include <signal.h>
void handler(int signumber) {std::cout << "我是: " << getpid() << ", 我获得了一个信号: " << signumber << std::endl;
}
int main() {std::cout << "我是进程: " << getpid() << std::endl;signal(SIGQUIT/*3*/, handler);while (true) {std::cout << "I am a process, I am waiting signal!" << std::endl;sleep(1);}return 0;
}

编译运行后,按下Ctrl+\,会输出信号编号,若注释掉信号捕捉代码,按下Ctrl+\,进程会退出并生成 core dump 文件。

  • Ctrl+Z(SIGTSTP,20 号信号):发送停止信号,默认将当前前台进程挂起到后台。
    • 示例:运行上述未捕捉SIGTSTP信号的循环程序,按下Ctrl+Z,进程会被挂起,使用jobs命令可查看后台进程,使用fg命令可将后台进程调回前台。

2.1.2  实操:使用signal函数自定义SIGINT信号的处理方式

以下是一个小实验,大家可以练下手加深理解

signal 函数用于设置信号的处理方式,它的原型是:

#include <signal.h>
void (*signal(int signum, void (*handler)(int)))(int);
  • signum:要设置处理方式的信号编号,比如 SIGINT
  • handler:指向信号处理函数的指针。信号处理函数的原型是 void handler(int signum),其中 signum 是接收到的信号编号。

代码解释

  • 首先,我们包含了必要的头文件:stdio.h(用于输入输出)、signal.h(用于信号相关操作)、unistd.h(用于 sleep 函数)。

  • 定义了自定义的信号处理函数 sigcb,它接收一个 int 类型的参数 signum(即接收到的信号编号),在函数内部打印出接收到的信号值。

  • 在 main 函数中,使用 signal(SIGINT, sigcb) 来设置 SIGINT 信号的处理函数为 sigcb。这样,当进程接收到 SIGINT 信号时,就会调用 sigcb 函数而不是默认的终止进程操作。

  • 然后打印提示信息,接着通过一个无限循环 while (1) 让程序保持运行,sleep(1) 是为了避免程序过度占用 CPU。

编译与运行

  1. 编译代码:使用 gcc 编译器,在终端中输入命令 gcc -o sig_demo sig_demo.c(假设代码文件名为 sig_demo.c)。

  2. 运行程序:在终端中输入 ./sig_demo,程序会开始运行并打印提示信息。

  3. 测试信号:按下 Ctrl + C,此时会触发 SIGINT 信号,程序会调用 sigcb 函数,打印出类似 接收到信号,信号值为:2SIGINT 的值通常为 2)的信息,而不是终止程序。

执行结果截图说明

  • 编译运行后,终端会显示 “程序正在运行,按下ctrl+c发送SIGINT信号”。

  • 当按下 Ctrl + C 时,终端会打印 “接收到信号为:2”,然后程序继续运行(因为我们的处理函数没有终止进程,而是让程序继续在循环中运行)。如果多次按下 Ctrl + C,会多次打印该信息。

SIGINT(Ctrl+C)被我们自定义处理了,但系统还有其他信号可以终止进程,比如 SIGQUIT(通常由 Ctrl+\ 触发)。

通过这个例子,你可以清楚地看到如何使用 signal 函数来自定义信号的处理方式,以及 SIGINT 信号的触发和处理过程。

2.1.3  实操2:使用sigaction函数自定义SIGINT信号的处理方式

sigaction 函数是比 signal 函数更强大、更可移植的信号处理接口。它可以更精细地控制信号的处理行为。sigaction 函数的原型如下:

#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
  • signum:要设置处理方式的信号编号,比如 SIGINT
  • act:指向 struct sigaction 结构体的指针,该结构体包含了新的信号处理方式等信息。
  • oldact:指向 struct sigaction 结构体的指针,用于保存原来的信号处理方式(可以为 NULL,表示不保存)。

struct sigaction 结构体的定义大致如下:

struct sigaction {void (*sa_handler)(int);sigset_t sa_mask;int sa_flags;void (*sa_sigaction)(int, siginfo_t *, void *);
};
  • sa_handler:指向信号处理函数的指针,和 signal 函数中的处理函数类似,处理函数原型为 void handler(int signum)
  • sa_mask:指定在信号处理函数执行期间,需要阻塞的信号集合。
  • sa_flags:用于设置信号处理的一些标志,比如 SA_SIGINFO 等。
  • sa_sigaction:当 sa_flags 中设置了 SA_SIGINFO 标志时,使用该函数指针所指向的函数来处理信号,这个函数可以获取更多关于信号的信息,原型为 void handler(int signum, siginfo_t *info, void *context)

代码实现

我们编写一个 C 程序,使用 sigaction 函数来自定义 SIGINT 信号的处理方式:

#include <stdio.h>
#include <signal.h>
#include <unistd.h>// 自定义的信号处理函数
void sigcb(int signum) {printf("接收到信号,信号值为:%d\n", signum);
}int main() {struct sigaction act;// 设置信号处理函数为 sigcbact.sa_handler = sigcb;// 清空 sa_mask,即处理信号期间不阻塞其他信号sigemptyset(&act.sa_mask);// 设置 sa_flags 为 0,使用默认的标志act.sa_flags = 0;// 使用 sigaction 函数设置 SIGINT 信号的处理方式sigaction(SIGINT, &act, NULL);printf("程序正在运行,按下 Ctrl + C 发送 SIGINT 信号\n");// 让程序保持运行,以便我们可以发送信号while (1) {sleep(1);}return 0;
}

代码解释

  • 首先包含必要的头文件:stdio.h(输入输出)、signal.h(信号相关操作)、unistd.hsleep 函数)。

  • 定义自定义的信号处理函数 sigcb,功能是打印接收到的信号值。

  • 在 main 函数中,定义 struct sigaction 类型的变量 act

  • 设置 act 的 sa_handler 成员为我们自定义的处理函数 sigcb

  • 使用 sigemptyset 函数清空 sa_mask 成员,这样在处理 SIGINT 信号期间,不会阻塞其他信号。

  • 将 sa_flags 成员设置为 0,使用默认的标志。

  • 调用 sigaction 函数,将 SIGINT 信号的处理方式设置为 act 所指定的方式,第三个参数为 NULL,表示不保存原来的信号处理方式。

  • 打印提示信息后,通过无限循环让程序保持运行,sleep(1) 避免程序过度占用 CPU。

编译与运行

  1. 编译代码:使用 gcc 编译器,在终端中输入命令 gcc -o sigaction_demo sigaction_demo.c(假设代码文件名为 sigaction_demo.c)。

  2. 运行程序:在终端中输入 ./sigaction_demo,程序开始运行并打印提示信息。

  3. 测试信号:按下 Ctrl + C,触发 SIGINT 信号,程序会调用 sigcb 函数,打印出类似 接收到信号,信号值为:2SIGINT 的值通常为 2)的信息,而不是终止程序。

执行结果截图说明

  • 编译运行后,终端会显示 “程序正在运行,按下 Ctrl + C 发送 SIGINT 信号”。

  • 当按下 Ctrl + C 时,终端会打印 “接收到信号,信号值为:2”,然后程序继续运行(因为我们的处理函数没有终止进程,程序在循环中继续运行)。多次按下 Ctrl + C,会多次打印该信息。

通过这个例子,你可以掌握 sigaction 函数的使用方法,以及如何更精细地控制信号的处理方式。

2.2 调用系统命令向进程发信号

我们可以使用kill命令向指定进程发送信号,例如:

  1. 后台运行一个死循环程序:./sig &sig为上述编译生成的可执行文件)。
  2. 查看进程 ID:ps ajx | grep sig,获取进程的 PID。
  3. 向进程发送SIGSEGV(11 号信号,段错误信号):kill -SIGSEGV PID(或kill -11 PID),进程会因段错误终止。

2.3 使用函数产生信号

2.3.1 kill 函数

kill函数可以给一个指定的进程发送指定的信号,函数原型如下:

#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
  • 参数说明
    • pid:目标进程的 PID。
    • sig:要发送的信号编号。
  • 返回值:成功返回 0,失败返回 - 1,并设置errno
  • 示例:实现自己的 kill 命令
#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <signal.h>
// mykill -signumber pid
int main(int argc, char *argv[]) {if (argc != 3) {std::cerr << "Usage: " << argv[0] << " -signumber pid" << std::endl;return 1;}int number = std::stoi(argv[1] + 1); // 去掉“-”pid_t pid = std::stoi(argv[2]);int n = kill(pid, number);return n;
}

编译生成mykill后,可像kill命令一样使用,例如./mykill -2 PID,向指定 PID 的进程发送 2 号信号。

2.3.2 raise 函数

raise函数可以给当前进程发送指定的信号(自己给自己发信号),函数原型如下:

#include <signal.h>
int raise(int sig);
  • 参数说明sig:要发送的信号编号。
  • 返回值:成功返回 0,失败返回非 0。
  • 示例
    #include <iostream>
    #include <unistd.h>
    #include <signal.h>
    void handler(int signumber) {// 整个代码就只有这一处打印std::cout << "获取了一个信号: " << signumber << std::endl;
    }
    int main() {signal(2, handler); // 先对2号信号进行捕捉// 每隔1S,自己给自己发送2号信号while (true) {sleep(1);raise(2);}return 0;
    }

编译运行后,每隔 1 秒会输出 “获取了一个信号: 2”。

2.3.3 abort 函数

abort函数使当前进程接收到信号而异常终止,函数原型如下:

#include <stdlib.h>
void abort(void);
  • 说明abort函数总是会成功,没有返回值,它会给当前进程发送SIGABRT(6 号信号)。
  • 示例
    #include <iostream>
    #include <unistd.h>
    #include <stdlib.h>
    #include <signal.h>
    void handler(int signumber) {// 整个代码就只有这一处打印std::cout << "获取了一个信号: " << signumber << std::endl;
    }
    int main() {signal(SIGABRT, handler);while (true) {sleep(1);abort();}return 0;
    }

编译运行后,会输出 “获取了一个信号: 6”,然后进程异常终止,即使捕捉了SIGABRT信号,进程也会退出。

2.4 由软件条件产生信号

alarm函数和SIGALRM(14 号信号)为例,alarm函数可以设定一个闹钟,告诉内核在指定秒数后给当前进程发SIGALRM信号,该信号的默认处理动作是终止当前进程,函数原型如下:

  • 参数说明seconds:闹钟时间,单位为秒。若seconds为 0,表示取消以前设定的闹钟。
  • 返回值:返回 0 或者以前设定的闹钟时间还余下的秒数。
2.4.1 基本 alarm 验证 - 体会 IO 效率问题
  • IO 多的情况
#include <iostream>
#include <unistd.h>
#include <signal.h>
int main() {int count = 0;alarm(1);while (true) {std::cout << "count : " << count << std::endl;count++;}return 0;
}

编译运行后,1 秒内输出的计数较少,因为std::cout是 IO 操作,效率较低。

  • IO 少的情况
#include <iostream>
#include <unistd.h>
#include <signal.h>
int count = 0;
void handler(int signumber) {std::cout << "count : " << count << std::endl;exit(0);
}
int main() {signal(SIGALRM, handler);alarm(1);while (true) {count++;}return 0;
}

编译运行后,1 秒内输出的计数会非常大,因为仅进行变量自增操作,IO 操作少,效率高。

2.4.2 设置重复闹钟
#include <iostream>
#include <unistd.h>
#include <signal.h>
int gcount = 0;
void handler(int signo) {std::cout << "gcount : " << gcount << std::endl;gcount++;int n = alarm(1); // 重设闹钟,会返回上一次闹钟的剩余时间std::cout << "剩余时间 : " << n << std::endl;
}
int main() {std::cout << "我的进程pid是: " << getpid() << std::endl;alarm(1); // 一次性的闹钟,超时alarm会自动被取消signal(SIGALRM, handler);while (true) {pause(); // 等待信号std::cout << "我醒来了..." << std::endl;}return 0;
}

编译运行后,每隔 1 秒会输出计数和剩余时间,实现了重复闹钟的功能。pause函数会使调用进程(或线程)睡眠,直到收到一个终止进程的信号或一个导致调用信号捕捉函数的信号。

2.5 硬件异常产生信号

硬件异常被硬件检测到并通知内核,然后内核向当前进程发送适当的信号。例如:

  • 除零异常:当进程执行除以 0 的指令时,CPU 的运算单元会产生异常,内核将这个异常解释为SIGFPE(8 号信号)发送给进程。
  • 示例
    #include <stdio.h>
    #include <signal.h>
    void handler(int sig) {printf("catch a sig : %d\n", sig);
    }
    int main() {signal(SIGFPE, handler); // 8) SIGFPEsleep(1);int a = 10;a /= 0;while (1);return 0;
    }

编译运行后,会不断输出 “catch a sig : 8”,因为除零异常一直存在,内核会持续发送SIGFPE信号。

  • 非法内存访问:当进程访问了非法内存地址,MMU(内存管理单元)会产生异常,内核将这个异常解释为SIGSEGV(11 号信号)发送给进程。
    • 示例
#include <stdio.h>
#include <signal.h>
void handler(int sig) {printf("catch a sig : %d\n", sig);
}
int main() {signal(SIGSEGV, handler);sleep(1);int *p = NULL;*p = 100;while (1);return 0;
}

编译运行后,会不断输出 “catch a sig : 11”,因为非法内存访问的异常一直存在。

三、信号递达和阻塞的概念与原理

3.1 相关概念

  • 信号递达(Delivery):实际执行信号的处理动作。 
  • 信号未决(Pending):信号从产生到递达之间的状态。
  • 信号阻塞(Block):进程可以选择阻塞某个信号,被阻塞的信号产生时将保持在未决状态,直到进程解除对此信号的阻塞,才执行递达的动作。

注意:阻塞和忽略是不同的,只要信号被阻塞就不会递达,而忽略是在递达之后可选的一种处理动作。

3.2 在内核中的表示

在进程控制块(task_struct)中,有三个与信号相关的重要部分:

  • block:信号屏蔽字,用sigset_t类型表示,每个 bit 代表对应信号是否被阻塞。
  • pending:未决信号集,同样用sigset_t类型表示,每个 bit 代表对应信号是否处于未决状态。
  • handler:信号处理动作数组,每个元素是一个函数指针,指向该信号的处理函数,若为SIG_DFL表示默认处理动作,SIG_IGN表示忽略该信号。

例如,对于SIGINT(2 号信号),若block中对应的 bit 为 1,表示该信号被阻塞;若pending中对应的 bit 为 1,表示该信号处于未决状态;handler中对应的函数指针指向该信号的处理函数。

3.3 信号集操作函数

sigset_t类型用于表示信号集,我们不能直接操作sigset_t变量的内部数据,需要使用专门的函数:

3.3.1 初始化和修改信号集
  • sigemptyset:初始化信号集,使其中所有信号的对应 bit 清零,表示该信号集不包含任何有效信号。

#include <signal.h>
int sigemptyset(sigset_t *set);

sigfillset:初始化信号集,使其中所有信号的对应 bit 置位,表示该信号集包含系统支持的所有信号。

#include <signal.h>
int sigfillset(sigset_t *set);

sigaddset:在信号集中添加某种有效信号。

#include <signal.h>
int sigaddset(sigset_t *set, int signo);

sigdelset:在信号集中删除某种有效信号。

#include <signal.h>
int sigdelset(sigset_t *set, int signo);
  • 返回值:以上四个函数成功返回 0,出错返回 - 1。
3.3.2 判断信号是否在信号集中

sigismember:判断一个信号集的有效信号中是否包含某种信号,若包含则返回 1,不包含则返回 0,出错返回 - 1。

#include <signal.h>
int sigismember(const sigset_t *set, int signo);
3.3.3 读取或更改进程的信号屏蔽字

sigprocmask:读取或更改进程的信号屏蔽字(阻塞信号集)。

#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oset);
  • 参数说明
    • how:指示如何更改信号屏蔽字,取值如下:
      • SIG_BLOCKset包含了我们希望添加到当前信号屏蔽字的信号,相当于mask = mask | set
      • SIG_UNBLOCKset包含了我们希望从当前信号屏蔽字中解除阻塞的信号,相当于mask = mask & ~set
      • SIG_SETMASK:设置当前信号屏蔽字为set所指向的值,相当于mask = set
    • set:若非空指针,则根据how更改进程的信号屏蔽字;若为空指针,则不更改信号屏蔽字。
    • oset:若非空指针,则读取进程的当前信号屏蔽字通过oset参数传出;若为空指针,则不读取信号

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

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

相关文章

解决多种类潮湿敏感元器件的多温度、多时长的排潮烘干

铠德科技ESD烘箱针对复杂电路产品的排潮烘干需求&#xff0c;可通过以下技术路径实现多品类元器件的高效兼容处理&#xff1a;多温区独立控制系统采用蜂窝式加热模块阵列&#xff0c;每个0.6m独立温区可设置1℃精度支持同时运行3种不同温度曲线&#xff08;典型值&#xff1a;8…

obdumper和obloader迁移OceanBase业务库(一):实施手册

obdumper和obloader迁移OceanBase业务库&#xff08;一&#xff09;&#xff1a;实施手册导出前准备全库&#xff08;模式&#xff09;数据导出全库&#xff08;模式&#xff09;数据导入导入后检查环境信息&#xff1a;OceanBase v4.3.5单机部署&#xff0c;MySQL租户OBDUMPER…

SQLSugar 快速入门:从基础到实战查询与使用指南

目录 ​编辑 一、SQLSugar 简介 二、SQLSugar 环境搭建 2.1 安装 SQLSugar 2.1.1 通过 Visual Studio NuGet 图形化界面安装 2.1.2 通过 NuGet 命令行安装 2.2 引用 SQLSugar 命名空间 三、SQLSugar 核心初始化配置 3.1 基础初始化&#xff08;非 IOC 模式&#xff09…

Python与Rust语法对比详解:从入门到精通

Python与Rust语法对比详解&#xff1a;从入门到精通 前言 Python和Rust作为当今最受关注的编程语言&#xff0c;分别代表了动态类型和静态类型语言的典型特征。本文将从语法层面深入对比这两种语言&#xff0c;帮助开发者理解它们的设计理念和使用场景。1. 基础语法结构 1.1 He…

视频加水印_带gif 加动态水印 gif水印 视频浮动水印

如果你有一个视频&#xff0c;你想给它加一个水印&#xff0c;让水印浮动&#xff0c;而且加的还是 GIF 动态图片水印&#xff0c;那么你可以使用这个工具。首先把你的两个文件拖进来&#xff0c;然后点击第三个按钮。加好了&#xff0c;打开看一下&#xff0c;我们看到这个水印…

C# 字符和字符串

原文&#xff1a;C# 字符和字符串_w3cschool 请勿将文章标记为付费&#xff01;&#xff01;&#xff01;&#xff01;&#xff01; C&#xff03;字符和字符串 C&#xff03;的 char 类型别名 System.Char 类型表示 Unicode 字符。 在单引号中指定char字面值&#xff1a; …

IntelliJ IDEA 反编译JAR包记录

本文记录了使用 IntelliJ IDEA 内置反编译工具对 JAR 包进行反编译的详细步骤&#xff0c;方便日后快速参考和使用。 &#x1f6e0;️ 工具准备 反编译工具使用的是 IntelliJ IDEA 内置的 Java 反编译器&#xff0c;无需额外安装其他工具。 工具路径&#xff1a; /Applications…

KingbaseES JDBC 驱动详解:连接、配置与最佳实践

目录KingbaseES JDBC 驱动详解&#xff1a;连接、配置与最佳实践引言一、JDBC 基础与 KingbaseES 实现1.1 JDBC 技术概述1.2 KingbaseES JDBC 驱动特点二、环境配置与驱动获取2.1 驱动包选择与依赖管理2.2 国密算法支持2.3 驱动版本信息获取三、数据库连接管理3.1 使用 DriverM…

破解 Aspose.Words 24.12,跳过 License 校验,实现 HTML 向 Word/PDF 的转换,附带 Demo。

说明 在Java生态中处理Office文档时&#xff0c;开发人员常面临格式兼容性和功能完整性的挑战。商业组件Aspose以其卓越的文档处理能力成为企业级解决方案之一&#xff0c;支持Word、Excel、PDF等多种格式的精准转换与操作。 请勿用于商业用途&#xff0c;若侵权请联系我。 参考…

php连接rabbitmq例子

首先确保安装好了Rabbitmq服务器。1.新建一个空白php项目&#xff0c;安装php客户端库&#xff1a;composer require php-amqplib/php-amqplib2.生产者然后添加生产者代码 (producer.php)<?php require_once __DIR__ . /vendor/autoload.php;use PhpAmqpLib\Connection\AMQ…

Docker Swarm vs Kubernetes vs Nomad:容器编排方案对比与选型建议

Docker Swarm vs Kubernetes vs Nomad&#xff1a;容器编排方案对比与选型建议 在微服务和云原生时代&#xff0c;容器编排成为支持大规模容器化应用的关键技术。本文将从问题背景、方案对比、优缺点分析、选型建议以及实际应用效果验证五个方面&#xff0c;对Docker Swarm、Ku…

似然函数对数似然函数负对数似然函数

目录1. 似然函数的定义2. 对数似然函数的定义3. 负对数似然函数的定义4. 负对数似然函数的优化5. 具体应用示例5.1 逻辑回归中的负对数似然函数5.2 优化逻辑回归的负对数似然函数1. 似然函数的定义 似然函数L(θ∣X)L(\theta | X)L(θ∣X)是在给定参数θ\thetaθ 下&#xff0…

鸿蒙地址选择库(ArkTs UI)

功能点&#xff1a;支持三级联动、点击确认返回省市区code及name&#xff08;安心&#xff09;、布局可以高度自定义 实现&#xff1a;TextPicker读取本地json&#xff08;也可用第三方的json 不过需要自行调整了&#xff09; 先上图吧、废话下面再说&#xff1a; 凑和看吧、…

YOLO 目标检测:数据集构建(LabelImg 实操)、评估指标(mAP/IOU)、 NMS 后处理

文章目录基本知识介绍1.视觉处理三大任务2.训练、验证、测试、推理3.数据集3.1 数据集格式3.2 数据集标注4.上游任务和下游任务YOLO指标1.真实框&#xff08;Ground Truth Box&#xff09;与边界框&#xff08;Bounding Box&#xff09;2.交并比&#xff08;IOU&#xff09;3.置…

进程状态 —— Linux内核(Kernel)

&#x1f381;个人主页&#xff1a;工藤新一 &#x1f50d;系列专栏&#xff1a;C面向对象&#xff08;类和对象篇&#xff09; &#x1f31f;心中的天空之城&#xff0c;终会照亮我前方的路 &#x1f389;欢迎大家点赞&#x1f44d;评论&#x1f4dd;收藏⭐文章 文章目录进…

计算机视觉与深度学习 | 低照度图像处理算法综述:发展、技术与趋势

文章目录 一、发展历程:从传统模型到智能融合 (一)传统模型构建阶段(1970s-2016) (二)深度学习应用阶段(2017-2020) (三)硬件-算法协同阶段(2021至今) 二、技术分类与性能对比 (一)传统方法体系 (二)深度学习方法 1. 监督学习模型 2. 无监督/自监督方法 3. 混…

责任链模式实践-开放银行数据保护及合规

责任链模式介绍什么是责任链模责任链模式是一种行为设计模式&#xff0c; 允许你将请求沿着处理者链进行发送。 收到请求后&#xff0c; 每个处理者均可对请求进行处理&#xff0c; 或将其传递给链上的下个处理者。责任链模式结构伪代码基于责任链的开放银行数据保护及合规实践…

npm install --global @dcloudio/uni-cli 时安装失败

这个日志显示在执行 npm install --global dcloudio/uni-cli 时安装失败&#xff0c;核心错误是 UNABLE_TO_GET_GET_ISSUER_CERT_LOCALLY&#xff08;无法获取本地颁发者证书&#xff09;&#xff0c;属于 HTTPS 证书验证失败 问题。错误原因npm 访问官方 registry&#xff08;…

吱吱企业通讯软件可私有化部署,构建安全可控的通讯办公平台

在当今激烈的市场竞争环境中&#xff0c;企业通讯已成为制胜的关键因素。吱吱作为一款专为企业管理设计的IM即时办公通讯软件&#xff0c;提供了高度安全的通讯办公环境&#xff0c;确保信息在内部流通的安全性与高效性&#xff0c;为企业数字化转型奠定了坚实的基础。 一、私有…

暄桐:唯有认真思考过死亡,才足以应对日常

暄桐是一间传统美学教育教室&#xff0c;创办于2011年&#xff0c;林曦是创办人和授课老师&#xff0c;教授以书法为主的传统文化和技艺&#xff0c;皆在以书法为起点&#xff0c;亲近中国传统之美&#xff0c;以实践和所得&#xff0c;滋养当下生活。初听庄子在妻子离世后“鼓…