1、背景

继上周《Linux下mktime耗时测试》,本次通过参考glibc、内核源码进行实验

2、接口说明

时间转换接口,主要用于所指向的结构转换为自 1970 年1月1日以来持续时间的秒数,发生错误时返回-1。

SYNOPSIS         top#include <time.h>time_t mktime(struct tm *timeptr);
DESCRIPTIONThe functionality described on this reference page is alignedwith the ISO C standard. Any conflict between the requirementsdescribed here and the ISO C standard is unintentional. Thisvolume of POSIX.1‐2017 defers to the ISO C standard.The mktime() function shall convert the broken-down time,expressed as local time, in the structure pointed to by timeptr,into a time since the Epoch value with the same encoding as thatof the values returned by time().  The original values of thetm_wday and tm_yday components of the structure shall be ignored,and the original values of the other components shall not berestricted to the ranges described in <time.h>.
---Broken-down time is stored in the structure tm, which is defined in <time.h> as follows:struct tm {int tm_sec;    /* Seconds (0-60) */int tm_min;    /* Minutes (0-59) */int tm_hour;   /* Hours (0-23) */int tm_mday;   /* Day of the month (1-31) */int tm_mon;    /* Month (0-11) */int tm_year;   /* Year - 1900 */int tm_wday;   /* Day of the week (0-6, Sunday = 0) */int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */int tm_isdst;  /* Daylight saving time */};

3、实现

3.1 参考内核实现(最简单)

核心接口实现,几行代码就搞定


/* Converts Gregorian date to seconds since 1970-01-01 00:00:00.* Assumes input in normal date format, i.e. 1980-12-31 23:59:59* => year=1980, mon=12, day=31, hour=23, min=59, sec=59.** [For the Julian calendar (which was used in Russia before 1917,* Britain & colonies before 1752, anywhere else before 1582,* and is still in use by some communities) leave out the* -year/100+year/400 terms, and add 10.]** This algorithm was first published by Gauss (I think).** WARNING: this function will overflow on 2106-02-07 06:28:16 on* machines where long is 32-bit! (However, as time_t is signed, we* will already get problems at other places on 2038-01-19 03:14:08)*/
unsigned long __mktime_kernel(const unsigned int year0, const unsigned int mon0,const unsigned int day,   const unsigned int hour,const unsigned int min,   const unsigned int sec)
{unsigned int mon = mon0, year = year0;/* 1..12 -> 11,12,1..10 */if (0 >= (int)(mon -= 2)) {mon += 12;  /* Puts Feb last since it has leap day */year -= 1;}return ((((unsigned long)(year / 4 - year / 100 + year / 400 + 367 * mon / 12 + day) +year * 365 - 719499) * 24 + hour /* now have hours */) * 60 + min /* now have minutes */) * 60 + sec; /* finally seconds */
}

然后再仔细看他调用的地方,发现时间如果是太小了得有个+100的修正;

time_t sdk_mktime_kernel(struct tm *tp)
{if (!tp) {printf("NULL\n");return -1;}if (unlikely(tp->tm_year + 1900 < 1970)) {tp->tm_year += 100;}return __mktime_kernel(tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday,tp->tm_hour, tp->tm_min, tp->tm_sec);
}

3.2 glibc实现参考(裁剪)

核心实现是 __mktime_internal,然后这里面处理比较复杂,就是拆解了一些:
1、去掉了一个夏令时DST相关的逻辑;
2、去掉了一个闰秒相关的逻辑;

