Linux用户态程序定时器——POSIX定时器

背景

    客户端和服务器采用异步请求/回复模式,客户端需要为每个API设置超时回调处理。

方案

    采用posix定时器。
    

posix定时器


    主要接口函数:
1)创建一个定时器:
    int timer_create(clockid_t clock_id, struct sigevent *evp, timer_t *timerid)

2)启动一个定时器:
    timer_create()所创建的定时器并未启动。要将它关联到一个到期时间以及启动时钟周期,可以使用timer_settime()。
    int timer_settime(timer_t timerid, int flags, const struct itimerspec *value, struct itimerspect *ovalue);

3)获得一个活动定时器的剩余时间:
int timer_gettime(timer_t timerid,struct itimerspec *value);

4)删除一个定时器:
    int timer_delete (timer_t timerid);
    
接口函数的参数解释,请通过man命令查看。

实例

timeout-handler.h

/***********************************************************
*
* File name  : timeout-handler.h
* Version    : 1.0
* Description: posix real timer test
*
************************************************************/

#ifndef _TIMEOUT_HANDLER_H_
#define _TIMEOUT_HANDLER_H_

#include 
#include 

#define MAX_TIMER_NUM	32
#define SIGNUM_RT_TIMER  (SIGRTMIN + 2)

typedef enum
{
	CLIENT_MIN_TIMER_ID,
	HEART_BEAT_TIMER_ID,
	REGISTER_TIMER_ID,
	LOGIN_TIMER_ID,
	BIND_TIMER_ID,
	UNBIND_TIMER_ID,
	CLIENT_MAX_TIMER_ID
} CLIENT_TIMER_ID;

typedef struct
{
	char *name;			/* API name */
	int count;			/* timeout times */
	int expires;		/* general 5s */
	timer_t timer;		/* timer id */
	void (*cb)(void);	/* call back */
} REQUEST_TIMEOUT_OBJECT;

/* initialize request timeout times */
int initReqTimeoutCount(int idx);
/* start or stop timer */
int clientTimerSwitch(int idx, int type);
/* delete timer */
int	clientDeleteTimer(timer_t timer_id);
/* delete all timer */
int clientDeleteAllTimer();
/* init timer */
int initClientTimer();

#endif

timeout-handler.c

/***********************************************************
*
* File name  : timer-handler.c
* Version    : 1.0
* Description: timeout handler
*
************************************************************/

#include 
#include 

#include "client-timer.h"
#include "timeout-handler.h"

timer_t timers[MAX_TIMER_NUM] = {0};

static void clientHeartBeatExpired();
static void clientRegisterExpired();
static void clientLoginExpired();
static void clientBindExpired();
static void clientUnbindExpired();

REQUEST_TIMEOUT_OBJECT gReqTimeoutObj[] =
{
	[CLIENT_MIN_TIMER_ID] =
	{
		.name = "min timer",
		.count = 0,
		.expires = 5,
		.cb = NULL,
		.timer = 0
	},

	[HEART_BEAT_TIMER_ID] =
	{
		.name = "heartbeat",
		.count = 0,
		.expires = 3,
		.cb = clientHeartBeatExpired,
		.timer = 0
	},

	[REGISTER_TIMER_ID] =
	{
		.name = "register",
		.count = 0,
		.expires = 5,
		.cb = clientRegisterExpired,
		.timer = 0
	},

	[LOGIN_TIMER_ID] =
	{
		.name = "login",
		.count = 0,
		.expires = 7,
		.cb = clientLoginExpired,
		.timer = 0
	},

	[BIND_TIMER_ID] =
	{
		.name = "bind",
		.count = 0,
		.expires = 2,
		.cb = clientBindExpired,
		.timer = 0
	},

	[UNBIND_TIMER_ID] =
	{
		.name = "unbind",
		.count = 0,
		.expires = 5,
		.cb = clientUnbindExpired,
		.timer = 0
	},

	[CLIENT_MAX_TIMER_ID] =
	{
		.name = "max timer",
		.count = 0,
		.expires = 5,
		.cb = NULL,
		.timer = 0
	},
};

