原创 linux 延时函数几个资料

2009-8-12 17:30 10599 12 12 分类: 软件与OS

http://www.cppblog.com/CppExplore/archive/2008/04/02/46111.html


一、 基础知识
1、时间类型。
Linux下常用的时间类型有4个:time_t,struct timeval,struct timespec,struct tm。
(1)time_t是一个长整型,一般用来表示用1970年以来的秒数。
(2)Struct timeval有两个成员,一个是秒,一个是微妙。


ExpandedBlockStart.gifContractedBlock.gifstruct timeval dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif               
long tv_sec;        /**//* seconds */
ExpandedSubBlockStart.gifContractedSubBlock.gif               
long tv_usec;  /**//* microseconds */
ExpandedBlockEnd.gif       }
;

(3)struct timespec有两个成员,一个是秒,一个是纳秒。


ExpandedBlockStart.gifContractedBlock.gifstruct timespecdot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif                      time_t  tv_sec;         
/**//* seconds */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
long    tv_nsec;        /**//* nanoseconds */
ExpandedBlockEnd.gif              }
;

(4)struct tm是直观意义上的时间表示方法:


ExpandedBlockStart.gifContractedBlock.gifstruct tm dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_sec;         /**//* seconds */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_min;         /**//* minutes */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_hour;        /**//* hours */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_mday;        /**//* day of the month */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_mon;         /**//* month */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_year;        /**//* year */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_wday;        /**//* day of the week */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_yday;        /**//* day in the year */
ExpandedSubBlockStart.gifContractedSubBlock.gif                      
int     tm_isdst;       /**//* daylight saving time */
ExpandedBlockEnd.gif              }
;

2、 时间操作
(1) 时间格式间的转换函数
主要是 time_t、struct tm、时间的字符串格式之间的转换。看下面的函数参数类型以及返回值类型:


None.gifchar *asctime(const struct tm *tm);
None.gif
char *ctime(const time_t *timep);
None.gifstruct tm 
*gmtime(const time_t *timep);
None.gifstruct tm 
*localtime(const time_t *timep);
None.giftime_t mktime(struct tm 
*tm);

gmtime和localtime的参数以及返回值类型相同,区别是前者返回的格林威治标准时间,后者是当地时间。
(2) 获取时间函数
两个函数,获取的时间类型看原型就知道了:


None.giftime_t time(time_t *t);
None.gif
int gettimeofday(struct timeval *tv, struct timezone *tz);

前者获取time_t类型,后者获取struct timeval类型,因为类型的缘故,前者只能精确到秒,后者可以精确到微秒。
二、 延迟函数
主要的延迟函数有:sleep(),usleep(),nanosleep(),select(),pselect().


None.gifunsigned int sleep(unsigned int seconds);
None.gif
void usleep(unsigned long usec);
None.gif
int nanosleep(const struct timespec *req, struct timespec *rem);
None.gif
int select(int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds,struct timeval *timeout);
None.gif
int pselect(int   n,   fd_set   *readfds,  fd_set  *writefds,  fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);

alarm函数是信号方式的延迟,这种方式不直观,这里不说了。
仅通过函数原型中时间参数类型,可以猜测sleep可以精确到秒级,usleep/select可以精确到微妙级,nanosleep和pselect可以精确到纳秒级。
而实际实现中,linux上的nanosleep和alarm相同,都是基于内核时钟机制实现,受linux内核时钟实现的影响,并不能达到纳秒级的精度,man nanosleep也可以看到这个说明,man里给出的精度是:Linux/i386上是10 ms ,Linux/Alpha上是1ms。
这里有一篇文章http://blog.csdn.net/zhoujunyi/archive/2007/03/30/1546330.aspx,测试了不同延迟函数之间的精确度。文章给出的结论是linux上精度最高的是select,10ms级别。我在本机器测试select和pselect相当,都达到了1ms级的精度,精度高于文章中给出的10ms,sleep在秒级以上和usleep/nanosleep相当。下面贴下我机器上1ms时候的测试结果,其他不贴了:


None.gifsleep           1000          0      -1000 
None.gifusleep           
1000       2974       1974 
None.gifnanosleep        
1000       2990       1990 
None.gifselect           
1000        991         -9 
None.gifpselect           
1000        990        -10 
None.gifgettimeofday           
1000       1000          0

而使用gettimeofday循环不停检测时间,可精确微秒级,不过不适宜用来做定时器模块。
因此后面的定时期模块将选择select为延迟函数。
三、 定时器模块需求以及实现概述
1、需求。从实现结果的角度说来,需求就是最终的使用方式。呵呵,不详细描述需求了,先直接给出我实现的CTimer类的三个主要接口:


