linux 系统编程复习07-信号

news2024/11/25 12:47:21

1 复习目标

  • 了解信号中的基本概念
  • 熟练使用信号相关的函数
  • 参考文档使用信号集操作相关函数
  • 熟练使用信号捕捉函数signal
  • 熟练使用信号捕捉函数sigaction
  • 熟练掌握使用信号完成子进程的回收

信号介绍

  • 信号的概念

信号是信息的载体,Linux/UNIX 环境下,古老、经典的通信方式, 现下依然是主要的通信手段。

  • 信号在我们的生活中随处可见,例如:
  • 古代战争中摔杯为号;
  • 现代战争中的信号弹;
  • 体育比赛中使用的信号枪......
  • 信号的特点
  • 简单
  • 不能携带大量信息
  • 满足某个特点条件才会产生

2 信号的机制

进程A给进程B发送信号,进程B收到信号之前执行自己的代码,收到信号后,不管执行到程序的什么位置,都要暂停运行,去处理信号,处理完毕后再继续执行。与硬件中断类似——异步模式。但信号是软件层面上实现的中断,早期常被称为“软中断”。

每个进程收到的所有信号,都是由内核负责发送的。

进程A给进程B发送信号示意图:

2.1 信号的状态

信号有三种状态:产生、未决和递达。

  • 信号的产生
  • 按键产生,如:Ctrl+c、Ctrl+z、Ctrl+\
  • 系统调用产生,如:kill、raise、abort
  • 软件条件产生,如:定时器alarm
  • 硬件异常产生,如:非法访问内存(段错误)、除0(浮点数例外)、内存对齐出错(总线错误)
  • 命令产生,如:kill命令
  • 未决:产生和递达之间的状态。主要由于阻塞(屏蔽)导致该状态。 
  • 递达:递送并且到达进程。

2.2 信号的处理方式

  • 执行默认动作
  • 忽略信号(丢弃不处理)
  • 捕捉信号(调用用户的自定义的处理函数)

2.3 信号的特质

信号的实现手段导致信号有很强的延时性,但对于用户来说,时间非常短,不易察觉。

Linux内核的进程控制块PCB是一个结构体,task_struct, 除了包含进程id,状态,工作目录,用户id,组id,文件描述符表,还包含了信号相关的信息,主要指阻塞信号集和未决信号集

注:表示PCB的task_struct结构体定义在:

  /usr/src/linux-headers-4.4.0-97/include/linux/sched.h:1390

2.4 阻塞信号集和未决信号集

    Linux内核的进程控制块PCB是一个结构体,这个结构体里面包含了信号相关的信息,主要有阻塞信号集和未决信号集。

  • 阻塞信号集中保存的都是被当前进程阻塞的信号。若当前进程收到的是阻塞信号集中的某些信号,这些信号需要暂时被阻塞,不予处理。
  • 信号产生后由于某些原因(主要是阻塞)不能抵达,这类信号的集合称之为未决信号集。在屏蔽解除前,信号一直处于未决状态;若是信号从阻塞信号集中解除阻塞,则该信号会被处理,并从未决信号集中去除。

2.5 信号的四要素

  • 通过man 7 signal可以查看信号相关信息

1 信号的编号

  • 使用kill -l命令可以查看当前系统有哪些信号,不存在编号为0的信号。其中1-31号信号称之为常规信号(也叫普通信号或标准信号),34-64称之为实时信号,驱动编程与硬件相关。

2 信号的名称

3 产生信号的事件

4信号的默认处理动作

  • Term:终止进程
  • Ign:忽略信号 (默认即时对该种信号忽略操作)
  • Core:终止进程,生成Core文件。(查验死亡原因,用于gdb调试)
  • Stop:停止(暂停)进程
  • Cont:继续运行进程
  • 特别需要注意的是:The signals SIGKILL and SIGSTOP cannot be caught, blocked, or ignored.
  • 几个常用到的信号

SIGINT、SIGQUIT、SIGKILL、SIGSEGV、SIGUSR1、SIGUSR2、SIGPIPE、SIGALRM、SIGTERM、SIGCHLD、SIGSTOP、SIGCONT

3 信号相关函数

