Linux操作系統之信號:保存與處理信號

目錄

前言:

前文回顧與補充:

信號的保存

PCB里的信號保存?

sigset_t

信號集操作函數

信號的處理

信號捕捉的流程:?編輯

操作系統的運行原理

硬件中斷

時鐘中斷

死循環

軟中斷?

總結:


前言:

在上一篇文章,我已經為大家詳細的闡述了信號產生的物種方式,相信大家對于信號已經有了較為深刻的印象與理解了。

那么今天我們就來繼續談論信號的另外兩個重要話題:信號的保存與捕捉。

前文回顧與補充:

我們之前說,?捕捉信號一共有三種方式,分別是:默認,忽略,與自定義。

在使用signal時,有兩個宏分別代表默認與忽略的處理:

#include <iostream>
#include <unistd.h>
#include <signal.h>int main()
{::signal(2,SIG_IGN);//ignore 忽略:本身就是一種處理方法,什么也不用做直接忽略掉::signal(2,SIG_DFL);//default:執行該信號默認的處理方法return 0;
}

我們之前說的所有信號的產生,最后都要由OS執行,這是為什么呢?

:因為OS是進程的管理者

信號的處理是否會被立即處理?

:不會,會在合適的時候進行處理

一個信號如果不是被立即處理,那么信號是否需要暫時被進程記錄下來?記錄在哪里最合適?

:需要,會被記錄在進程的PCB中

?個進程在沒有收到信號的時候,能否能知道,??應該對合法信號作何處理呢?
:進程的處理方法一早就被設置好了
而進程時如何被存儲到PCB中的呢?只是我們上節課說的位圖結構嗎?
這就要涉及我們今天的內容:信號的保存了!!

我們先補充一點信號的其他知識:

實際執行信號的處理動作被稱為信號遞達(Delivery)

信號從產生到遞達之間的狀態,被稱為信號未決(Pending)

進程可以選擇阻塞(block)某個信號

被阻塞的信號產生時將保持在未決狀態,直到進程解除對此信號的阻塞,才會執行遞達的動作。

這里要注意,阻塞與忽略是不同的,只要信號被阻塞就不會遞達,而忽略時在遞達之后我們選擇的一種處理信號的動作


信號的保存

PCB里的信號保存?

我們剛剛說,由于信號的處理不是立即處理,所以我們需要保存好進程,就保存在進程的PCB中,我們先來看一下具體保存信號的結構:

這個pending就是我們之前說的信號位圖,保存這個進程是否收到了相應的信號。

這個blcok也是一個位圖,保存的是對應位置的信號是否被我們阻塞/屏蔽了。

而這個handler,則是一個函數指針數組,里面存儲的就是對應信號的默認處理方法。我們信號的編號-1,就是對應的數組下標。

這也就是為什么我們只需要signal一次,就能永久改變處理方法的原因:因為我們是直接把方法拷貝替換成了自己的處理函數。

每個信號都有兩個標志位分別表?阻塞(block)和未決(pending),還有?個函數指針表?處理動
作。信號產生時,內核在進程控制塊中設置該信號的未決標志,直到信號遞達才清除該標志。在上
圖的例?中,SIGHUP信號未阻塞也未產?過,當它遞達時執?默認處理動作。
SIGINT信號產?過,但正在被阻塞,所以暫時不能遞達。雖然它的處理動作是忽略,但在沒有解除阻
塞之前不能忽略這個信號,因為進程仍有機會改變處理動作之后再解除阻塞。
SIGQUIT信號未產生過,?旦產生SIGQUIT信號將被阻塞,它的處理動作用戶自定義函數sighandler。??

sigset_t

從上圖來看,每個信號只有?個bit的未決標志, ?0即1, 不記錄該信號產生了多少次,阻塞標志也是這樣表示的。因此, 未決和阻塞標志可以用相同的數據類型sigset_t來存儲,這個sigset_t稱為信號集?, 這個類型可以表示每個信號的“有效”或“無效”狀態,。
在阻塞信號集中“有效”和“無效”的含義是該信號是否被阻塞, 而在未決信號集中“有效”和“無效”的含義是該信號是否處于未決狀態。阻塞信號集也叫做當前進程的 信號屏蔽字(Signal Mask),這里的“屏蔽”應該理解為阻塞而不是忽略。