#define EPOCH_YEAR 1970
#define TM_YEAR_BASE 1900#if INT_MAX <= (LONG_MAX / 4 / 366 / 24 / 60 / 60)
typedef long int long_int;
#else
typedef long long int long_int;
#endifstatic const unsigned short int __g_mon_yday[2][13] = {/* Normal years.  */{ 0, 31, 59, 90, 120, 151, 181, 212, 243, 273, 304, 334, 365 },/* Leap years.  */{ 0, 31, 60, 91, 121, 152, 182, 213, 244, 274, 305, 335, 366 }
};#ifdef __cplusplus
extern "C" {#endif/* Is YEAR + TM_YEAR_BASE a leap year?  */
static inline bool __is_leapyear(long_int year)
{/* Don't add YEAR to TM_YEAR_BASE, as that might overflow.Also, work even if YEAR is negative.  */return ((year & 3) == 0 &&(year % 100 != 0 || ((year / 100) & 3) == (- (TM_YEAR_BASE / 100) & 3)));
}/* Shift A right by B bits portably, by dividing A by 2**B andtruncating towards minus infinity.  B should be in the range 0 <= B<= long_BITS - 2, where long_BITS is the number of usefulbits in a long.  long_BITS is at least 32.ISO C99 says that A >> B is implementation-defined if A < 0.  Someimplementations (e.g., UNICOS 9.0 on a Cray Y-MP EL) don't shiftright in the usual way when A < 0, so SHR falls back on division ifordinary A >> B doesn't seem to be the usual signed shift.  */
static inline long_int __shr(long_int a, int b)
{long_int one = 1;return ((-one >> 1 == -1) ? (a >> b) : (a + (a < 0)) / (one << b) - (a < 0));
}static inline long_int __do_ydhms_diff(long_int year1, long_int yday1, int hour1, int min1,int sec1,long_int year0, long_int yday0, int hour0, int min0, int sec0)
{//    verify(-1 / 2 == 0);/* Compute intervening leap days correctly even if year is negative.Take care to avoid integer overflow here.  */int a4 = __shr(year1, 2) + __shr(TM_YEAR_BASE, 2) - !(year1 & 3);int b4 = __shr(year0, 2) + __shr(TM_YEAR_BASE, 2) - !(year0 & 3);int a100 = (a4 + (a4 < 0)) / 25 - (a4 < 0);int b100 = (b4 + (b4 < 0)) / 25 - (b4 < 0);int a400 = __shr(a100, 2);int b400 = __shr(b100, 2);int intervening_leap_days = (a4 - b4) - (a100 - b100) + (a400 - b400);/* Compute the desired time without overflowing.  */long_int years = year1 - year0;long_int days = 365 * years + yday1 - yday0 + intervening_leap_days;long_int hours = 24 * days + hour1 - hour0;long_int minutes = 60 * hours + min1 - min0;long_int seconds = 60 * minutes + sec1 - sec0;return seconds;
}/* Convert *TP to a __time64_t value, invertingthe monotonic and mostly-unit-linear conversion function CONVERT.Use *OFFSET to keep track of a guess at the offset of the result,compared to what the result would be for UTC without leap seconds.If *OFFSET's guess is correct, only one CONVERT call is needed.If successful, set *TP to the canonicalized struct tm;otherwise leave *TP alone, return ((time_t) -1) and set errno.This function is external because it is used also by timegm.c. */
static inline time_t __mktime_internal(struct tm *tp)
{/* The maximum number of probes (calls to CONVERT) should be enoughto handle any combinations of time zone rule changes, solar time,leap seconds, and oscillations around a spring-forward gap.POSIX.1 prohibits leap seconds, but some hosts have them anyway.  */
//    int remaining_probes = 6;/* Time requested.  Copy it in case CONVERT modifies *TP; this canoccur if TP is localtime's returned value and CONVERT is localtime.  */int sec  = tp->tm_sec;int min  = tp->tm_min;int hour = tp->tm_hour;int mday = tp->tm_mday;int mon  = tp->tm_mon;int year_requested = tp->tm_year;/* Ensure that mon is in range, and set year accordingly.  */int mon_remainder = mon % 12;int negative_mon_remainder = mon_remainder < 0;int mon_years = mon / 12 - negative_mon_remainder;long_int lyear_requested = year_requested;long_int year = lyear_requested + mon_years;/* The other values need not be in range:the remaining code handles overflows correctly.  *//* Calculate day of year from year, month, and day of month.The result need not be in range.  */int mon_yday = ((__g_mon_yday[__is_leapyear(year)][mon_remainder + 12 * negative_mon_remainder]) - 1);long_int lmday = mday;long_int yday  = mon_yday + lmday;if (tp->tm_isdst != 0) {printf("WARN: DST not support !!!\n");}/* skip LEAP_SECONDS_POSSIBLE &* skip NEGATIVE_OFFSET_GUESS* skip tm.tm_isdst */return (time_t)__do_ydhms_diff(year, yday, hour, min, sec,EPOCH_YEAR - TM_YEAR_BASE, 0, 0, 0, 0);
}

最后封装统一了一下格式

time_t sdk_mktime_glibc(struct tm *tp)
{if (!tp) {printf("NULL\n");return -1;}return __mktime_internal(tp);
}

3.3 实验结果

2021-09-26 20:24:09
Running ./bm_mktime
Run on (16 X 2199.09 MHz CPU s)
CPU Caches:L1 Data 32K (x4)L1 Instruction 32K (x4)L2 Unified 256K (x4)L3 Unified 25600K (x4)
Load Average: 0.00, 0.00, 0.00
----------------------------------------------------------------------------------
Benchmark                                        Time             CPU   Iterations
----------------------------------------------------------------------------------
bm_mktime/mktime_TZ_NULL_isDST                 886 ns          886 ns       787062
bm_mktime/mktime_TZ_NULL_noDST                 886 ns          886 ns       789607
bm_mktime/mktime_TZ_empty_isDST                406 ns          406 ns      1723817
bm_mktime/mktime_TZ_empty_noDST                406 ns          406 ns      1727658
bm_mktime/mktime_TZ_shanghai_isDST             405 ns          405 ns      1721386
bm_mktime/mktime_TZ_shanghai_noDST             406 ns          406 ns      1725817
bm_mktime/sdk_mktime_glibc                    18.4 ns         18.4 ns     38107389
bm_mktime/sdk_mktime_kernel                   18.4 ns         18.4 ns     38096369

4、结论

实验结果看,用户态直接计算,提升呈几何增长;glibc、kernel实现测试的速度不相上下;但实现复杂度来看,kernel版本更加简洁。

Linux下用户态mktime实现参考相关推荐

  1. 红帽企业版Linux成为Linux下的.NET Core的参考平台

    微软和红帽声明将在红帽企业版Linux运行的.NET纳入官方支持.经两家公司透露,"红帽企业级Linux将成为Linux下的.NET Core主要参考操作系统". \\ 来自红帽资 ...

  2. Linux下用户管理

    Linux下用户管理 linux下用户管理有两个重要的配置文件 一个是:/etc/shadow                           #保存的是用户的密码信息 另一个是:/etc/pas ...

  3. LINUX下用户和组的操作与相关的配置文件

    LINUX下用户和组的操作与相关的配置文件 与用户相关的配置文件 passwd文件 shadow文件 group文件 用户和组的操作 和用户相关的操作 useradd userdel usermod ...

  4. linux 下用户管理

    linux 下用户管理 一.用户的分类 1.超级用户:root UID=0 2.系统用户:不需要登录系统,对应用程序服务,主要维护系统的正常运行:UID = 1 ~ 499(RHEL7 = 1 ~ 9 ...

  5. linux下用户的添加

    linux下用户的添加 在linux下用户添加的命令使用: useradd 然再使用:  passwd  为用户添加密码 例如,使用useradd    zhangsan         #就创建了一 ...

  6. linux下用户配置文件与系统配置文件

    linux下用户配置文件与系统配置文件 linux下用户配置文件目录在用户的目录下 以我的ubuntu为例,我的用户配置文件所在目录为:/home/liyuanhong 使用命令:ls  -A   来 ...

  7. linux添加三个用户lab1,linux操作系统实验linux下用户与组的管理

    姓名学号班级 实验三Linux下用户与组的管理 一.实验目的 1.理解/etc/passwd和/etc/group文件的含义: 2.掌握桌面环境下管理用户与组群的方法: 3.掌握利用shell命令管理 ...

  8. Linux对用户态的动态内存管理

    Linux对内核态内存分配请求与用户态内存分配请求处理上分别对待 Linux本身信任自己,因此Linux内核请求分配多少内存,就会马上分配相同数量的内存出来. 但内核本身不相信应用程序,而且通常应用程 ...

  9. linux 内核空间 sy,在 Linux 下用户空间与内核空间数据交换的方式,第 1 部分: 内核启动参数、模块参数与sysf...

    级别: 初级 燚 杨 (), 计算机科学硕士 2006 年 2 月 16 日 本系列文章包括两篇,它们文详细地介绍了 Linux 系统下用户空间与内核空间数据交换的九种方式,包括内核启动参数.模块参数 ...

最新文章

  1. 2021年春季学期-信号与系统-第二次作业参考答案-第十小题
  2. 解决MPLAB X IDE的文件注释出现乱码的问题
  3. Android-上传图片(-)_HttpURLConnection
  4. Java的poi的excel导入怎么验证整型格式的单元格
  5. python 爬虫 包_python爬虫学习之路-抓包分析
  6. java settcpnodelay_Python Twisted TCP socket如何设置TCP的NODELAY(禁用Nagle算法)?
  7. Mr.J--蓝桥杯--去注释
  8. 分账汇总少了一笔 和 对账和商户汇总比基础表少了一笔 问题处理方式
  9. RAD与non-RAD
  10. 校外实习-第三周总结
  11. 使用对象方式管理XML文件
  12. 简单个人网页设计作业 静态HTML个人博客主页 DW个人网站模板下载 大学生简单个人网页作品代码 个人网页制作 学生个人网页设计作业
  13. python入门(三) 实现QQ自动发送消息
  14. 机器学习库sklearn之怎么划分训练集和测试集
  15. [html] 你有使用过blockquote标签吗?说说它的用途有哪些?
  16. IC卡,ID卡,M1卡等各种卡扫盲篇
  17. 十八、疑案系列之——永远的无头公案
  18. 交互式可视化报表怎么弄?看过来!
  19. 身份证提取生日和性别
  20. js实现图片放大预览(jq+css)

热门文章

  1. (转)互联网保险O2O平台微服务架构设计
  2. npm学习:安装、更新以及管理npm版本
  3. 使用STM32CubeMX中的芯片仿真
  4. ubuntu如何在开发板上挂载文件
  5. GNN-CS224W: 17 Scaling Up GNNs
  6. 读易[17]·成功不需要一定当老板(坤卦)
  7. 用鸢尾花数据集实现knn分类算法
  8. IC设计行业分类辨析
  9. html input 标签 防止空输入
  10. VC之PDF文件操作