3.1 signal函数

  • 函数作用:注册信号捕捉函数
  • 函数原型

  typedef void (*sighandler_t)(int);

        sighandler_t signal(int signum, sighandler_t handler);

  • 函数参数
  • signum:信号编号
  • handler:信号处理函数

3.2 kill函数/命令

  • 描述:给指定进程发送指定信号
  • kill命令:kill -SIGKILL 进程PID
  • kill函数原型:int kill(pid_t pid, int sig);
  • 函数返回值:
  • 成功:0;
  • 失败:-1,设置errno
  • 函数参数:
  • sig信号参数:不推荐直接使用数字,应使用宏名,因为不同操作系统信号编号可能不同,但名称一致。
  • pid参数:
  • pid > 0: 发送信号给指定的进程。
  • pid = 0: 发送信号给与调用kill函数进程属于同一进程组的所有进程。
  • pid < -1:  取|pid|发给对应进程组。
  • pid = -1:发送给进程有权限发送的系统中所有进程。

进程组:每个进程都属于一个进程组,进程组是一个或多个进程集合,他们相互关联,共同完成一个实体任务,每个进程组都有一个进程组长,默认进程组ID与进程组长ID相同。

3.3 abort函数raise函数

  • raise函数
  • 函说描述:给当前进程发送指定信号(自己给自己发)
  • 函数原型:int raise(int sig);
  • 函数返回值:成功:0,失败非0值
  • 函数拓展:raise(signo) == kill(getpid(), signo);
  • abort函数
  • 函数描述:给自己发送异常终止信号 6) SIGABRT,并产生core文件
  • 函数原型:void abort(void);  
  • 函数拓展:abort() == kill(getpid(), SIGABRT);

3.4 alarm函数

  • 函数原型:unsigned int alarm(unsigned int seconds); 
  • 函数描述:设置定时器(闹钟)。在指定seconds后,内核会给当前进程发送14)SIGALRM信号。进程收到该信号,默认动作终止每个进程都有且只有唯一的一个定时器。
  • 函数返回值:返回0或剩余的秒数,无失败。例如:
  • 常用操作:取消定时器alarm(0),返回旧闹钟余下秒数。

alarm使用的是自然定时法,与进程状态无关,就绪、运行、挂起(阻塞、暂停)、终止、僵尸...无论进程处于何种状态,alarm都计时。

=========================================

练习题1:编写一个程序测试alarm函数

//signal函数测试---注册信号处理函数
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>

//信号处理函数
void sighandler(int signo)
{
	printf("signo==[%d]\n", signo);
}

int main()
{
	//注册信号处理函数
	signal(SIGINT, sighandler);
	signal(SIGALRM, sighandler);

	int n = alarm(5);
	printf("first: n==[%d]\n", n);

	sleep(2);
	n = alarm(5);
	//n = alarm(0); //取消时钟
	printf("second: n==[%d]\n", n);

	while(1)
	{
		sleep(10);
	}

	return 0;
}

练习题2:编写程序,测试你的电脑1秒种能数多个数字。

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








int main()
{

 alarm(1);
 int i=0;

 while(1){
    printf("[%d]",i++);
 }



return 0;
}

  • 使用time命令查看程序执行的时间。程序运行的瓶颈在于IO,优化程序,首选优化IO。
  • 实际执行时间 = 系统时间 + 用户时间 + 损耗时间

损耗的时间主要来来自文件IO操作,IO操作会有用户区到内核区的切换,切换的次数越多越耗时。

3.5 setitimer函数

  • 函数原型       

int setitimer(int which, const struct itimerval *new_value,

                     struct itimerval *old_value);

  • 函数描述

设置定时器(闹钟),可代替alarm函数,精度微秒us,可以实现周期定时。

  • 函数返回值
  • 成功:0;
  • 失败:-1,设置errno值
  • 函数参数:
  • which:指定定时方式
  • 自然定时:ITIMER_REAL → 14)SIGALRM计算自然时间
  • 虚拟空间计时(用户空间):ITIMER_VIRTUAL → 26)SIGVTALRM  只计算进程占用cpu的时间
  • 运行时计时(用户+内核):ITIMER_PROF → 27)SIGPROF计算占用cpu及执行系统调用的时间
  • new_value:struct itimerval, 负责设定timeout时间。
  • itimerval.it_value: 设定第一次执行function所延迟的秒数 itimerval.it_interval: 设定以后每几秒执行function