ExpandedBlockStart.gifContractedBlock.gifClass CTimerdot.gif{
InBlock.gifPublic:
InBlock.gifCTimer(unsigned 
int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
InBlock.gif
void start();
InBlock.gif
void stop();
InBlock.gif
void reset(unsigned int vinterval);
ExpandedBlockEnd.gif}
;

使用定时器模块的步骤如下:
(1) 实例化一个CTimer,参数的含义依次是:vinterval间隔时间(单位ms),vfunc是时间到回调的函数,vdata回调函数使用的参数,vtype定时器的类型,分一次型和循环型两种。
(2) 调用start方法。
(3) 必要的时候调用stop和reset。
2、实现。简单描述下定时器模块的实现,有一个manager单例类保存所有CTimer对象,开启一线程运行延迟函数,每次延迟间隔到,扫描保存CTimer的容器,对每个CTimer对象执行减少时间操作,减少到0则执行回调函数。对一次性CTimer,超时则从容器中删除,循环型的将间隔时间重置,不从容器中移除。
CTimer的start执行将对象插入到manager容器中操作;stop执行将对象从manager容器中删除的操作;reset执行先删除,重置间隔,然后再放到容器中,reset不改变CTimer的定时器类型属性。
四、 定时器模块的数据结构选择
Manager类的容器要频繁进行的操作涉及插入、删除、查询等。
误区:(1)简单看,好象该容器要是有序的,方便插入删除等,貌似红黑树比较合适。其实不然,插入删除操作的频率很低,最频繁的还是每次时延到,对容器的扫描并做时间减少操作,红黑树在做顺序扫描相对链表并没什么优势。
(2) 插入的时候依照顺序链表的方式插入到合适的位置保持排序,以保证超时的对象都在链表的头端。其实这也是没必要的,每次时延到,对每一个对象都要做时间减少操作,因此不管是有序还是无序,都是一次扫描就执行完下面操作:减少时间、判断是否超时,是则执行回调,继续判断是什么类型,一次型的则执行完就移除,循环型则执行完直接重置间隔就可。
因此,只需要能快速插入头、删除结点、遍历就好。我的实现直接使用BSD内核中的数据结构LIST,插入头、删除时间复杂度都是1,遍历就不说了。linux下/usr/include/sys下有头文件queue.h里也有LIST结构以及操作的定义。貌似linux下的少了遍历宏:


None.gif#define LIST_FOREACH(var, head, field)     \
None.gif 
for((var) = LIST_FIRST(head);     \
None.gif     (var)
!= LIST_END(head);     \
None.gif     (var) 
= LIST_NEXT(var, field))

五、 详细实现
这里帖出主要的代码,请重点关注CTimerManager:: process方法,不再详细说了。需要详细的全部代码,可来信索取,整体代码很简单,就两个类。


None.gifclass CTimer
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.giffriend 
class CTimerManager;
InBlock.gif
public:
InBlock.gif    typedef 
enum
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        TIMER_IDLE
=0,  //start前以及手动调用stop后的状态
InBlock.gif
        TIMER_ALIVE,  //在manager的list里时候的状态
InBlock.gif
        TIMER_TIMEOUT  //超时后被移除的状态,循环型的没有
ExpandedSubBlockEnd.gif
    }
TimerState;
InBlock.gif    typedef 
enum
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        TIMER_ONCE
=0,  //一次型
InBlock.gif
        TIMER_CIRCLE   //循环型
ExpandedSubBlockEnd.gif
    }
TimerType;
InBlock.gif    CTimer(unsigned 
int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype);
InBlock.gif    
void start();
InBlock.gif    
void stop();
InBlock.gif    
void reset(unsigned int vinterval);
InBlock.gif    
~CTimer();
InBlock.gif
private:
InBlock.gif    unsigned 
int id_;     //测试用
InBlock.gif
    unsigned int m_interval;  //间隔,不变
InBlock.gif
    unsigned int m_counter;  //开始设置为interval,随延迟时间到,减少
InBlock.gif
    TimerState m_state;      //状态
InBlock.gif
    TimerType m_type;        //类型
InBlock.gif
    void (*m_func)(CTimer *,void *);//回调函数
InBlock.gif
    void * m_data;  //回调函数参数
InBlock.gif
    LIST_ENTRY(CTimer) entry_;  //LIST的使用方式
ExpandedBlockEnd.gif
}
;
ExpandedBlockStart.gifContractedBlock.gif
/**//*构造函数*/
None.gifCTimer::CTimer(unsigned 
int vinterval,void (*vfunc)(CTimer *,void *),void *vdata,TimerType vtype):
None.gif
None.gif    m_interval(vinterval),m_counter(vinterval),
None.gif    m_state(TIMER_IDLE),m_type(vtype),
None.gif    m_func(vfunc),m_data(vdata)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{}
ExpandedBlockStart.gifContractedBlock.gif
/**//*开始定时器*/
None.gif
void CTimer::start()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    CTimerManager::instance()
->add_timer(this);
ExpandedBlockEnd.gif}

