代码实现是在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语言实现任务调度与定时器的更多相关文章

  1. Spring Task 任务调度(定时器)

    1.任务调度SpringTask 1.1什么是任务调度 在企业级应用中,经常会制定一些“计划任务”,即在某个时间点做某件事情,核心是以时间为关注点,即在一个特定的时间点,系统执行指定的一个操作.常见的 ...

  2. C语言实现的多线程定时器

    目录 1. 大致功能介绍 2. API库介绍 3. 一个例子 4. 库文件源码 注意事项 1. 大致功能介绍 实现任务列表,定时器会间隔一段时间遍历列表发现要执行的任务 任务列表中的所有任务并行执行 ...

  3. go语言之进阶篇定时器重置

    1.定时器重置 示例: package main import ( "fmt" "time" ) func main() { timer := time.New ...

  4. go语言之进阶篇定时器停止

    1.定时器停止 示例: package main import ( "fmt" "time" ) func main() { timer := time.New ...

  5. go语言之进阶篇定时器Timer的使用

    1.Timer的使用 示例: #创建一个定时器,设置时间为2s,2s后,往time通道写内容(当前时间) package main import ( "fmt" "tim ...

  6. ESP8266 LUA脚本语言开发: 外设篇-定时器,延时,看门狗

    https://nodemcu.readthedocs.io/en/master/modules/tmr/ local mytimer1 = tmr.create() function TimeFun ...

  7. 分布式任务调度平台XXL-JOB

    <分布式任务调度平台XXL-JOB>       一.简介 1.1 概述 XXL-JOB是一个轻量级分布式任务调度框架,其核心设计目标是开发迅速.学习简单.轻量级.易扩展.现已开放源代码并 ...

  8. 原生js简单调用百度翻译API实现的翻译工具

    先来个在线demo: js翻译工具 或者百度搜索js简单调用百度翻译API工具(不过有个小小的界面显示bug,我想细心的人应该会发现) 或者直接前往该网址:js翻译工具 或者前往我的github:gi ...

  9. JavaWeb学习笔记八 监听器

    监听器Listener jservlet规范包括三个技术点:servlet :listener :filter:监听器就是监听某个对象的的状态变化的组件.监听器的相关概念事件源: 被监听的对象(三个域 ...

随机推荐

  1. git合作开发流程

    一.创建项目与管理 创建项目和管理项目都是管理账号需要做的事情,如果只是合作开发不进行管理,只需要浏览第二部分的内容即可. 1.创建项目 登录代码托管网站,点击添加项目,如下图所示: 填写相应的项目信 ...

  2. finalize() 方法——Java中垃圾回收提醒方法

    finalize() Java 允许定义这样的方法,它在对象被垃圾收集器析构(回收)之前调用,这个方法叫做 finalize( ),它用来清除回收对象. 例如,你可以使用 finalize() 来确保 ...

  3. 面试官:实现一个带值变更通知能力的Dictionary

    如题, 你知道字典KEY对应的Value什么时候被覆盖了吗?今天我们聊这个刚性需求. 前文提要: 数据获取组件维护了业务方所有(在用)的连接对象,DBA能在后台无侵入的切换备份库. 上文中:DBA在为 ...

  4. TotalCommander的两款目录插件

    CatalogMaker 与 DiskDir Extended 是两个用于生成文件夹目录的totalCmd插件. 将指定目录下所有文件.文件夹以指定格式存储在一个文本文件中,可作为EverCD+的轻量 ...

  5. MySQL-04-SQL简单介绍

    SQL介绍 SQL 结构化查询语言 5.7 以后符合SQL92严格模式 通过sql_mode参数来控制 常用SQL分类 DDL:数据定义语言 DCL:数据控制语言 DML:数据操作语言 DQL:数据的 ...

  6. SpringBoot开发八-会话管理

    需求介绍-会话管理 利用Cookie和Seesion使得HTTP变成有会话的连接,写几个实例演示一下 代码实现 先写个例子,表示客户端第一次访问服务器,服务器端创建一个Cookie发送给客户端. 不管 ...

  7. elk 7.9.3 版本容器化部署

    ELK-V7.9.3 部署 为什么用到ELK? 平时我们需要进行日志分析的时候,可以直接在日志文件中 grep.awk 就可以过滤出自己想要的信息及关键字,但规模较大的场景中,此方法极大的减低了效率, ...

  8. Sqli-Labs less17-19

    less-17 前置基础知识: UPDATEXML (XML_document, XPath_string, new_value); 第一个参数:XML_document是String格式,为XML文 ...

  9. MATLAB—面向复数和数组的基本运算

    文章目录 一.MATLAB基本运算说明 二.面向复数的计算特点 1.基础知识 2.对复数的基本操作 3.复数的开方问题 二.面向数组 1.数组的输入形式 2.对矩阵中的元素进行并行操作 3.利用数组运 ...

  10. SQL 练习10

    查询没有学全所有课程的同学的信息 分析 先查询出所有课程的数量 select count(cid) from course 再查询出成绩表中课程数量=总课数的人员 select sid from sc ...