用於進程間通信,通信機制由操作系統保證,比較穩定。
在linux中可以通過kill -l查看所有信號的類型。
kill -信號類型 進程ID
int kill(pid_t pid, int sig);
入參pid :
pid > 0: 發送信號給指定的進程。
pid = 0: 發送信號給 與調用kill函數進程屬於同一進程組的所有進程。
pid < 0: 取|pid|發給對應進程組。
pid = -1:發送給進程有許可權發送的系統中所有進程。
sig :信號類型。
返回值 :成功:0;失敗:-1 (ID非法,信號非法,普通用戶殺init進程等權級問題),設置errno
以OpenHarmony源碼為例,應用ANR後,AbilityManagerService會通知應用mp堆棧信息,就是通過信號量做的。
頭文件位置 :
include <signal.h>
函數解釋 :
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
當接收到指定的信號signum時,就會跳轉到參數handler指定的函數執行。其中handler的入參是信號值。
函數原型 :
signum參數指出要捕獲的信號類型,act參數指定新的信號處理方式,oldact參數輸出先前信號的處理方式(如果不為NULL的話)。
sigaction結構體
sa_handler 信號處理函數
sa_mask 在處理該信號時可以暫時將sa_mask 指定的信號集擱置
sa_flags 指定一組修改信號行為的標志。 它由以下零個或多個的按位或組成
SA_RESETHAND:當調用信號處理函數時,將信號的處理函數重置為預設值SIG_DFL
SA_RESTART:如果信號中斷了進程的某個系統調用,則系統自動啟動該系統調用
SA_NODEFER :一般情況下, 當信號處理函數運行時,內核將阻塞該給定信號。但是如果設置了 SA_NODEFER標記, 那麼在該信號處理函數運行時,內核將不會阻塞該信號
sa_restorer 是一個替代的信號處理程序,當設置SA_SIGINFO時才會用它。
相關函數
int sigemptyset( sigset_t *set);
sigemptyset()用來將參數set信號集初始化並清空。
執行成功則返回0,如果有錯誤則返回-1。
完整示例
② linux signal(SIGINT,SIG_IGN);大概解釋一下
signal,此函數相對簡單一些,給定一個信號,給出信號處理函數則可,當然,函數簡單,其功能也相對簡單許多,簡單給出個函數例子如下:
#include<signal.h>
#include<stdio.h>
#include<unistd.h>
voidouch(intsig)
{
printf("Igotsignal%d ",sig);
//(void)signal(SIGINT,SIG_DFL);
//(void)signal(SIGINT,ouch);
}
intmain()
{
(void)signal(SIGINT,ouch);
while(1)
{
printf("helloworld... ");
sleep(1);
}
}
當然,實際運用中,需要對不同到signal設定不同的到信號處理函數,SIG_IGN忽略/SIG_DFL默認,這倆宏也可以作為信號處理函數。同時SIGSTOP/SIGKILL這倆信號無法捕獲和忽略。注意,經過實驗發現,signal函數也會堵塞當前正在處理的signal,但是沒有辦法阻塞其它signal,比如正在處理SIG_INT,再來一個SIG_INT則會堵塞,但是來SIG_QUIT則會被其中斷,如果SIG_QUIT有處理,則需要等待SIG_QUIT處理完了,SIG_INT才會接著剛才處理。
③ linux c signal函數
是第二次調用的時候輸出的,第一次調用只是綁定了SIGUSR1的信號處理函數,不會進入該處理函數
為什麼會有這樣的輸出呢?
signal函數是將信號與處理函數進行綁定,成功綁定則返回綁定之前的信號處理函數。那麼來看看你的代碼,第一次調用將sig_fun1綁定,無輸出;第二次調用將sig_fun2綁定,也就是把sig_fun1替換下來,並且你還調用了它,參數為30,所以會有那樣的輸出。
該如何改呢?
其實你並沒有涉及到linux的信號處理機制,光綁定是不夠的,還需要發信號給它,才能真正進入信號處理過程。給你一個示例代碼吧
#include<signal.h>
#include<stdlib.h>
#include<stdio.h>
#include<unistd.h>
void sig_fun2(int signo)
{
printf("in sig_fun2:%d\n", signo);
}
void sig_fun1(int signo)
{
printf("in sig_fun1:%d\n", signo);
}
int main()
{
unsigned long i;
if (signal(SIGUSR1, sig_fun1) == SIG_ERR)
{
printf("signal fun1 error\n");
exit(1);
}
sleep(15);
(signal(SIGUSR1, sig_fun2))(30);
sleep(15);
printf("done\n");
return 0;
}
/****************************C 代碼完,下面是如何運行***************************/
首先編譯,假設生成可執行程序為test
然後運行,我用的是後台運行: nohup ./test>output.txt &
注意,這種方法要將輸出重定向到文件output.txt(名字無所謂),然後你會看到一個數字,就是pid進程號
最後,在15秒之內發送信號:kill -SIGUSR1 進程號
現在你就可以打開output.txt看輸出結果了。如果用sleep的話會被打斷,所以只有兩個輸出加上替換處理函數時的輸出共3個,也可以換成 int n=15;while(n--)sleep(1);
-------------------------------------------------------------
怎麼樣,加分吧
-------------------------------------------------------------
1.我就是想問第二次綁定sig_fun2的時候,調用了第一次綁定的sig_fun1么?
調用了, (signal(SIGUSR1, sig_fun2))(30);就是這一句, signal(SIGUSR1, sig_fun2)是個函數指針,你這樣寫就是調用它了,但是這和信號處理沒關系,寫成signal(SIGUSR1, sig_fun2);就可以了
這就是你所說的成功則返回綁定之前的函數???
對
那當時綁定sig_fun1的時候,返回之前的處理函數是什麼??
這個就是系統默認的了,比如SIGINT就是你ctrl+c取消程序執行發送的信號,它的處理函數就是結束程序的一系列動作,不過SIGUSR1是留給用戶自定義的信號,系統默認應該是啥也不做的一個函數,例如void fun(int signo){},你也可以第一次綁定的時候就調用試試看對不對
2.還有我在看signal函數定義的時候,void(//...)(int) 最後傳入的這個int整形參數就是我們自定義sig_fun()中所接收的30么??我看例子裡面有的signal(SIGINT,myfunc);也沒有帶參數啊,搞不懂
是你理解錯了,signal函數只是綁定,沒涉及到調用綁定函數,不用帶參數,信號處理函數不是像你這樣調用的。callback回調你知道吧,就是先做好一個函數或過程放著,事件觸發的時候才調用。那個30是你用普通函數調用的方式時的參數,跟信號處理一點關系也沒有,你用60,70也沒半毛錢關系。我猜你是想要調用信號處理函數,然後迷糊了,其實我上面說的「kill -SIGUSR1 進程號」就是觸發程序調用該處理函數的信號,這和kill -9 殺死進程一個道理,只不過處理函數不同,結果不一樣。ctrl+c也可以用信號的方式發送,kill -2 進程號,或者 kill -SIGINT 進程號
另外,站長團上有產品團購,便宜有保證
④ 嵌入式linux怎麼檢內存泄漏雨
檢測內存泄露主要有以下5種方法:
1、在需要內存泄漏檢查的代碼的開始調用void mtrace(void) (該函數在頭文件mcheck.h中有聲明)。mtrace為malloc等函數安裝hook,用於記錄內存分配信息.在需要內存泄漏檢查的代碼的結束調用void muntrace(void)。
注意: 一般情況下不要調用muntrace, 而讓程序自然結束. 因為可能有些釋放內存代碼要到muntrace之後才運行.
2、用debug模式編譯被檢查代碼(-g或-ggdb)。
3、設置環境變數MALLOC_TRACE為一文件名, 這一文件將存有內存分配信息。
4、運行被檢查程序, 直至結束或muntrace被調用。
5、用mtrace命令解析內存分配Log文件($MALLOC_TRACE)
(mtrace foo $MALLOC_TRACE, where foo is the executible name)
如果有內存泄漏,mtrace會輸出分配泄漏
內存的代碼位置,以及分配數量。
⑤ 關於Linux系統信號處理函數及返回的問題
信號的處理由用戶態和內核態共同完成,當信號來了,系統會首先切換到內核態來做一些工作處理這個信號,此時該CPU上無法執行其他的線程。但是如果你有多個CPU或者多個core,那麼其他的線程的工作就不會受影響。
⑥ 這個linux里的信號安裝函數大概是什麼意思
編號為1 ~ 31的信號為傳統UNIX支持的信號,是不可靠信號(非實時的),編號為32 ~ 63的信號是後來擴充的,稱做可靠信號(實時信號)。不可靠信號和可靠信號的區別在於前者不支持排隊,可能會造成信號丟失,而後者不會。
下面我們對編號小於SIGRTMIN的信號進行討論。
1) SIGHUP
本信號在用戶終端連接(正常或非正常)結束時發出, 通常是在終端的控制進程結束時, 通知同一session內的各個作業, 這時它們與控制終端不再關聯。
登錄Linux時,系統會分配給登錄用戶一個終端(Session)。在這個終端運行的所有程序,包括前台進程組和後台進程組,一般都屬於這個Session。當用戶退出Linux登錄時,前台進程組和後台有對終端輸出的進程將會收到SIGHUP信號。這個信號的默認操作為終止進程,因此前台進程組和後台有終端輸出的進程就會中止。不過可以捕獲這個信號,比如wget能捕獲SIGHUP信號,並忽略它,這樣就算退出了Linux登錄,wget也能繼續下載。
此外,對於與終端脫離關系的守護進程,這個信號用於通知它重新讀取配置文件。
2) SIGINT
程序終止(interrupt)信號, 在用戶鍵入INTR字元(通常是Ctrl-C)時發出,用於通知前台進程組終止進程。
3) SIGQUIT
和SIGINT類似, 但由QUIT字元(通常是Ctrl-\)來控制. 進程在因收到SIGQUIT退出時會產生core文件, 在這個意義上類似於一個程序錯誤信號。
4) SIGILL
執行了非法指令. 通常是因為可執行文件本身出現錯誤, 或者試圖執行數據段. 堆棧溢出時也有可能產生這個信號。
5) SIGTRAP
由斷點指令或其它trap指令產生. 由debugger使用。
6) SIGABRT
調用abort函數生成的信號。
7) SIGBUS
非法地址, 包括內存地址對齊(alignment)出錯。比如訪問一個四個字長的整數, 但其地址不是4的倍數。它與SIGSEGV的區別在於後者是由於對合法存儲地址的非法訪問觸發的(如訪問不屬於自己存儲空間或只讀存儲空間)。
8) SIGFPE
在發生致命的算術運算錯誤時發出. 不僅包括浮點運算錯誤, 還包括溢出及除數為0等其它所有的算術的錯誤。
9) SIGKILL
用來立即結束程序的運行. 本信號不能被阻塞、處理和忽略。如果管理員發現某個進程終止不了,可嘗試發送這個信號。
10) SIGUSR1
留給用戶使用
11) SIGSEGV
試圖訪問未分配給自己的內存, 或試圖往沒有寫許可權的內存地址寫數據.
12) SIGUSR2
留給用戶使用
13) SIGPIPE
管道破裂。這個信號通常在進程間通信產生,比如採用FIFO(管道)通信的兩個進程,讀管道沒打開或者意外終止就往管道寫,寫進程會收到SIGPIPE信號。此外用Socket通信的兩個進程,寫進程在寫Socket的時候,讀進程已經終止。
14) SIGALRM
時鍾定時信號, 計算的是實際的時間或時鍾時間. alarm函數使用該信號.
15) SIGTERM
程序結束(terminate)信號, 與SIGKILL不同的是該信號可以被阻塞和處理。通常用來要求程序自己正常退出,shell命令kill預設產生這個信號。如果進程終止不了,我們才會嘗試SIGKILL。
17) SIGCHLD
子進程結束時, 父進程會收到這個信號。
如果父進程沒有處理這個信號,也沒有等待(wait)子進程,子進程雖然終止,但是還會在內核進程表中佔有表項,這時的子進程稱為僵屍進程。這種情況我們應該避免(父進程或者忽略SIGCHILD信號,或者捕捉它,或者wait它派生的子進程,或者父進程先終止,這時子進程的終止自動由init進程來接管)。
18) SIGCONT
讓一個停止(stopped)的進程繼續執行. 本信號不能被阻塞. 可以用一個handler來讓程序在由stopped狀態變為繼續執行時完成特定的工作. 例如, 重新顯示提示符
19) SIGSTOP
停止(stopped)進程的執行. 注意它和terminate以及interrupt的區別:該進程還未結束, 只是暫停執行. 本信號不能被阻塞, 處理或忽略.
20) SIGTSTP
停止進程的運行, 但該信號可以被處理和忽略. 用戶鍵入SUSP字元時(通常是Ctrl-Z)發出這個信號
21) SIGTTIN
當後台作業要從用戶終端讀數據時, 該作業中的所有進程會收到SIGTTIN信號. 預設時這些進程會停止執行.
22) SIGTTOU
類似於SIGTTIN, 但在寫終端(或修改終端模式)時收到.
23) SIGURG
有"緊急"數據或out-of-band數據到達socket時產生.
24) SIGXCPU
超過CPU時間資源限制. 這個限制可以由getrlimit/setrlimit來讀取/改變。
25) SIGXFSZ
當進程企圖擴大文件以至於超過文件大小資源限制。
26) SIGVTALRM
虛擬時鍾信號. 類似於SIGALRM, 但是計算的是該進程佔用的CPU時間.
27) SIGPROF
類似於SIGALRM/SIGVTALRM, 但包括該進程用的CPU時間以及系統調用的時間.
28) SIGWINCH
窗口大小改變時發出.
29) SIGIO
文件描述符准備就緒, 可以開始進行輸入/輸出操作.
30) SIGPWR
Power failure
31) SIGSYS
非法的系統調用。
在以上列出的信號中,程序不可捕獲、阻塞或忽略的信號有:SIGKILL,SIGSTOP
不能恢復至默認動作的信號有:SIGILL,SIGTRAP
默認會導致進程流產的信號有:SIGABRT,SIGBUS,SIGFPE,SIGILL,SIGIOT,SIGQUIT,SIGSEGV,SIGTRAP,SIGXCPU,SIGXFSZ
默認會導致進程退出的信號有:SIGALRM,SIGHUP,SIGINT,SIGKILL,SIGPIPE,SIGPOLL,SIGPROF,SIGSYS,SIGTERM,SIGUSR1,SIGUSR2,SIGVTALRM
默認會導致進程停止的信號有:SIGSTOP,SIGTSTP,SIGTTIN,SIGTTOU
默認進程忽略的信號有:SIGCHLD,SIGPWR,SIGURG,SIGWINCH
此外,SIGIO在SVR4是退出,在4.3BSD中是忽略;SIGCONT在進程掛起時是繼續,否則是忽略,不能被阻塞
⑦ linux嵌套字的三種類型分別是什麼
阻塞和非阻塞
對於一個套接字的 I/O通信,它會涉及到兩個系統對象,一個是調用這個IO的進程或者線程,另一個就是系統內核。比如當一個讀操作發生時,它會經歷兩個階段:
①等待數據准備 (Waiting for the data to be ready)
②將數據從內核拷貝到進程中 (Copying the data from the kernel to the process)
阻塞,在Linux中,默認情況下所有的socket都是blocking,當用戶進程調用了recvfrom/recv這個系統調用,嚙合就開始了IO的第一個階段:准備數據。但是很多時候數據在一開始還沒有到達(比如,還沒有收到一個完整的UDP/TCP包),這個時候內核就要等待足夠的數據到來。而在用戶進程這邊,整個進程會被阻塞。當內核一直等到數據准備好了,它就會將數據從內核中拷貝到用戶內存,然後直到返回結果,用戶進程才解除阻塞的狀態,重新運行起來。
所以,阻塞IO的特點就是在IO執行的兩個階段都被阻塞了。
調用阻塞IO會一直阻塞對應的進程直到操作完成,而非阻塞IO在內核還准備數據的情況下會立刻返回。
阻塞
因此我們給出阻塞的簡單定義,阻塞調用是指調用結果返回之前,當前線程會被掛起(線程進入非可執行狀態,在這個狀態下,cpu不會給線程分配時間片,即線程暫停運行)。函數只有在得到結果之後才會返回。
有人也許會把阻塞調用和同步調用等同起來,實際上他是不同的。對於同步調用來說,很多時候當前線程還是激活的,只是從邏輯上當前函數沒有返回而已。 例如,我們在socket中調用recv函數,如果緩沖區中沒有數據,這個函數就會一直等待,直到有數據才返回。而此時,當前線程還會繼續處理各種各樣的消息,所以我們應該說我們的線程現在是阻塞的。
快遞的例子:比如到你某個時候到A樓一層(假如是內核緩沖區)取快遞,但是你不知道快遞什麼時候過來,你又不能幹別的事,只能死等著。但你可以睡覺(進程處於休眠狀態),因為你知道快遞把貨送來時一定會給你打個電話(假定一定能叫醒你)。
非阻塞
非阻塞和阻塞的概念相對應,指在不能立刻得到結果之前,該函數不會阻塞當前線程,而會立刻返回。
還是等快遞的例子:如果用忙輪詢的方法,每隔5分鍾到A樓一層(內核緩沖區)去看快遞來了沒有。如果沒來,立即返回。而快遞來了,就放在A樓一層,等你去取。
對象的阻塞模式和阻塞函數調用
對象是否處於阻塞模式和函數是不是阻塞調用有很強的相關性,但是並不是一一對應的。阻塞對象上可以有非阻塞的調用方式,我們可以通過一定的API去輪詢狀 態,在適當的時候調用阻塞函數,就可以避免阻塞。而對於非阻塞對象,調用特殊的函數也可以進入阻塞調用。我們經常使用的函數select就是這樣的一個例子。
同步與非同步
首先我們給出POSIX的定義
A synchronous I/O operation causes the requesting process to be blocked until that I/O operation completes;
An asynchronous I/O operation does not cause the requesting process to be blocked; 1212
同步
用我們的話說,所謂同步,就是在發出一個功能調用時,在沒有得到結果之前,該調用就不返回。也就是必須一件一件事做,等前一件做完了才能做下一件事,而只有當所有的工作按照順序執行完之後,才會返回調用的位置,繼續往下執行。
這個很好理解,我們一般做的函數調用,都是同步的,我們的程序按照我們既定的順序一步一步執行,在前一個操作返回後,我們根據操作的結果,進行下一個階段的處理,這就是一個同步的過程。
非同步
非同步,非同步的概念和同步相對。當一個非同步過程調用發出後,調用者不能立刻得到結果。實際處理這個調用的部件在完成後,通過狀態、通知和回調來通知調用者。
簡單來說
一個同步接收的方式,在這個埠下如果同是來了兩個客戶端請求,第一個連接得到響應,與服務端建立通訊,而第二個請求就會被一直阻塞直到第一個請求完成操作,各個請求之間就好像排個隊,順序執行,這就是同步。
而一個非同步接收方式,就是同時來兩個或者多個請求,服務端就同時響應多個客戶端,同時給他們連接。各個客戶端與伺服器的通訊是並行的,一個客戶端不必等另一個客戶端完成操作。
兩者的區別就在於同步IO做IO操作的時候會將process阻塞
其實阻塞IO,非阻塞IO,以及我們後面會提到IO復用都屬於同步 IO。有人可能會說,非阻塞IO並沒有被阻塞啊。這里有個非常「狡猾」的地方,定義中所指的」IO operation」是指真實的IO操作,比如非阻塞IO在執行recvfrom這個系統調用的時候,如果內核的數據沒有準備好,這時候不會阻塞進程。但是,當內核中數據准備好的時候,recvfrom會將數據從內核拷貝到用戶內存中,在這段時間內,進程是被阻塞的。而非同步IO則不一樣,當進程發起IO 操作之後,就直接返回再也不理睬了,直到內核發送一個信號,告訴進程說IO完成。在這整個過程中,進程完全沒有被阻塞。
我們可以總結為一下幾點
同步 就是我調用一個功能,該功能沒有結束前,我死等結果。只能順序執行。
非同步 就是我調用一個功能,不需要知道該功能結果,該功能有結果後通知我(回調通知),往往用回調函數或者其他類方式實現。
阻塞 就是調用我(函數),我(函數)沒有接收完數據或者沒有得到結果之前,我不會返回。
非阻塞 就是調用我(函數),我(函數)立即返回,而當我准備完畢時,通過select通知調用者。
同步IO和非同步IO的區別就在於:數據拷貝的時候進程是否可以被阻塞
阻塞IO和非阻塞IO的區別就在於:應用程序的調用是否能立即返回
並發與迭代
套接字編程經常使用在客戶/伺服器編程模型(簡稱C/S模型)中,C/S模型根據復雜度分為簡單的客戶/伺服器模型和復雜的客戶/伺服器模型。
C/S簡單客戶/伺服器模型是一對一關系,一個伺服器端某一時間段內只對應處理一個客戶端的請求,迭代伺服器模型屬於此模型。
C/S復雜伺服器模型是一對多關系,一個伺服器端某一時間段內對應處理多個客戶端的請求,並發伺服器模型屬於此模型。
迭代伺服器模型和並發伺服器模型是socket編程中最常見使用的兩種編程模型。
通常來說大多數TCP伺服器是並發的,大多數UDP伺服器是迭代的
比如我們通常在做聊天室的時候,使用UDP協議來發送消息,但是在用戶需要上傳下載數據的時候,我們通常在伺服器和客戶端之間建立一個TCP連接來傳送文件。。。
但是如果服務一個客戶請求的時間不長,使用迭代伺服器沒有太大問題,一旦客戶請求的時間需要花費很長,不希望整個伺服器被單個客戶長期佔用,而希望同時服務多個客戶,就需要選擇並發伺服器了。