ExpandedBlockStart.gifContractedBlock.gif
/**//*停止定时器*/
None.gif
void CTimer::stop()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    CTimerManager::instance()
->remove_timer(this);
ExpandedBlockEnd.gif}

ExpandedBlockStart.gifContractedBlock.gif
/**//*reset定时器*/
None.gif
void CTimer::reset(unsigned int vinterval)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    CTimerManager::instance()
->remove_timer(this);
InBlock.gif    m_counter
=m_interval=vinterval;
InBlock.gif    CTimerManager::instance()
->add_timer(this);
ExpandedBlockEnd.gif}

ExpandedBlockStart.gifContractedBlock.gif
/**//*析构函数,stop操作不能省略,避免delete前忘记stop*/
None.gifCTimer::
~CTimer()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
if(m_state==TIMER_ALIVE)
InBlock.gif        stop();
ExpandedBlockEnd.gif}

CTimerManager的:


None.gifclass CTimerManager
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif
public:
InBlock.gif    
InBlock.gif    typedef 
enum
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        TIMER_MANAGER_STOP
=0,
InBlock.gif        TIMER_MANAGER_START
ExpandedSubBlockEnd.gif    }
TimerManagerState;
InBlock.gif    
InBlock.gif    
static CTimerManager * instance();
InBlock.gif    
void add_timer(CTimer * vtimer);//线程安全的add
InBlock.gif
    void remove_timer(CTimer * vtimer);//线程安全的remove
InBlock.gif
    void start();  //开始process线程
InBlock.gif
    void stop();  //停止process线程
InBlock.gif
    void dump();
InBlock.gif
protected:
InBlock.gif    
static void * process(void *); //实际的定时器时间延迟线程
InBlock.gif
private:    
InBlock.gif    
void add_timer_(CTimer * vtimer);//非线程安全的add
InBlock.gif
    void remove_timer_(CTimer * vtimer);//非线程安全的remove
InBlock.gif
    
InBlock.gif    CTimerManager();
InBlock.gif    
static pthread_mutex_t m_mutex;
InBlock.gif    
static CTimerManager * m_instance;
InBlock.gif    
InBlock.gif    TimerManagerState m_state;
InBlock.gif    LIST_HEAD(,CTimer) list_;  
//LIST使用方式
InBlock.gif

InBlock.gif    
static unsigned int mark;  //测试,配合dump
ExpandedBlockEnd.gif
}
;
ExpandedBlockStart.gifContractedBlock.gif
/**//*singlton的double-check实现*/
None.gifCTimerManager 
* CTimerManager::instance()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    
if(m_instance==NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        pthread_mutex_lock(
&m_mutex);
InBlock.gif        
if(m_instance==NULL)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            m_instance
=new CTimerManager();
ExpandedSubBlockEnd.gif        }

InBlock.gif        pthread_mutex_unlock(
&m_mutex);
ExpandedSubBlockEnd.gif    }

InBlock.gif    
return m_instance;
ExpandedBlockEnd.gif}

ExpandedBlockStart.gifContractedBlock.gif
/**//*process必须static,不能操作非static属性,因此传递this指针*/
None.gif
void CTimerManager:: start()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
         if(m_state==TIMER_MANAGER_STOP){
InBlock.gif    m_state
=TIMER_MANAGER_START;
InBlock.gif    pthread_t pid;
InBlock.gif    pthread_create(
&pid,0,process,this);
      }
ExpandedBlockEnd.gif}