struct itimerval {

    struct timerval it_interval; // 闹钟触发周期

    struct timerval it_value; // 闹钟触发时间

  };

  struct timeval {

    long tv_sec; // 秒

    long tv_usec; // 微秒

 }             

  • old_value: 存放旧的timeout值,一般指定为NULL

练习: 使用setitimer实现每隔一秒打印一次hello, world

4 信号集相关

4.1 未决信号集和阻塞信号集的关系

阻塞信号集是当前进程要阻塞的信号的集合,未决信号集是当前进程中还处于未决状态的信号的集合,这两个集合存储在内核的PCB中。

  • 下面以SIGINT为例说明信号未决信号集和阻塞信号集的关系:

当进程收到一个SIGINT信号(信号编号为2),首先这个信号会保存在未决信号集合中,此时对应的2号编号的这个位置上置为1,表示处于未决状态;在这个信号需要被处理之前首先要在阻塞信号集中的编号为2的位置上去检查该值是否为1:

      • 如果为1,表示SIGNIT信号被当前进程阻塞了,这个信号暂时不被处理,所以未决信号集上该位置上的值保持为1,表示该信号处于未决状态;
      • 如果为0,表示SIGINT信号没有被当前进程阻塞,这个信号需要被处理,内核会对SIGINT信号进行处理(执行默认动作,忽略或者执行用户自定义的信号处理函数),并将未决信号集中编号为2的位置上将1变为0,表示该信号已经处理了,这个时间非常短暂,用户感知不到。

当SIGINT信号从阻塞信号集中解除阻塞之后,该信号就会被处理。

4.2 信号集相关函数

由于信号集属于内核的一块区域,用户不能直接操作内核空间,为此,内核提供了一些信号集相关的接口函数,使用这些函数用户就可以完成对信号集的相关操作。

信号集是一个能表示多个信号的数据类型,sigset_t set,set即一个信号集。既然是一个集合,就需要对集进行添加、删除等操作。

sigset_t类型的定义在signal.h文件中的第49行处:

typedef __sigset_t sigset_t;

__sigset_t的定义在sigset.h文件中的26,27行处:

# define _SIGSET_NWORDS (1024 / (8 * sizeof (unsigned long int)))

  typedef struct

  {

    unsigned long int __val[_SIGSET_NWORDS];

  } __sigset_t;

上述变量类型的定义的查找有个小窍门: 可以执行gcc的预处理命令:

gcc -E test.c -o test.i 这样头文件就会展开,可以直接到test.i文件中看到相关变量类型的定义。

  • 信号集相关函数
  • int sigemptyset(sigset_t *set);

函数说明:将某个信号集清0  

函数返回值:成功:0;失败:-1,设置errno

  • int sigfillset(sigset_t *set);

函数说明:将某个信号集置1   

函数返回值:成功:0;失败:-1,设置errno

  • int sigaddset(sigset_t *set, int signum);

函数说明:将某个信号加入信号集合中

函数返回值:成功:0;失败:-1,设置errno

  • int sigdelset(sigset_t *set, int signum);

函数说明:将某信号从信号清出信号集   

函数返回值:成功:0;失败:-1,设置errno

  • int sigismember(const sigset_t *set, int signum);

函数说明:判断某个信号是否在信号集中

函数返回值:在:1;不在:0;出错:-1,设置errno

  • sigprocmask函数

函数说明:用来屏蔽信号、解除屏蔽也使用该函数。其本质,读

取或修改进程控制块中的信号屏蔽字(阻塞信号集)。

特别注意,屏蔽信号只是将信号处理延后执行(延至解除屏蔽);而忽略表示将信号丢弃处理。

  • 函数原型:int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
  • 函数返回值:成功:0;失败:-1,设置errno
  • 函数参数:
  1. how参数取值:假设当前的信号屏蔽字为mask
  1. SIG_BLOCK: 当how设置为此值,set表示需要屏蔽的信号。相当于 mask = mask|set
  2. SIG_UNBLOCK: 当how设置为此,set表示需要解除屏蔽的信号。相当于 mask = mask & ~set
  3. SIG_SETMASK: 当how设置为此,set表示用于替代原始屏蔽及的新屏蔽集。相当于mask = set若,调用sigprocmask解除了对当前若干个信号的阻塞,则在sigprocmask返回前,至少将其中一个信号递达。
        1. set:传入参数,是一个自定义信号集合。由参数how来指示如何修改当前信号屏蔽字。
        2. oldset:传出参数,保存旧的信号屏蔽字。
  • sigpending函数
  • 函数原型:int sigpending(sigset_t *set);    
  • 函数说明:读取当前进程的未决信号集
  • 函数参数:set传出参数
  • 函数返回值:成功:0;失败:-1,设置errno