信號集操作函數

由于保管信號的是位圖結構,我們不好每次都進行位操作來修改位圖,所以有對應的系統調用:

函數sigemptyset初始化set所指向的信號集,使其中所有信號的對應bit清零,表示該信號集不包含
任何有效信號。一般我們用這個是為了給自己定義的sigset_t的變量初始化。
函數sigfillset初始化set所指向的信號集,使其中所有信號的對應bit置位,表示該信號集的有效信號
包括系統?持的所有信號。
注意,在使?sigset_tt類型的變量之前,?定要調用sigemptyset或sigfillset做初始化,使信號集處于
確定的狀態。初始化sigset_t變量之后就可以在調?sigaddset和sigdelset在該信號集中添加或刪
除某種有效信號。
以上四個函數都是成功返回0,出錯返回-1。sigismember是?個布爾函數,?于判斷?個信號集的有效信號中是否包含某種信號,若包含則返回1,不包含則返回0,出錯返回-1。
此外,還有一個系統調用,我們專門用來讀取或更改進程的信號屏蔽字(阻塞信號集)。
如果oldset是?空指針,則讀取進程的當前信號屏蔽字通過oldset參數傳出。如果set是非空指針,則 更改進程的信號屏蔽字,參數how指示如何更改。如果oldset和set都是?空指針,則先將原來的信號 屏蔽字備份到oldset?,然后 根據set和how參數更改信號屏蔽字。假設當前的信號屏蔽字為mask,下表說明了 how參數的可選值。

?

如果我們想要恢復成老的數據,該怎么辦?

所以有輸出型參數oldset供我們使用。

如果調?sigprocmask解除了對當前若?個未決信號的阻塞,則在sigprocmask返回前,?少將其中?
個信號遞達。
還有一個系統調用:sigpending
我們通過傳遞一個參數set,可以獲取當前進程的未決信號集。
以上幾個方法,就實現了我們對位圖結構的簡單的增刪查改。
我們可以寫一段代碼來加深理解:

?

#include <iostream>
#include <string>
#include <functional>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>int main()
{//初始化,把指定的位圖全部清0sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);//設置信號,此時我們有沒有把對2號信號的屏蔽,設置進入內核中?:只是在用戶棧上設置了block的位圖結構// 并沒有設置進入內核中!sigaddset(&block,2);//把我們對2號信號的屏蔽,設置進入內核中sigprocmask(SIG_SETMASK,&block,&oblock);while(true){//獲取該進程的pending表并打印sigset_t pending;sigpending(&pending);std::cout<<getpid()<<":";for(int i=31;i>=0;i--){if(sigismember(&pending,i))//挨個挨個檢查是否存在在該位圖里{std::cout<<"1";}else{std::cout<<"0";}}std::cout<<std::endl;sleep(1);}return 0;
}

運行代碼,我們打開另外一個bash給該進程發送信號

那我們此時在增加一段代碼,使得他在一定時間后自動解除屏蔽試試??

#include <iostream>
#include <string>
#include <functional>
#include <vector>
#include <unistd.h>
#include <stdlib.h>
#include <signal.h>
#include <sys/wait.h>void handler(int signo)
{std::cout<<"我已經解除屏蔽"<<signo<<std::endl;
}int main()
{signal(2,handler);//初始化,把指定的位圖全部清0sigset_t block,oblock;sigemptyset(&block);sigemptyset(&oblock);//設置信號,此時我們有沒有把對2號信號的屏蔽,設置進入內核中?:只是在用戶棧上設置了block的位圖結構// 并沒有設置進入內核中!sigaddset(&block,2);//把我們對2號信號的屏蔽,設置進入內核中sigprocmask(SIG_SETMASK,&block,&oblock);int count=0;while(true){//獲取該進程的pending表并打印sigset_t pending;sigpending(&pending);std::cout<<getpid()<<":";for(int i=31;i>0;i--){if(sigismember(&pending,i))//挨個挨個檢查是否存在在該位圖里{std::cout<<"1";}else{std::cout<<"0";}}std::cout<<std::endl;count++;if(count>=10){sigprocmask(SIG_SETMASK, &oblock, nullptr);//解除屏蔽}sleep(1);}return 0;
}