ExpandedBlockStart.gifContractedBlock.gif
/**//*定时器模块延迟时间线程*/
None.gif
void * CTimerManager:: process(void * arg)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif{
InBlock.gif    pthread_detach(pthread_self());
InBlock.gif
InBlock.gif    CTimerManager 
*manage=(CTimerManager *)arg;
InBlock.gif
InBlock.gif    CTimer 
*item;
InBlock.gif    struct timeval start,end;
InBlock.gif    unsigned 
int delay;
InBlock.gif    
InBlock.gif    struct timeval tm;
InBlock.gif    gettimeofday(
&end,0);
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*使用状态控制线程运行,进而容易实现stop,也可以使用pthread_cancel粗暴的停止,需要考虑暂停点等问题*/
InBlock.gif    
while(manage->m_state==TIMER_MANAGER_START)
ExpandedSubBlockStart.gifContractedSubBlock.gif    
dot.gif{
InBlock.gif        tm.tv_sec
=0;
InBlock.gif        tm.tv_usec
=DEFULT_INTERVAL*1000;
InBlock.gif        start.tv_sec
=end.tv_sec;
InBlock.gif        start.tv_usec
=end.tv_usec;
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*不同系统的延迟函数精度不同,如果需要替换为其他延迟函数,这附近修改下就好*/
InBlock.gif        
while(select(0,0,0,0,&tm)<0&&errno==EINTR);
InBlock.gif        gettimeofday(
&end,0);
InBlock.gif
InBlock.gif        delay
=(end.tv_sec-start.tv_sec)*1000+(end.tv_usec-start.tv_usec)/1000;
InBlock.gif        pthread_mutex_lock(
&manage->m_mutex);
InBlock.gif
InBlock.gif        LIST_FOREACH(item, 
&(manage->list_), entry_)
ExpandedSubBlockStart.gifContractedSubBlock.gif        
dot.gif{
InBlock.gif            item
->m_counter<delay?item->m_counter=0:item->m_counter-=delay;
InBlock.gif            
if(item->m_counter==0)
ExpandedSubBlockStart.gifContractedSubBlock.gif            
dot.gif{
InBlock.gif                
if(item->m_func)
InBlock.gif                item
->m_func(item,item->m_data);
InBlock.gif
InBlock.gif                
if(item->m_type==CTimer::TIMER_ONCE)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*一次型的,超时,移除,并状态CTimer::TIMER_TIMEOUT*/
InBlock.gif                    manage
->remove_timer_(item);
InBlock.gif                    item
->m_state=CTimer::TIMER_TIMEOUT;
ExpandedSubBlockEnd.gif                }

InBlock.gif                
else if(item->m_type==CTimer::TIMER_CIRCLE)
ExpandedSubBlockStart.gifContractedSubBlock.gif                
dot.gif{
ExpandedSubBlockStart.gifContractedSubBlock.gif
/**//*循环型的,重置counter就好*/
InBlock.gif                    item
->m_counter=item->m_interval;
ExpandedSubBlockEnd.gif                }

ExpandedSubBlockEnd.gif            }

ExpandedSubBlockEnd.gif        }

InBlock.gif
InBlock.gif        pthread_mutex_unlock(
&manage->m_mutex);
ExpandedSubBlockEnd.gif    }

ExpandedBlockEnd.gif}



六、 讨论
(1)精度问题。精度高,实时性高,但要求select等待的时间缩短,进而增加对LIST结构的扫描操作。精度低,实时性差,但会增加定时器线程的睡眠时间,减少对cpu的占用。一般的应用系统,应该尽量降低精度,避免不必要的扫描,对具体系统可考察所用到的所有定时器的实际间隔,在允许的情况下,尽量降低精度,可通过修改代码中的宏实现。为了降低定时器线程对cpu的占有时间,甚有更为粗犷型的定时器模块实现为将延迟时间取list中最小的那个间隔,保证每次延迟时间到都有回调。
(2)加锁区域问题。本文中的定时器模块实现,将定时器对象的时间减少以及函数回调的执行等再同一个临界区内执行,而有的定时器模块实现是在加锁区域执行“时间减少”操作,将减少到0的对象放到另一个超时链表中,解锁后再单独扫描超时链表执行回调操作。很明显,后者缩短了加锁时间,能及时响应其他的线程的定时器对象的start以及stop操作。但是后者对定时器操作的时序性有误差,直观反应就是可能在定时器执行了stop操作以后,仍然会有超时回调发生,特别是回调参数是指针的情况,可能引起难以发现的bug,增加调试困难。在衡量两者的利弊后,本文采用延长加锁时间以保证操作的时序性。因此,在实际的使用,回调函数应尽快返回,另一方面,尽量减少系统内使用的定时器数目,这个主要原因是延迟时间到要扫描LIST,哪种方式都避免不了。


七、使用示例


None.gif#include "timer_manager.h"
None.gif#include 
<stdio.h>
None.gif#include 
<unistd.h>
None.gif#include 
<stdlib.h>
None.gif
void func(CTimer * timer, void *data)
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
{
InBlock.gif    printf(
"hi,%d\n",(int
)(data));
ExpandedBlockEnd.gif}

