5.setitimer()函数
现在的系统中很多程序不再使用alarm调用,而是使用setitimer调用来设置定时器,用getitimer来得到定时器的状态。
误差不累计;
#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
表示类型,可选的值有:
ITIMER_REAL:按实际时间计时,计时到达将给进程发送SIGALRM信号。
ITIMER_VIRTUAL:仅当进程执行时才进行计时。计时到达将发送SIGVTALRM信号给进程。
ITIMER_PROF:当进程执行时和系统为该进程执行动作时都计时。与ITIMER_VIR-TUAL是一对,该定时器经常用来统计进程在用户态和内核态花费的时间。计时到达将发送SIGPROF信号给进程。
new_value和old_value均为itimerval结构体;
new_value:
用来对计时器进行设置,it_interval为计时间隔,it_value为第一次延时时长,后面时间间隔就为it_interval。
old_value
通常使用不上。设置为NULL,它是用来存储上一次setitimer调用时设置的new_value值。
返回值:
成功返回0,失败返回-1。
struct itimerval {
struct timeval it_interval; /* next value */
struct timeval it_value; /* current value */
};
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
it_interval
表示两次定时任务之间的时间间隔
it_value
表示设置定时器后间隔多久开始执行定时任务;
工作机制:
先对it_value倒计时,当it_value为零时触发信号。然后重置为it_interval(原子操作)。然后继续对it_value倒计时,一直这样循环下去。
基于此机制。setitimer既能够用来延时运行,也可定时运行。
itimeval由两个timeval结构体组成,timeval包括tv_sec和tv_usec两部分。其中tv_se为秒,tv_usec为微秒(即1/1000000秒)
假如it_value为0是不会触发信号的,所以要能触发信号,it_value得大于0;假设it_interval为零,仅仅会延时。不会定时(也就是说仅仅会触发一次信号)。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <signal.h>
#include <sys/time.h>
#define CPS 10
#define BUFFSIZE CPS
static volatile int loop = 0;
static void alrm_handler(int s)
{
//alarm(1);
loop = 1;
}
int main(int argc, char* argv[])
{
if(argc < 2)
{
fprintf(stderr, "usage: %s <src_file>", argv[0]);
exit(1);
}
int sfd, dfd = 1;
int ret;
int wet;
int pos;
char buf[BUFFSIZE];
struct itimerval itv;
do
{
sfd = open(argv[1], O_RDONLY);
if(sfd < 0)
{
if(errno != EINTR)
{
perror("open src_file");
exit(1);
}
}
}while(sfd < 0);
signal(SIGALRM, alrm_handler);
//alarm(1);
itv.it_interval.tv_sec = 1;
itv.it_interval.tv_usec = 0;
itv.it_value.tv_sec = 5;
itv.it_value.tv_usec = 0;
if(setitimer(ITIMER_REAL, &itv, NULL) < 0)
{
perror("setitimer()");
exit(1);
}
while(1)
{
while(!loop)
pause; //此处使用pause能够让当前进程休眠,不用一直监测
//; 会让进程一直监测,占用cpu资源
loop= 0;
while((ret = read(sfd, buf, BUFFSIZE)) < 0)
{
if(errno == EINTR)
continue;
perror("read src_file");
break;
}
if(ret == 0)
{
break;
}
//wet = write(dfd, buf, BUFFSIZE);
//检测如果没有一次写完,就继续写,直至把这次读到的数据全部写完
pos = 0;
while(ret > 0)
{
wet = write(dfd, buf+pos, ret);
if(wet < 0)
{
if(errno == EINTR)
continue;
perror("write des_file");
exit(1);
}
pos += wet;
ret -= wet;
}
}
close(sfd);
printf("\n");
exit(0);
}
6.abort()函数
给当前进程发送SIGABRT信号,结束当前进程并产生core dump文件。
7.system()函数
可以简单的理解为exec + wait + fork的封装;
如果在有信号相关的程序当中,想正常使用system函数,就要:
1.block住SIGCHLD信号
2.忽略掉SIGINT和SIGQUIT
8.sleep()函数
可以用nanosleep()、usleep()和select()代替实现休眠;
9.信号集
信号集类型:sigset_t
sigemptyset();
sigfillset();
sigaddset();
sigdelset();
sigismember();
10.信号屏蔽字/pending集的处理
sigpromask(); //给定一种接口可以控制pending位图中的mask
不能决定信号什么时候到来,但是可以通过此函数决定信号什么时候被响应;
#include <signal.h>
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
参数:
how:
用于指定信号修改的方式,可能选择有三种
SIG_BLOCK //加入信号到进程屏蔽。
SIG_UNBLOCK //从进程屏蔽里将信号删除。
SIG_SETMASK //将set的值设定为新的进程屏蔽。
set:
为指向信号集的指针,在此专指新设的信号集,如果仅想读取现在的屏蔽值,可将其置为NULL。
oldset:
也是指向信号集的指针,在此存放操作之前的信号集。
返回:
成功执行时,返回0。失败返回-1,errno被设为EINVAL。
sigprocmask就是用来在进程执行过程中,对某段程序
不想让其它的信号量影响其执行而设计的,一般在程序段开始处调用一次,屏蔽需要屏蔽的信号量,
在程序段结束的地方调用一次,恢复被屏蔽的信号量。
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
void func(int i)
{
write(1, "!", 1);
}
void hook(void)
{
write(1, "\n", 1);
}
int main()
{
int i,j;
sigset_t set, saveset;
signal(SIGINT, func);
sigemptyset(&set);//集合初始化为空
sigaddset(&set, SIGINT);//加入SIGINIT信号
sigprocmask(SIG_UNBLOCK, &set, &saveset);//将之前信号状态保存到saveset中
for(j = 0; j < 100; 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);
}
sigprocmask(SIG_SETMASK, &saveset, NULL);
atexit(hook);
exit(0);
}
答应期间,SIGINT被屏蔽,直到换行后,才恢复执行信号的动作。
多个SIGINT在一行中只打印一个*,因为标准信号的丢失问题。
其中,sigprocmask(SIG_UNBLOCK, &set, &saveset);和sigprocmask(SIG_SETMASK, &saveset, NULL)保证了经过此模块,整体的信号状态不会受影响。
int i,j;
sigset_t set, oset;
signal(SIGINT, func);
sigemptyset(&set);//集合初始化为空
sigaddset(&set, SIGINT);//加入SIGINIT信号
for(j = 0; j < 100; 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);
}
atexit(hook);
exit(0);
11.sigsuspend()函数
#include <signal.h>
int sigsuspend(const sigset_t *mask);
参数:
@mask 希望屏蔽的信号
返回值:
sigsuspend返回后将恢复调用之前的的信号掩码。信号处理函数完成后,进程将继续执行。该系统调用始终返回-1,并将errno设置为EINTR.
备注:
进程执行到sigsuspend时,sigsuspend并不会立刻返回,进程处于TASK_INTERRUPTIBLE状态并立刻放弃CPU,
等待UNBLOCK(mask之外的)信号的唤醒。进程在接收到UNBLOCK(mask之外)信号后,调用处理函数,然后还原信号集,
sigsuspend返回,进程恢复执行。
使用场景:
sigsuspend() 函数可以更改进程的信号屏蔽字可以阻塞所选择的信号,或解除对它们的阻塞。
使用这种技术可以保护不希望由信号中断的代码临界区。
如果希望对一个信号解除阻塞,然后pause等待以前被阻塞的信号发生,那么必须使用 sigsuspend() 函数
pause() 函数无法达成上述目的
信号驱动程序
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
/* 1.打印一行*期间,进程不能被信号打断 2.打印一行换行后,接收到信号在继续打印下一行 3.打印一行*期间,到来的多个标准信号会有丢失,后续只响应一个标准信号 */
void func(int i)
{
write(1, "!", 1);
}
void hook(void)
{
write(1, "\n", 1);
}
int main()
{
int i,j;
sigset_t set, oset, saveset;
signal(SIGINT, func);
sigemptyset(&set);//集合初始化为空
sigaddset(&set, SIGINT);
sigprocmask(SIG_UNBLOCK, &set, &saveset);//将之前信号状态保存到saveset中
sigprocmask(SIG_BLOCK, &set, &oset); //对set里信号进行block并保存之前的状态到oset里面去
for(j = 0; j < 100; j++)
{
for(i = 0; i < 5; i++)
{
write(1, "*", 1);
sleep(1);
}
write(1, "\n", 1);
sigsuspend(&oset); //原子操作,解除set信号的阻塞后,马上进入等待阶段
/* sigset_t tmpset; sigprocmask(SIG_SETMASK, &oset, &tmpset); //恢复成oset里面的状态并将set里面的状态保存到tmpset中 pause(); sigprocmask(SIG_SETMASK, &tmpset, NULL); */
}
sigprocmask(SIG_SETMASK, &saveset, NULL); //做恢复用
atexit(hook);
exit(0);
}
打印*期间发送中断信号,换行后会响应此信号。
12.sigaction()函数
signal 函数的使用方法简单,但并不属于 POSIX 标准,在各类 UNIX 平台上的实现不尽相同,因此其用途受到了一定的限制。而 POSIX 标准定义的信号处理接口是 sigaction 函数,其接口头文件及原型如下:
int sigaction(int signum, const struct sigaction *act,
struct sigaction *oldact);
struct sigaction 类型用来描述对信号的处理,定义如下:
struct sigaction
{
void (*sa_handler)(int);
void (*sa_sigaction)(int, siginfo_t *, void *);
sigset_t sa_mask;
int sa_flags;
void (*sa_restorer)(void);
};
在这个结构体中,成员 sa_handler 是一个函数指针,其含义与 signal 函数中的信号处理函数类似。成员sa_sigaction 则是另一个信号处理函数,它有三个参数,可以获得关于信号的更详细的信息。当 sa_flags 成员的值包含了 SA_SIGINFO 标志时,系统将使用 sa_sigaction 函数作为信号处理函数,否则使用 sa_handler 作为信号处理函数。在某些系统中,成员 sa_handler 与 sa_sigaction 被放在联合体中,因此使用时不要同时设置。sa_mask 成员用来指定在信号处理函数执行期间需要被屏蔽的信号,特别是当某个信号被处理时,它自身会被自动放入进程的信号掩码,因此在信号处理函数执行期间这个信号不会再度发生。 sa_flags 成员用于指定信号处理的行为,它可以是一下值的“按位或”组合。
◆ SA_RESTART:使被信号打断的系统调用自动重新发起。
◆ SA_NOCLDSTOP:使父进程在它的子进程暂停或继续运行时不会收到 SIGCHLD 信号。
◆ SA_NOCLDWAIT:使父进程在它的子进程退出时不会收到 SIGCHLD 信号,这时子进程如果退出也不会成为僵尸进程。
◆ SA_NODEFER:使对信号的屏蔽无效,即在信号处理函数执行期间仍能发出这个信号。
◆ SA_RESETHAND:信号处理之后重新设置为默认的处理方式。
◆ SA_SIGINFO:使用 sa_sigaction 成员而不是 sa_handler 作为信号处理函数。
re_restorer 成员则是一个已经废弃的数据域,不要使用。
siginfo_t 结构体
The siginfo_t argument to sa_sigaction is a struct with the following fields:
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适用于SIGCHLD,代表被终止进程的PID */
uid_t si_uid; /* Real user ID of sending process适用于SIGCHLD,代表被终止进程所拥有进程的UID */
int si_status; /* Exit value or signal 适用于SIGCHLD,代表被终止进程的状态 */
clock_t si_utime; /* User time consumed 适用于SIGCHLD,代表被终止进程所消耗的用户时间 */
clock_t si_stime; /* System time consumed 适用于SIGCHLD,代表被终止进程所消耗系统的时间 */
==================
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) */
}
1.当多个信号共用同一个处理函数的时候,希望在响应某一个信号期间,把其他信号阻塞住。
signal函数有这种问题,可以用sigaction()函数
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <syslog.h>
#include <errno.h>
#include <string.h>
#include <signal.h>
#define FNAME "/tmp/out"
static FILE* fp;
static int daemonize(void)
{
pid_t pid;
int fd;
pid = fork();
if(pid < 0)
return -1;
if(pid > 0)
{
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("/"); //将工作路径设置到根路径下
return 0;
}
//添加部分,s是对多个信号进行区分,可以进行不同的处理
//碰到异常情况退出时,需要做的事情
static void daemon_exit(int s)
{
/* if(s == SIGINT) else if(s == SIGQUIT) else(s == SIGQUIT) */
fclose(fp);
closelog();
exit(0);//可以调用钩子函数
}
int main()
{
int i;
struct sigaction sa;
sa.sa_handler = daemon_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(SIGQUIT, &sa, NULL);
/* 采用signal可能会造成信号重入。 因为信号可以嵌套,假如SIGINT响应后 signal(SIGINT, daemon_exit); signal(SIGQUIT, daemon_exit); signal(SIGTERM, daemon_exit); */
openlog("mydaemon", LOG_PID, LOG_DAEMON);
if(daemonize())
{
syslog(LOG_ERR, "daemonize() failed!");
exit(1);
}
else
{
syslog(LOG_INFO, "daemonize() sucess");
}
fp = fopen(FNAME, "w");
if(fp == NULL)
{
syslog(LOG_ERR, "fopen(): %s", strerror(errno));
exit(1);
}
syslog(LOG_INFO, "%s was opened.", FNAME);
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);
}
2.signal函数不能区分信号的来源,只是接收到信号然后执行此信号对应的行为。
sigation函数可以区分。
参见令牌桶算法(二)
13.实时信号
标准信号的响应顺序是未定义的;
如果进程中收到标准和实时信号,则先响应标准信号;
用到的信号是否排队,相应是否有要求,取决于用哪种信号。
实时信号终端查看:
实时信号最多能排多长:
#include<stdio.h>
#include<stdlib.h>
#include<signal.h>
#define MYRTSIG (SIGRTMIN+6)
/* 1.打印一行*期间,进程不能被信号打断 2.打印一行换行后,接收到信号在继续打印下一行 3.打印一行*期间,会对过来的多个实时信号依次响应 */
void func(int i)
{
write(1, "!", 1);
}
void hook(void)
{
write(1, "\n", 1);
}
int main()
{
int i,j;
sigset_t set, oset, saveset;
signal(MYRTSIG, func); //接收信号类型为实时信号
sigemptyset(&set);//集合初始化为空
sigaddset(&set, MYRTSIG);
sigprocmask(SIG_UNBLOCK, &set, &saveset);//将之前信号状态保存到saveset中
sigprocmask(SIG_BLOCK, &set, &oset); //对set里信号进行block并保存之前的状态到oset里面去
for(j = 0; j < 100; j++)
{
for(i = 0; i < 5; i++)
{
write(1, "*", 1);
sleep(1);
}
write(1, "\n", 1);
sigsuspend(&oset); //原子操作,解除set信号的阻塞后,马上进入等待阶段
}
sigprocmask(SIG_SETMASK, &saveset, NULL); //做恢复用
atexit(hook);
exit(0);
}
文章评论