练习:编写程序,设置阻塞信号集并把所有常规信号的未决状态打印至屏幕。

5 信号捕捉函数

  • signal函数
  • sigaction函数
    • 函数说明:

注册一个信号处理函数

    • 函数原型:

  int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);

    • 函数参数:
      1. signum:捕捉的信号
      2. act:    传入参数,新的处理方式。
      3. oldact: 传出参数,旧的处理方式

struct sigaction {

       void  (*sa_handler)(int); // 信号处理函数

       void  (*sa_sigaction)(int, siginfo_t *, void *); //信号处理函数

       sigset_t  sa_mask; //信号处理函数执行期间需要阻塞的信号

       int      sa_flags; //通常为0,表示使用默认标识

       void     (*sa_restorer)(void);

};

  • 总结:
    • sa_handler:指定信号捕捉后的处理函数名(即注册函数)。也可赋值为SIG_IGN表忽略 或 SIG_DFL表执行默认动作
    • sa_mask: 用来指定在信号处理函数执行期间需要被屏蔽的信号,特别是当某个信号被处理时,它自身会被自动放入进程的信号掩码,因此在信号处理函数执行期间这个信号不会再度发生。注意:仅在处理函数被调用期间屏蔽生效,是临时性设置。
    • sa_flags:通常设置为0,使用默认属性。
    • sa_restorer:已不再使用

  • 练习:编写程序,使用sigaction函数注册信号捕捉函数,并使用这个程序验证信号是否支持排队。
  • 知识点: 信号处理不支持排队:
  • 在XXX信号处理函数执行期间, XXX信号是被阻塞的, 如果该信号产生了多次, 在XXX信号处理函数结束之后,  该XXX信号只被处理一次.
  • 在XXX信号处理函数执行期间,如果阻塞了YYY信号, 若YYY信号产生了多次, 当XXX信号处理函数结束后, YYY信号只会被处理一次.

  • 内核实现信号捕捉的过程

如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,这称为捕捉信号。由于信号处理函数的代码是在用户空间的,处理过程比较复杂,举例如下:

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

当前正在执行main函数,这时发生中断或异常切换到内核态。

在中断处理完毕后要返回用户态的main函数之前检查到有信号SIGQUIT递达。

内核决定返回用户态后不是恢复main函数的上下文继续执行,而是执行sighandler函数,sighandler和main函数使用不同的堆栈空间,它们之间不存在调用和被调用的关系,是两个独立的控制流程。

sighandler函数返回后自动执行特殊的系统调用sigreturn再次进入内核态。

如果没有新的信号要递达,这次再返回用户态就是恢复main函数的上下文继续执行了。

6 SIGCHLD信号

6.1 产生SIGCHLD信号的条件

  • 子进程结束的时候
  • 子进程收到SIGSTOP信号
  • 当子进程停止时,收到SIGCONT信号

6.2 SIGCHLD信号的作用

子进程退出后,内核会给它的父进程发送SIGCHLD信号,父进程收到这个信号后可以对子进程进行回收。

使用SIGCHLD信号完成对子进程的回收可以避免父进程阻塞等待而不能执行其他操作,只有当父进程收到SIGCHLD信号之后才去调用信号捕捉函数完成对子进程的回收,未收到SIGCHLD信号之前可以处理其他操作。

6.3 使用SIGCHLD信号完成对子进程的回收

  • 练习:父进程创建三个子进程,然后让父进程捕获SIGCHLD信号完成对子进程的回收。
  • 注意点:
  • 有可能还未完成信号处理函数的注册三个子进程都退出了。
  • 解决办法:可以在fork之前先将SIGCHLD信号阻塞,当完成信号处理函数的注册后在解除阻塞。
  • SIGCHLD信号函数处理期间, SIGCHLD信号若再次产生是被阻塞的,而且若产生了多次, 则该信号只会被处理一次, 这样可能会产生僵尸进程。
  • 解决办法: 可以在信号处理函数里面使用while(1)循环回收, 这样就有可能出现捕获一次SIGCHLD信号但是回收了多个子进程的情况,从而可以避免产生僵尸进程。

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

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