ExpandedBlockStart.gifContractedBlock.gif
/**//*随便写的,凑合着看吧。没有CTimerManager::instance()->stop();也没new对象。定时器对象可多次start和stop,使用上对暴露的接口没有任何的契约式限制,可随意调用*/
None.gif
int main()
ExpandedBlockStart.gifContractedBlock.gif
dot.gif
{
InBlock.gif    CTimerManager::instance()
->
start();
InBlock.gif    CTimer a(
1000,func,(void *)1
,CTimer::TIMER_CIRCLE);
InBlock.gif    CTimer a1(
2000,func,(void *)11
,CTimer::TIMER_ONCE);
InBlock.gif    CTimer a2(
3000,func,(void *)12
,CTimer::TIMER_ONCE);
InBlock.gif    CTimer a3(
1000,func,(void *)13
,CTimer::TIMER_ONCE);
InBlock.gif    
InBlock.gif    a.start();
InBlock.gif    a1.start();
InBlock.gif    a2.start();
InBlock.gif    a3.start();
InBlock.gif    a.start();
InBlock.gif    a1.start();
InBlock.gif    a2.start();
InBlock.gif    a3.start();
InBlock.gif
InBlock.gif    sleep(
1
);
InBlock.gif    CTimerManager::instance()
->
dump();
InBlock.gif    sleep(
1
);
InBlock.gif    CTimerManager::instance()
->
dump();
InBlock.gif    a.reset(
2000
);
InBlock.gif    a1.stop();
InBlock.gif    a3.stop();
InBlock.gif
InBlock.gif    sleep(
10
);
InBlock.gif    
return 0
;
ExpandedBlockEnd.gif}


八、后记
昨晚写好文章,不知为何无故丢失大半,郁闷。今早醒来,感觉还是有点地方要修改,:在start定时器线程的时候,传入精度和误差补偿,一是根据实际需要调整精确度,二是弥补延迟函数的稍许误差,以具有更好的伸缩性和精确度。本文旨在说明定时器模块的内部实现机制,详细细节不再修改了。


http://muscle-liu.javaeye.com/blog/413353


在多线程的应用中要用到延时函数,开始时我只用到 sleep 这个秒级函数,但在 solaris  上跑时,程序运行到sleep时,却显示 “Alarm clock” 这句话后就中止了。据说是产生了 alarm 这个信号,而系统默认信号处理就是中止程序,所以要在程序中把这个设置为忽略:




C代码 复制代码


  1. signal(SIGALRM, SIG_IGN);  
signal(SIGALRM, SIG_IGN);



unix 上的延时函数有好几种:


引用


一、 基础知识
1、时间类型。Linux下常用的时间类型有4个:time_t,struct timeval,struct timespec,struct tm。
(1)time_t是一个长整型,一般用来表示用1970年以来的秒数。
(2)Struct timeval有两个成员,一个是秒,一个是微妙。



