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公共程序之总体描述的更多相关文章

  1. sloop公共程序之初始过程及启动

    1:sloop_init() 初始化主要是初始化静态sloop_*** 结构体和填充struct sloop_data 结构体中的成员. //初始化静态存储区给sloop_***结构体 static ...

  2. Verilog HDL的程序结构及其描述

    这篇博文是写给要入门Verilog HDL及其初学者的,也算是我对Verilog  HDL学习的一个总结,主要是Verilog HDL的程序结构及其描述,如果有错,欢迎评论指出. 一.Verilog ...

  3. Microsoft.VisualBasic.dll的妙用and 改善C#公共程序类库质量的10种方法

    Microsoft.VisualBasic.dll的妙用(开发中肯定会用到哦) 前言 做过VB开发的都知道,有一些VB里面的好的函数在.NET里面都没有,而Microsoft.VisualBasic. ...

  4. UCOS2系统内核讲述_总体描述

    Ⅰ.写在前面 学习本文之前可以参考我前面基于STM32硬件平台移植UCOS2的几篇文章,我将其汇总在一起: UCOS2_STM32F1移植详细过程(汇总文章) 要想学习,或使用系统配套的资源(如:信号 ...

  5. 改善C#公共程序类库质量的10种方法

    最近重构一套代码,运用以下几种方法,供参考. 1  公共方法尽可能的使用缓存 public static List<string> GetRegisteredCompany() { Str ...

  6. 改善C#公共程序类库质量的10种方法和工具

    最近重构一套代码,运用以下几种方法,供参考. 1  公共方法尽可能的使用缓存 public static List<string> GetRegisteredCompany() { Str ...

  7. 程序员如何描述清楚线上bug

    案例 一个管理后台的bug,把操作记录中的操作员姓名,写成了该操作员的id.原因是修改了一个返回操作人姓名的函数,返回了操作人的id.但是还有其他地方也用这个函数,导致其他地方把姓名字段填写成了操作员 ...

  8. 改善C#公共程序类库质量的10种方法(转)

    出处:http://www.cnblogs.com/JamesLi2015/p/3140897.html 最近重构一套代码,运用以下几种方法,供参考. 1  公共方法尽可能的使用缓存 public s ...

  9. sloop公共函数之添加信号,定时器及socket

    1:添加信号 1.1 原型:sloop_handle sloop_register_signal(int sig, sloop_signal_handler handler, void * param ...

随机推荐

  1. day09,10 函数

    一.函数 什么是函数 函数: 对代码块和功能的封装和定义 定义一个事情或者功能. 等到需要的时候直接去用就好了. 那么这里定义的东西就是一个函数. 语法: def 函数名(形参): 函数体 函数名(实 ...

  2. 英文Datasheet没那么难读

    话说学好数理化,走遍天下都不怕.可是在这个所谓的全球化时代,真要走遍天下的话,数理化还真未必比得上一门外语.作为技术人员,可以看到的是目前多数前沿的产品和技术多来自发达的欧美等国家,而英语目前才是真正 ...

  3. Summarize to the Power of Two(map+思维)

    A sequence a1,a2,…,ana1,a2,…,an is called good if, for each element aiai, there exists an element aj ...

  4. 20172326『Java程序设计』课程结对编程练习_四则运算第二周阶段总结

    20172326『Java程序设计』课程结对编程练习_四则运算第二周阶段总结 小组成员 20172313 余坤澎 20172332 于欣月 20172326 康皓越 小组编程照片 设计思路 通过一个E ...

  5. [pascal入门]数组

    一.本节目标 本节我们将要讲述数组.本节目标: 一维数组 二维数组 字符数组 二.一维数组 我们通过一个案例来简单的理解数组.班主任要计算班级里面50个同学数学成绩的平均成绩,道理上讲这是一个比较简单 ...

  6. JAVA里面json和java对象之间的相互转换

    1. 把java 对象列表转换为json对象数组,并转为字符串 JSONArray array = JSONArray.fromObject(list);    String jsonstr = ar ...

  7. size和STL中的size_type

    为了使自己的程序有很好的移植性,c++程序员应该尽量使用size_t和size_type而不是int, unsigned 1. size_t是全局定义的类型:size_type是STL类中定义的类型属 ...

  8. workstation vmware 制作vm模板

    [root@VM166136 ~]# cat copy_vmware.sh #!/bin/bash if [ $(id -u) -ne 0 ];then echo "Please use t ...

  9. webgl 初识2

    之前的文章介绍了webgl. 这里进一步精简. WebGL的全部内容就是创建不同的着色器, 向着色器提供数据然后调用gl.drawArrays 或 gl.drawElements 让WebGL调用当前 ...

  10. SQL中的declare用法

    平时写SQL查询.存储过程都是凭着感觉来,没有探究过SQL的具体语法,一直都是按c#那一套往SQL上模仿,前几天项目中碰到一个问题引起了我对declare定义变量的作用域的兴趣. 大家都知道c#中的局 ...