信号常用函数
kill()发信号的
--------------------------------------- send signal to a process
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
关于pid的解析:
If pid is positive, then signal sig is sent to the process with the ID
specified by pid.//如果pid是正数,那么就把指定的signal发给指定的进程
If pid equals 0, then sig is sent to every process in the process group
of the calling process.//如果pid=0的时候,那么signal将会发送给当前进程同组的所有进程(组内广播)
If pid equals -1, then sig is sent to every process for which the call‐
ing process has permission to send signals, except for process 1
(init), but see below.//当pid=-1的时候,signal将会发送给每一个当前进程有权限发送信号的每一个进程(全局广播信号,除了1号)
If pid is less than -1, then sig is sent to every process in the
process group whose ID is -pid.
If sig is 0, then no signal is sent, but error checking is still per‐
formed; this can be used to check for the existence of a process ID or
process group ID.//如果当前的信号是0的话,表示没有信号发送,用来检测一个进程或是进程组是否存在
RETURN VALUE:返回值
On success (at least one signal was sent), zero is returned. On error,
-1 is returned, and errno is set appropriately.
ERRORS:
EINVAL An invalid signal was specified.
EPERM The process does not have permission to send the signal to any
of the target processes.
ESRCH The pid or process group does not exist. Note that an existing
process might be a zombie, a process which already committed
termination, but has not yet been wait(2)ed for.
当指定sig=0时,可以检测进程或是进程组是否存在,那么如果返回值是-1能否可以确定这个进程或是进程组是不存在?不可以。
不一定表示,这个时候要看errno,如果是EPERM表明这个进程或是进程组存在但是你没有权限。如果是ESRCH说明进程或是进程组是真的不存在
raise()
--------- send a signal to the caller//给当前进程发送一个信号,自己给自己发
#include <signal.h>
int raise(int sig);
DESCRIPTION
The raise() function sends a signal to the calling process or thread.
In a single-threaded program it is equivalent to
kill(getpid(), sig);
In a multithreaded program it is equivalent to
pthread_kill(pthread_self(), sig);
RETURN VALUE(返回值)
raise() returns 0 on success, and nonzero for failure.
alarm
------------------------------ set an alarm clock for delivery of a signal
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
DESCRIPTION
alarm() arranges for a SIGALRM signal to be delivered to the calling
process in seconds seconds.//倒计时为0之后,发送给当前进程一个
SIGALRM的信号:SIGALRM的默认动作是杀掉当前进程
If seconds is zero, any pending alarm is canceled.
In any event any previously set alarm() is canceled.
例子:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
alarm(5);
while(1);
exit(0);
}
运行结果:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
alarm(10);
alarm(1);
alarm(5);
while(1);
exit(0);
}
运行时间是5秒钟
把alarm(1)放到最后,运行时间就是1秒,可以知道alarm没有办法实现多任务的计时器
pause
------------------------------ wait for signal
#include <unistd.h>
int pause(void);
例子:
这样程序就不是一个忙等的状态了
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{
alarm(10);
alarm(1);
alarm(5);
while(1)
pause()
exit(0);
}
sleep()
sleep->alarm+pause来封装的
sleep->nanosleep来封装的
做一个例子:
描述:定时循环,在一个时间内一直循环,一个数累加
从0开始,然后一直自累加
先用time函数来完成
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
int main()
{
time_t end;
end=time(NULL)+5;
int64_t count = 0;
while(time(NULL)<=end)
{
count++;
}
printf("%lld\n",count);
exit(0);
}
运行结果(和运行时间):
在这里插入图片描述
再用信号来实现
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static int loop=1;
static void alrm_handler(int s)
{
loop=0;
}
int main()
{
int64_t count=0;
alarm(5);
signal(SIGALRM,alrm_handler);
while(loop)
{
count++;
}
printf("%lld\n",count);
exit(0);
}
运行结果以及运行时间
比较来看,可以知道,信号的实现比使用time函数实现的时间精度高
并且信号的实现出的数值明显大,也就是说循环次数多。
对程序进行优化
运行发现时间很长,等不到结果
优化后认为loop值不变(可以看汇编代码)
所以这个时候要用到volatile关键字
改成如下代码:这个时候在做优化就不会出现什么问题了
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
static volatile int loop=1;
static void alrm_handler(int s)
{
loop=0;
}
int main()
{
int64_t count=0;
alarm(5);
signal(SIGALRM,alrm_handler);
while(loop)
{
count++;
}
printf("%lld\n",count);
exit(0);
}
运行结果:可以看出数值更大了,循环次数变得更多了
程序还有一个问题就是alarm语句和signal语句的顺序是错误的
在写程序的时候,我们应该将signal语句写在alarm语句之前,防止有延迟
实现一个mycat功能
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#define BUFSIZE 1024
int main(int argc,char *argv[])
{ int len;
int ret,pos;
int sfd,dfd=1;
char buf[BUFSIZE];
if(argc < 2)
{
fprintf(stderr,"Usage....\n");
exit(1);
}
sfd=open(argv[1],O_RDONLY);
if(sfd < 0)
{
perror("open():");
exit(1);
}
while(1)
{
len=read(sfd,buf,BUFSIZE);
if(len<0)
{
perror("read():");
break;
}
if(len==0)
{
break;
}
pos=0;
while(len>0)
{
ret=write(dfd,buf+pos,len);
if(ret<0)
{
perror("write()");
exit(1);
}
pos+=ret;
len-=ret;
}
}
close(sfd);
exit(0);
}
运行结果:
cat功能改编:以每秒钟几个字符是显示在终端上(流控)
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#define CPS 10
#define BUFSIZE CPS
static volatile int loop=0;
static void alrm_handler(int s)
{
alarm(1);
loop=1;
}
int main(int argc,char *argv[])
{ int len;
int ret,pos;
int sfd,dfd=1;
char buf[BUFSIZE];
if(argc < 2)
{
fprintf(stderr,"Usage....\n");
exit(1);
}
signal(SIGALRM,alrm_handler);
alarm(1);
sfd=open(argv[1],O_RDONLY);
if(sfd < 0)
{
perror("open():");
exit(1);
}
while(1)
{
while(!loop)
{
pause();
}
loop=0;
len=read(sfd,buf,BUFSIZE);
if(len<0)
{
perror("read():");
break;
}
if(len==0)
{
break;
}
pos=0;
while(len>0)
{
ret=write(dfd,buf+pos,len);
if(ret<0)
{
perror("write()");
exit(1);
}
pos+=ret;
len-=ret;
}
}
close(sfd);
exit(0);
}
运行结果:可以看到每秒打出10个字符的效果
以上程序算是一个漏桶
漏桶:海量的数据来了,还是按照每秒钟多少的量来输出
更希望程序达到的效果:
如果数据量突然增加,就去处理,如果没有数据量,也不要闲着,要攒着权限.
如果闲着三秒钟,那么就相当于攒下三秒钟的权限,之前有数据是一秒传输10个字符,那么现在就是攒下30个字符(令牌桶)。计算机上的数据,不是匀速传输的,所以漏桶的实例在计算机上较少使用。
令牌桶的实现:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#define CPS 10
#define BUFSIZE CPS
#define BURST 100
static volatile int token=0;
static void alrm_handler(int s)
{
alarm(1);
token++;
if(token>BURST)
{
token=BURST;
}
}
int main(int argc,char *argv[])
{ int len;
int ret,pos;
int sfd,dfd=1;
char buf[BUFSIZE];
if(argc < 2)
{
fprintf(stderr,"Usage....\n");
exit(1);
}
signal(SIGALRM,alrm_handler);
alarm(1);
sfd=open(argv[1],O_RDONLY);
if(sfd < 0)
{
perror("open():");
exit(1);
}
while(1)
{
while(token<=0)
{
pause();
}
token--;
while((len=read(sfd,buf,BUFSIZE))<0)
{
if(errno==EINTR)
continue;
perror("read():");
break;
}
if(len==0)
{
break;
}
pos=0;
while(len>0)
{
ret=write(dfd,buf+pos,len);
if(ret<0)
{
perror("write()");
exit(1);
}
pos+=ret;
len-=ret;
}
}
close(sfd);
exit(0);
}
将以上的程序重构成一个线程的库
main.c文件代码内容:
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include "mytbf.h"
#define CPS 10
#define BUFSIZE 1024
#define BURST 100
int main(int argc,char *argv[])
{ int len;
int ret,pos;
int size;
int sfd,dfd=1;
char buf[BUFSIZE];
mytbf_t *tbf;
if(argc < 2)
{
fprintf(stderr,"Usage....\n");
exit(1);
}
tbf=mytbf_init(CPS,BURST);
if(tbf==NULL)
{
fprintf(stderr,"mytbf_init() failed!\n");
exit(1);
}
sfd=open(argv[1],O_RDONLY);
if(sfd < 0)
{
perror("open():");
exit(1);
}
while(1)
{
size=mytbf_fetchtoken(tbf,BUFSIZE);
if(size<0)
{
fprintf(stderr,"mytbf_fetchtoken():%s\n",strerror(-size));
exit(-1);
}
while((len=read(sfd,buf,size))<0)
{
if(errno==EINTR)
continue;
perror("read():");
break;
}
if(len==0)
{
break;
}
if(size-len>0)
{
mytbf_returntoken(tbf,size-len);
}
pos=0;
while(len>0)
{
ret=write(dfd,buf+pos,len);
if(ret<0)
{
perror("write()");
exit(1);
}
pos+=ret;
len-=ret;
}
}
close(sfd);
mytbf_destroy(tbf);
exit(0);
}
mytbf.h文件代码内容
#ifndef MYTBF_H__
#define MYTBF_H__
#define MYTBF_MAX 1024
typedef void mytbf_t;
mytbf_t *mytbf_init(int cps,int burst);
int mytbf_fetchtoken(mytbf_t *,int);
int mytbf_returntoken(mytbf_t *,int);
int mytbf_destroy(mytbf_t*);
#endif
mytbf.c文件代码内容:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "mytbf.h"
typedef void (*sighandler_t)(int);
static int inited=0;
static sighandler_t alrm_handler_save;
struct mytbf_st
{
int cps;
int burst;
int token;
int pos;
};
static struct mytbf_st* job[MYTBF_MAX];
static void alrm_handler(int s)
{
int i=0;
alarm(1);
for(i=0;i<MYTBF_MAX;i++)
{
if(job[i]!=NULL)
{
job[i]->token +=job[i]->cps;
if(job[i]->token>job[i]->burst)
{
job[i]->token=job[i]->burst;
}
}
}
}
static void module_unload(void)
{
int i;
signal(SIGALRM,alrm_handler_save);
alarm(0);
for(i=0;i<MYTBF_MAX;i++)
{
free(job[i]);
}
}
static void module_load(void)
{
alrm_handler_save=signal(SIGALRM,alrm_handler);
alarm(1);
atexit(module_unload);
}
static int get_free_pos(void)
{
int i;
for(i=0;i<MYTBF_MAX;i++)
{
if(job[i]==NULL)
{
return i;
}
}
return -1;
}
mytbf_t *mytbf_init(int cps,int burst)
{
struct mytbf_st*me;
int pos;
if(!inited)
{
module_load();
} inited=1;
pos=get_free_pos();
if(pos<0)
{
return NULL;
}
me=malloc(sizeof(*me));
if(me==NULL)
{
return NULL;
}
me->token=0;
me->cps=cps;
me->burst=burst;
me->pos=pos;
job[pos]=me;
return me;
}
static int min(int a,int b)
{
if(a<b)
{
return a;
}
return b;
}
int mytbf_fetchtoken(mytbf_t *ptr,int size)
{
struct mytbf_st *me=ptr;
int n;
if(size<=0)
{
return -EINVAL;
}
while(me->token<=0)
{
pause();
}
n=min(me->token,size);
me->token-=n;
return n;
}
int mytbf_returntoken(mytbf_t *ptr,int size)
{
struct mytbf_st *me=ptr;
if(size<=0)
{
return -EINVAL;
}
me->token+=size;
if(me->token>me->burst)
{
me->token=me->burst;
}
return size;
}
int mytbf_destroy(mytbf_t *ptr)
{
struct mytbf_st *me=ptr;
job[me->pos]=NULL;
free(ptr);
return 0;
}
使用单一计时器,构造一组函数,实现任意数量的计时器
可以用alarm实现这个功能
还可以使用setitimer//精度控制的更好
自己尝试编写:
setitimer()//误差不累计,对于长期运行来说非常重要
#include <sys/time.h>
int getitimer(int which, struct itimerval *curr_value);
int setitimer(int which, const struct itimerval *new_value,
struct itimerval *old_value);
参数解析:
which:要设置哪个时钟
const struct itimerval *new_value:时钟周期
三种时钟:
ITIMER_REAL decrements in real time, and delivers SIGALRM upon expi‐
ration.//实时递减,递减到0为止,发出一个SIGALRM信号
ITIMER_VIRTUAL decrements only when the process is executing, and
delivers SIGVTALRM upon expiration.//虚拟时钟,倒计时为0之后,发送一个SIGVTALRM信号,只有当进程运行的时候才会递减
ITIMER_PROF decrements both when the process executes and when the
system is executing on behalf of the process. Coupled
with ITIMER_VIRTUAL, this timer is usually used to pro‐
file the time spent by the application in user and ker‐
nel space. SIGPROF is delivered upon expiration.//时间终止以后会发送一个SIGPROF信号,递减的是user+sys时间
struct itimerval {
struct timeval it_interval; /* Interval for periodic timer */
struct timeval it_value; /* Time until next expiration */
};//是一个原子化的赋值it_interval -》it_value
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
RETURN VALUE(返回值)
On success, zero is returned. On error, -1 is returned, and errno is
set appropriately.
由上面alarm代码改编:
#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <unistd.h>
#include <signal.h>
#include <sys/time.h>
#define CPS 10
#define BUFSIZE CPS
static volatile int loop=0;
static void alrm_handler(int s)
{
loop=1;
}
int main(int argc,char *argv[])
{ int len;
int ret,pos;
struct itimerval itv;
int sfd,dfd=1;
char buf[BUFSIZE];
if(argc < 2)
{
fprintf(stderr,"Usage....\n");
exit(1);
}
signal(SIGALRM,alrm_handler);
//alarm(1);
itv.it_interval.tv_sec=1;
itv.it_interval.tv_usec=0;
itv.it_value.tv_sec=1;
itv.it_value.tv_usec=0;
if(setitimer(ITIMER_REAL,&itv,NULL)<0)
{
perror("setitimer fail()");
exit(1);
}
sfd=open(argv[1],O_RDONLY);
if(sfd < 0)
{
perror("open():");
exit(1);
}
while(1)
{
while(!loop)
{
pause();
}
loop=0;
len=read(sfd,buf,BUFSIZE);
if(len<0)
{
perror("read():");
break;
}
if(len==0)
{
break;
}
pos=0;
while(len>0)
{
ret=write(dfd,buf+pos,len);
if(ret<0)
{
perror("write()");
exit(1);
}
pos+=ret;
len-=ret;
}
}
close(sfd);
exit(0);
}
abort
------------------- cause abnormal process termination
#include <stdlib.h>
void abort(void);
The abort() first unblocks the SIGABRT signal
目的:结束当前进程顺便产生一个core dump文件
system()
------------------------------如果在有信号相关的程序中,想正常使用system函数的话,就需要block一个信号,ignore两个信号
#include <stdlib.h>
int system(const char *command);
During execution of the command, SIGCHLD will be blocked, and SIGINT
and SIGQUIT will be ignored。
这里system自己会实现阻塞和忽略的功能
sleep()----------------------------(自行man手册查看)
sleep->alarm+pause来封装的
sleep->nanosleep来封装的
nanosleep可以来替换sleep
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
struct timespec {
time_t tv_sec; /* seconds */
long tv_nsec; /* nanoseconds */
};
usleep()--------------------(自行man手册查看)
#include <unistd.h>
int usleep(useconds_t usec);//以微秒为单位
select()-----------------------(man手册查看)
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *exceptfds, struct timeval *timeout);
最后一个参数是超时设置
如果设置一个时间,在这个时间内发生了想要的行为,那么这个函数会正常返回
如果没有发生想要的行为,那么这个函数就会结束
如果前面都是无意义的参数,就最后一项设置了数值,那么就是一个安全可靠的休眠
信号集
相关函数:
信号集类型:sigset_t(就是一个整形)
#include <signal.h>
int sigemptyset(sigset_t *set);//把一个信号集的内容清空
int sigfillset(sigset_t *set);//把一个信号集置为全集
int sigaddset(sigset_t *set, int signum);//往一个集合当中添加一个信号
int sigdelset(sigset_t *set, int signum);//从一个集合当中删掉一个信号
int sigismember(const sigset_t *set, int signum);//判断signum是否存在于set当中
信号屏蔽字/pending集的处理
相关函数:
sigprocmask()
-------------------多用来干扰mask值的状态,决定信号什么时候响应
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
how有三个选项:
SIG_BLOCK
The set of blocked signals is the union of the current set and
the set argument.
SIG_UNBLOCK
The signals in set are removed from the current set of blocked
signals. It is permissible to attempt to unblock a signal which
is not blocked.
SIG_SETMASK
The set of blocked signals is set to the argument set.
const sigset_t *set:针对的对象
sigset_t *oldset:对mask进行how动作之前的状态
RETURN VALUE
sigprocmask() returns 0 on success and -1 on error. In the event of an
error, errno is set to indicate the cause.
实例;
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i,j;
sigset_t set;
signal(SIGINT,int_handler);
sigemptyset(&set);
sigaddset(&set,SIGINT);
for(j=0;j<1000;j++)
{
sigprocmask(SIG_BLOCK,&set,NULL);
for(i=0;i<5;i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigprocmask(SIG_UNBLOCK,&set,NULL);
}
exit(0);
}
运行结果:
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i,j;
sigset_t set,oset;
signal(SIGINT,int_handler);
sigemptyset(&set);
sigaddset(&set,SIGINT);
for(j=0;j<15;j++)
{
sigprocmask(SIG_BLOCK,&set,&oset);//阻塞,保存之前的状态
for(i=0;i<5;i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigprocmask(SIG_SETMASK,&oset,NULL);//恢复之前的状态
}
exit(0);
}
运行结果和上面的一样
上面的代码有个问题就是最后执行的解除阻塞,我们要保证进入这个模块和出去这个模块的状态不会发生改变,这个程序就没有考虑这一点,所以要做保存和恢复的工作。
改善:
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i,j;
sigset_t set,oset,saveset;
signal(SIGINT,int_handler);
sigemptyset(&set);
sigaddset(&set,SIGINT);
sigprocmask(SIG_UNBLOCK,&set,&saveset);//将之前的状态存在saveset当中
for(j=0;j<15;j++)
{
sigprocmask(SIG_BLOCK,&set,&oset);
for(i=0;i<5;i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigprocmask(SIG_SETMASK,&oset,NULL);
}
sigprocmask(SIG_SETMASK,&saveset,NULL);//对saveset进行恢复,对之前的状态不关心
exit(0);
}
红色的代码确保了在进入这个模块和出去这个模块,整体状态是不受影响的
扩展函数
sigsuspend()
----------------------------wait for a signal
#include <signal.h>
int sigsuspend(const sigset_t *mask);
pause也是wait for a signal
但在一些情况下pause就使用不了
代码实例:
信号驱动程序
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i,j;
sigset_t set,oset,saveset;
signal(SIGINT,int_handler);
sigemptyset(&set);
sigaddset(&set,SIGINT);
for(j=0;j<15;j++)
{
for(i=0;i<5;i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
pause();
}
exit(0);
}
运行结果描述:先输出五个*在下一行的时候就会暂停,等待信号的输入,当有信号输入的时候,就会继续输出星号
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i,j;
sigset_t set,oset,saveset;
signal(SIGINT,int_handler);
sigemptyset(&set);
sigaddset(&set,SIGINT);
sigprocmask(SIG_UNBLOCK,&set,&saveset);
for(j=0;j<15;j++)
{
sigprocmask(SIG_BLOCK,&set,&oset);
for(i=0;i<5;i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigprocmask(SIG_SETMASK,&oset,NULL);
pause();//这两句是非原子化的
}
sigprocmask(SIG_SETMASK,&saveset,NULL);
exit(0);
}
运行结果:
运行的时候,ctrl+c会让*继续打印,但是如果一行多个ctrl+c的信号,到下一行会响应,打印出一个!但是不会驱动程序打印星号。
因为在pause和上一个语句不是原子化执行的。
所以实现驱动使用pause是不可取的,这时候就需要使用sigsuspend
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
static void int_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i,j;
sigset_t set,oset,saveset;
signal(SIGINT,int_handler);
sigemptyset(&set);
sigaddset(&set,SIGINT);
sigprocmask(SIG_UNBLOCK,&set,&saveset);
sigprocmask(SIG_BLOCK,&set,&oset);
for(j=0;j<15;j++)
{
for(i=0;i<5;i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigsuspend(&oset);
/*
sigset_t tmpset;
sigprocmask(SIG_SETMASK,&oset,&tmpset);
pause();
sigprocmask(SIG_SETMASK,&tmpset,NULL);
*/ 这几句相当于sigsuspend的原子化操作
}
sigprocmask(SIG_SETMASK,&saveset,NULL);
exit(0);
}
运行结果:
当一行有多个信号时,在下一行响应并且驱动程序打印*
sigaction()
----------------------可以用来替换signal
signal在使用上时有缺陷的:
1、在之前系统日志的实例中,最后的两个语句是执行不到的,重构这个程序
signal在多个信号共用一个信号处理函数的时候不太妥当
#include <signal.h>
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);
};
第二个信号处理函数的第三个参数,第三个参数表示的是被打断的前面的现场
相关函数:
#include <ucontext.h>
int getcontext(ucontext_t *ucp);
int setcontext(const ucontext_t *ucp);
ucp可以说是一个可还原的执行现场
siginfo_t {
int si_signo; /* Signal number */
int si_errno; /* An errno value */
int si_code; /* Signal code */记录当前信号是从哪里来的
int si_trapno; /* Trap number that caused
hardware-generated signal
(unused on most architectures) */
pid_t si_pid; /* Sending process ID */
uid_t si_uid; /* Real user ID of sending process */
int si_status; /* Exit value or signal */
clock_t si_utime; /* User time consumed */
clock_t si_stime; /* System time consumed */
sigval_t si_value; /* Signal value */
int si_int; /* POSIX.1b signal */
void *si_ptr; /* POSIX.1b signal */
int si_overrun; /* Timer overrun count;
POSIX.1b timers */
int si_timerid; /* Timer ID; POSIX.1b timers */
void *si_addr; /* Memory location which caused fault */
long si_band; /* Band event (was int in
glibc 2.3.2 and earlier) */
int si_fd; /* File descriptor */
short si_addr_lsb; /* Least significant bit of address
(since Linux 2.6.32) */
void *si_call_addr; /* Address of system call instruction
(since Linux 3.5) */
int si_syscall; /* Number of attempted system call
(since Linux 3.5) */
unsigned int si_arch; /* Architecture of attempted system call
(since Linux 3.5) */
}//记录收到的信号的属性信息(有些平台是定义为共用体)
For a regular signal, the following list shows the values which can be
placed in si_code for any signal, along with reason that the signal was
generated.
SI_USER
kill(2).
SI_KERNEL
Sent by the kernel.
SI_QUEUE
sigqueue(3).
SI_TIMER
POSIX timer expired.
#include <stdio.h>
#include <errno.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <syslog.h>
#include <signal.h>
#define FILENAME "/tmp/out"
static FILE *fp;
static int deamonize(void)
{
int fd;
pid_t pid;
pid=fork();
if(pid<0)
{
return -1;
}
if(pid>0) //parent
{
exit(0);
}
fd=open("/dev/null",O_RDWR);
if(fd<0)
{
return -1;
}
dup2(fd,0);
dup2(fd,1);
dup2(fd,2);
if(fd>2)
{
close(fd);
}
setsid();
chdir("/");
umask(0);
return 0;
}
static void deamon_exit(int s)
{
/*
if(s==SIGINT)
do sth;
else if(s==SIGTERM)
do sth;
else(s==....)
...
*/
fclose(fp);
closelog();
exit(0);
}
int main()
{
openlog("mydeamon",LOG_PID,LOG_DAEMON);
if(deamonize())
{
syslog(LOG_ERR,"deamonize() failed!");
exit(1);
}
else
{
syslog(LOG_INFO,"deamonize() successded!");
}
int i;
struct sigaction sa;
sa.sa_handler=deamon_exit;
sigemptyset(&sa.sa_mask);
sigaddset(&sa.sa_mask,SIGQUIT);
sigaddset(&sa.sa_mask,SIGTERM);
sigaddset(&sa,sa_mask,SIGINT);
sa.sa_flags=0;
sigaction(SIGINT,&sa,NULL);
sigaction(SIGQUIT,&sa,NULL);
sigaction(SIGTERM,&sa,NULL);
/*signal(SIGINT,deamon_exit);
signal(SIGQUIT,deamon_exit);
signal(SIGTERM,deamon_exit);
这里面的会发生重入的可能,所以用上面的sigaction函数来实现*/
fp=fopen(FILENAME,"w");
if(fp==NULL)
{
syslog(LOG_ERR,"fopen:%s",strerror(errno));
exit(1);
}
syslog(LOG_INFO,"%s was open",FILENAME);
for(i=0;;i++)
{
fprintf(fp,"%d\n",i);
fflush(fp);
syslog(LOG_DEBUG,"%d is printed",i);
sleep(1);
}
fclose(fp);
closelog();
exit(0);
}
还有之前写的流量控制的代码,当我们在终端上不听发送信号的时候,这个时候流量控制就不成立了。(在另一个终端上while ture ; do kill - ALRM 进程号; done )相当于从user的角度,给某一个指定的进程发送一个信号,这个问题signal是没有办法解决的,这个问题的根源是在于signal并没有管信号的来源,信号的属性信息等等,并没有区分信号的来源是哪里,直接执行这个信号的行为。如果可以区分信号的来源,可以指定只响应从kernel来的信号。因为在程序当中利用alrm来发信号,实际上这个信号是从kernel来的,而我们在终端上发送的信号明显就是user态来的。就要用到sigaction函数
重构流控文件。
mytbf.c文件的代码:
#include <stdio.h>
#include <errno.h>
#include <stdlib.h>
#include <signal.h>
#include <unistd.h>
#include "mytbf.h"
#include <sys/time.h>
static int inited=0;
//static sighandler_t alrm_handler_save;
static struct sigaction alrm_sa_save;
struct mytbf_st
{
int cps;
int burst;
int token;
int pos;
};
static struct mytbf_st* job[MYTBF_MAX];
static void alrm_action(int s,siginfo_t *infop,void *unused)
{
int i=0;
// alarm(1);
if(infop->si_code!=SI_KERNEL)
{
return;
}
for(i=0;i<MYTBF_MAX;i++)
{
if(job[i]!=NULL)
{
job[i]->token +=job[i]->cps;
if(job[i]->token>job[i]->burst)
{
job[i]->token=job[i]->burst;
}
}
}
}
static void module_unload(void)
{
int i;
/*signal(SIGALRM,alrm_handler_save);
alarm(0);*/
struct itimerval itv;
sigaction(SIGALRM,&alrm_sa_save,NULL);
itv.it_interval.tv_sec=0;
itv.it_interval.tv_usec=0;
itv.it_value.tv_sec=0;
itv.it_value.tv_usec=0;
setitimer(ITIMER_REAL,&itv,NULL);
for(i=0;i<MYTBF_MAX;i++)
{
free(job[i]);
}
}
static void module_load(void)
{
/* alrm_handler_save=signal(SIGALRM,alrm_handler);
alarm(1);*/
struct sigaction sa;
struct itimerval itv;
sa.sa_sigaction=alrm_action;
sigemptyset(&sa.sa_mask);
sa.sa_flags=SA_SIGINFO;
sigaction(SIGALRM,&sa,&alrm_sa_save);
/*if error*/
itv.it_interval.tv_sec=1;
itv.it_interval.tv_usec=0;
itv.it_value.tv_sec=1;
itv.it_value.tv_usec=0;
setitimer(ITIMER_REAL,&itv,NULL);
/*if error*/
atexit(module_unload);
}
static int get_free_pos(void)
{
int i;
for(i=0;i<MYTBF_MAX;i++)
{
if(job[i]==NULL)
{
return i;
}
}
return -1;
}
mytbf_t *mytbf_init(int cps,int burst)
{
struct mytbf_st*me;
int pos;
if(!inited)
{
module_load();
} inited=1;
pos=get_free_pos();
if(pos<0)
{
return NULL;
}
me=malloc(sizeof(*me));
if(me==NULL)
{
return NULL;
}
me->token=0;
me->cps=cps;
me->burst=burst;
me->pos=pos;
job[pos]=me;
return me;
}
static int min(int a,int b)
{
if(a<b)
{
return a;
}
return b;
}
int mytbf_fetchtoken(mytbf_t *ptr,int size)
{
struct mytbf_st *me=ptr;
int n;
if(size<=0)
{
return -EINVAL;
}
while(me->token<=0)
{
pause();
}
n=min(me->token,size);
me->token-=n;
return n;
}
int mytbf_returntoken(mytbf_t *ptr,int size)
{
struct mytbf_st *me=ptr;
if(size<=0)
{
return -EINVAL;
}
me->token+=size;
if(me->token>me->burst)
{
me->token=me->burst;
}
return size;
}
int mytbf_destroy(mytbf_t *ptr)
{
struct mytbf_st *me=ptr;
job[me->pos]=NULL;
free(ptr);
return 0;
}
流控一直成立
实时信号的相关内容
1、实时信号是要排队的
2、实时信号的响应是有个顺序要求
如果既有实时信号也有标准信号,首先响应的是标准信号
实例:
#include <stdio.h>
#include <stdlib.h>
#include<signal.h>
#include <unistd.h>
#define MYRTSIG (SIGRTMIN+6)
static void mysig_handler(int s)
{
write(1,"!",1);
}
int main()
{
int i,j;
sigset_t set,oset,saveset;
signal(MYRTSIG,mysig_handler);
sigemptyset(&set);
sigaddset(&set,MYRTSIG);
sigprocmask(SIG_UNBLOCK,&set,&saveset);
sigprocmask(SIG_BLOCK,&set,&oset);
for(j=0;j<15;j++)
{
for(i=0;i<5;i++)
{
write(1,"*",1);
sleep(1);
}
write(1,"\n",1);
sigsuspend(&oset);
}
sigprocmask(SIG_SETMASK,&saveset,NULL);
exit(0);
}
运行结果表示,实时信号不会丢失
实时信号的排队不可以无穷多个,通过命令ulmit -a
说明可以排15832个