?運行代碼,可以看見

?


信號的處理

?我們可以先說出結論,之前所說的合適的時候,指的就是,進程在從內核態切換為用戶態時,會檢測當前進程的pending與blcok,根據這兩個位圖是決定是否執行處理方法handler。

這里引出了兩個新概念,用戶態與內核態。

大家不用急,且聽我慢慢道來:

信號捕捉的流程:

信號處理函數的代碼是在??空間的,處理過程還是比較復雜的,我們可以舉例如下:?

用戶程序注冊了SIGQUIT 信號的處理函數 sighandler

當前正在執行main函數,這個時候會發生中斷或者異常切換到內核態(為什么一定會發生中斷我后面會解釋)

在中斷處理完畢后,要返回用戶態的main函數前會檢查到有信號SIGQUIT 遞達。

內核決定返回用戶態后不是恢復main函數的上下文繼續執行代碼,而是執行sighhandler函數(此時sighandler 和 main 函數使?不同的堆棧空間,它們之間不存在調?和被調?的關系,是兩個獨?的控制流程

sighandler 函數返回后自動執?特殊的系統調? sigreturn 再次進?內核態。
如果沒有新的信號要遞達,這次再返回??態就是恢復 main 函數的上下?繼續執?了。
所以總的過程可以總結為上圖的一個無窮的形狀,整個過程一共會進行四次內核態與用戶態的切換。
所以我們可以先簡單說明:執行自己的代碼是用戶態,執行系統調用是內核態。

操作系統的運行原理

我們這里不得不插一句嘴說一下操作系統的運行原理,只有這樣才能讓大家理解內核態與用戶態更加深入。

這一內容還是很重要的,我們會填上之前說的很多坑。

硬件中斷

我們之前在鍵盤產生信號時一直在說硬件中斷,這個到底是怎么一回事呢?
?

當我們的硬件準備就緒時,就會開始中斷,每一個硬件都有自己的一個中斷號,并且進程會通過高電壓的形式通知CPU,我已經準備就緒了。當我們的CPU知道硬件中斷后,會去獲取這個硬件對應的中斷號。

此時,CPU會保護現場,包括存儲此時運行進程代碼數據的CPU的數據,保存在PCB中(之前講頁表時我們提到過保護現場這個現象)?。

在這之后,會根據中斷號來進行處理的方法,即:


這個中斷向量表IDT,本質是還是一個函數指針數組,每一個硬件對應的中斷號,就對應了下標。

所以每一個硬件產生中斷后,他的處理方法我們一開始就知道了,該如何處理。

這里的中斷處理例程,一共有四步:
1、保存現場

2、根據中斷號,查中斷向量表

3、調用對應的處理方法

4、恢復現場

而這個中斷向量表,是操作系統的?部分,啟動就加載到內存中了。

通過外部硬件中斷,操作系統就不需要對外設進?任何周期性的檢測或者輪詢

由外部設備觸發的,中斷系統運?流程,叫做硬件中斷

?


時鐘中斷

但是我們還是有一個疑問,進程可以在操作系統的指揮下,被調度,被執?,那么操作系統??被誰指揮,被誰推動執?呢?

外部設備可以觸發硬件中斷,但是這個是需要??或者設備??觸發,有沒有??可以定期觸發的 設備?

這里就要引出我們時鐘中斷的概念了

我們規定了一個時間,固定的觸發硬件中斷,這個中斷,被我們稱為時鐘中斷,負責定期幫我們實現中斷!!

而這個中斷在中斷向量表的處理方法只有一個,那就是去調度進程!!

注意,調度進程不代表一定要進行進程切換。

還記得時間片這個概念嗎?

其實就是一個整數count。

我們初始規定count=1000;

那么每一次進行調度,就回讓count--,當count為0時,就會進行進程的切換。

所以我們還有主頻這個概念,就是指 CPU 內部時鐘信號的工作頻率,通常以?赫茲(Hz)?為單位。你的CPU主頻越高,價格越貴,同一個時間進行進程調度越多,性能越好。

這樣,操作系統不就在硬件(時鐘中斷)的推動下,自動進行調度了么!!!
所以操作系統,就是基于中斷向量表進行工作的。

死循環

如果是這樣,操作系統不就可以躺平了嗎?
對,操作系統??不做任何事情,需要什么功能,就向中斷向量表??添加?法即可.操作系統的本質:就是?個死循環!
void main(void) /* 這?確實是void,并沒錯。 */
{ ????????/* startup 程序(head.s)中就是這樣假設的。 */
????????...
????????/*
????????* 注意!! 對于任何其它的任務,'pause()'將意味著我們必須等待收到?個信號才會返
????????* 回就緒運?態,但任務0task0)是唯?的意外情況(參?'schedule()'),因為任
????????* 務0 在任何空閑時間?都會被激活(當沒有其它任務在運?時),
????????* 因此對于任務0'pause()'僅意味著我們返回來查看是否有其它任務可以運?,如果沒
????????* 有的話我們就回到這?,?直循環執?'pause()'
????????*/
????????for (;;)
????????pause();
} // end main

軟中斷?

上述外部硬件中斷,需要硬件設備觸發。
有沒有可能,因為軟件原因,也觸發上?的邏輯,我們不是說過軟件中斷嗎?所以自然有!
為了讓操作系統?持進?系統調用,CPU也設計了對應的匯編指令(int 或者 syscall),可以讓CPU內
部觸發中斷邏輯。
用戶層怎么把系統調?號給操作系統?
: 寄存器(比如EAX)
操作系統怎么把返回值給用戶?
: 寄存器或者用戶傳入的緩沖區地址
系統調用的過程,其實就是先int 0x80、syscall陷?內核,本質就是觸發軟中斷,CPU就會自動執
?系統調用的處理方法,而這個?法會根據系統調?號,自動查表,執?對應的?法
所以系統調?號的本質就是數組下標!
// sys.h
// 系統調?函數指針表。?于系統調?中斷處理程序(int 0x80),作為跳轉表。
extern int sys_setup (); // 系統啟動初始化設置函數。 (kernel/blk_drv/hd.c,71)
extern int sys_exit (); // 程序退出。 (kernel/exit.c, 137)
extern int sys_fork (); // 創建進程。 (kernel/system_call.s, 208)
extern int sys_read (); // 讀?件。 (fs/read_write.c, 55)
extern int sys_write (); // 寫?件。 (fs/read_write.c, 83)
extern int sys_open (); // 打開?件。 (fs/open.c, 138)
extern int sys_close (); // 關閉?件。 (fs/open.c, 192)
extern int sys_waitpid (); // 等待進程終?。 (kernel/exit.c, 142)
extern int sys_creat (); // 創建?件。 (fs/open.c, 187)
extern int sys_link (); // 創建?個?件的硬連接。 (fs/namei.c, 721)
extern int sys_unlink (); // 刪除?個?件名(或刪除?件)。 (fs/namei.c, 663)
extern int sys_execve (); // 執?程序。 (kernel/system_call.s, 200)
extern int sys_chdir (); // 更改當前?錄。 (fs/open.c, 75)extern int sys_time (); // 取當前時間。 (kernel/sys.c, 102)extern int sys_mknod (); // 建?塊/字符特殊?件。 (fs/namei.c, 412)extern int sys_chmod (); // 修改?件屬性。 (fs/open.c, 105)extern int sys_chown (); // 修改?件宿主和所屬組。 (fs/open.c, 121)extern int sys_break (); // (-kernel/sys.c, 21)extern int sys_stat (); // 使?路徑名取?件的狀態信息。 (fs/stat.c, 36)extern int sys_lseek (); // 重新定位讀/寫?件偏移。 (fs/read_write.c, 25)extern int sys_getpid (); // 取進程id。 (kernel/sched.c, 348)extern int sys_mount (); // 安裝?件系統。 (fs/super.c, 200)extern int sys_umount (); // 卸載?件系統。 (fs/super.c, 167)extern int sys_setuid (); // 設置進程??id。 (kernel/sys.c, 143)extern int sys_getuid (); // 取進程??id。 (kernel/sched.c, 358)extern int sys_stime (); // 設置系統時間?期。 (-kernel/sys.c, 148)extern int sys_ptrace (); // 程序調試。 (-kernel/sys.c, 26)extern int sys_alarm (); // 設置報警。 (kernel/sched.c, 338)extern int sys_fstat (); // 使??件句柄取?件的狀態信息。(fs/stat.c, 47)extern int sys_pause (); // 暫停進程運?。 (kernel/sched.c, 144)extern int sys_utime (); // 改變?件的訪問和修改時間。 (fs/open.c, 24)extern int sys_stty (); // 修改終端?設置。 (-kernel/sys.c, 31)...   extern int sys_dup2 (); // 復制?件句柄。 (fs/fcntl.c, 36)extern int sys_getppid (); // 取?進程id。 (kernel/sched.c, 353)extern int sys_getpgrp (); // 取進程組id,等于getpgid(0)。(kernel/sys.c, 201)extern int sys_setsid (); // 在新會話中運?程序。 (kernel/sys.c, 206)extern int sys_sigaction (); // 改變信號處理過程。 (kernel/signal.c, 63)extern int sys_sgetmask (); // 取信號屏蔽碼。 (kernel/signal.c, 15)extern int sys_ssetmask (); // 設置信號屏蔽碼。 (kernel/signal.c, 20)extern int sys_setreuid (); // 設置真實與/或有效??id。 (kernel/sys.c,118)extern int sys_setregid (); // 設置真實與/或有效組id。 (kernel/sys.c, 51)// 系統調?函數指針表。?于系統調?中斷處理程序(int 0x80),作為跳轉表。fn_ptr sys_call_table[] = { sys_setup, sys_exit, sys_fork, sys_read,sys_write, sys_open, sys_close, sys_waitpid, sys_creat, sys_link,sys_unlink, sys_execve, sys_chdir, sys_time, sys_mknod, sys_chmod,sys_chown, sys_break, sys_stat, sys_lseek, sys_getpid, sys_mount,sys_umount, sys_setuid, sys_getuid, sys_stime, sys_ptrace, sys_alarm,sys_fstat, sys_pause, sys_utime, sys_stty, sys_gtty, sys_access,sys_nice, sys_ftime, sys_sync, sys_kill, sys_rename, sys_mkdir,sys_rmdir, sys_dup, sys_pipe, sys_times, sys_prof, sys_brk, sys_setgid,sys_getgid, sys_signal, sys_geteuid, sys_getegid, sys_acct, sys_phys,sys_lock, sys_ioctl, sys_fcntl, sys_mpx, sys_setpgid, sys_ulimit,sys_uname, sys_umask, sys_chroot, sys_ustat, sys_dup2, sys_getppid,sys_getpgrp, sys_setsid, sys_sigaction, sys_sgetmask, sys_ssetmask,sys_setreuid, sys_setregid};
所以缺?中斷?內存碎?處理?除零野指針錯誤?這些問題,全部都會被轉換成為CPU內部的軟中斷, 然后?中斷處理例程,完成所有處理。有的是進?申請內存,填充?表,進?映射的。有的是?來處理內存碎?的,有的是?來給?標進?發送信號,殺掉進程等等。
所以,操作系統就是躺在中斷處理例程上的代碼塊!
CPU內部的軟中斷,比如int 0x80或者syscall,我們叫做 陷阱,CPU內部的軟中斷,比如除零/野指針等,我們叫做 異常。(這也是“缺頁異常”為什么這么叫的原因)

總結:

由于時間關系。

今天的博客就寫到這里,明天我們將會進行最后的結尾,為大家更加具體的說一下什么是內核態與用戶態。

相信今天的知識已經把大家之前所學的內容串聯起來,大家對操作系統也有了更加深刻的理解!

明天我們將會完成信號部分的內容,并給大家講一些信號done的相關內容,之后我們將會開始線程的學習!!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/914635.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/914635.shtml
英文地址,請注明出處:http://en.pswp.cn/news/914635.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Spring Boot 設置滾動日志logback

Spring Boot 的 logback 框架 Spring Boot 默認內置了 Logback 作為日志實現框架&#xff0c;只需要在resources文件夾下添加一個logback-spring.xml&#xff0c;springboot會按照你的設置自動開啟logback日志功能。 配置 logback-spring.xml 實現每天產生一個日志文件&#xf…

如何定義一個只能在堆上或棧上生成對象的類

在C中&#xff0c;可以通過特定的技術手段來控制對象只能在堆(heap)或棧(stack)上創建。只能在堆上創建對象的類要實現這一點&#xff0c;我們需要阻止用戶直接實例化對象&#xff0c;而只能通過new操作符創建。class HeapOnly { public:static HeapOnly* create() {return new…

1.1 前端-vue3項目的創建

構建工具先搭好vue3框架 vue2的vue-cli腳手架基于webpack構建工具創建vue的框架. 而在vue3&#xff0c;可以通過vite構建工具創建vue3項目&#xff0c;性能更優。 兩者創建方式的區別&#xff1a;cmd命令基于的構建工具vue2/vue3vue create 項目名稱&#xff08;或 vue ui圖形化…

PHP password_get_info() 函數

password_get_info() 函數用于返回指定散列&#xff08;hash&#xff09;的相關信息。 PHP 版本要求: PHP 5 > 5.5.0, PHP 7 語法 array password_get_info ( string $hash ) 參數說明&#xff1a; $hash: 一個由 password_hash() 創建的散列值。 返回值 返回三個元素…

mac上的app如何自動分類

使用文件夾進行手動分類在Finder中創建文件夾&#xff0c;將同類應用拖入同一文件夾。右鍵點擊Dock上的應用圖標&#xff0c;選擇「選項」→「在Finder中顯示」&#xff0c;可快速定位應用安裝位置。利用Launchpad自動分組打開Launchpad&#xff08;觸控板四指捏合或按F4鍵&…

LLM面試題目 3

LLM面試題目 3 什么是自注意力機制(Self-Attention)?為什么它在LLM中很重要?如何評估LLM的性能?LLM面臨的挑戰有哪些?Transformer和RNN的區別是什么?LLM如何處理多輪對話? 題目講解 什么是自注意力機制(Self-Attention)?為什么它在LLM中很重要? 自注意力機制是一種…

linux上的軟掛載操作方法

針對linux上的軟掛載 可以查看linux已經掛載和存儲的磁盤分區 df -hfdisk 命令是檢索相同信息的另一種方法&#xff0c;可以看到所有的磁盤分區 sudo fdisk -l 要將磁盤分區 /dev/sda1 掛載到 /home/visionx/EXD1 目錄 步驟 1&#xff1a;準備工作 1.創建掛載目錄&#xff08;如…

SecretFlow 隱語 (2) --- 隱語架構概覽

在前邊兩篇文章中&#xff0c;介紹了數據要素和可信流通相關的內容&#xff0c;以及基于p2p模式的安裝方法 SecretFlow 隱語 (1) --- 快速入門 關于在Linux上部署 SecretFlow --- P2P部署模式 由于安裝過程中出現意外報錯&#xff0c;現已提交issue等待官方技術人員查閱&#x…

PHP語言基礎知識(超詳細)第二節

二十七. 數組的遍歷 1)通過函數進行遍歷:(例:demo07) (此方式不能完全遍歷數組,需要借助其他功能輔助)(不推薦,了解即可) key():返回數組中當前指針所在位置的鍵。 current():返回數組中當前指針所在位置的值。 例如:demo07: <?php/*key():返回數組中…

網絡--OSPF實驗

目錄 OSPF實驗報告 一、實驗拓撲 二、實驗要求 三、實驗思路 1.IP地址劃分 2. OSPF 部署 3. 其它配置 4. 驗證測試 四、實驗步驟 1.IP 地址配置 2.OSPF 部署 3.其它配置 4.驗證測試 OSPF實驗報告 一、實驗拓撲 二、實驗要求 1、R1-R3為區域0&#xff0c;R3-R4為…

Go語言第一個程序--hello world!

文章目錄一、Go 語言程序安裝二、運行程序三、go mod tidy 命令四、遇到的問題五、VS Code 調試 go 程序的相關配置說明一、Go 語言程序安裝 Go語言下載鏈接&#xff1a;https://studygolang.com/dl 雙擊打開下一步下一步即可。 驗證安裝&#xff1a;go version 二、運行程序 創…

【MCU控制 初級手札】1.1 電阻

作者&#xff1a;電控工程手札 本博文內容著作權歸作者所有&#xff0c;轉載請務必保留本文鏈接 目錄1. 定義2. 電導3. 電阻率4. 電導率5. 伏安特性6. 開路與短路7. 功率8. 應用元件特性&#xff08;端子特性&#xff09;&#xff1a;元件的兩個端子的電路物理量之間的代數函數…

JS中async/await功能介紹和使用演示

JS 中 async/await 功能介紹與使用演示 一、功能介紹基本概念 async&#xff1a;用于聲明異步函數&#xff0c;返回一個 Promise 對象。即使函數內沒有顯式返回 Promise&#xff0c;也會隱式將返回值封裝為 Promise.resolve()。await&#xff1a;僅能在 async 函數內部使用&…

系統調用入口機制:多架構對比理解(以 ARM64 為主)

&#x1f4d6; 推薦閱讀&#xff1a;《Yocto項目實戰教程:高效定制嵌入式Linux系統》 &#x1f3a5; 更多學習視頻請關注 B 站&#xff1a;嵌入式Jerry 系統調用入口機制&#xff1a;多架構對比理解&#xff08;以 ARM64 為主&#xff09; 本篇內容聚焦于系統調用的入口實現機…

java MultipartFile初始化

在Java中&#xff0c;MultipartFile 是Spring框架中用于處理文件上傳的接口。?開發者通常不會直接初始化MultipartFile對象&#xff0c;而是通過Spring MVC的控制器方法參數接收上傳的文件。如果需要在測試或模擬場景中創建其實例&#xff0c;可以使用Spring的MockMultipartFi…

Linux C IO多路復用

在上一節利用管道實現了一個簡單的聊天室&#xff0c;但這個聊天室有一個很明顯的問題就是&#xff0c;當A處于讀阻塞情況下是不能向B發送消息的&#xff0c;只有收到B的消息才能發送。如何實現同時既能接受B的消息&#xff0c;又能向其發送消息&#xff1f;很遺憾&#xff0c;…

day21——特殊文件:XML、Properties、以及日志框架

文章目錄一、特殊文件概述二、Properties屬性文件2.1 文件特點2.2 Properties類解析2.3 寫入屬性文件三、XML文件詳解3.1 XML核心特性3.2 XML解析&#xff08;Dom4J&#xff09;3.3 XML寫入3.4 XML約束&#xff08;了解&#xff09;四、日志技術&#xff08;Logback&#xff09…

經典VB與現代VB(VB.NET)

Visual Basic&#xff08;VB&#xff09;目前其發展狀態可以分為經典VB&#xff08;VB6及之前&#xff09;?和現代VB&#xff08;VB.NET&#xff09;?兩個階段。經典VB誕生于1991年&#xff0c;憑借?“快速開發&#xff08;Rapid Application Development, RAD&#xff09;”…

iOS UI視圖面試相關

iOS UI視圖面試相關 UITableVIew相關 重用機制 cell [tableView dequeueReusableCellWillIdentifier:identifer];其中A2、A3、A4、A5是完全顯示在屏幕&#xff0c;A2、A6顯示部分&#xff0c;A1和A7不在顯示范圍內&#xff0c;假如現在是從下滑時的結果&#xff0c;在A1消失時…

網絡編程-tcp連接:服務器與客戶端

使用服務器和客戶端的代碼&#xff0c;實現服務器和客戶端的互相聊天功能 實現兩臺電腦之間互相聊天 方案一&#xff1a;服務器代碼&#xff08;server.c&#xff09;#include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h>…