做網(wǎng)站如何避免侵權網(wǎng)絡營銷的重要性
目錄
一、信號的概念
?二、定時器
1. alarm函數(shù)
?2.?setitimer函數(shù)
3.signal和sigaction函數(shù)
三、使用SIGCHLD信號實現(xiàn)回收子進程
一、信號的概念
????????概念:信號是在軟件層次上對中斷機制的一種模擬,是一種異步通信方式 。所有信號的產(chǎn)生及處理全部都是由內(nèi)核完成的。
信號處理方式:
????????1 缺省方式
????????2 忽略信號
????????3 捕捉信號????????
在信號處理中,通常有三種處理方式:
-
缺省方式(Default):這是操作系統(tǒng)針對每種信號定義的默認行為。對于大多數(shù)信號,缺省行為是終止進程。例如,
SIGINT
的缺省行為是終止進程。 -
忽略信號(Ignore):這種方式下,進程對收到的信號不做任何響應。這意味著當進程收到指定信號時,不會采取任何動作。這通常用于某些不需要處理的信號,或者是在某些特定情況下臨時禁用信號處理器。
-
捕捉信號(Catch):這種方式下,進程定義一個信號處理函數(shù),用于處理特定信號。當進程收到指定信號時,會調(diào)用這個信號處理函數(shù)。這允許程序員自定義對信號的處理方式,例如,收到
SIGINT
時執(zhí)行特定的操作而不是終止進程。
????????在如下代碼中使用了第三種方式,即捕捉信號。通過調(diào)用 signal(SIGINT, handle)
,定義了一個信號處理函數(shù) handle
,用于處理 SIGINT
信號。當程序收到 SIGINT
信號時,會調(diào)用 handle
函數(shù)來處理它,而不是按照默認的方式終止進程。
?注意typedef void (*sighandler_t)(int);不能少
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<signal.h>
#include<stdlib.h>typedef void (*sighandler_t)(int);
sighandler_t oldact;void handle(int sig)
{printf("I cath the SIGINT\n");signal(SIGINT,oldact);
}int main()
{oldact = signal(SIGINT,handle);while(1){sleep(1);}return 0;
}
?代碼注釋:
使用 signal 函數(shù)來設置信號處理函數(shù),并捕獲 SIGINT 信號(通常由鍵盤上的Ctrl+C組合鍵發(fā)送)。這段代碼的功能是在收到 SIGINT 信號時打印一條消息,并重新設置 SIGINT 信號的處理函數(shù)為先前的處理函數(shù)。下面是代碼的簡要解釋:signal(SIGINT, handle):這行代碼設置了 SIGINT 信號的處理函數(shù)為 handle。
在收到 SIGINT 信號時,系統(tǒng)將調(diào)用 handle 函數(shù)來處理該信號。handle 函數(shù):這是 SIGINT 信號的處理函數(shù)。當程序收到 SIGINT 信號時,會調(diào)用這個函數(shù),并打印一條消息 "I catch the SIGINT"。
然后,它將信號的處理函數(shù)重新設置為 oldact,這樣可以恢復先前的信號處理函數(shù)。oldact:這是一個全局變量,用于保存先前 SIGINT 信號的處理函數(shù)。while(1) 循環(huán):這是一個無限循環(huán),程序在這里持續(xù)運行,每次循環(huán)睡眠1秒鐘。
為什么“它將信號的處理函數(shù)重新設置為 oldact,這樣可以恢復先前的信號處理函數(shù)”?
?????????先前的處理函數(shù),在這里就是系統(tǒng)的默認狀態(tài),即CTRL+C可以終止信號。
在這段代碼中,oldact 保存了先前 SIGINT 信號的處理函數(shù)。
當程序收到 SIGINT 信號時,handle 函數(shù)會被調(diào)用。
在 handle 函數(shù)中,執(zhí)行了 signal(SIGINT, oldact),這樣做的目的是將 SIGINT 信號的處理函數(shù)重新設置為先前保存的處理函數(shù) oldact。這樣做的原因是為了確保在 handle 函數(shù)執(zhí)行完畢后,再次收到 SIGINT 信號時,會調(diào)用先前的處理函數(shù),而不是再次調(diào)用 handle 函數(shù)。
這樣可以恢復程序在接收 SIGINT 信號時的默認行為,或者是由用戶自定義的其他行為。
????????通過在調(diào)用 signal 函數(shù)時將 oldact 作為第二個參數(shù)傳遞進去,可以獲取先前的信號處理函數(shù)。然后,你可以在需要的時候使用這個指針來重新設置信號處理函數(shù),從而恢復到先前的處理方式。?
在 signal 函數(shù)中,oldact 是一個指向先前信號處理函數(shù)的指針。signal 函數(shù)的原型如下:void (*signal(int signum, void (*handler)(int)))(int);它返回了一個指向先前信號處理函數(shù)的指針。
這個指針的類型是 sighandler_t,它是一個函數(shù)指針類型,它指向一個接受 int 參數(shù)并返回 void 的函數(shù)。
-
當程序啟動時,通常會有一些默認的信號處理方式。例如,對于
SIGINT
信號(通常由用戶按下 Ctrl+C 發(fā)送),默認情況下會終止程序。 -
當你調(diào)用
signal(SIGINT, handle)
時,你指定了一個自定義的信號處理函數(shù)handle
,用于處理SIGINT
信號。在這之前,如果有其他函數(shù)被注冊為SIGINT
信號的處理函數(shù),那么signal
函數(shù)會返回這個先前的處理函數(shù),并將其保存在oldact
變量中。 -
之后,如果你想要恢復先前的處理方式,你可以調(diào)用
signal(SIGINT, oldact)
,這樣SIGINT
信號會再次被先前的處理函數(shù)處理,而不是你自定義的handle
函數(shù)。
常用信號:
信號名 | 含義 | 默認操作 |
SIGKILL | 該信號用來結(jié)束進程,并且不能被捕捉和忽略 | 終止 |
SIGSTOP | 該信號用于暫停進程,并且不能被捕捉和忽略 | 暫停進程 |
SIGTSTP | 該信號用于暫停進程,用戶可鍵入SUSP字符( 通常是Ctrl-Z)發(fā)出這個信號 | 暫停進程 |
SIGCONT | 該信號讓進程進入運行態(tài) | 繼續(xù)運行 |
SIGALRM | 該信號用于通知進程定時器時間已到 | 終止 |
SIGUSR1/2 | 該信號保留給用戶程序使用 | 終止 |
SIGCHLD | 是子進程狀態(tài)改變發(fā)給父進程的。 | 忽略 |
信號名 | 含義 | 默認操作 |
SIGHUP | 該信號在用戶終端關閉時產(chǎn)生,通常是發(fā)給和該 終端關聯(lián)的會話內(nèi)的所有進程 | 終止 |
SIGINT | 該信號在用戶鍵入INTR字符(Ctrl-C)時產(chǎn)生,內(nèi) 核發(fā)送此信號送到當前終端的所有前臺進程 | 終止 |
SIGQUIT | 該信號和SIGINT類似,但由QUIT字符(通常是 Ctrl-\)來產(chǎn)生 | 終止 |
SIGILL | 該信號在一個進程企圖執(zhí)行一條非法指令時產(chǎn)生 | 終止 |
SIGSEV | 該信號在非法訪問內(nèi)存時產(chǎn)生,如野指針、緩 沖區(qū)溢出 | 終止 |
SIGPIPE | 當進程往一個沒有讀端的管道中寫入時產(chǎn)生,代 表“管道斷裂” | 終止 |
?二、定時器
1. alarm函數(shù)
alarm
函數(shù)的原型如下:
????????unsigned int alarm(unsigned int seconds);
????????它接受一個無符號整數(shù)參數(shù) seconds
,表示定時器的超時時間,單位是秒。調(diào)用 alarm
函數(shù)會設置一個定時器,在指定的秒數(shù)之后,會產(chǎn)生 SIGALRM
信號。
#include <stdio.h>
#include <unistd.h>
#include <signal.h>void alarm_handler(int signum)
{printf("Alarm signal received!\n");}int main()
{signal(SIGALRM, alarm_handler);alarm(5);printf("Waiting for alarm...\n");while (1) {sleep(1);}return 0;
}
? ?alarm(5)
函數(shù)調(diào)用設置了一個5秒的定時器。但是,在程序調(diào)用 alarm(5)
設置定時器之后,程序會立即繼續(xù)執(zhí)行下一條語句,而不會等待5秒鐘。這意味著在調(diào)用 alarm(5)
之后,立即執(zhí)行了 printf("Waiting for alarm...\n");
這行代碼,所以會立即打印出 "Waiting for alarm..."。然后,程序會進入 while
循環(huán),在那里它會等待5秒鐘,直到定時器超時并產(chǎn)生 SIGALRM
信號。
因此,程序執(zhí)行的步驟是這樣的:
- 執(zhí)行
alarm(5);
設置一代碼個5秒的定時器。 - 立即執(zhí)行
printf("Waiting for alarm...\n");
,打印 "Waiting for alarm..."。 - 進入
while
循環(huán),程序會等待5秒鐘。 - 定時器超時,產(chǎn)生
SIGALRM
信號,調(diào)用alarm_handler
函數(shù)。 - 打印 "Alarm signal received!\n"。
- 程序繼續(xù)執(zhí)行
while
循環(huán)中的其他代碼。
?2.?setitimer函數(shù)
????????int setitimer(int which, const struct itimerval *new_value, struct itimerval *old_value);
????????功能:定時的發(fā)送alarm信號參數(shù):which:?? ITIMER_REAL:以逝去時間遞減。發(fā)送SIGALRM信號、ITIMER_VIRTUAL: 計算進程(用戶模式)執(zhí)行的時間。 發(fā)送SIGVTALRM信號new_value:? 負責設定 timout 時間?????????????????old_value: ??存放舊的timeout值,一般指定為NULLstruct itimerval{struct timeval it_interval;? // 鬧鐘觸發(fā)周期struct timeval it_value;??? // 鬧鐘觸發(fā)時間};struct timeval{time_t????? tv_sec;???????? /* seconds */suseconds_t tv_usec;??????? /* microseconds */};
#include<sys/time.h>
#include<stdio.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>void timer_handler(int signum)
{printf("Timer expired!\n");
}int main()
{struct itimerval timer;struct sigaction act;act.sa_handler = timer_handler;act.sa_flags = 0;sigemptyset(&act.sa_mask);timer.it_value.tv_sec = 5; // 5秒后定時器啟動timer.it_value.tv_usec = 0;timer.it_interval.tv_sec = 1; // 間隔1秒timer.it_interval.tv_usec = 0;sigaction(SIGALRM, &act,NULL);if (setitimer(ITIMER_REAL, &timer, NULL) == -1){perror("setitimer");exit(EXIT_FAILURE);}while (1){sleep(1);}return 0;
}
定義了一個信號處理函數(shù) timer_handler,用于在定時器到期時打印 "Timer expired!"。創(chuàng)建了一個 struct itimerval 結(jié)構體變量 timer,并設置了定時器的參數(shù):it_value 成員設置為 5 秒,表示定時器將在 5 秒后啟動。
it_interval 成員設置為 1 秒,表示定時器在啟動后每隔 1 秒觸發(fā)一次。
創(chuàng)建了一個 struct sigaction 結(jié)構體變量 act,并設置了其中的成員:sa_handler 成員設置為 timer_handler 函數(shù),表示 SIGALRM 信號的處理函數(shù)為 timer_handler。
sa_flags 設置為 0,表示不設置特殊標志。
sa_mask 使用 sigemptyset 函數(shù)清空,表示在執(zhí)行 timer_handler 函數(shù)期間不阻塞任何信號。
使用 sigaction 函數(shù)將 SIGALRM 信號的處理函數(shù)設置為 timer_handler 函數(shù)。使用 setitimer 函數(shù)設置實時定時器,并傳入 timer 結(jié)構體,啟動定時器。進入一個無限循環(huán),程序?qū)⒊掷m(xù)運行,每隔 1 秒調(diào)用 sleep(1) 函數(shù),等待定時器的觸發(fā)。總的來說,這段代碼實現(xiàn)了一個定時器,在程序啟動 5 秒后啟動,并且每隔 1 秒觸發(fā)一次定時器,在定時器觸發(fā)時打印一條消息。
3.signal和sigaction函數(shù)
signal函數(shù):?
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
功能:捕捉信號執(zhí)行自定義函數(shù)
返回值:成功時返回原先的信號處理函數(shù),失敗時返回SIG_ERR
參數(shù):signo 要設置的信號類型handler 指定的信號處理函數(shù): SIG_DFL代表缺省方式; SIG_IGN 代表忽略信號; 系統(tǒng)建議使用sigaction函數(shù),因為signal在不同類unix系統(tǒng)的行為不完全一樣。
sigaction函數(shù):?
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
struct sigaction {void (*sa_handler)(int);void (*sa_sigaction)(int, siginfo_t *, void *);sigset_t sa_mask;int sa_flags;void (*sa_restorer)(void);
}
參數(shù):
signum:處理的信號
act,oldact: 處理信號的新行為和舊的行為,是一個sigaction結(jié)構體。sigaction結(jié)構體成員定義如下:
sa_handler: 是一個函數(shù)指針,其含義與 signal 函數(shù)中的信號處理函數(shù)類似
sa_sigaction: 另一個信號處理函數(shù),它有三個參數(shù),可以獲得關于信號的更詳細的信息。
sa_flags參考值如下:
SA_SIGINFO:使用 sa_sigaction 成員而不是 sa_handler 作為信號處理函數(shù)
SA_RESTART:使被信號打斷的系統(tǒng)調(diào)用自動重新發(fā)起。
SA_RESETHAND:信號處理之后重新設置為默認的處理方式。
SA_NODEFER:使對信號的屏蔽無效,即在信號處理函數(shù)執(zhí)行期間仍能發(fā)出這個信號。
re_restorer:是一個已經(jīng)廢棄的數(shù)據(jù)域
三、使用SIGCHLD信號實現(xiàn)回收子進程
SIGCHLD的產(chǎn)生條件
????????1子進程終止時
????????2子進程接收到SIGSTOP信號停止時
????????3子進程處在停止態(tài),接受到SIGCONT后喚醒時
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/wait.h>
#include<signal.h>
#include<string.h>
#include<errno.h>void handle(int sig)
{wait(NULL);printf("GET sig = %d\n",sig);
}
int main()
{pid_t pid;struct sigaction act;act.sa_handler = handle;act.sa_flags = 0;sigemptyset(&act.sa_mask);pid = fork();if(pid > 0){//wait(NULL);sigaction(SIGCHLD,&act,NULL);while(1){printf("this is father process\n");sleep(1);}}else if(pid == 0){sleep(5);exit(0);}else{perror("fork");return 0;}
}
這段代碼創(chuàng)建了一個父子進程,并使用 wait 函數(shù)等待子進程結(jié)束。
在父進程中,設置了 SIGCHLD 信號的處理函數(shù)為 handle 函數(shù),用于處理子進程終止的信號。
具體來說,父進程會周期性地打印一條消息,而子進程在啟動后會等待 5 秒后自行退出。下面是這段代碼的執(zhí)行邏輯:程序開始執(zhí)行,父進程調(diào)用 fork 創(chuàng)建了一個子進程。父進程中,如果 fork 成功(返回大于 0 的值),則進入了一個無限循環(huán),不斷地打印 "this is father process",并在其中使用 sigaction 函數(shù)設置了 SIGCHLD 信號的處理函數(shù)為 handle 函數(shù)。子進程中,如果 fork 成功(返回 0),則進入了 if (pid == 0) 的分支,子進程會休眠 5 秒后自行退出。如果 fork 出錯(返回小于 0 的值),則父進程中輸出錯誤信息并退出。當子進程結(jié)束時,父進程會收到 SIGCHLD 信號,進而調(diào)用 handle 函數(shù)來處理。在 handle 函數(shù)中,調(diào)用 wait 函數(shù)等待子進程結(jié)束,并打印 "GET sig = xx" 的信息,其中 xx 是接收到的信號值。總的來說,這段代碼通過信號處理機制實現(xiàn)了在父進程中對子進程的終止進行處理。
wait(NULL)
當一個父進程調(diào)用 `wait(NULL)` 時,它會發(fā)生以下事情:
1. **阻塞父進程**:如果沒有任何子進程已經(jīng)結(jié)束,`wait(NULL)` 會阻塞父進程,直到至少有一個子進程結(jié)束。
2. **回收子進程**:一旦有子進程結(jié)束,`wait(NULL)` 會回收該子進程的資源。這意味著操作系統(tǒng)會清理與該子進程相關的所有資源,比如內(nèi)存和進程控制塊。
3. **不指定子進程**:由于 `wait(NULL)` 不指定等待特定的子進程,它適用于等待任何一個子進程。如果需要等待特定的子進程,可以使用 `waitpid(pid, &status, options)` 函數(shù),其中 `pid` 是特定子進程的進程號。
linux wait(null)函數(shù)_wait(null)-CSDN博客https://blog.csdn.net/weixin_44652882/article/details/134363936