相关文章

【C语言】优化通讯录管理系统

大家好&#xff0c;我是苏貝&#xff0c;本篇博客带大家优化上一篇的通讯录&#xff0c;如果你觉得我写的还不错的话&#xff0c;可以给我一个赞&#x1f44d;吗&#xff0c;感谢❤️ 目录 一. 前言二. 动态通讯录2.1 通讯录结构体2.2 初始化通讯录2.3 增加联系人2.4 销毁通讯…

【需要理解】80 单词搜索

单词搜索 题解1 回溯&#xff08;需要改变起点&#xff09; 给定一个 m x n 二维字符网格 board 和一个字符串单词 word 。如果 word 存在于网格中&#xff0c;返回 true &#xff1b;否则&#xff0c;返回 false 。 单词必须按照字母顺序&#xff0c;通过相邻的单元格内…

数据结构——线性表①(顺序表)

一、线性表定义 线性表是一种数据结构&#xff0c;它是由n个具有相同数据类型的数据元素a1,a2,…,an组成的有限序列。 其中&#xff0c;除第一个元素a1外&#xff0c;每一个元素有且只有一个直接前驱元素&#xff0c;除了最后一个元素an外&#xff0c;每一个元素有且只有一个…

IDEA 如何运行 SpringBoot 项目

步骤一&#xff1a;配置 Maven 第一步&#xff1a;用 IDEA 打开项目&#xff0c;准备配置 maven 环境 &#xff0c;当然如果本地没有提前配置好 maven&#xff0c;就用 IDEA 默认的配置即可 配置 maven 步骤 情况 1&#xff1a;如果本地没有配置过 maven&#xff0c;可以保持如…

【Spring】IOC容器与Bean的常用属性配置

文章目录 1.前言2.IOC容器2.1 BeanFactory 容器2.2 ApplicationContext 容器 3.Bean的常用属性配置4. 总结 1.前言 在之前的文章-IOC的快速入门中讲过Bean这个概念. 本来就来介绍容器与Bean的常用属性配置 在Spring框架中&#xff0c;Bean指的是被Spring加载生成出来的对象。 …

ubuntu 18.04 编译安装flexpart 10.4(2023年) —— 筑梦之路

2023年10月29日 环境说明 操作系统版本&#xff1a;ubuntu 18.04 python版本&#xff1a;3.6.9 gcc版本&#xff1a;7.5.0 编译安装路径&#xff1a;/usr/local cmake: 3.10.2 所需要的源码包我已经打包放到我的资源。 2021年1月份已经写过一篇Ubuntu 编译安装的帖子F…

Android OpenGL ES 2.0入门实践

本文既然是入门实践&#xff0c;就先从简单的2D图形开始&#xff0c;首先&#xff0c;参考两篇官方文档搭建个框架&#xff0c;便于写OpenGL ES相关的代码&#xff1a;构建 OpenGL ES 环境、OpenGL ES 2.0 及更高版本中的投影和相机视图。 先上代码&#xff0c;代码效果如下图…

MQ——进阶

文章目录 消息可靠性生产者消息确认消息持久化消费者确认演示none模式演示auto模式 失败重试机制本地重试失败策略 死信交换机初始死信交换机TTL延迟队列安装DelayExchange插件使用DelayExchange 惰性队列消息堆积问题惰性队列 MQ集群集群分类普通集群镜像模式镜像模式的配置 仲…

私有云:【6】VCenter安装SqlServer

私有云&#xff1a;【6】VCenter安装SqlServer 1、VCenter安装SqlServer1.1、通过模板创建虚拟机1.2、安装sqlserver服务 2、搭建sqlserver群集2.1、安装群集功能2.2、在ad域服务器创建共享文件夹&#xff0c;供集群选举使用 3、创建故障转移群集【只需安装一台即可】3.1、创建…

操作系统 --- 存储器管理

