C语言实现任务调度与定时器
代码实现是在xl2tpd的源码中get到的,感觉很有意思的一段代码。基本功能就是实现定时器,时间到后从定时队列中取出,然后完成指定的任务。
1. schedule.c代码(自己添加了main函数,用来调试)
/*
* Layer Two Tunnelling Protocol Daemon
* Copyright (C) 1998 Adtran, Inc.
* Copyright (C) 2002 Jeff McAdams
*
* Mark Spencer
*
* This software is distributed under the terms
* of the GPL, which you should have received
* along with this source.
*
* Scheduler code for time based functionality
*
*/
#include <stdio.h>
#include <stdlib.h>
#include <signal.h>
#include <string.h>
#include "scheduler.h"
struct schedule_entry *events;
static struct timeval zero;
static sigset_t alarm;
/*
init_scheduler配合schedule_lock,schedule_unlock使用,用来屏蔽当前进程中特定协议的处理
*/
void init_scheduler (void)/*初始化了两个不同的信号集*/
{
struct sigaction act;
act.sa_handler = alarm_handler;/*alarm信号执行体*/
#if defined (LINUX) && (__i386__)
act.sa_restorer = NULL;
#endif
act.sa_flags = 0;
sigemptyset (&act.sa_mask);
sigaddset (&act.sa_mask, SIGALRM);/*将SIGALRM信号添加到信号集sa_mask中,SIGALRM信号会阻塞*/
sigaction (SIGALRM, &act, NULL);/*安装登记信号*/
events = NULL;
zero.tv_usec = 0;
zero.tv_sec = 0;
sigemptyset (&alarm);
sigaddset (&alarm, SIGALRM);/*将SIGALRM信号添加到信号集alarm中,SIGALRM信号会阻塞*/
}
void alarm_handler (int signal)
{
/* Check queue for events which should be
executed right now. Execute them, then
see how long we should set the next timer
*/
struct schedule_entry *p = events;
struct timeval now;
struct timeval then;
struct itimerval itv;
static int cnt = 0;
cnt++;
if (cnt != 1)
{
/* Whoa, we got called from within ourselves! */
//log (LOG_DEBUG, "%s : Whoa... cnt = %d\n", __FUNCTION__, cnt);
return;
}
while (events)
{
gettimeofday (&now, NULL);
p = events;
if (TVLESSEQ (p->tv, now))
{
events = events->next;
/* This needs to be executed, as it has expired.
It is expected that p->func will free p->data
if it is necessary */
(*p->func) (p->data);
free (p);
}
else
break;
}
/* When we get here, either there are no more events
in the queue, or the remaining events need to happen
in the future, so we should schedule another alarm */
if (events)
{
then.tv_sec = events->tv.tv_sec - now.tv_sec;
then.tv_usec = events->tv.tv_usec - now.tv_usec;
if (then.tv_usec < 0)
{
then.tv_sec -= 1;
then.tv_usec += 1000000;
}
if ((then.tv_sec <= 0) && (then.tv_usec <= 0))
{
//log (LOG_WARN, "%s: Whoa... Scheduling for <=0 time???\n",__FUNCTION__);
}
else
{
itv.it_interval = zero;
itv.it_value = then;
setitimer (ITIMER_REAL, &itv, NULL);/*重新定时,时间到后发送SIGALRM信号*/
}
}
cnt--;
}
void schedule_lock ()/*将alarm添加到当前进程阻塞信号集中,信号来时会被阻塞,暂时捕获不到,移除后会捕获?/
{
while (sigprocmask (SIG_BLOCK, &alarm, NULL));
};
void schedule_unlock ()/*将alarm从当前进程阻塞信号集中移除*/
{
/* See if we missed any events */
/* alarm_handler(0); */
while (sigprocmask (SIG_UNBLOCK, &alarm, NULL));
raise (SIGALRM);/*用来向本进程发送信号*/
};
struct schedule_entry *schedule (struct timeval tv, void (*func) (void *),
void *data)
{
/* Schedule func to be run at relative time tv with data
as arguments. If it has already expired, run it
immediately. The queue should be in order of
increasing time */
struct schedule_entry *p = events, *q = NULL; /*时间间隔递增的队列*/
int need_timer = 0;
struct timeval diff;
struct itimerval itv; /*队列中越靠前,越早发生*/
diff = tv;
gettimeofday (&tv, NULL);
tv.tv_sec += diff.tv_sec; /*转换为本地系统时间*/
tv.tv_usec += diff.tv_usec;
if (tv.tv_usec > 1000000)
{
tv.tv_sec++;
tv.tv_usec -= 1000000;/*进制转换*/
}
while (p)
{
if (TVLESS (tv, p->tv)) /*tv < p->tv*/
break;
q = p;
p = p->next;
};
if (q)
{
q->next =
(struct schedule_entry *) malloc (sizeof (struct schedule_entry));
q = q->next;
}
else
{ /*时间比队列中的第一个时间还小*/
q = (struct schedule_entry *) malloc (sizeof (struct schedule_entry));
events = q;
need_timer = -1;
}
q->tv = tv;
q->func = func;
q->data = data;
q->next = p;
if (need_timer)
{
itv.it_interval = zero;
itv.it_value = diff;
setitimer (ITIMER_REAL, &itv, NULL); /*重新修改定时*/
}
return q;
}
inline struct schedule_entry *aschedule (struct timeval tv,/*tv传递的为绝对时间*/
void (*func) (void *), void *data)
{
/* Schedule func to be run at absolute time tv in the future with data
as arguments */
struct timeval now;
gettimeofday (&now, NULL);
tv.tv_usec -= now.tv_usec;
if (tv.tv_usec < 0)
{
tv.tv_usec += 1000000;
tv.tv_sec--;
}
tv.tv_sec -= now.tv_sec;
return schedule (tv, func, data);
}
void deschedule (struct schedule_entry *s)/*取消任务*/
{
struct schedule_entry *p = events, *q = NULL;
if (!s)
return;
while (p)
{
if (p == s)
{
if (q)
{
q->next = p->next;
}
else
{
events = events->next;
}
free (p);
break;
}
q = p;
p = p->next;
}
}
/*****************************************************************/
void func_test(void *data)
{
struct timeval tv;
tv.tv_sec = 5;
tv.tv_usec = 0;
printf("落霞与孤鹜齐飞,秋水共长天一色\n");
schedule(tv, func_test, NULL);
}
void main(int argc, char *argv[])
{
struct timeval tv;
struct timeval timeout;
printf("------scheduler-------\n");
init_scheduler ();
tv.tv_sec = 5;
tv.tv_usec = 0;
schedule(tv, func_test, NULL);
timeout.tv_sec = 1;
timeout.tv_usec = 0;
while(1){
select(0,NULL,NULL,NULL, &timeout);
}
}
2. schedule.h
/*
* Layer Two Tunnelling Protocol Daemon
* Copyright (C) 1998 Adtran, Inc.
* Copyright (C) 2002 Jeff McAdams
*
* Mark Spencer
*
* This software is distributed under the terms
* of the GPL, which you should have received
* along with this source.
*
* Scheduler structures and functions
*
*/
#ifndef _SCHEDULER_H
#define _SCHEDULER_H
#include <sys/time.h>
/*
* The idea is to provide a general scheduler which can schedule
* events to be run periodically
*/
struct schedule_entry
{
struct timeval tv; /* Scheduled time to execute */
void (*func) (void *); /* Function to execute */
void *data; /* Data to be passed to func */
struct schedule_entry *next; /* Next entry in queue */
};
extern struct schedule_entry *events;
/* Schedule func to be executed with argument data sometime
tv in the future. */
struct schedule_entry *schedule (struct timeval tv, void (*func) (void *),
void *data);
/* Like schedule() but tv represents an absolute time in the future */
struct schedule_entry *aschedule (struct timeval tv, void (*func) (void *),
void *data);
/* Remove a scheduled event from the queue */
void deschedule (struct schedule_entry *);
/* The alarm handler */
void alarm_handler (int);
/* Initialization function */
void init_scheduler (void);
/* Prevent the scheduler from running */
void schedule_lock ();
/* Restore normal scheduling functions */
void schedule_unlock ();
/* Compare two timeval functions and see if a <= b */
#define TVLESS(a,b) ((a).tv_sec == (b).tv_sec ? \
((a).tv_usec < (b).tv_usec) : \
((a).tv_sec < (b).tv_sec))
#define TVLESSEQ(a,b) ((a).tv_sec == (b).tv_sec ? \
((a).tv_usec <= (b).tv_usec) : \
((a).tv_sec <= (b).tv_sec))
#define TVGT(a,b) ((a).tv_sec == (b).tv_sec ? \
((a).tv_usec > (b).tv_usec) : \
((a).tv_sec > (b).tv_sec))
#endif
3. 最简单的Makefile
CC = gcc
CFLAGS = -g
LFLAGS =
OBJS = scheduler.o
all : scheduler
scheduler : $(OBJS)
$(CC) $^ -o $@ $(LFLAGS)
%*.o:%*.c
$(CC) $(CFLAGS) $< -o $@
.PHNOY: clean
clean:
rm *.o scheduler
4. demo结果
调度时,每隔5秒钟,执行一次func_tesr()。
C语言实现任务调度与定时器的更多相关文章
- Spring Task 任务调度(定时器)
1.任务调度SpringTask 1.1什么是任务调度 在企业级应用中,经常会制定一些“计划任务”,即在某个时间点做某件事情,核心是以时间为关注点,即在一个特定的时间点,系统执行指定的一个操作.常见的 ...
- C语言实现的多线程定时器
目录 1. 大致功能介绍 2. API库介绍 3. 一个例子 4. 库文件源码 注意事项 1. 大致功能介绍 实现任务列表,定时器会间隔一段时间遍历列表发现要执行的任务 任务列表中的所有任务并行执行 ...
- go语言之进阶篇定时器重置
1.定时器重置 示例: package main import ( "fmt" "time" ) func main() { timer := time.New ...
- go语言之进阶篇定时器停止
1.定时器停止 示例: package main import ( "fmt" "time" ) func main() { timer := time.New ...
- go语言之进阶篇定时器Timer的使用
1.Timer的使用 示例: #创建一个定时器,设置时间为2s,2s后,往time通道写内容(当前时间) package main import ( "fmt" "tim ...
- ESP8266 LUA脚本语言开发: 外设篇-定时器,延时,看门狗
https://nodemcu.readthedocs.io/en/master/modules/tmr/ local mytimer1 = tmr.create() function TimeFun ...
- 分布式任务调度平台XXL-JOB
<分布式任务调度平台XXL-JOB> 一.简介 1.1 概述 XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源代码并 ...
- 原生js简单调用百度翻译API实现的翻译工具
先来个在线demo: js翻译工具 或者百度搜索js简单调用百度翻译API工具(不过有个小小的界面显示bug,我想细心的人应该会发现) 或者直接前往该网址:js翻译工具 或者前往我的github:gi ...
- JavaWeb学习笔记八 监听器
监听器Listener jservlet规范包括三个技术点:servlet :listener :filter:监听器就是监听某个对象的的状态变化的组件.监听器的相关概念事件源: 被监听的对象(三个域 ...
随机推荐
- SpringBoot系列——动态定时任务
前言 定时器是我们项目中经常会用到的,SpringBoot使用@Scheduled注解可以快速启用一个简单的定时器(详情请看我们之前的博客<SpringBoot系列--定时器>),然而这种 ...
- 网络编程之TCP客户端开发和TCP服务端开发
开发 TCP 客户端程序开发步骤 创建客户端套接字对象 和服务端套接字建立连接 发送数据 接收数据 关闭客户端套接字 import socket if __name__ == '__main__': ...
- goproxy.io
goproxy.io 是全球最早的 Go modules 镜像代理服务之一, 采用 CDN 加速服务为开发者提供依赖下载, 该服务由一批热爱开源, 热爱 Go 语言的年轻人开发维护.从 Go 1.11 ...
- 『Java』StringBuilder类使用方法
String类存在的问题 String类的底层是一个被final修饰的byte[],不能改变. 为了解决以上问题,可以使用java.lang.StringBuilder类. StringBuilder ...
- OpenResty Lua钩子调用完整流程
前面一篇文章介绍了Openresty Lua协程调度机制,主要关心的是核心调度函数run_thread()内部发生的事情,而对于外部的事情我们并没有涉及.本篇作为其姊妹篇,准备补上剩余的部分.本篇将通 ...
- Docker部署ELK之部署kibana7.6.0(2)
1. 拉取kibana镜像 sudo docker pull kibana:7.6.0 2. 输入命令构建kibana容器,关于挂载kibana配置文件的问题,也可以先构建一个容器,然后把配置文件co ...
- How to name a slf4j logger
Use logger in a non-static context: Logger logger = LoggerFactory.getLogger(this.getClass().getName( ...
- 一张图说明 iaas paas saas的区别
图片来源:https://www.bilibili.com/video/BV1QJ411S7c4 P2 云服务的三种模式 1laaS(基础设施即服务) laas(Infrastructure as ...
- springboot项目出现Whitelabel Error Page的问题
springboot项目出现Whitelabel Error Page的问题 大概就是这种情况,然而昨天还是没问题的,通过对比就发现,是自己手欠了 简单来说解决办法就是将springboot的启动项目 ...
- 把对象交给spring管理的3种方法及经典应用
背景 先说一说什么叫把对象交给spring管理.它区别于把类交给spring管理.在spring里采用注解方式@Service.@Component这些,实际上管理的是类,把这些类交给spring来负 ...