C代码 复制代码


  1. struct timeval {   
  2.    long tv_sec;        /**//* seconds */  
  3.    long tv_usec;  /**//* microseconds */  
  4. ;  
 struct timeval {
long tv_sec; /**//* seconds */
long tv_usec; /**//* microseconds */
};

(3)struct timespec有两个成员,一个是秒,一个是纳秒。



C代码 复制代码


  1. struct timespec{   
  2.     time_t  tv_sec;         /**//* seconds */  
  3.     long    tv_nsec;        /**//* nanoseconds */  
  4. };  
struct timespec{
time_t tv_sec; /**//* seconds */
long tv_nsec; /**//* nanoseconds */
};

(4)struct tm是直观意义上的时间表示方法:



C代码 复制代码


  1. struct tm {   
  2.     int     tm_sec;         /**//* seconds */  
  3.     int     tm_min;         /**//* minutes */  
  4.     int     tm_hour;        /**//* hours */  
  5.     int     tm_mday;        /**//* day of the month */  
  6.     int     tm_mon;         /**//* month */  
  7.     int     tm_year;        /**//* year */  
  8.     int     tm_wday;        /**//* day of the week */  
  9.     int     tm_yday;        /**//* day in the year */  
  10.     int     tm_isdst;       /**//* daylight saving time */  
  11. };  
struct tm {
int tm_sec; /**//* seconds */
int tm_min; /**//* minutes */
int tm_hour; /**//* hours */
int tm_mday; /**//* day of the month */
int tm_mon; /**//* month */
int tm_year; /**//* year */
int tm_wday; /**//* day of the week */
int tm_yday; /**//* day in the year */
int tm_isdst; /**//* daylight saving time */
};

2、 时间操作
(1) 时间格式间的转换函数
主要是 time_t、struct tm、时间的字符串格式之间的转换。看下面的函数参数类型以及返回值类型:



C代码 复制代码


  1. char *asctime(const struct tm *tm);   
  2. char *ctime(const time_t *timep);   
  3. struct tm *gmtime(const time_t *timep);   
  4. struct tm *localtime(const time_t *timep);   
  5. time_t mktime(struct tm *tm);  
char *asctime(const struct tm *tm);
char *ctime(const time_t *timep);
struct tm *gmtime(const time_t *timep);
struct tm *localtime(const time_t *timep);
time_t mktime(struct tm *tm);

gmtime和localtime的参数以及返回值类型相同,区别是前者返回的格林威治标准时间,后者是当地时间。
(2) 获取时间函数
两个函数,获取的时间类型看原型就知道了:



C代码 复制代码


  1. time_t time(time_t *t);   
  2. int gettimeofday(struct timeval *tv, struct timezone *tz);  
time_t time(time_t *t);
int gettimeofday(struct timeval *tv, struct timezone *tz);

前者获取time_t类型,后者获取struct timeval类型,因为类型的缘故,前者只能精确到秒,后者可以精确到微秒。


二、 延迟函数
主要的延迟函数有:sleep(),usleep(),nanosleep(),select(),pselect().



C代码 复制代码


  1. unsigned int sleep(unsigned int seconds);   
  2.   
  3. void usleep(unsigned long usec);   
  4.   
  5. int nanosleep(const struct timespec *req, struct timespec *rem);   
  6.   
  7. int select(int n, fd_set *readfds,fd_set *writefds,fd_set *exceptfds,   
  8. struct timeval *timeout);   
  9.   
  10. int pselect(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,   
  11.  const struct timespec *timeout, const sigset_t *sigmask);  
unsigned int sleep(unsigned int seconds);

void usleep(unsigned long usec);

int nanosleep(const struct timespec *req, struct timespec *rem);

int select(int n, fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
struct timeval *timeout);

int pselect(int n,fd_set *readfds,fd_set *writefds,fd_set *exceptfds,
const struct timespec *timeout, const sigset_t *sigmask);

alarm函数是信号方式的延迟,这种方式不直观,这里不说了。
仅通过函数原型中时间参数类型,可以猜测sleep可以精确到秒级,usleep/select可以精确到微妙级,nanosleep和pselect可以精确到纳秒级。
而 实际实现中,linux上的nanosleep和alarm相同,都是基于内核时钟机制实现,受linux内核时钟实现的影响,并不能达到纳秒级的精度, man nanosleep也可以看到这个说明,man里给出的精度是:Linux/i386上是10 ms ,Linux/Alpha上是1ms。
这里有一篇文章http://blog.csdn.net/zhoujunyi/archive/2007/03/30/1546330.aspx, 测试了不同延迟函数之间的精确度。文章给出的结论是linux上精度最高的是select,10ms级别。我在本机器测试select和pselect相 当,都达到了1ms级的精度,精度高于文章中给出的10ms,sleep在秒级以上和usleep/nanosleep相当。下面贴下我机器上1ms时候 的测试结果,其他不贴了:



C代码 复制代码


  1. sleep           1000          0      -1000    
  2. usleep           1000       2974       1974    
  3. nanosleep        1000       2990       1990    
  4. select           1000        991         -9    
  5. pselect           1000        990        -10    
  6. gettimeofday           1000       1000          0  
sleep           1000          0      -1000 
usleep 1000 2974 1974
nanosleep 1000 2990 1990
select 1000 991 -9
pselect 1000 990 -10
gettimeofday 1000 1000 0

而使用gettimeofday循环不停检测时间,可精确微秒级,不过不适宜用来做定时器模块。
因此后面的定时期模块将选择select为延迟函数。


本文来自CSDN博客,转载请标明出处:http://blog.csdn.net/wbj1234566/archive/2008/05/13/2442264.aspx



我在用 usleep 时却发现有部分线程完全在等待中,没有醒过来, 最后换用了 nanosleep
正常回了。注意,要调用 nanosleep, 编译时要带 -lposix4
nanosleep 的例子(来自http://hi.baidu.com/zengzhaonong/blog/item/2fa4a799e282bb096f068c62.html)



C代码 复制代码



  1. #include <stdio.h>   
  2. #include <stdlib.h>   
  3. #include <unistd.h>   
  4. #include <sys/time.h>   
  5. #include <sched.h>   
  6. #define COUNT 1000   
  7. #define MILLION 1000000L   
  8.   
  9. int main(void)   
  10. {   
  11.     int i;   
  12.     struct timespec slptm;   
  13.     long   tdif;   
  14.     struct timeval tend, tstart;   
  15.   
  16.     slptm.tv_sec = 0;   
  17.     slptm.tv_nsec = 1000;      //1000 ns = 1 us   
  18.   
  19.     //struct sched_param param;       
  20.     //param.sched_priority = 0;   
  21.     //sched_setscheduler(getpid(), SCHED_FIFO, &param);   
  22.   
  23.     if (gettimeofday(&tstart, NULL) == -1) {   
  24.         fprintf(stderr, "Failed to get start time\n");   
  25.         return 1;   
  26.     }   
  27.     for (i = 0; i < COUNT; i++) {   
  28.         if (nanosleep(&slptm, NULL) == -1) {   
  29.             perror("Failed to nanosleep");   
  30.             return 1;   
  31.         }   
  32.     }   
  33.     if (gettimeofday(&tend, NULL) == -1) {   
  34.         fprintf(stderr, "Failed to get end time\n");   
  35.         return 1;   
  36.     }   
  37.     tdif = MILLION * (tend.tv_sec - tstart.tv_sec) + (tend.tv_usec - tstart.tv_usec);   
  38.     printf("nanosleep() time is %ld us\n", tdif/COUNT);   
  39.     return 0;   
  40. }   
  41.   
  42.   
  43.   
  44. HZ                                 250HZ   
  45. 时钟中断的时间间隔:                   4 ms   (1000ms/250)   
  46. ----------------------------------------   
  47. nanosleep() time is 4019 us        (4.019 ms)   
  48. 说明nanosleep的睡眠定时器依赖于时钟中断   
  49.   
  50.   
  51.   
  52. HZ                                 1000HZ   
  53. 时钟中断的时间间隔:                   1 ms   
  54. ----------------------------------------   
  55. nanosleep() time is 12 us   
  56. 注: 最小睡眠时间为1 us  

http://bbs.chinaunix.net/viewthread.php?tid=340598&extra=&page=1


测试
IBM AIX 3.4 单CPU
        sleep  可以在多线程中使用,只阻塞本线程,不影响所属进程中的其它线程
        不支持 nanosleep
        支持 usleep  和 select
        以下采用 gettimeofday 对 usleep 和 select 的实际精确情况进行测试分析
          function  time(usec)    realTime      reduce
-------------------------------------------------------------------
         usleep         500000     500026         26
         nanosleep      500000   not support
         select         500000     500026         26
         usleep         100000     100021         21
         nanosleep      100000   not support
         select         100000     100025         25
         usleep          50000      50021         21
         nanosleep       50000   not support
         select          50000      50107        107
         usleep          10000      10099         99
         nanosleep       10000   not support
         select          10000      10025         25
         usleep           1000       1021         21
         nanosleep        1000   not support
         select           1000       1024         24
         usleep            900        920         20
         nanosleep         900   not support
         select            900       1024        124
         usleep            500        523         23
         nanosleep         500   not support
         select            500       1024        524
         usleep            100        119         19
         nanosleep         100   not support
         select            100       1023        923
         usleep             10         31         21
         nanosleep          10   not support
         select             10       1024       1014
         usleep              1         19         18
         nanosleep           1   not support
         select              1       1026       1025

       
        由此可以得出,在AIX 3.4下:
                select 只能精确到毫秒级别
                usleep 可以精确到微秒级
                在1毫秒以上,两者的精确度基本一样

同上,在 linux 2.4.20-8smp 双CPU 下测试
          function  time(usec)    realTime      reduce
-------------------------------------------------------------------
         usleep         500000     506453       6453
         nanosleep      500000     509930       9930
         select         500000     499990        -10
         usleep         100000     110023      10023
         nanosleep      100000     109955       9955
         select         100000      99992         -8
         usleep          50000      59971       9971
         nanosleep       50000      59990       9990
         select          50000      50025         25
         usleep          10000      19991       9991
         nanosleep       10000      19988       9988
         select          10000       9956        -44
         usleep           1000      19990      18990
         nanosleep        1000      19989      18989
         select           1000      10024       9024
         usleep            900      20009      19109
         nanosleep         900      19972      19072
         select            900       9943       9043
         usleep            500      19975      19475
         nanosleep         500      19971      19471
         select            500      10012       9512
         usleep            100      19975      19875
         nanosleep         100      19976      19876
         select            100       9943       9843
         usleep             10      19988      19978
         nanosleep          10      19961      19951
         select             10      10011      10001
         usleep              1      19978      19977
         nanosleep           1      19985      19984
         select              1       9932       9931
在 2.4.21-4.ELsmp #1 SMP  4 CPU 下测试
           function  time(usec)    realTime      reduce
-------------------------------------------------------------------
         usleep         500000     501267       1267
         nanosleep      500000     509964       9964
         select         500000     499981        -19
         usleep         100000     109944       9944
         nanosleep      100000     109925       9925
         select         100000      99963        -37
         usleep          50000      59904       9904
         nanosleep       50000      59973       9973
         select          50000      49956        -44
         usleep          10000      19988       9988
         nanosleep       10000      20008      10008
         select          10000      10020         20
         usleep           1000      19988      18988
         nanosleep        1000      19980      18980
         select           1000       9943       8943
         usleep            900      19975      19075
         nanosleep         900      19986      19086
         select            900       9905       9005
         usleep            500      19989      19489
         nanosleep         500      19910      19410
         select            500      10000       9500
         usleep            100      19355      19255
         nanosleep         100      19902      19802
         select            100       9988       9888
         usleep             10      19977      19967
         nanosleep          10      19988      19978
         select             10       9943       9933
         usleep              1      20007      20006
         nanosleep           1      19947      19946
         select              1       9980       9979
         
                由此可以得出如下结论,在 linux 2.4 下:
                        1、支持 usleep,nanosleep,select
                        2、select 的 精确度为 10毫秒。在10毫秒以上很精确
                        3、usleep, nanosleep  很不精确
                同样,通过其它测试程序能得出如下结论:
                        sleep  可以在多线程中使用,只阻塞本线程,不影响所属进程中的其它线程
                                       
                       
               
我只有以上3种测试环境,有其它测试环境的帮我在其它环境下测试一下,将分析结果贴出来。
测试程序在之后发上来

再论精确延时(usleep,nanosleep,select)


/*
        make:  gcc -o test_sleep test_sleep.c
*/
/*        #include  "comm_main.h" */
#include <stdio.h>;
#include <stdlib.h>;
#include <time.h>;
#include <sys/time.h>;
#include <errno.h>;
#include <string.h>;
#include <unistd.h>;
#include <sys/types.h>;

#define PRINT_USEAGE  { \
   fprintf(stderr,"\n Usage: %s usec ",argv[0]); \
   fprintf(stderr,"\n\n";\
  }

int
main (int argc, char **argv)
{
  unsigned int nTimeTestSec = 0;        /* sec */
  unsigned int nTimeTest = 0;        /* usec */
  struct timeval tvBegin;
  struct timeval tvNow;
  int ret = 0;
  unsigned int nDelay = 0;        /* usec */
  fd_set rfds;
  struct timeval tv;
  int fd = 1;
  int i = 0;
  struct timespec req;
  unsigned int delay[20] =
    { 500000, 100000, 50000, 10000, 1000, 900, 500, 100, 10, 1, 0 };
  int nReduce = 0;                /* 误差  */

#if 0
  if (argc < 2)
    {
      PRINT_USEAGE;
      exit (1);
    }
  nDelay = atoi (argv[1]);
#endif

  fprintf (stderr, "%18s%12s%12s%12s\n", "function", "time(usec)", "realTime",
           "reduce";
  fprintf (stderr,
           "-------------------------------------------------------------------\n";

  for (i = 0; i < 20; i++)
    {
      if (delay <= 0)
        break;
      nDelay = delay;

      /*      test usleep */
      gettimeofday (&tvBegin, NULL);
      ret = usleep (nDelay);
      if (-1 == ret)
        {
          fprintf (stderr, " usleep error . errno=%d [%s]\n", errno,
                   strerror (errno));
        }
      gettimeofday (&tvNow, NULL);
      nTimeTest =
        (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec -
        tvBegin.tv_usec;
      nReduce = nTimeTest - nDelay;
      fprintf (stderr, "\t usleep       %8u   %8u   %8d\n", nDelay, nTimeTest,
               nReduce);


      /*      test nanosleep */
      gettimeofday (&tvBegin, NULL);
      req.tv_sec = nDelay / 1000000;
      req.tv_nsec = (nDelay % 1000000) * 1000;
      ret = nanosleep (&req, NULL);
      if (-1 == ret)
        {
          fprintf (stderr, "\t nanosleep    %8u   not support\n", nDelay);
        }
      else
        {
          gettimeofday (&tvNow, NULL);
          nTimeTest =
            (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec -
            tvBegin.tv_usec;
          nReduce = nTimeTest - nDelay;
          fprintf (stderr, "\t nanosleep    %8u   %8u   %8d\n", nDelay,
                   nTimeTest, nReduce);
        }

      /*      test select */
      gettimeofday (&tvBegin, NULL);
      FD_ZERO (&rfds);
      FD_SET (fd, &rfds);
      tv.tv_sec = 0;
      tv.tv_usec = nDelay;
      ret = select (0, NULL, NULL, NULL, &tv);
      if (-1 == ret)
        {
          fprintf (stderr, " select error . errno=%d [%s]\n", errno,
                   strerror (errno));
        }
      gettimeofday (&tvNow, NULL);
      nTimeTest =
        (tvNow.tv_sec - tvBegin.tv_sec) * 1000000 + tvNow.tv_usec -
        tvBegin.tv_usec;
      nReduce = nTimeTest - nDelay;
      fprintf (stderr, "\t select       %8u   %8u   %8d\n", nDelay, nTimeTest,
               nReduce);

    }

  return 0;
}
PARTNER CONTENT

文章评论0条评论)

登录后参与讨论
EE直播间
更多
我要评论
0
12
关闭 站长推荐上一条 /3 下一条