Linux提供了应用编程接口,通过这些接口,进程可以向其他进程或进程组发送信号。root权限的进程可以向任何进程发送信号,非root权限的进程智能向属于同一个回话或同一个用户的进程发送信号。
###发送信号
常用的函数原型如下
/*
向进程发送信号
pid>0 进程ID为pid的进程
pid=0 同一进程组的进程
pid<0 && pid!=-1 进程组ID为-pid的所有进程
pid=-1 除发送给进程自身外,还发送给所有进程ID>1的进程
成功返回0,否则-1
*/
int kill(pid_t pid,int signo);
/*向进程本身发送信号,等价于kill(getpid(),sig),成功返回0,否则-1*/
int raise(int signo);
/*向进程发送SIGABORT信号,默认情况下进程会退出*/
void abort(void);
/*
向进程发送实时信号
pid 接收信号的进程ID,只能向一个进程发送信号
sig 指定即将发送的信号
val指定信号传递的参数
成功返回0,否则-1
*/
int sigqueue(pid_t pid,int sig,const union sigval val);
union sigval
{
int sival_int; //传送一个整形数
void *sival_ptr; //传送任何数据结构的指针
};
typedef struct {
int si_signo;
int si_code;
union sigval si_value;
int si_errno;
pid_t si_pid;
uid_t si_uid;
void *si_addr;
int si_status;
int si_band;
} siginfo_t;
案例:使用sigqueue发送带参数的信号
#include <signal.h>
#include <stdio.h>
#include <unistd.h>
void SigHandler(int signo,siginfo_t *info,void *context)
{
printf("%s\n",(char *)info->si_value.sival_ptr);
}
int main()
{
struct sigaction sigAct;
sigval_t val;
char *pMsg = "i still believe";
sigAct.sa_flags = SA_SIGINFO;
sigAct.sa_sigaction=SigHandler;
if(sigaction(SIGUSR1,&sigAct,NULL)==-1)
{
printf("fail set sig_handler");
return 1;
}
val.sival_ptr=pMsg;
if(sigqueue(getpid(),SIGUSR1,val)==-1)
{
printf("fail send sigqueue");
return 2;
}
sleep(3);
}
###sleep睡眠延时
可以使用sleep函数将程序延迟一段时间后继续执行,其实现机制是:
- 调用alarm函数设置延迟时间
- 调用pause函数挂起进程,等待系统发送SIGALARM信号,当SIGALARM信号到达进程时,进程被唤醒。
/*
设置时间闹钟
seconds表示闹钟间隔时间,原有闹钟无效
若调用alarm函数前,进程已经设置了闹钟,则返回上一个闹钟剩余时间,否则返回0
*/
unsigned int alarm(unsigned int seconds);
//等待信号,进程收到信号后,执行信号处理函数,pause函数返回,原进程继续执行
void pause();
案例:实现sleep函数
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
void alarmhandler(int signum)
{
printf("Alarm received from kernel\n");
}
int mysleep(unsigned int time)
{
printf("about to sleep for %d seconds\n",time);
signal(SIGALRM,alarmhandler);
alarm(time);
pause();
printf("continue from alarm \n");
return 0;
}
int main(int argc,char *argv[])
{
printf("start run the program.\n");
unsigned int time = atoi(argv[1]);
mysleep(time);
printf("i am awake,haha\n");
return 0;
}
间隔计时器
alarm函数计时单位是秒,当延迟时间到来,只能触发一次。不能满足需要高精度时间、有周期性定时需求的需求。为此,引入间隔计时器,其原理是:
当等待时间来到,内核向处于等待状态的进程发送信号,同时,再次设置时间间隔。间隔计时器属于面向进程的计时器。
进程运行时间
通常,LInux系统最小时钟间隔是10ms,意味着每秒产生100个时钟中断。进程以时间片的形式分享CPU,进程的执行有两种模式:用户态和内核态。当进程执行的是用户地址空间的代码,称进程运行在用户态;当进程进入系统调用或硬件中断,称进程运行在内核态。此外,进程还有休眠态,即将CPU交给其他进程。所以进程并非时刻都在运行,而是在用户态、内核态、休眠态之间切换。
由此内核提供三种计时器:
- 真实时间 用户态+内核态+休眠态时间
- 虚拟时间 用户态时间
- 实用时间 用户态+内核态时间
/*
获得当前进程中指定类型间隔计时器的值
which 计时器类型
ITIMER_REAL 真实时间,经过指定时间,内核发送SIGALRM限号
ITIMER_VIRTUAL 用户态时间,经过指定时间,内核发送SIGVTALRM信号
ITIMER_PROF 实用时间,经过指定时间,内核发送SIGPRT信号
value 存储获得的间隔计时器的值
*/
int getitimer(int which,struct itmerval *value);
struct itimerval
{
struct timeval it_interval; //下一个值
struct timeval it_value //当前值
};
struct timeval
{
long tv_sec; //秒
long tv_usec; //微秒
};
/*
设置间隔计时器
which 指定定时器类型
newval指向被设置值
oldval指向被替换设置值
成功返回0,否则-1
若oldval不为NULL,之前计时器的值将被复制到oldval
*/
int setitimer(int which,const struct itimerval *newval,struct itimerval *oldval);