【计算机网络】信号处理接口 Signal API(3)

news/2024/5/18 21:51:11 标签: 计算机网络, 信号处理, KILL, SIGNAL, SIGBUS

          收发信号思想是 Linux 程序设计特性之一,一个信号可以认为是一种软中断,通过用来向进程通知异步事件。

        本文讲述的 信号处理内容源自 Linux man。本文主要对各 API 进行详细介绍,从而更好的理解信号编程。


信号概述

遵循 POSIX.1,特别说明除外

1.描述

        Linux 机制 POSIX 可靠信号(我们成为标准信号),也支持 POSIX 实时信号。

        信号处置方法

        每个信号都有一个定义如何处理信号的处置方法。

        下面列表中 “行为” 列定义了每个信号的默认处置行为:

信号默认行为
Term默认行为是终止进程
Ign默认行为是忽略这个信号
Core默认行为是终止进程并转储内核(dump core)
Stop默认行为是停止进程
Cont默认行为是在进程停止后继续执行该进程

        进程可以通过 sigaction(2) 或者 signal(2) 接口修改信号的处置方法。(后者在建立信号处理函数时移植性差一些,参考 signal(2) 获得详细信息。)通过这些系统调用,在信号发生时,进程可以选择以下行为处理信号:

  • 使用默认行为
  • 过略信号
  • 使用信号处理函数(signal handler)捕获信号
  • 使用用户自定义的函数

        默认情况下,信号处理函数是在普通进程栈上调用的,不过也可以设置它使用其他可选的栈,参考 sigaltstack(2) 查看一些关于如何使用以及这样做有哪些好处的讨论。

        信号处置是每个进程的属性:在多线程应用中,信号处置函数对于所有线程都是一样的。

        通过 fork(2) 创建的子进程会复制一份父进程的信号处置方法,在 execve(2) 执行的过程中,进程想要处理的信号处置方法会被设置为默认值,而处置方法为忽略的信号保持不变。

        发送信号

        下面系统调用和库函数提供了调用者发送信号的相关接口:

        raise(3)

                向调用线程发送一个信号

        kill(2)

                向指定进程、指定进程组下的所有进程、系统的所有进程发送信号

        pidfd_send_signal(2)

                向 PID 文件描述符指定的进程发送一个信号

        killpg(3)

                向指定进程组所有成员发送一个信号

        pthread_kill(3)

                向调用者所在进程中其他 pthread 线程发送一个信号

        tgkill(2)

                向指定进程中的特定线程发送一个信号(这是用来实现 pthread_kill(3) 的系统调用)

        sigqueue(3)

                向指定进程发送一个福袋数据的实时信号

        等待捕捉信号

        下面系统调用会暂停当前调用线程直至捕捉到一个信号(或者无法处理的信号终止该进程):

        pause(2)

                暂停直至捕捉到一个信号

        sigsuspend(2)

                临时修改信号屏蔽值并暂停执行直至捕捉到任意未屏蔽的信号

        同步接收信号

        除了通过信号处理函数异步处理信号,我们还可以通过同步接收信号,也就是说程序会阻塞运行直至接收到信号,同时也是在这一点内核会返回调用者一些关于信号的信息。通常有两种方式实现同步信号接收:

  • sigwaitinfo(2)、sigtimedwait(2)、sigwait(3) 都会阻塞执行直到收到指定集合里的信号发生,每个调用都会返回接收到信号的信息。
  • signalfd(2) 会返回一个用于读取关于接收信号的文件描述符,每次对文件描述符的 read(2)调用都会一直阻塞直到通过 signalfd(2) 指定的信号集合里的任何一个信号发生。read(2) 返回的 buffer 里包含关于该信号的描述结构。

        信号屏蔽和等待信号

        一个信号是可以被屏蔽的,也就是说只有在打开屏蔽后才会得到分发。其实我们上面说的信号发生更确切的说是信号分发到了进程。那么信号在生成和被分发之前的状态我们成为等待(pending)。

        一个进程中的每个线程都有一个独立的信号屏蔽(signal mask),或者也可以称为掩码,它只是了线程当前屏蔽掉的信号集合。线程可以通过 pthread_sigmask(3) 来操作信号掩码,在传统的单线程应用中可以通过 sigprocmask(2) 来操作信号掩码。

        通过 fork(2) 创建的子进程复制一份父进程的信号掩码,信号掩码在整个 execve(2) 过程中会一直保留。

        一个信号可以是发给进程的也可以是发给线程的。发给进程的信号通常是由内核产生的而不是由特定硬件异常产生的,通常是由类似 kill(2) 或者 sigqueue(3) 产生的。而发给线程的信号可能是因为执行了一些特定机器语言指令导致硬件异常(比如非法内存访问的 SIGSEGV 信号,或者数学错误的 SIGFPE)导致的,或者是线程执行了特定接口,比如 tgkill(2) 或者 pthread_kill(3)。

        发给进程的信号是可以被分发到进程内任何没有屏蔽整个信号的线程的。如果不止一个线程没有屏蔽该信号,那么内核会随便选一个线程分发该信号。

        线程可以通过 sigpending(2) 接口来获得当前处于等待状态的信号集,信号集是一个进程等待信号和线程等待信号的共用体。

        通过 fork(2) 创建的进程初始化为控的等待信号集,在整个 execve(2) 过程中,该等待信号集保持不变。

        信号处理函数执行

        在每次内核模式到用户模式的切换时(也就是一个系统调用返回或者调用一个线程到 CPU 上运行),内核都会检查该进程建立信号处理函数的信号的非屏蔽信号的等待状态。如果有这样的信号存在,那么就会进行以下几步:

        (1)内核会在执行信号处理函数前做一些准备工作:

                (1.1)信号从等待信号集合中移除

                (1.2)如果信号处理函数是通过 sigaction(2) 安装并指定了 SA_ONSTACK 标记,集线程定义了一个候补信号栈(使用 sigaltstack(2)),那么内核就会安装这个栈

                (1.3)信号相关的一些上下文片段会被封装到栈上的特定帧里,保存的信息包括:

                           a)程序计数寄存(也就是处理函数退出后主程序要执行的下一条指令)

                           b)一些为恢复被打断程序运行的架构相关的寄存器

                           c)线程当前的信号掩码

                           d)线程的备用信号栈设置

                          (如果信号是通过 sigaction(2) SA_SIGINFO 标记安装的,那么上述信息是可以通过 ucontex_t 对象获取的,这个对象由信号处理函数的第三个参数指定。

        (2)内核在栈上为信号处理函数构建一个栈帧,内核设置线程的 PC 为信号处理函数的第一条指令,并配置信号处理函数的返回地址到一个称为信号蹦床的用户空间代码片段(在 sigreturn(2) 中有描述)。

        (3)内核将控制权返还给用户空间,这样就会从信号处理函数的开始执行。

        (4)当信号处理函数返回时,控制器交给信号蹦床代码。

        (5)信号蹦床调用 sigreturn(2),sigreturn(2) 是一个系统调用,用来使用步骤 1 中创建的栈帧来恢复信号发生之前的线程状态。信号掩码和备用信号栈也会在这个过程中得到恢复。在完成 sigreturn(2) 调用后,内核将控制权交给用户空间,线程会接着从被信号打断的点继续执行。

        值得注意的是,如果信号处理函数不返回(比如通过 siglongjmp(3) 将控制权交给了处理函数外或者处理函数通过 execve(2) 执行了一个程序),那么最后的步骤就不会执行。这种情况下,程序设计者应该特别注意,如果想要恢复在进入信号处理函数时屏蔽掉的信号的话,就必须自己恢复这些信号掩码(使用 sigprocmask(2))。(注意:siglongjmp(3) 可能恢复信号掩码,但是这个要依赖于 sigsetjmp(3) 的 savesigs 值。

        从内核的角度来看,执行信号处理函数和执行其他任何用户空间代码没有什么区别,也就是说内核并不会记录线程当前是否在执行信号处理函数。所有的状态都被保存到了用户空间寄存器和用户空间栈里了,所以信号处理函数的嵌套深度完全取决于用户栈大小(以及合理的软件设计)。

        标准信号

        Linux 支持的标准信号入下表所示。表中第二列标明了信号出现的规范:“P1990” 表示信号是在原先的 POSIX.1-1990 标准中描述的;“P2001” 表示信号是在 SUSv2 和 POSIX.1-2001 中添加的。

信号标准行为备注
SIGABRTP1990Coreabort(3) 的终止信号
SIGALRMP1990Termalarm(2) 的定时器信号
SIGBUSP2001Core总线错误(错误内存访问)
SIGCHLDP1990Ign子进程停止或者终止了
SIGCLD-Ign同 SIGCHLD
SIGCONTP1990Cont如果停止了,就继续执行
SIGEMT-Term模拟器陷入
SIGFPEP1990Core浮点异常
SIGHUPP1990Term控制终端上检测到挂机或者控制进程死掉了
SIGILLP1990Core非法指令
SIGINFO-同 SIGPWR
SIGINTP1990Term键盘中断
SIGIO-TermI/O 现在可用(4.2 BSD)
SIGIOT-CoreIOT 陷入,同 SIGABRT
SIGKILLP1990Term杀死信号
SIGLOST-Term文件锁丢失(不用了)
SIGPIPEP1990Term破坏的管道:写一个没有读者的管道,参考 pipe(7)
SIGPOLLP2001Term可查询事件(Sys V),同 SIGIO
SIGPROFP2001Term分析定时器超时
SIGPWR-Term电源失效(System V)
SIGQUITP1990Core键盘上的退出
SIGSEGVP1990Core无效的内存引用
SIGSTKFLT-Term协处理器栈错误(不用了)
SIGSTOPP1990Stop停止进程
SIGTSTPP1990Stop终端输入的停止信号
SIGSYSP2001Core非法的系统调用(SVr4),参考 seccomp()
SIGTERMP1990Term终止信号
SIGTRAPP2001Core追踪/断电 陷入
SIGTTINP1990Stop后台进程终端输入
SIGTTOUP1990Stop后台进程终端输出
SIGUNUSED-Core同 SIGSYS
SIGURGP2001Ign套接字上的紧急事件(4.2 BSD)
SIGUSR1P1990Term用户定义的信号 1
SIGUSR2P1990Term用户定义的信号 2
SIGVTALRMP2001Term虚拟定时器时钟(4.2 BSD)
SIGXCPUP2001Core超过 CPU 时间限制,参考 setrlimit(2)
SIGXFSZP2001

Core

文件大小超过限制(4.2 BSD),参考 setrlimit(2)
SIGWINCH-Ign窗口大小重设信号(4.3 BSD,Sun)

       SIGKILL 和 SIGSTOP 不能被捕捉或者忽略。

        Linux 2.2 前,SIGSYS/SIGXCPU/SIGXFSZ(除了 SPARC 和 MIPS 架构)以及 SIGBUS  的默认行为是终止进程(并没有内核转储)。(在一些 UNIX 的系统上,SIGXCPU 和 SIGXFSZ 的默认行为也是不转储的终止。)Linux 2.4 遵循 POSIX.1-2001 对这些信号的要求,会终止进程并进行内核转储。

        SIGEMT 并没有在 POSIX.1-2001 中描述,但是至少出现在了大多数 UNIX 系统中,并且其默认行为是终止进程并内核转储。

        SIGPWR 也没有在 POSIX.1-2001 中描述,它在起源的一些 UNIX 系统中的默认行为是忽略。

        SIGIO 也一样,在一些 UNIX 系统上的默认行为是忽略。

标准信号的排队和分发语义

        如果多个标准信号正在等待某个进程,那么信号的顺序并没有指定。

        标准信号没有排队,如果一个标准信号的多个实例在信号屏蔽期间到达,那么只有一个信号会被设置为等待状态(信号只能在其取消屏蔽后会分发给进程)。如果一个信号已经在等待,和这个信号关联的 siginfo_t 结构(参考 sigaction(2))并不会被后面的同一信号的其他实例覆盖。因此,进程会接收到第一个达到的信号实例。

标准信号编码

        下表列出了各个标准信号的号码。在表中我们可以看出,一些信号在不同架构上的号码是不一样的。- 表示对应架构上没有该信号。

信号x86/ARM 以及其他大多数Alpha/SPARCMIPSPARISC备注
SIGHUP1111
SIGINT2222
SIGQUIT3333
SIGILL4444
SIGTRAP5555
SIGABRT6666
SIGIOT66        66
SIGBUS7101010
SIGEMT-77-
SIGFPE8889
SIGKILL9999
SIGUSR110301616
SIGSEGV11111111
SIGUSR212311717
SIGPIPE13131313
SIGALRM14141414
SIGTERM15151515
SIGSTKFLT16---
SIGCHLD17201818
SIGCLD--18-
SIGCONT18192526
SIGSTOP19172324
SIGTSTP20182425
SIGTTIN21212627
SIGTTOU22222728
SIGURG23162129
SIGXCPU24243012
SIGXFSZ25253130
SIGVTVLRM26262820
SIGPROF27272921
SIGWINCH28282023
SIGIO29232222
SIGPOLL
SIGPWR3029/-1919
SIGINFO-29/---
SIGLOST----
SIGSYS31121231
SIGUNUSED31--31

        注意:

  •         如果有定义,那么 SIGUNUSED 和 SIGSYS 含义相同。但是 glibc 2.26 后,不管是什么架构上都没有 SIGUNUSED 定义了。
  • Alpha 上 29 号信号是 SIGINFO/SIGPWR(含义相同),而在 SPARC 上是 SIGLOST 信号。

实时信号

        从 Linux 2.2 开始,Linux 开始支持 POSIX.1b 实时扩展定义的实时信号(目前包含在 POSIX.1-2001 中)。支持的实时信号范围由宏 SIGRTMIN 和 SIGRTMAX 定义,POSIX.1-2001 要求至少支持 _POSIX_RTSIG_MAX(8) 个实时信号。

        Linux 内核支持 33 个不同的实时信号,号码是从 32 到 64。然而,glibc 的 POSIX 线程使用了 2个(对于 NPTL)或者 3 个(对于 LinuxThreads)实时信号(参考 pthreads(7)),所以调整 SIGRTMIN 为 34 或者 35。正因为实时信号的范围随着 glibc 多线程的实现不同而不同(这个在运行时会根据内核和 glibc 而定),并且事实上也会根据不同 UNIX 系统不同而不同,所以程序不应该使用硬编码值指定实时信号,而应该使用 SIGMIN+n 这种形式来引用实时信号,并且需要在运行时检查其是否超过了 SIGRTMAX。

        和标准信号不同,实时信号没有预定义的含义,即整个实时信号集都可以被应用使用。

        实时信号的默认处理行为是终止接收进程。

        实时信号通过以下方式来区分:

  • 实时信号的多个实例可以排队,这个和标准信号完全相反
  • 如果信号由 sigqueque(3) 发送,可以附带一个这个数值(可以是整数或者指针)。接收进程通过sigaction(2) 的 SA_SIGINFO 来建立该信号的处理函数,然后就可以通过处理函数第二个参数的 siginfo_t 结构的 si_value 来获得这个数值。并且可以通过结构的 si_pid 和 si_uid 可以获得发送信号进程的进程 ID 和用户 ID。
  • 实时信号的分发的顺序是能够得到保证的。同一个类型实时信号的多个实例会按照它们实际的发送顺序分发。多个信号发送到同一个进程,那么会从号码最小的信号依次分发(即号码越小,优先级越高。)相反,多个标准信号处于等待状态,先分发谁是没有定义的。

        如果一个进程有标准信号和实时信号一起处在等待状态,那么 POSIX 并没有指定应该先发送谁。Linux 以及很多其他实现会优先标准信号。

        根据 POSIX 规定,系统实现应该最少支持 _POSIX_SIGQUEQUE_MAX (32) 个实时信号等待,然而 Linux 却采用了不同的方式。Linux 2.6.7 之前,只暴漏了对所有进程总共实时信号数的系统级限制,这个限制可以通过(需要特权)/proc/sys/kernel/rtsig-max 文件查看或者修改。可以通过 /proce/sys/kernel/rtsig-nr 查看当前有多少个实时信号正在等待处理。在 Linux 2.6.8,/proc 接口被 RLIMIT_SIGPENDING 资源限制取代,这是一个每个用户下排队信号的限制,可以参考 setrlimit(2)。

        实时信号要求信号集结构(sigset_t)的长度从 32位变为 64 位。所以各个系统调用被新的系统调用取代,下面是接口的对照:


       Linux 2.0 and earlier   Linux 2.2 and later
       sigaction(2)            rt_sigaction(2)
       sigpending(2)           rt_sigpending(2)
       sigprocmask(2)          rt_sigprocmask(2)
       sigreturn(2)            rt_sigreturn(2)
       sigsuspend(2)           rt_sigsuspend(2)
       sigtimedwait(2)         rt_sigtimedwait(2)

 信号处理函数打断系统调用和库函数调用

        如果在系统调用或者函数调用过程中调用信号处理函数,那么:

  • 系统调用会在信号处理函数返回后自动重启,或者
  • 调用失败并返回 EINTR

        至于采用哪种行为主要取决于具体的接口以及建立信号处理函数时是否使用了 SA_RESTART 标记(参考 sigaction(2))。具体行为会随着不同的 UNIX 而不同,下面主要描述 Linux 平台上的相关细节。

        如果如下接口中的一个阻塞调用被信号处理函数自动打断,那么如果使用了 SA_RESTART 标记,那么调用会在处理函数返回后自动重启,或者会返回 EINTR 错误:

  • 慢速设备上的 read(2)/readv(2)/write(2)/writev(2)/ioctl(2) 调用。慢速设备指的是 I/O 调用可能会无限期阻塞的设备,比如终端、管道以及套接字。如果这些慢速设备上的 I/O 调用在被打断时已经传输了一些数据,那么调用会返回成功状态(通常情况下时传输的字节数)。注意根据这个定义本地磁盘不是慢速设备,磁盘上的 I/O 操作不会被信号打断。
  • open(2),如果它可能阻塞(比如当打开一个 FIFO,参考 fifo(7))
  • wait(2)/wait3(2)/wait4(2)/waitid(2)/waipid(2)
  • 套接字接口:accept(2)/connect(2)/recv(2)/recvfrom(2)/recvmmsg(2)/recvmsg(2)/send(2)/sendto(2)/sendmsg(2),除非套接字设置了超时(下面会提到)
  • 文件锁定接口:flock(2) 以及 fcntl(2) 的 F_SETLKW/F_OFD_SETLKW 操作
  • POSIX 消息队列接口:mq_receive(3)/mq_timedreceive(3)/mq_send(3)/mq_timedsend(3)
  • futex(2) FUTEX_WAIT(Linux 2.6.22 后,之前总是会返回 EINTR)
  • getrandom(2)
  • pthread_mutex_lock(3)/pthread_cond_wait 以及相关 APIs
  • futex(2) FUTEX_WAIT_BITSET
  • POSIX 信号量语义接口:wem_wait(3)/sem_timedwait(3)(Linux 2.6.22 后,之前会失败返回 EINTR)
  • 从 inotify(7) 文件描述符的 read(2)(从 Linux 3.8 后,之前会返回 EINTR)

        下面接口在被信号处理函数打断时不会重启,无论是否设置 SA_RESTART 标记,会返回 EINTR 错误:

  • 使用 setsockopt(2) 设置了超时(SO_RCVTIMEO)的输入套接字接口:accept(2)/recv(2)/recvfrom(2)/recvmmsg(2)(同时有非空 timeout 参数,以及 recvmsg(2)
  • 使用 setsockopt(2) 设置了超时(SO_RCVTIMEO)的输出套接字接口:connect(2)/send(2)/sendto(2)/sendmsg(2)
  • 等待信号的接口:pause(2)/sigsuspend(2)/sigtimedwait(2)/sigwaitinfo(2)
  • 文件描述符多路复用接口:epoll_wait(2)/epoll_pwait(2)/poll(2)/select(2)/pselect(2)
  • System V IPC 接口:msgrcv(2)/msgsnd(2)/semop(2)/semtimedop(2)
  • 睡眠接口:clock_nanosleep(2)/nanosleep(2)/usleep(3)
  • io_getevents(2)

        usleep(3) 在被信号处理函数打断时不会重启,会成功返回睡眠剩余时间的秒数。

        在一些情况下,sseccomp(2) 用户空间通知特性会导致通过 SA_RESTART 配置不会重启的系统调用重启,具体可以参考 seccomp_unotify(2)。

停止信号打断系统调用和库函数调用

        在 Linux 上,即使没有信号处理函数,一些阻塞接口也可能在收到停止信号停止在收到 SIGCONT 信号恢复时返回  EINTR 错误。这个行为并没有受到 POSIX.1 的限制,并不会在其他系统上发生。

        Linux 上会显示出这种行为的接口有:

  • 使用 setsockopt(2) 设置了超时(SO_RCVTIMEO)的输入套接字接口:accept(2)/recv(2)/recvfrom(2)/recvmmsg(2)(同时有非空 timeout 参数,以及 recvmsg(2)
  • 使用 setsockopt(2) 设置了超时(SO_RCVTIMEO)的输出套接字接口:connect(2)/send(2)/sendto(2)/sendmsg(2)
  • epoll_wait(2)/epoll_pwait(2)
  • semop(2)/semtimedop(2)
  • sigtimedwait(2)/gitwaitinfo(2)
  • Linux 3.7 前从 inotify(7) 文件描述符读取的 read(2)
  • Linux 2.6.21 前 futex(2) FUTEX_WAIT/sem_timedwait(3)/sem_wait(3)
  • Linux 2.6.8 前 msgrcv(2)/msgsnd(2)
  • Linux 2.4 前 nanosleep(2)

2.注意

        对于安全的异步信号函数,可以参考 sigal-safety(7)。

        /proc/pid/task/tid/status 文件包含了一些用于显示一个线程正处于阻塞(SigBlk)、捕获(SigCgt)或者忽略(SigIgn)的字段。(进程中所有线程的阻塞和忽略信号相同。)其他字段展示了发给线程(SigPnd)或者进程(ShdPnd)的等待信号。/proc/pid/status 展示了关于主线程的信息。参考 proc(5) 获得详细信息。

3.BUGS

        一共有 6 中关于硬件异常的信号:SIGBUS/SIGEMT/SIGFPE/SIGILL/SIGSEGV/SIGTRAP。至于这些信号哪些会真的分发,文档中并没有给出描述,并且也并不总是有效。

        比如,一个架构非法地址访问可能导致 SIGSEGV,但是在其他架构上可能会导致 SIGBUS,反过来也是一样。

        再比如,使用 x86 的 int 指令携带了禁止参数(除了 3 和 128 的任意数)会导致 SIGSEGV,尽管这时发送 SIGILL 会更合适,因为它是在向内核报告禁止操作。   

4.例程

        下面是一个 sigaction 的信号处理例程。

       #include <signal.h>
       #include <stdio.h>
       #include <stdlib.h>
       #include <unistd.h>
 
       void
       handler(int signo, siginfo_t *info, void *context)
       {
           struct sigaction oldact;
 
           if (sigaction(SIGSEGV, NULL, &oldact) == -1
               || (oldact.sa_flags & SA_UNSUPPORTED)
               || !(oldact.sa_flags & SA_EXPOSE_TAGBITS))
           {
               _exit(EXIT_FAILURE);
           }
           _exit(EXIT_SUCCESS);
       }
 
       int
       main(void)
       {
           struct sigaction act = { 0 };
 
           act.sa_flags = SA_SIGINFO | SA_UNSUPPORTED | SA_EXPOSE_TAGBITS;
           act.sa_sigaction = &handler;
           if (sigaction(SIGSEGV, &act, NULL) == -1) {
               perror("sigaction");
               exit(EXIT_FAILURE);
           }
 
           raise(SIGSEGV);
       }


http://www.niftyadmin.cn/n/5044141.html

相关文章

【漏洞复现】企望制造 ERP命令执行

漏洞描述 由于企望制造 ERP comboxstore.action接口权限设置不当&#xff0c;默认的配置可执行任意SQL语句&#xff0c;利用xp_cmdshell函数可远程执行命令&#xff0c;未经认证的攻击者可通过该漏洞获取服务器权限。 免责声明 技术文章仅供参考&#xff0c;任何个人和组织…

虹科方案 | LIN/CAN总线汽车零部件测试方案

文章目录 摘要一、汽车零部件测试的重要性&#xff1f;二、虹科的测试仿真工具如何在汽车零部件测试展露头角&#xff1f;三、应用场景**应用场景1&#xff1a;方向盘开关的功能测试****应用场景2&#xff1a;各类型电机的控制测试****应用场景3&#xff1a;RGB氛围灯的功能测试…

Java里面int、Integer、String相互转换

由于这几个东西转来转去老是搞混了&#xff0c;所以我特意做了一个这三者之间互转的总结&#xff0c;代码如下&#xff1a; public class MyWork {public static void main(String[] args) {String str "12345";Integer integer 123456;int i 123456;//int转Stri…

电路的基本定律——基尔霍夫定律

基尔霍夫定律 &#x1f391;预备知识&#x1f391;基尔霍夫电流定律(KCL)&#x1f383;基尔霍夫电流定律的本质&#xff1a;节点上电荷具有连续性(不会突变)&#x1f383;基尔霍夫电流定律的推广&#xff1a; &#x1f391;基尔霍夫的电压定律(KVL)&#x1f383;基尔霍夫电压定…

【02】FISCOBCOS搭建区块链网络

官方文档https://fisco-bcos-documentation.readthedocs.io/zh_CN/latest/docs/installation.html 第一步. 安装依赖 开发部署工具 build_chain.sh脚本依赖于openssl, curl&#xff0c;根据您使用的操作系统&#xff0c;使用以下命令安装依赖。 sudo apt install -y openssl …

android13(T) SystemUI 运营商显示 bug 修复

aosp 本身 bug&#xff0c;开启状态栏显示运营商时&#xff0c;会有 npe 问题 frameworks/base/packages/SystemUI/src/com/android/systemui/util/CarrierConfigTracker.java -213,6 213,10 public class CarrierConfigTracker* param subId the subscription id for which …

打开网站显示“不安全”怎么办?

在互联网世界中&#xff0c;安全是一个至关重要的问题。然而&#xff0c;当您尝试访问某些网站时&#xff0c;可能会看到“不安全”的警告。这通常是因为这些网站没有部署SSL证书。SSL证书是一种数字证书&#xff0c;可确保互联网通信的安全性和保密性。 “打开网站显示不安全”…

FL Studio21水果编曲软件怎么下载中文版?

FL Studio21这款软件在国内被广泛使用&#xff0c;因此又被称为"水果"。它提供音符编辑器&#xff0c;可以针对作曲者的要求编辑出不同音律的节奏&#xff0c;例如鼓、镲、锣、钢琴、笛、大提琴、筝、扬琴等等任何乐器的节奏律动。此外&#xff0c;它还提供了方便快捷…