linux程式設計---訊號中斷處理
訊號
linux提供的訊號機制是一種程序間非同步的通訊機制,在實現上是一種軟中斷。訊號可以導致一個正在執行的程序被另一個非同步程序中斷。
訊號的處理流程
產生訊號:產生訊號有多種說法。一個程序建立一個訊號用於傳送給另一個程序叫傳送一個訊號;核心建立一個訊號叫生成一個訊號;
一個程序向自己傳送一個訊號叫喚起一個訊號
為使某個訊號到來時程序能夠執行相應的中斷服務程式,即設定某訊號到來時執行的程式碼,稱為安裝中斷
如果一個訊號被正確傳送到一個程序稱為訊號被遞給。
如果一個訊號的遞送導致一段處理程式被執行,稱為該訊號被捕捉。
如果一個訊號被髮送並且還沒有引起任何動作(一般是對應程序阻塞了此訊號),稱為訊號處於等待狀態。
產生訊號
1 kill產生一個訊號
kill(getpid(), sig);
2 raise自舉一個訊號
int raise(int sig);
3 alarm定時
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
4 ualarm定時
useconds_t ualarm(useconds_t usecs, useconds_t interval);
訊號處理與signal安裝訊號
1.訊號處理辦法
(1)忽略此訊號
大多數訊號都可以這樣處理。除了sigkill和sigstop。
(2)捕捉訊號
通知核心在某種訊號發生時呼叫一個使用者函式。在使用者函式中,如果需要按使用者希望執行這種事件進行的處理,即需要安裝此訊號。
(3)執行系統預設操作。
2.signal安裝訊號
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
2 sigaction安裝訊號
#include <signal.h>
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
使用例子
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
void myhandler(int sig);
int main()
{
struct sigaction act,oact;
act.sa_handler = myhandler;
sigemptyset(&act.sa_mask);
act.sa_flags = 0;
sigaction(SIGUSR1,&act,&oact);
while(1)
{
printf("hello world\n");
pause();
}
}
void myhandler(int sig)
{
printf("i get signal %d\n",sig);
}
訊號集與遮蔽訊號
中斷可以被遮蔽(阻塞)(部分硬體中斷則必須立即處理,例如復位中斷),因此,linux的訊號是可以遮蔽,即阻塞訊號。
(1)清空訊號集
int sigemptyset(sigset_t *set);
(2)完全填空訊號集
int sigfillset(sigset_t *set);
(3)新增訊號到訊號集中
int sigaddset(sigset_t *set, int signum);
(4)從訊號集中刪除某個訊號
int sigdelset(sigset_t *set, int signum);
(5)檢測訊號是否在訊號集中
int sigismember(const sigset_t *set, int signum);
//將阻塞的訊號新增到set中
int sigpending(sigset_t *set);
//設定或獲取當前程序阻塞的訊號集
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
第1個引數
#define SIG_BLOCK 0
#define SIG_UNBLOCK 1
#define SIG_SETMASK 2
SIG_BLOCK 將第2個引數所描述的集合新增到當前程序阻塞的訊號集中。
SIG_UNBLOCK 將第2個引數所描述的集合從當前程序阻塞的訊號集中刪除。
SIG_SETMASK 無論之前的阻塞訊號,設定當前程序阻塞的集合為第2個引數描述的物件。
如果set是空指標,則引數how的值沒有意義,且不會更改程序的阻塞訊號集,因此該呼叫可用於查詢當前受阻塞的訊號 。
函式使用示例
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
static void sig_quit(int);
int main(int argc,char* argv[])
{
sigset_t newmask,oldmask,pendmask;
if(signal(SIGQUIT,sig_quit) == SIG_ERR)
{
perror("signal");
exit(-1);
}
printf("install sig_quit\n");
sleep(10);
sigemptyset(&newmask);
sigaddset(&newmask,SIGQUIT);
if(sigprocmask(SIG_BLOCK,&newmask,&oldmask) < 0)
{
perror("signalmask ");
exit(-1);
}
printf("block sigquit,wait 15 second...\n");
sleep(15);
if(sigpending(&pendmask) < 0)
{
perror("sigpending ");
exit(-1);
}
if(sigprocmask(SIG_SETMASK,&oldmask,NULL) < 0)
{
perror("sigprocmask ");
exit(-1);
}
printf("sigquit unblocked\n");
sleep(10);
return 0;
}
static void sig_quit(int signo)
{
printf("caught sigquit,the process will quit\n");
if(signal(SIGQUIT,SIG_DFL) == SIG_ERR) //重新設定
{
perror("signal ");
exit(-1);
}
}
//等待訊號 使當前程序處於等待狀態,直到當前程序阻塞訊號外任意一個訊號出現。
int pause(void);
//將呼叫程序阻塞的訊號集替換為其引數值,然後掛起該執行緒,直到傳遞一個非指定集合中訊號為止。
int sigsuspend(const sigset_t *mask);
針對sa_mask成員測試應用程式碼
#include<stdio.h>
#include<signal.h>
#include<stdlib.h>
int output(sigset_t set)
{
printf("set.val[0]=%x\n",set.__val[0]);
}
void handler(int sig)
{
int i;
sigset_t sysset;
printf("\nin handler sig=%d\n",sig);
sigprocmask(SIG_SETMASK,NULL,&sysset);
output(sysset);
printf("return\n");
}
int main()
{
struct sigaction act;
sigset_t set,sysset,newset;
sigemptyset(&set);
sigemptyset(&newset);
//分別加入不同值
sigaddset(&set,SIGUSR1);
sigaddset(&newset,SIGUSR2);
printf("\nadd SIGUSR1,the value of set:");
output(set);
printf("\nadd SIGUSR2,the value of set:");
output(newset);
printf("\nafter set proc block set,and then read to sysset\n");
//設定阻塞值
sigprocmask(SIG_SETMASK,&set,NULL);
//讀配置
sigprocmask(SIG_SETMASK,NULL,&sysset);
output(sysset);
printf("install SIGALRM,and the act.sa_mask is newset(SIGUSR2)\n");
act.sa_handler = handler;
act.sa_flags = 0;
act.sa_mask = newset;
//安裝訊號
sigaction(SIGALRM,&act,NULL);
//等待訊號
pause();
printf("after exec ISR\n");
sigemptyset(&sysset);
//讀配置
sigprocmask(SIG_SETMASK,NULL,&sysset);
output(sysset);
}
訊號應用示例
建立兩個程序,父程序執行復制,子程序傳送訊號給父程序,查詢複製進度。
#include<stdio.h>
#include<fcntl.h>
#include<unistd.h>
#include<stdlib.h>
#include<string.h>
#include<signal.h>
int count;
int file_size;
//處理alarm訊號
void sig_alarm(int arg);
//處理普通訊號sigusr1
void sig_usr(int arg);
int main(int argc,char* argv[])
{
pid_t pid;
int i;
int fd_src,fd_des;
char buf[128];
if(argc != 3)
{
printf("check the format:comm src_file des_file\n");
return -1;
}
//只讀方式開啟原始檔
if((fd_src = open(argv[1],O_RDONLY)) == -1)
{
perror("open file src");
exit(-1);
}
//獲取檔案長度
file_size = lseek(fd_src,0,SEEK_END);
//重新獲取寫位置
lseek(fd_src,0,SEEK_SET);
//以讀方式開啟,若沒有則建立
if((fd_des = open(argv[2],O_RDWR|O_CREAT,0644)) == -1)
{
perror("open fd_des");
exit(-1);
}
//建立子程序
if((pid=fork()) == -1)
{
perror("fork");
exit(-1);
}
//父程序
else if(pid > 0)
{
//安裝訊號
signal(SIGUSR1,sig_usr);
do
{
memset(buf,'\0',128);
if((i=read(fd_src,buf,1)) == -1)
{
perror("read");
exit(-1);
}
else if(i == 0)
{
kill(pid,SIGINT);
break;
}
else
{
if(write(fd_des,buf,i) == -1)
{
perror("write");
exit(-1);
}
count += i;
}
}
while(i != 0);
//等待子程序退出,防止僵死程序
wait(pid,NULL,0);
exit(0);
}
//子程序
else if(pid == 0)
{
usleep(2);
//安裝訊號
signal(SIGALRM,sig_alarm);
ualarm(1,1);
//一直執行
while(1){;}
exit(0);
}
}
void sig_alarm(int arg)
{
//向父程序傳送sigalarm
kill(getppid(),SIGUSR1);
}
void sig_usr(int arg)
{
float i;
i = (float)count / (float) file_size;
printf("cureent over:%0.0f%%\n",i*100);
}