一、简答题 1.存储器管理的基本任务&#xff0c;是为多道程序的并发执行提供良好的存储器环境。请问好的存储器环境”应包含哪几个方面&#xff1f; 答&#xff1a; 2.内存保护是否可以完全由软件实现&#xff1f;为什么&#xff1f; 答&#xff1a;内存保护的主要任务是确保每…

LeetCode热题100——双指针

双指针 1.移动零2.盛最多水的容器3.三数之和 1.移动零 给定一个数组 nums&#xff0c;编写一个函数将所有 0 移动到数组的末尾&#xff0c;同时保持非零元素的相对顺序。 // 题解&#xff1a;使用双指针&#xff0c;其中快指针指向非零元素&#xff0c;慢指针指向首个零元素下…

msvcp120.dll怎么修复,五种方法教你如何修复msvcp120.dll文件

在运行软件时&#xff0c;我们常常会遇到一些错误提示&#xff0c;其中之一就是“由于找不到msvcp120.dll无法继续执行代码”。这个错误通常发生在使用Microsoft Visual C 2013编译的程序运行时。本文将介绍5种修复这个问题的方法&#xff0c;帮助各位解决这个困扰。 方法一、使…

如何使用批量重命名的方法替换重复文件名内容

在文件管理过程中&#xff0c;我们有时会遇到文件名中包含相同部分内容的情况&#xff0c;这不仅会使文件显得混乱&#xff0c;而且还会给文件检索和使用带来不便。为了解决这个问题&#xff0c;我们可以使用云炫文件管理器批量重命名进行批量替换。下面是如何使用这种方法进行…

RT-Thread入门

1、初识RT-Thread RT-Thread&#xff0c;全称是Real Time-Thread&#xff0c;即嵌入式实时多线程操作系统。其基本属性之一是支持多任务&#xff0c;但是允许多任务同时运行&#xff0c;但是并不是意味着处理器在同一时刻真的执行了多个任务。实际上&#xff0c;一个处理器核心…

mac 安装homebrew ,golang

mac 安装homebrew ,golang 安装homebrew安装golang选择 apple arm 版本安装配置环境变量 安装homebrew /bin/zsh -c "$(curl -fsSL https://gitee.com/cunkai/HomebrewCN/raw/master/Homebrew.sh)"回车执行指令后&#xff0c;根据提示操作。具体包括以下提示操作&am…

深度学习(4)---生成式对抗网络(GAN)

文章目录 一、原理讲述1.1 概念讲解1.2 生成模型和判别模型 二、训练过程2.1 训练原理2.2 损失函数 三、应用 一、原理讲述 1.1 概念讲解 1. 生成式对抗网络&#xff08;Generative Adversarial Network&#xff0c;GAN&#xff09;是一种深度学习模型&#xff0c;是近年来复杂…

vue vant van-uploader使用compressorjs解决拍照上传的图片被旋转 90 度方法,图片压缩上传

vue vant van-uploader使用compressorjs解决拍照上传的图片被旋转 90 度方法&#xff0c;图片压缩上传_van-uploader 拍照上传服务器后图片翻转-CSDN博客文章浏览阅读3.2k次&#xff0c;点赞4次&#xff0c;收藏6次。van-uploader使用compressorjs解决拍照上传的图片被旋转 90 …

游戏找不到x3daudio1_7.dll无法继续执行的解决方法,快速解决dll问题

x3daudio1_7.dll是一个音频处理库&#xff0c;主要用于实现三维音频渲染。它包含了微软的XAudio2音频API&#xff0c;该API被许多游戏和应用程序用于实现高质量的音频效果。这个库文件主要处理音频的空间定位、响度均衡以及多通道音频输出等功能。在游戏中&#xff0c;它可以为…

08 MIT线性代数-求解Ax=b:可解性与结构Complete Solution of Ax=b

1. 可解的条件 Solvability conditions on b 检验Axb是否可解的方法是对增广矩阵进行行消元。如果矩阵A的行被完全消去的话&#xff0c;则对应的b的分量也要得0 两条关于b的限制条件(等价) 1. if a comb. of rows of A gives zero row, then same comb. of enties of b must …

笔记46:ResNeXt 网络详解

本地笔记地址&#xff1a;D:\work_file\DeepLearning_Learning\03_个人笔记\2.图像处理任务\ResNeXt网络学习 a a a a a a a a a a a a a a