/******************************************************************************
* FUNCTION		: recordTimeoutCount()
* DESCRIPTION	: record timeout times for every API
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static int recordTimeoutCount(int idx)
{
	gReqTimeoutObj[idx].count++;
	
	MSG_LOG("API: %s, timeout %d times.\n", 
	gReqTimeoutObj[idx].name, gReqTimeoutObj[idx].count);
	
	return CLIENT_OK;
}

/******************************************************************************
* FUNCTION		: initReqTimeoutCount()
* DESCRIPTION	:
* INPUT 		:
* OUTPUT		:
* RETURN		:
******************************************************************************/
int initReqTimeoutCount(int idx)
{
	int i = 0;

	if (idx > CLIENT_MIN_TIMER_ID && idx < CLIENT_MAX_TIMER_ID)
	{
		gReqTimeoutObj[idx].count = 0;
		return 0;
	}

	MSG_LOG("initialize all count 0.\n");
	for (i = CLIENT_MIN_TIMER_ID + 1; i < CLIENT_MAX_TIMER_ID; i++)
	{
		gReqTimeoutObj[i].count = 0;
	}

	return 0;
}

/******************************************************************************
* FUNCTION		: clientHeartBeatExpired()
* DESCRIPTION	:
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static void clientHeartBeatExpired()
{
	MSG_LOG("heartBeat timeout!\n");
	(void)recordTimeoutCount(HEART_BEAT_TIMER_ID);
	return ;
}

/******************************************************************************
* FUNCTION		: clientRegisterExpired()
* DESCRIPTION	:
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static void clientRegisterExpired()
{
	MSG_LOG("register timeout!\n");
	(void)recordTimeoutCount(REGISTER_TIMER_ID);
	return ;
}

/******************************************************************************
* FUNCTION		: clientLoginExpired()
* DESCRIPTION	:
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static void clientLoginExpired()
{
	MSG_LOG("Login timeout!\n");
	(void)recordTimeoutCount(LOGIN_TIMER_ID);
	return ;
}

/******************************************************************************
* FUNCTION		: clientBindExpired()
* DESCRIPTION	:
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static void clientBindExpired()
{
	MSG_LOG("Bind timeout!\n");
	(void)recordTimeoutCount(BIND_TIMER_ID);
	return ;
}

/******************************************************************************
* FUNCTION		: clientRegisterExpired()
* DESCRIPTION	:
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static void clientUnbindExpired()
{
	MSG_LOG("Unbind timeout!\n");
	(void)recordTimeoutCount(UNBIND_TIMER_ID);
	return ;
}

/******************************************************************************
* FUNCTION		: clientTimerHandler()
* DESCRIPTION	:
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static void clientTimerHandler(int signum, siginfo_t *siginfo, void *context)
{
	int sigval = siginfo->si_value.sival_int;

	if (gReqTimeoutObj[sigval].cb)
	{
		gReqTimeoutObj[sigval].cb();
	}
}

/******************************************************************************
* FUNCTION		: clientCreateTimer()
* DESCRIPTION	: create a timer, when time's up, will send a signal "signum" to the handler_func
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static timer_t clientCreateTimer(int sigval)
{
	static int isHanderSet = 0;
	struct sigevent se;
	struct sigaction sa;
	int index = sigval;

	if (sigval < CLIENT_MIN_TIMER_ID || sigval > CLIENT_MAX_TIMER_ID)
	{
		MSG_ERR("tpCreateTimer: signum is too small\n");
		return 0;
	}

	/* signal already registered? */
	if (timers[index])
	{
		MSG_ERR("tpCreateTimer: signal %d has been used\n", sigval);
		return 0;
	}

	if (isHanderSet == 0)
	{
		memset(&sa, 0, sizeof(sa));
		sa.sa_sigaction = clientTimerHandler;
		sa.sa_flags = SA_SIGINFO;
		sa.sa_restorer = NULL;
		sigemptyset(&sa.sa_mask);

		if (sigaction(SIGNUM_RT_TIMER, &sa, NULL) < 0)
		{
			MSG_ERR("register timer signal cb failed. errno = %d.\n", errno);
			return 0;
		}

		isHanderSet = 1;
	}

	memset (&se, 0, sizeof (se));
	se.sigev_notify = SIGEV_SIGNAL;
	se.sigev_signo = SIGNUM_RT_TIMER;
	se.sigev_value.sival_int = sigval;
	se.sigev_notify_function = NULL;

	if (timer_create(CLOCK_MONOTONIC, &se, &timers[index]) < 0)
	{
		timers[index] = 0;
		MSG_ERR("timer_creat failed\n");
		return 0;
	}

	return timers[index];
}

