sloop公共程序之总体描述
1:功能需求
开发一个公共库文件sloop.c,实现三个常用功能以供其它模块调用。三个功能如下:
功能一:实现一般的信号监听,调用模块只需传入要监听的信号和相应的回调函数就可以在信号到时调用回调函数处理信号(优先级高)。
功能二:实现定时器,精度至usecs,调用模块只需传入过期的sec,usec和相应的回调函数就可以在时间到后执行回调函数(可以有一定时间误差)(优先级中)。
功能三:实现套接字的监听,调用模块只需传入要监听的套接字描述符和相应的回调处理函数就可以在描述符就绪是执行回调函数,分为监听读,写两种(优先级低)。
2:设计思路
2.1 技术选择
对于信号监听,使用移植性高的sigaction函数,为了统一管理,将使用pipe(fd)+sigaction + select的模式,选择pipe是为了减少信号处理函数的复杂度,在信号处理函数中只需将收到的信号write给fd[1],然后在select中检测fd[0]的可读状态,就可以得到收到的信号的值。这样就可以将信号的处理过程过渡为文件描述符的监听,与功能三重合,统一实现。这里的pipe其实也可以使用socketpair,但socketpair返回的两个套接字都是全双工的,似乎有些"浪费",而pipe正好的单双工,刚好符合要求也不"浪费"。可参考我的博文socketpair + signal + select 的套路描述了这样做的优势。
对于定时器,大体思路就是登记定时器时传入秒、微妙,过期时间就是当前时间加上传入的秒、微妙,主循环每次将登记的时间与当前时间比较,若当前时间>登记时间,就表示定时到,再执行回调函数,这样可能会造成一定的时间误差。
对于套接字的监听,分为可读和可写两种,选择select就足够满足需求了。
2.2 数据结构
通过上面的分析,我们关心的有三种情况,所以拟定三种结构体来描述它们,即信号结构体struct sloop_struct、时间结构体struct sloop_timeout、套接字结构体struct sloop_socket。那么如何来管理它们呢?最先想到的当然是用链表来进行管理,而且使用双链表,优势在于插入和遍历都比单链表要简易。因为考虑到监听的任务量不会很大,所以使用静态数据的方式替代在堆上分配内存。我们假定最多监听的socket和最多定时器都为128个,而最多监听的信号个数为16个,这样就可以先将静态分配的结构体数组挂在free_socket、free_timeout及free_signal三个双链表中,当调用者登记时,再从free_***双链表中取出挂在readers、writers、signals、timeout等使用的双链表中,这样管理可用的链表和正使用链表。
综上所述,三个结构体定义如下:
//记录一个待监听(读 or 写)套接字
struct sloop_socket
{
struct dlist_head list;//双链表挂载点(不用时挂在free_socket,使用时挂在readers or writers)
unsigned int flags;
int sock;//套接字描述符
void * param;
sloop_socket_handler handler;//状态就绪回调函数
}; //记录一个定时器
struct sloop_timeout
{
struct dlist_head list;//双链表挂载点(不用时挂在free_timeout,使用时挂在timeout)
unsigned int flags;
struct timeval time;//超时时间
void * param;
sloop_timeout_handler handler;//超时回调函数
}; //记录一个信号
struct sloop_signal
{
struct dlist_head list;//双链表挂载点(不用时挂在free_signal,使用时挂在signals)
unsigned int flags;
int sig;//信号值
void * param;
sloop_signal_handler handler;//信号回调函数
};
将free_***等组织到一个结构体中:
struct sloop_data
{
int terminate;//退出标志
int signal_pipe[];//信号监听会使用到的管道
void * sloop_data;
struct dlist_head free_sockets;
struct dlist_head free_timeout;
struct dlist_head free_signals;
struct dlist_head readers;
struct dlist_head writers;
struct dlist_head signals;
struct dlist_head timeout;
};
2.3 双链表操作
双链表的操作通通定义在一个dlist.h文件中,包括添加、删除等一般操作:
/* dlist.h */
ifndef _DLIST_H_
#define _DLIST_H_ typedef struct dlist_head dlist_t;
struct dlist_head
{
struct dlist_head * next;
struct dlist_head * prev;
}; #define DLIST_HEAD_INIT(e) {&(e),&(e)}
#define DLIST_HEAD(name) dlist_t name = {&(name),&(name)}
#define INIT_DLIST_HEAD(e) do { (e)->next = (e)->prev = (e); } while (0) static inline void __dlist_add(dlist_t * entry, dlist_t * prev, dlist_t * next)
{
next->prev = entry;
entry->next = next;
entry->prev = prev;
prev->next = entry;
}
static inline void __dlist_del(dlist_t * prev, dlist_t * next)
{
next->prev = prev;
prev->next = next;
} /***************************************************************************/ #define dlist_entry(e, t, m) ((t *)((char *)(e)-(unsigned long)(&((t *)0)->m))) static inline int dlist_empty(struct dlist_head * head) { return head->next == head; }
static inline void dlist_add(dlist_t * entry, dlist_t * head) { __dlist_add(entry, head, head->next); }
static inline void dlist_add_tail(dlist_t * entry, dlist_t * head) { __dlist_add(entry, head->prev, head); }
static inline void dlist_del(dlist_t * entry)
{
__dlist_del(entry->prev, entry->next);
entry->next = entry->prev = (void *);
}
static inline void dlist_del_init(dlist_t * entry)
{
__dlist_del(entry->prev, entry->next);
entry->next = entry->prev = entry;
}
static inline dlist_t * dlist_get_next(dlist_t * entry, dlist_t * head)
{
entry = entry ? entry->next : head->next;
return (entry == head) ? NULL : entry;
}
static inline dlist_t * dlist_get_prev(dlist_t * entry, dlist_t * head)
{
entry = entry ? entry->prev : head->prev;
return (entry == head) ? NULL : entry;
} #endif
有一个宏定义dlist_entry是为了通过链表指针来获得指向整个结构体的指针,因为我们操作的都是结构体的list成员,它是一个指向struct dlist_head结构体的指针,而list又是sloop_***结构体的第一个成员,所以需要将指向list的指针转换为指向list所在的结构体的指针,这样才能去访问此结构体的其它成员。
结语:总体说明了模块的功能和实现的大体方法,描述了结果比较重要的结构体如何组织的和为何这样组织。下面将记录详细的实现过程 sloop公共程序之初始过程
sloop公共程序之总体描述的更多相关文章
- sloop公共程序之初始过程及启动
1:sloop_init() 初始化主要是初始化静态sloop_*** 结构体和填充struct sloop_data 结构体中的成员. //初始化静态存储区给sloop_***结构体 static ...
- Verilog HDL的程序结构及其描述
这篇博文是写给要入门Verilog HDL及其初学者的,也算是我对Verilog HDL学习的一个总结,主要是Verilog HDL的程序结构及其描述,如果有错,欢迎评论指出. 一.Verilog ...
- Microsoft.VisualBasic.dll的妙用and 改善C#公共程序类库质量的10种方法
Microsoft.VisualBasic.dll的妙用(开发中肯定会用到哦) 前言 做过VB开发的都知道,有一些VB里面的好的函数在.NET里面都没有,而Microsoft.VisualBasic. ...
- UCOS2系统内核讲述_总体描述
Ⅰ.写在前面 学习本文之前可以参考我前面基于STM32硬件平台移植UCOS2的几篇文章,我将其汇总在一起: UCOS2_STM32F1移植详细过程(汇总文章) 要想学习,或使用系统配套的资源(如:信号 ...
- 改善C#公共程序类库质量的10种方法
最近重构一套代码,运用以下几种方法,供参考. 1 公共方法尽可能的使用缓存 public static List<string> GetRegisteredCompany() { Str ...
- 改善C#公共程序类库质量的10种方法和工具
最近重构一套代码,运用以下几种方法,供参考. 1 公共方法尽可能的使用缓存 public static List<string> GetRegisteredCompany() { Str ...
- 程序员如何描述清楚线上bug
案例 一个管理后台的bug,把操作记录中的操作员姓名,写成了该操作员的id.原因是修改了一个返回操作人姓名的函数,返回了操作人的id.但是还有其他地方也用这个函数,导致其他地方把姓名字段填写成了操作员 ...
- 改善C#公共程序类库质量的10种方法(转)
出处:http://www.cnblogs.com/JamesLi2015/p/3140897.html 最近重构一套代码,运用以下几种方法,供参考. 1 公共方法尽可能的使用缓存 public s ...
- sloop公共函数之添加信号,定时器及socket
1:添加信号 1.1 原型:sloop_handle sloop_register_signal(int sig, sloop_signal_handler handler, void * param ...
随机推荐
- 三点须知:当我们在开发过程中需要用到分布式缓存Redis的时候
当我们在开发过程中需要用到分布式缓存Redis的时候,我们首先要明白缓存在系统中用来做什么? 1. 少量数据存储,高速读写访问.通过数据全部in-momery 的方式来保证高速访问,同时提供数据落地的 ...
- Linux系统进程管理
Linux系统进程管理 什么是进程 进程是已启动的可执行程序的运行实例,进程有以下组成部分: 分配内存, 已分配内存的地址空间 安全属性, 进程的运行身份和权限 进程代码, 运行一个或多个的线程 进程 ...
- presto 配置mysql.properties异常Database (catalog) must not be specified in JDBC URL for MySQL connector
在presto 0.210 以后配置mysql.properties的时候,对于jdbc-url属性配置后面要加上对应要链接的database connection-url=jdbc:mysql:// ...
- Android数据储存之SQLiteDatabase 简单增删改查
SQLiteDatabase 使用 SQLiteDatabase提供如下方法来打开一个文件对应的数据库: openDatabase(String path, SQLiteDatabase.Cursor ...
- c# 加载图片 正在被占用问题
问题情境:图片文件加载到pdf中,程序没有退出,再次加载该图片文件,提示被占用. 解决办法: 1.加载文件会锁定该文件,fromfile方法会导致占用内存较大,不使用该方法. FileStream f ...
- CSU 1808: 地铁 最短路
题目链接: http://acm.csu.edu.cn/OnlineJudge/problem.php?id=1808 1808: 地铁 Time Limit: 5 SecMemory Limit: ...
- Markdown github 风格语法
某些效果cnblog无法支持,见 https://github.com/tanghammer/note/blob/master/Markdown%20github%E9%A3%8E%E6%A0%BC% ...
- Java实现的词频统计
要求: 1.读取文件: 2.记录出现的词汇及出现频率: 3.按照频率降序排列: 4.输出结果. 概要: 1.读取的文件路径是默认的,为了方便调试,将要统计的文章.段落复制到文本中即可:2.只支持英文: ...
- p2 关节
P2中使用Constraint及其子类表示关节,也就是将两个刚体按照指定的规则约束在一起,形成有规律的.相互限制的运动模拟.P2关节模拟中,两个刚体没有通过任何刚体连接,只是通过算法模拟出关节运动轨迹 ...
- redis 同步化操作
异步化操作是很麻烦的的.不好控.下面介绍个同步化的库bluebird.用法很简单.看下你还子就知道了 const redis = require('redis'); const bluebird = ...