訊號程式設計之訊號傳送及訊號處理函式遇到不可重入函式
kill函式
- 函式原型:
Int kill(pid_t pid, int siq)
- 功能:既可以向自身傳送訊號,也可以向其他程序傳送訊號;
- 引數:
- pid>0 將訊號sig發給pid程序
- pid=0 將訊號sig發給同組程序
- pid=-1 將訊號sig傳送給所有程序,呼叫者程序有許可權傳送的每一個程序(除了1號程序之外,還有它自身)
- pid<-1 將訊號sig傳送給程序組是pid(絕對值)的每一個程序
注意,如果在fork之前安裝訊號,則子程序可以繼承訊號。
補充:getpgrp()函式獲取程序組pid
sleep函式
函式原型:unsigned int sleep(unsigned int seconds);
功能:讓程序睡眠。原理和wait類似,都有可能被其他訊號打斷!
注意:
1)能被訊號打斷,然後處理訊號函式以後,就不再睡眠了。直接向下執行程式碼
2)sleep函式的返回值,是剩餘的秒數
- 示例程式碼:
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
#include <unistd.h>
#include <sys/stat.h>
#include <sys/wait.h>
#include <sys/types.h>
#include <fcntl.h>
#include <stdlib.h>
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
void myhandle(int num)
{
if (num == SIGINT)
{
printf("recv signal SIGINT \n");
}
else if (num == SIGUSR1)
{
printf("recv signal SIGUSR1 \n");
}
else
{
printf("recv signal id num : %d \n", num);
}
}
int main(void)
{
pid_t pid;
printf("main ....begin\n");
if (signal(SIGINT, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
if (signal(SIGUSR1, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
pid = fork();
if (pid == -1)
{
printf("fork err....\n");
return 0;
}
//子程序向父程序傳送訊號
//子程序向同組程序傳送訊號
/*
if (pid == 0)
{
//pid = getpgrp();
pid = getppid();
//kill(pid, SIGUSR1); //向老爹發訊號
kill(0, SIGUSR1); //向程序組發訊號
//killpg(pid, SIGUSR1);
exit(0);
}
*/
if (pid == 0)
{
pid = getpgrp();
killpg(pid, SIGUSR1);
exit(0);
}
int n = 3;
do
{
printf("父程序開始睡眠\n");
n = sleep(n);
printf("父程序開始喚醒\n");
} while (n > 0);
//sleep(n);
printf("sleep 函式執行完畢以後返回...\n");
return 0;
}
raise函式
函式原型: int raise(int sig);
功能:給自己傳送訊號。raise(sig)等價於kill(getpid(), sig);
kill可以傳送訊號給其他程序
killpg函式
函式原型:int killpg(int pgrp, int sig);
功能:給程序組傳送訊號。killpg(pgrp, sig)等價於kill(-pgrp, sig);
sigqueue函式
函式原型:int sigqueue(pid_t pid, int sig, const union sigval value);
函式功能: 給程序傳送訊號,支援排隊,可以附帶資訊
pause函式
- 將程序置為可中斷睡眠狀態。然後它呼叫核心函式schedule(),使linux程序排程器找到另一個程序來執行。
- pause使呼叫者程序掛起,直到一個訊號被捕獲就返回,不再阻塞呼叫者程序。
alarm函式
功能:設定一個鬧鐘延遲傳送訊號。告訴linux核心n秒以後,傳送SIGALRM訊號;
函式原型:unsigned int alarm(unsigned int seconds);
注意:該鬧鐘只是一次有效,如果要重複有效,則需要在訊號處理函式裡面再次呼叫alarm函式設定鬧鐘。
void myhandle(int num)
{
printf("recv signal id num : %d \n", num);
alarm(1);
}
int main(void)
{
printf("main ....begin\n");
//註冊訊號處理函式
if (signal(SIGALRM, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
alarm(1);
while(1)
{
pause();
printf("pause return\n");
}
return 0;
}
訊號處理函式遇上可重入和不可重入函式
所謂可重入函式是指一個可以被多個任務呼叫的過程,任務在呼叫時不必擔心資料是否會出錯。因為程序在收到訊號後,就將跳轉到訊號處理函式去接著執行。--也就是說可重入函式可以被多個程序或者函式同時呼叫,但不必關心內部資料和全域性資料是否會被修改。
如果訊號處理函式中使用了不可重入函式,那麼訊號處理函式可能會修改原來程序中不應該被修改的資料,這樣程序從訊號處理函式中返回接著執行時,可能會出現不可預料的後果。不可重入函式在訊號處理函式中被視為不安全函式。
滿足下列條件的函式多數是不可再入的:
(1)使用靜態的資料結構,如getlogin(),gmtime(),getgrgid(),getgrnam(),getpwuid()
以及getpwnam()
等等;
(2)函式實現時,呼叫了malloc()
或者free()
函式
(3)實現時使用了標準I/O函式的
結論:在訊號處理函式中,儘量不使用含有全域性變數和靜態變數的函式。特別是這個變數在程式中隨時可能讀寫。
man 7 signal 查詢可重入函式和不可重入函式。
#include <sys/types.h>
#include <unistd.h>
#include <signal.h>
#include <stdlib.h>
#include <string.h>
#include <stdio.h>
#include <errno.h>
#include <signal.h>
typedef struct _Teacher
{
int age;
int num;
}Teacher;
Teacher g_t;
char *p = NULL:
void printfGlobTeacher()
{
printf("g_t.age:%d \n", g_t.age);//含有全域性變數--不可重入--gt一直在改變
printf("g_t.num:%d \n", g_t.num);
//p = malloc(100);//容易引起不可重入
}
void myhandle(int num)
{
printf("recv signal id num : %d \n", num);
printfGlobTeacher();
alarm(1);
}
int main(void)
{
Teacher t1, t2;
t1.age = 30;
t1.num = 30;
t2.age = 40;
t2.num = 40;
printf("main ....begin\n");
//註冊訊號處理函式
if (signal(SIGALRM, myhandle) == SIG_ERR)
{
perror("func signal err\n");
return 0;
}
//間接遞迴
//myhandle----->alarm=====>myhandle
alarm(1);
while(1)
{
g_t = t1;
g_t = t2;
}
return 0;
}