/******************************************************************************
* FUNCTION		: initClientTimer()
* DESCRIPTION	:
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
int initClientTimer()
{
	int ret = CLIENT_ERR;
	int idx = 0;

	for (idx = CLIENT_MIN_TIMER_ID + 1; idx < CLIENT_MAX_TIMER_ID; idx++)
	{
		if (NULL == gReqTimeoutObj[idx].cb)
		{
			continue;
		}

		gReqTimeoutObj[idx].timer = clientCreateTimer(idx);
		if (0 >= gReqTimeoutObj[idx].timer)
		{
			MSG_ERR("create gReqTimeoutObj[%d] timer failed!\n", idx);
			return CLIENT_ERR;
		}
	}

	return CLIENT_OK;
}

/******************************************************************************
* FUNCTION		: clientSetTimer()
* DESCRIPTION	: let the timer run
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static int clientSetTimer(timer_t timer_id, int firstRun, int interval)
{
	struct itimerspec ts, ots;

	ts.it_value.tv_sec = firstRun;
	ts.it_value.tv_nsec =  0;
	ts.it_interval.tv_sec = interval;
	ts.it_interval.tv_nsec = 0;

	if (timer_settime(timer_id, 0, &ts, &ots) < 0)
	{
		MSG_ERR("setTimer failed!\n");
		return (-1);
	}

	return (0);
}

/******************************************************************************
* FUNCTION		: clientTimerSwitch()
* DESCRIPTION	: start or stop timer
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
int clientTimerSwitch(int idx, int type)
{
	if (idx <= CLIENT_MIN_TIMER_ID
		|| idx >= CLIENT_MAX_TIMER_ID)
	{
		MSG_ERR("idx of timer invalid.\n");
		return CLIENT_ERR;
	}

	/* stop timer */
	if (0 == type)
	{
		MSG_LOG("stop timer: %s.\n", gReqTimeoutObj[idx].name);
		return clientSetTimer(gReqTimeoutObj[idx].timer, 0, 0);
	}
	/* start timer */
	else
	{
		MSG_LOG("start timer: %s.\n", gReqTimeoutObj[idx].name);
		return clientSetTimer(gReqTimeoutObj[idx].timer, gReqTimeoutObj[idx].expires, 0);
	}
}

/******************************************************************************
* FUNCTION		: clientDeleteTimer()
* DESCRIPTION	: delete a time which has been created.
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
int clientDeleteTimer(timer_t timer_id)
{
	int i;

	for (i = 0; i < MAX_TIMER_NUM; i ++)
	{
		if (timers[i] == timer_id)
		{
			timers[i] = 0;
			return timer_delete(timer_id);
		}
	}

	MSG_LOG("delete timer id %d failed: not found\n", (int)timer_id);
	return -1;
}

/******************************************************************************
* FUNCTION		: clientDeleteAllTimer()
* DESCRIPTION	: delete all timer.
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
int clientDeleteAllTimer()
{
	int i = 0;
	int ret = -1;

	for (i = 0; i < MAX_TIMER_NUM; i ++)
	{
		if (0 < timers[i])
		{
			ret = timer_delete(timers[i]);
			if (0 != ret)
			{
				MSG_ERR("delete timer %d failed\n", (int)timers[i]);
				return CLIENT_ERR;
			}

			timers[i] = 0;
		}
	}

	MSG_LOG("delete timer successed!\n");
	return CLIENT_OK;
}


client-timer.h

/******************************************************************************
*
* FILE NAME  :	client-timer.h
* VERSION    :	1.0
* DESCRIPTION:	program for testing posix timer.
*
******************************************************************************/

#ifndef _CLIENT_TIMER_H_
#define _CLIENT_TIMER_H_

#include 

typedef struct
{
	int opera;	/* opera type */
	int timer;	/* timer index */
} OPEAR_ARR;

#define OPERA_NUM 5
#define SLEEP_TIME (OPERA_NUM + 2)

