信号
linux提供的信号机制是一种进程间异步的通信机制,在实现上是一种软中断。信号可以导致一个正在运行的进程被另一个异步进程中断。
信号的处理流程
产生信号:产生信号有多种说法。一个进程创建一个信号用于发送给另一个进程叫发送一个信号;内核创建一个信号叫生成一个信号;
一个进程向自己发送一个信号叫唤起一个信号
为使某个信号到来时进程能够执行相应的中断服务程序,即设置某信号到来时执行的代码,称为安装中断
如果一个信号被正确发送到一个进程称为信号被递给。
如果一个信号的递送导致一段处理程序被执行,称为该信号被捕捉。
如果一个信号被发送并且还没有引起任何动作(一般是对应进程阻塞了此信号),称为信号处于等待状态。
产生信号
1 kill产生一个信号
kill(getpid(), sig);
2 raise自举一个信号
int raise(int sig);
3 alarm定时
#include
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
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
2 sigaction安装信号
#include
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
使用例子
#include
#include
#include
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
#include
#include
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
#include
#include
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
#include
#include
#include
#include
#include
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);
}