#define CLIENT_OK		0
#define CLIENT_ERR	-1

#define MSG_LOG(...) do { \
	printf("Function:%s, Line:%d.", __FUNCTION__, __LINE__); \
	printf(__VA_ARGS__); \
	printf("\n"); \
} while(0)

#define MSG_ERR(...) do { \
	printf("Function:%s, Line:%d.", __FUNCTION__, __LINE__); \
	printf(__VA_ARGS__); \
	printf("\n"); \
} while(0)

#endif


client-timer.c
/******************************************************************************
*
* FILE NAME  :	client-timer.c
* VERSION    :	1.0
* DESCRIPTION:	program for testing posix timer...
*
******************************************************************************/

#include 
#include "client-timer.h"
#include "timeout-handler.h"

/******************************************************************************
* FUNCTION		: clientClientInit()
* DESCRIPTION	: init environment for running
* INPUT			:
* OUTPUT		:
* RETURN		: 0:success; -1:failed
******************************************************************************/
int clientClientInit(void)
{
	int iRet = CLIENT_ERR;

	/* init timer, used to handle timeout event */
	iRet = initClientTimer();
	if (CLIENT_OK != iRet)
	{
		MSG_ERR("init client-client's timer failed!\n");
		(void)clientDeleteAllTimer();
		return CLIENT_ERR;
	}
	
	MSG_LOG("Init client-client environment successful.\n");
	return CLIENT_OK;
}

/******************************************************************************
* FUNCTION		: clientClientExit()
* DESCRIPTION	: clean source befor exit client client
* INPUT			:
* OUTPUT		:
* RETURN		: 0:success; -1:failed
******************************************************************************/
int clientClientExit(void)
{
	int iRet = CLIENT_ERR;

	iRet = clientDeleteAllTimer();
	if (CLIENT_OK != iRet)
	{
		MSG_ERR("fatal error: delete timer failed.\n");
		return CLIENT_ERR;
	}

	return CLIENT_OK;
}

/******************************************************************************
* FUNCTION		: posixTimerTest()
* DESCRIPTION	: test posix timer
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
static void posixTimerTest()
{
	int i = 0;
	OPEAR_ARR operas[OPERA_NUM] = {0};
	
	MSG_LOG("please input %d operations: \n", OPERA_NUM);
	for (i = 0; i < OPERA_NUM; i++)
	{
		printf("No%d:\n", i);
		scanf("%d %d", &(operas[i].opera), &(operas[i].timer));
	}
	
	for (i = 0; i < OPERA_NUM; i++)
	{
		(void)clientTimerSwitch(operas[i].timer, operas[i].opera);
		sleep(SLEEP_TIME);
	}
	
	return ;
}

/******************************************************************************
* FUNCTION		: main()
* DESCRIPTION	: 
* INPUT			:
* OUTPUT		:
* RETURN		:
******************************************************************************/
int main(int argc, char** argv)
{
	int iRet = CLIENT_ERR;

	/* init posix timer */
	iRet = clientClientInit();
	if (CLIENT_OK != iRet)
	{
		MSG_ERR("init client client environment failed!\n");
		return CLIENT_ERR;
	}

	/* test posix timer */
	posixTimerTest();

	/* release timers before exit */
	iRet = clientClientExit();
	if (CLIENT_OK != iRet)
	{
		MSG_ERR("clientClientExit failed!\n");
		return CLIENT_ERR;
	}

	return CLIENT_OK;
}



Makefile

# build client-timer executable when user executes "make"

LDFLAGS += -lrt
CFLAGS += -std=gnu99

APP_NAME = client-timer
OBJ = client-timer.o
OBJ += timeout-handler.o

$(APP_NAME): $(OBJ)
	$(CC) $^ -o $(APP_NAME) $(LDFLAGS)

%.o:%.c
	$(CC) $(CFLAGS) -c $^ -o $@

# remove object files and executable when user executes "make clean"
clean:
	rm *.o $(APP_NAME)

定时器接口(timer-handler.h和timer-handler.c两个文件的函数),已在平常开发中严格验证过,请放心使用。对于本文中的测试接口写得偏简单,仅供参考。

代码也可以从github上下载,地址:https://github.com/wangfuyu/posix-timer


你可能感兴趣的:(C,Linux)