精华内容
下载资源
问答
  • timekeeper

    2011-08-03 14:16:47
    设定某时间关机或提醒 或者 设定一定时间间隔关机或提醒
  • TimeKeeper-开源

    2021-07-19 02:44:28
    TimeKeeper 是一个时间管理系统,可让您跟踪在项目上花费的时间以及在会议期间添加的评论。 易用性是 TimeKeeper 的主要关注点,它带有一个 Web 界面以及一个命令行界面。
  • timekeeper-源码

    2021-03-17 05:25:42
    Timekeeper是在PC,Mac和手机上运行的简单应用程序。 它允许主持人及其来宾(例如,在直播中)跟踪时间。 这是概念证明的应用。 想象一下,您是直播节目的主持人,进行一些录音或进行远程访客的任何活动。 最困难...
  • Timekeeper是一个秘密合同,它使用比特币网络充当秘密网络上的时间参考。在没有办法验证秘密飞地内的块高之前,它应作为一种解决方法。 合同存储起始块高度,当前偏移量和块哈希。 通过对块头值进行来更新此偏移量。...
  • Timekeeper 是由 falconraptor 为内华达州 CCSD 的高中制作的。 如果您希望我添加某些内容或修复某些内容,请提交问题? 该计划于 2014 年 2 月启动。 该程序通过 dropbox 自动更新,如果有人有更好的自动更新方法,...
  • Timekeeper 是一个通过将测量值发送到 InfluxDB 来检测实时 Python 代码的库。 需要 Python 2.7 或 3.2+,以及 InfluxDB 0.9.0+。 用法 您可以使用上下文管理器或装饰一个函数来记录该函数或代码块完成所需的时间:...
  • 请注意,如果您使用时间冻结,则setTimeout和setInterval将无法按预期工作,因为它们使用的是Date类,但是直到调用timekeeper#reset ,时间才会改变。 安装 NPM: npm install timekeeper --save-dev Bower:Bower ...
  • 等离子计时器 时间跟踪等离子小程序 作者:Jan Grulich < > 根据GPL 2.0+发布 ... 在其存储库中提供plasma-timekeeper的发行版列表: Archlinux - //aur.archlinux.org/packages/timekeeper-git
  • React时间守护者 基于应用样式的时间选择器。 特征 支持12小时和24小时模式以及灵活的时间...import TimeKeeper from 'react-timekeeper' ; function YourComponent ( ) { const [ time , setTime ] = useState
  • timekeeperslock:Pwn2Win CTF 2016 Timekeeper's Lock挑战源代码
  • TimeKeeper(安装: ) 我所做的这个项目可以帮助我跟踪时间。 这是一个非常简单的倒数计时器。 每当我使用Visual Studio时,我都会使用一个浅色主题(用于对比)。 所有操作(“开始”,“暂停”,“重新启动...
  • linux时间子系统 - clocksource/timekeeper

    千次阅读 2016-12-11 20:13:15
    linux时间子系统中有一个很重要的记录时间的模块就是timekeeper,而timekeeper需要底层clock硬件设备的支持,每一个设备用结构体clocksource来表示,注册进系统的每一个clocksource会凭借优先级最终被选择成为...

    linux时间子系统中有一个很重要的记录时间的模块就是timekeeper,而timekeeper需要底层clock硬件设备的支持,每一个设备用结构体clocksource来表示,注册进系统的每一个clocksource会凭借优先级最终被选择成为timekeeper的时钟源。

    1. clocksource的注册


    1.1 clocksource结构体

    struct clocksource {
        cycle_t (*read)(struct clocksource *cs);
        cycle_t mask;
        u32 mult;
        u32 shift;
        u64 max_idle_ns;
        u32 maxadj;
    #ifdef CONFIG_ARCH_CLOCKSOURCE_DATA
        struct arch_clocksource_data archdata;
    #endif
        u64 max_cycles;
        const char *name;
        struct list_head list;
        int rating;----------------------------------------clock设备的优先级
        int (*enable)(struct clocksource *cs);-------------clock设备使能
        void (*disable)(struct clocksource *cs);-----------clock设备关闭
        unsigned long flags;-------------------------------clock源的标志
        void (*suspend)(struct clocksource *cs);-----------PM调用函数suspend
        void (*resume)(struct clocksource *cs);------------PM调用函数resume
    
        /* private: */
    #ifdef CONFIG_CLOCKSOURCE_WATCHDOG
        /* Watchdog related data, used by the framework */
        struct list_head wd_list;
        cycle_t cs_last;
        cycle_t wd_last;
    #endif
        struct module *owner;
    };

    1.2 clocksource_list

    每一个注册进系统的clock设备都会加入一个全局链表clocksource_list

    (kernel/time/clocksource.c)

    static LIST_HEAD(clocksource_list);

    但是timekeeper只会选取其中最合适的一个作为主clock,用一下变量来表示

    static struct clocksource *curr_clocksource;

    1.3 注册函数

    clocksource的注册函数有如下几个:

    (include/linux/clocksource.h)

    static inline int __clocksource_register(struct clocksource *cs)
    {
        return __clocksource_register_scale(cs, 1, 0);
    }
    
    static inline int clocksource_register_hz(struct clocksource *cs, u32 hz)
    {
        return __clocksource_register_scale(cs, 1, hz);
    }
    
    static inline int clocksource_register_khz(struct clocksource *cs, u32 khz)
    {
    

    1.4 clocksource注册过程

    clocksource设备的注册可以当作一个驱动,所以一般clock设备的文件都在dricers/clocksource/目录下,下面以arm global timer为例来介绍注册过程。

    clocksource设备的初始化接口由宏定义:

    CLOCKSOURCE_OF_DECLARE(arm_gt, "arm,cortex-a9-global-timer",
                global_timer_of_register);
    

    global的初始化函数为global_timer_of_register,下图是注册的函数调用关系图:

    这里写图片描述

    1.4.1 把clock设备加入clocksource_list

    函数clocksource_enqueue实现这个过程

    static void clocksource_enqueue(struct clocksource *cs)
    {
        struct list_head *entry = &clocksource_list;
        struct clocksource *tmp;
    
        list_for_each_entry(tmp, &clocksource_list, list)
            /* Keep track of the place, where to insert */
            if (tmp->rating >= cs->rating)
                entry = &tmp->list;--------------得到最合适插入的地方
        list_add(&cs->list, entry);--------------加入clocksource_list
    }

    1.4.2 选取最合适的curr_clocksource

    函数__clocksource_select实现这个过程:

    static void __clocksource_select(bool skipcur)
    {
        bool oneshot = tick_oneshot_mode_active();
        struct clocksource *best, *cs;
    
        /* Find the best suitable clocksource */
        best = clocksource_find_best(oneshot, skipcur);
        if (!best)
            return;--------------------------------------------没有找到合适的就直接返回
    
        /* Check for the override clocksource. */
        list_for_each_entry(cs, &clocksource_list, list) {
            if (skipcur && cs == curr_clocksource)
                continue;
            if (strcmp(cs->name, override_name) != 0)
                continue;
            /*
             * Check to make sure we don't switch to a non-highres
             * capable clocksource if the tick code is in oneshot
             * mode (highres or nohz)
             */
            if (!(cs->flags & CLOCK_SOURCE_VALID_FOR_HRES) && oneshot) {
                /* Override clocksource cannot be used. */
                pr_warn("Override clocksource %s is not HRT compatible - cannot switch while in HRT/NOHZ mode\n",
                    cs->name);
                override_name[0] = 0;
            } else
                /* Override clocksource can be used. */
                best = cs;-------------------------------------根据上面各个条件的过滤,最后得到最好的clocksource
            break;
        }
    
        if (curr_clocksource != best && !timekeeping_notify(best)) {
            pr_info("Switched to clocksource %s\n", best->name);
            curr_clocksource = best;--------------------------把clocksource给curr_clocksource 
        }
    }

    2. timekeeper


    struct timekeeper {
        struct tk_read_base tkr_mono;
        struct tk_read_base tkr_raw;
        u64         xtime_sec;
        unsigned long       ktime_sec;
        struct timespec64   wall_to_monotonic;
        ktime_t         offs_real;
        ktime_t         offs_boot;
        ktime_t         offs_tai;
        s32         tai_offset;
        unsigned int        clock_was_set_seq;
        u8          cs_was_changed_seq;
        ktime_t         next_leap_ktime;
        struct timespec64   raw_time;
    
        /* The following members are for timekeeping internal use */
        cycle_t         cycle_interval;
        u64         xtime_interval;
        s64         xtime_remainder;
        u32         raw_interval;
        /* The ntp_tick_length() value currently being used.
         * This cached copy ensures we consistently apply the tick
         * length for an entire tick, as ntp_tick_length may change
         * mid-tick, and we don't want to apply that new value to
         * the tick in progress.
         */
        u64         ntp_tick;
        /* Difference between accumulated time and NTP time in ntp
         * shifted nano seconds. */
        s64         ntp_error;
        u32         ntp_error_shift;
        u32         ntp_err_mult;
    #ifdef CONFIG_DEBUG_TIMEKEEPING
        long            last_warning;
        /*
         * These simple flag variables are managed
         * without locks, which is racy, but they are
         * ok since we don't really care about being
         * super precise about how many events were
         * seen, just that a problem was observed.
         */
        int         underflow_seen;
        int         overflow_seen;
    #endif
    };

    TODO

    Change Log


    datecontentlinux
    2016.12.11clocksourcelinux 4.6.3
    展开全文
  • $ git clone https://github.com/grepcats/very_timekeeper 安装.NET Framework和MAMP .NET Core 1.1 SDK(软件开发工具包).NET运行时。 曼普 有关说明和链接,请参见 。 在MAMP中启动Apache和MySql服务器。 ...
  • 继续ctf的旅程,开始攻防世界web高手进阶区的10分题,本文是TimeKeeper的writeup

    前言

    继续ctf的旅程
    开始攻防世界web高手进阶区的10分题
    本文是TimeKeeper的writeup

    解题过程

    进入界面

    在这里插入图片描述
    惯例源码+御剑
    源码没发现
    御剑扫到一个Flask debug console

    先正常注册登录
    尝试在注册和登录界面sqli
    失败

    在这里插入图片描述
    没有别的头绪
    就尝试研究debug
    参考Flask debug pin安全问题

    在支付界面抓包
    修改id和price
    制造bug
    进入debug界面

    在这里插入图片描述
    在这里插入图片描述
    根据Flask debug pin安全问题
    获取pin码需要以下这些信息

    list = [ 当前用户,#通过读取/etc/passwd获取 
    		'flask.app', #一般情况为固定值 	
    		'Flask',#一般情况为固定值 
     		'/usr/local/lib/python2.7/dist-packages/flask/app.py',#flask目录下的一个app.py的绝对路径,通过debug错误页面获取 
     		mac地址的十进制,#通过读取/sys/class/net/eth0/address获取mac地址 如果不是映射端口 可以通过arp ip命令获取 
     		机器名,#通过读取/proc/self/cgroup或/proc/sys/kernel/random/boot_id 或/etc/machine-id获取 ]
    

    那就要想办法读取剩下的三个信息

    没有什么可以操作的想法

    在源码里看到个asserts目录

    在这里插入图片描述

    尝试了下目录穿越

    在这里插入图片描述
    成功了
    打开

    在这里插入图片描述
    用户可以用root或ctf

    类似的
    读取剩下的两个信息
    mac地址

    在这里插入图片描述
    再转换为十进制2583524700623

    机器名
    /proc/self/cgroup得到的如下

    在这里插入图片描述
    19ef9f241e480f2bca3437d27505a657eda03c05014023382e1b00667d4c82ad

    /proc/sys/kernel/random/boot_id得到的如下
    在这里插入图片描述
    161cbdb0-fbcf-4ddb-bb86-5af547adc20c
    这俩为什么不一样?
    困惑

    先继续下去
    于是得到所有信息如下

    list = [ 'root', #当前用户,通过读取/etc/passwd获取 
    		'flask.app', #一般情况为固定值 	
    		'Flask',#一般情况为固定值 
     		'/usr/local/lib/python2.7/dist-packages/flask/app.py',#flask目录下的一个app.py的绝对路径,通过debug错误页面获取 
     		'2583524700623',#mac地址的十进制,通过读取/sys/class/net/eth0/address获取mac地址 如果不是映射端口 可以通过arp ip命令获取 
     		'19ef9f241e480f2bca3437d27505a657eda03c05014023382e1b00667d4c82ad'#机器名,通过读取/proc/self/cgroup或/proc/sys/kernel/random/boot_id 或/etc/machine-id获取 ]
    

    脚本

    import hashlib
    from itertools import chain
    probably_public_bits = [
        'root',# username
        'flask.app',# modname
        'Flask',# getattr(app, '__name__', getattr(app.__class__, '__name__'))
        '/usr/local/lib/python2.7/dist-packages/flask/app.py' # getattr(mod, '__file__', None),
    ]
    
    private_bits = [
        '2583524700623',# str(uuid.getnode()),  /sys/class/net/ens33/address
        '19ef9f241e480f2bca3437d27505a657eda03c05014023382e1b00667d4c82ad'# get_machine_id(), /etc/machine-id
    ]
    
    h = hashlib.md5()
    for bit in chain(probably_public_bits, private_bits):
        if not bit:
            continue
        if isinstance(bit, str):
            bit = bit.encode('utf-8')
        h.update(bit)
    h.update(b'cookiesalt')
    
    cookie_name = '__wzd' + h.hexdigest()[:20]
    
    num = None
    if num is None:
        h.update(b'pinsalt')
        num = ('%09d' % int(h.hexdigest(), 16))[:9]
    
    rv =None
    if rv is None:
        for group_size in 5, 4, 3:
            if len(num) % group_size == 0:
                rv = '-'.join(num[x:x + group_size].rjust(group_size, '0') for x in range(0, len(num), group_size))
                break
        else:
            rv = num
    
    print(rv)
    

    得到pin码
    但是做了好些尝试
    还是一直pin码不对
    在这里插入图片描述
    人傻了

    。。。
    然后突然想到
    何不直接目录穿越试试获取flag呢

    在这里插入图片描述
    得到flag
    提交
    。。。
    假的flag
    。。。

    尝试改下后缀
    在这里插入图片描述
    得到真flag
    。。。
    无话可说

    结语

    这题给我搞傻了
    搞了半天
    结果是最简单的目录穿越就可以得到flag

    不过学到了关于flask debug的知识
    但本题的pin码一直搞不对
    有师傅教教么

    还是有好些问题没搞明白
    先记一笔
    回头再来看看

    展开全文
  • Tmux Timekeeper (TT) 是名为终端多路复用器的自动时间跟踪插件。 该插件处于初始开发阶段。 它可以并且将会以向后不兼容的方式改变 这个怎么运作 TT 会密切关注您正在使用的窗格,并记录您在每个会话、窗口、窗格...
  • 欢迎来到TimeKeeper项目页面。 这个开源项目由Lunatech创建,使用Apache 2.0许可证分发。 它允许您向您展示时间跟踪应用程序的完整示例。 我们首先为Lunatech员工构建了此软件。 该工具使我们能够跟踪项目和客户所...
  • Consultant Communicator (ConsultComm) 是一个用 Java 编写的小型、轻量级、独立于平台的程序,它允许管理多个项目、客户或任务的任何人有效地准确跟踪他们在每个项目上花费的时间。
  • 演讲和辩论比赛的计时器。 跟踪各种辩论形式(政策、LD、议会、公共论坛等)和个别活动的演讲顺序、时间限制和准备时间。 提供口头和视觉时间信号。
  • ui-timekeeper-react-源码

    2021-04-08 05:30:38
    Create React App入门 该项目是通过引导的。 可用脚本 在项目目录中,可以运行: npm start 在开发模式下运行应用程序。 打开在浏览器中查看它。 如果您进行编辑,则页面将重新加载。 您还将在控制台中看到任何...
  • 原文地址:Linux时间子系统之三:时间的维护者:timekeeper 本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法,但是对于真实的用户来说,我们感知的是真实世界的真实时间...

    专题文档汇总目录

    Notes:

    原文地址:Linux时间子系统之三:时间的维护者:timekeeper

     

    本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法,但是对于真实的用户来说,我们感知的是真实世界的真实时间,也就是所谓的墙上时间,clocksource只能提供一个按给定频率不停递增的周期计数,如何把它和真实的墙上时间相关联?本节的内容正是要讨论这一点。

    1.  时间的种类

    内核管理着多种时间,它们分别是:

    • RTC时间
    • wall time:墙上时间
    • monotonic time
    • raw monotonic time
    • boot time:总启动时间

    RTC时间  在PC中,RTC时间又叫CMOS时间,它通常由一个专门的计时硬件来实现,软件可以读取该硬件来获得年月日、时分秒等时间信息,而在嵌入式系统中,有使用专门的RTC芯片,也有直接把RTC集成到Soc芯片中,读取Soc中的某个寄存器即可获取当前时间信息。一般来说,RTC是一种可持续计时的,也就是说,不管系统是否上电,RTC中的时间信息都不会丢失,计时会一直持续进行,硬件上通常使用一个后备电池对RTC硬件进行单独的供电。因为RTC硬件的多样性,开发者需要为每种RTC时钟硬件提供相应的驱动程序,内核和用户空间通过驱动程序访问RTC硬件来获取或设置时间信息。

    xtime  xtime和RTC时间一样,都是人们日常所使用的墙上时间,只是RTC时间的精度通常比较低,大多数情况下只能达到毫秒级别的精度,如果是使用外部的RTC芯片,访问速度也比较慢,为此,内核维护了另外一个wall time时间:xtime,取决于用于对xtime计时的clocksource,它的精度甚至可以达到纳秒级别,因为xtime实际上是一个内存中的变量,它的访问速度非常快,内核大部分时间都是使用xtime来获得当前时间信息。xtime记录的是自1970年1月1日24时到当前时刻所经历的纳秒数。

    monotonic time  该时间自系统开机后就一直单调地增加,它不像xtime可以因用户的调整时间而产生跳变,不过该时间不计算系统休眠的时间,也就是说,系统休眠时,monotoic时间不会递增。

    raw monotonic time  该时间与monotonic时间类似,也是单调递增的时间,唯一的不同是:raw monotonic time“更纯净”,他不会受到NTP时间调整的影响,它代表着系统独立时钟硬件对时间的统计。

    boot time  与monotonic时间相同,不过会累加上系统休眠的时间,它代表着系统上电后的总时间

    时间种类精度(统计单位)访问速度累计休眠时间受NTP调整的影响
    RTCYesYes
    xtimeYesYes
    monotonicNoYes
    raw monotonicNoNo
    boot timeYesYes

    2.  struct timekeeper

    内核用timekeeper结构来组织与时间相关的数据,它的定义如下:

    struct timekeeper {
        /* Current clocksource used for timekeeping. */
        struct clocksource *clock;
        /* NTP adjusted clock multiplier */
        u32    mult;
        /* The shift value of the current clocksource. */
        int    shift;
    
        /* Number of clock cycles in one NTP interval. */
        cycle_t cycle_interval;
        /* Number of clock shifted nano seconds in one NTP interval. */
        u64    xtime_interval;
        /* shifted nano seconds left over when rounding cycle_interval */
        s64    xtime_remainder;
        /* Raw nano seconds accumulated per NTP interval. */
        u32    raw_interval;
    
        /* Clock shifted nano seconds remainder not stored in xtime.tv_nsec. */
        u64    xtime_nsec;
        /* Difference between accumulated time and NTP time in ntp
         * shifted nano seconds. */
        s64    ntp_error;
        /* Shift conversion between clock shifted nano seconds and
         * ntp shifted nano seconds. */
        int    ntp_error_shift;
    
        /* The current time */
        struct timespec xtime;
        /*
         * wall_to_monotonic is what we need to add to xtime (or xtime corrected
         * for sub jiffie times) to get to monotonic time.  Monotonic is pegged
         * at zero at system boot time, so wall_to_monotonic will be negative,
         * however, we will ALWAYS keep the tv_nsec part positive so we can use
         * the usual normalization.
         *
         * wall_to_monotonic is moved after resume from suspend for the
         * monotonic time not to jump. We need to add total_sleep_time to
         * wall_to_monotonic to get the real boot based time offset.
         *
         * - wall_to_monotonic is no longer the boot time, getboottime must be
         * used instead.
         */
        struct timespec wall_to_monotonic;
        /* time spent in suspend */
        struct timespec total_sleep_time;
        /* The raw monotonic time for the CLOCK_MONOTONIC_RAW posix clock. */
        struct timespec raw_time;
    
        /* Offset clock monotonic -> clock realtime */
        ktime_t offs_real;----------------------------------------------------------------------monotonic-realtime的差值,一般未负数。
    
        /* Offset clock monotonic -> clock boottime */
        ktime_t offs_boot;----------------------------------------------------------------------boottime-monotonic的差值。
    
        /* Seqlock for all timekeeper values */
        seqlock_t lock;
    }

     

     

    其中的xtime字段就是上面所说的墙上时间,它是一个timespec结构的变量,它记录了自1970年1月1日以来所经过的时间,因为是timespec结构,所以它的精度可以达到纳秒级,当然那要取决于系统的硬件是否支持这一精度。

    内核除了用xtime表示墙上的真实时间外,还维护了另外一个时间:monotonic time,可以把它理解为自系统启动以来所经过的时间,该时间只能单调递增,可以理解为xtime虽然正常情况下也是递增的,但是毕竟用户可以主动向前或向后调整墙上时间,从而修改xtime值。但是monotonic时间不可以往后退,系统启动后只能不断递增。奇怪的是,内核并没有直接定义一个这样的变量来记录monotonic时间,而是定义了一个变量wall_to_monotonic,记录了墙上时间和monotonic时间之间的偏移量,当需要获得monotonic时间时,把xtime和wall_to_monotonic相加即可,因为默认启动时monotonic时间为0,所以实际上wall_to_monotonic的值是一个负数,它和xtime同一时间被初始化,请参考timekeeping_init函数。

    计算monotonic时间要去除系统休眠期间花费的时间,内核用total_sleep_time记录休眠的时间,每次休眠醒来后重新累加该时间,并调整wall_to_monotonic的值,使其在系统休眠醒来后,monotonic时间不会发生跳变。因为wall_to_monotonic值被调整。所以如果想获取boot time,需要加入该变量的值:

    void get_monotonic_boottime(struct timespec *ts)
    {
        struct timespec tomono, sleep;
        unsigned int seq;
        s64 nsecs;
    
        WARN_ON(timekeeping_suspended);
    
        do {
            seq = read_seqbegin(&timekeeper.lock);
            *ts = timekeeper.xtime;--------------------------------walltime
            tomono = timekeeper.wall_to_monotonic;-----------------monotonic time
            sleep = timekeeper.total_sleep_time;-------------------sleep time
            nsecs = timekeeping_get_ns();
    
        } while (read_seqretry(&timekeeper.lock, seq));
        
        set_normalized_timespec(ts, ts->tv_sec + tomono.tv_sec + sleep.tv_sec,
            (s64)ts->tv_nsec + tomono.tv_nsec + sleep.tv_nsec + nsecs);
    }

     

    raw_time字段用来表示真正的硬件时间,也就是上面所说的raw monotonic time,它不受时间调整的影响,monotonic时间虽然也不受settimeofday的影响,但会受到ntp调整的影响,但是raw_time不受ntp的影响,他真的就是开完机后就单调地递增。xtime、monotonic-time和raw_time可以通过用户空间的clock_gettime函数获得,对应的ID参数分别是 CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_MONOTONIC_RAW。

    clock字段则指向了目前timekeeper所使用的时钟源,xtime,monotonic time和raw time都是基于该时钟源进行计时操作,当有新的精度更高的时钟源被注册时,通过timekeeping_notify函数,change_clocksource函数将会被调用,timekeeper.clock字段将会被更新,指向新的clocksource。

    早期的内核版本中,xtime、wall_to_monotonic、raw_time其实是定义为全局静态变量,到我目前的版本(V3.4.10),这几个变量被移入到了timekeeper结构中,现在只需维护一个timekeeper全局静态变量即可:

    static struct timekeeper timekeeper;

     

    3.  timekeeper的初始化

    timekeeper的初始化由timekeeping_init完成,该函数在start_kernel的初始化序列中被调用,timekeeping_init首先从RTC中获取当前时间:

    void __init timekeeping_init(void)
    {
        struct clocksource *clock;
        unsigned long flags;
        struct timespec now, boot;
    
        read_persistent_clock(&now);
        if (!timespec_valid_strict(&now)) {
            pr_warn("WARNING: Persistent clock returned invalid value!\n"
                "         Check your CMOS/BIOS settings.\n");
            now.tv_sec = 0;
            now.tv_nsec = 0;
        }
    
        read_boot_clock(&boot);
        if (!timespec_valid_strict(&boot)) {
            pr_warn("WARNING: Boot clock returned invalid value!\n"
                "         Check your CMOS/BIOS settings.\n");
            boot.tv_sec = 0;
            boot.tv_nsec = 0;
        }
    
        seqlock_init(&timekeeper.lock);
    
        ntp_init();--------------------------------------------对锁和ntp进行必要的初始化
    
        write_seqlock_irqsave(&timekeeper.lock, flags);
        clock = clocksource_default_clock();-------------------获取默认的clocksource,如果平台没有重新实现clocksource_default_clock函数,默认的clocksource就是基于jiffies的clocksource_jiffies,然后通过timekeeper_setup_inernals内部函数把timekeeper和clocksource进行关联
        if (clock->enable)
            clock->enable(clock);
        timekeeper_setup_internals(clock);
    
        timekeeper.xtime.tv_sec = now.tv_sec;----------------利用RTC的当前时间,初始化xtime,raw_time,wall_to_monotonic等字段
        timekeeper.xtime.tv_nsec = now.tv_nsec;
        timekeeper.raw_time.tv_sec = 0;
        timekeeper.raw_time.tv_nsec = 0;
        if (boot.tv_sec == 0 && boot.tv_nsec == 0) {
            boot.tv_sec = timekeeper.xtime.tv_sec;
            boot.tv_nsec = timekeeper.xtime.tv_nsec;
        }
        set_normalized_timespec(&timekeeper.wall_to_monotonic,
                    -boot.tv_sec, -boot.tv_nsec);
        update_rt_offset();--------------------------------初始化代表实时时间和monotonic时间之间偏移量的offs_real字段,total_sleep_time字段初始化为0
        timekeeper.total_sleep_time.tv_sec = 0;
        timekeeper.total_sleep_time.tv_nsec = 0;
        write_sequnlock_irqrestore(&timekeeper.lock, flags);
    }

     

    xtime字段因为是保存在内存中,系统掉电后无法保存时间信息,所以每次启动时都要通过timekeeping_init从RTC中同步正确的时间信息。其中,read_persistent_clock和read_boot_clock是平台级的函数,分别用于获取RTC硬件时间和启动时的时间,不过值得注意到是,到目前为止(我的代码树基于3.4版本),ARM体系中,只有tegra和omap平台实现了read_persistent_clock函数。如果平台没有实现该函数,内核提供了一个默认的实现:

    void __attribute__((weak)) read_persistent_clock(struct timespec *ts)
    {
        ts->tv_sec = 0;
        ts->tv_nsec = 0;
    }
    
    void __attribute__((weak)) read_boot_clock(struct timespec *ts)
    {
        ts->tv_sec = 0;
        ts->tv_nsec = 0;
    }

     

     

     

    那么,其他ARM平台是如何初始化xtime的?答案就是CONFIG_RTC_HCTOSYS这个内核配置项,打开该配置后,driver/rtc/hctosys.c将会编译到系统中,由rtc_hctosys函数通过do_settimeofday在系统初始化时完成xtime变量的初始化:

    static int __init rtc_hctosys(void)
    {
        int err = -ENODEV;
        struct rtc_time tm;
        struct timespec tv = {
            .tv_nsec = NSEC_PER_SEC >> 1,
        };
        struct rtc_device *rtc = rtc_class_open(CONFIG_RTC_HCTOSYS_DEVICE);--------找到rtc设备
    
        if (rtc == NULL) {
            pr_err("%s: unable to open rtc device (%s)\n",
                __FILE__, CONFIG_RTC_HCTOSYS_DEVICE);
            goto err_open;
        }
    
        err = rtc_read_time(rtc, &tm);---------------------------------------------读取rtc时间到tm
        if (err) {
            dev_err(rtc->dev.parent,
                "hctosys: unable to read the hardware clock\n");
            goto err_read;
    
        }
    
        err = rtc_valid_tm(&tm);
        if (err) {
            dev_err(rtc->dev.parent,
                "hctosys: invalid date/time\n");
            goto err_invalid;
        }
    
        rtc_tm_to_time(&tm, &tv.tv_sec);---------------------------------------rtc时间转换成timespec时间
    
        do_settimeofday(&tv);--------------------------------------------------设置walltime
    
        dev_info(rtc->dev.parent,
            "setting system clock to "
            "%d-%02d-%02d %02d:%02d:%02d UTC (%u)\n",
            tm.tm_year + 1900, tm.tm_mon + 1, tm.tm_mday,
            tm.tm_hour, tm.tm_min, tm.tm_sec,
            (unsigned int) tv.tv_sec);
    
    err_invalid:
    err_read:
        rtc_class_close(rtc);
    
    err_open:
        rtc_hctosys_ret = err;
    
        return err;
    }

     

     

    4.  时间的更新

    xtime一旦初始化完成后,timekeeper就开始独立于RTC,利用自身关联的clocksource进行时间的更新操作,根据内核的配置项的不同,更新时间的操作发生的频度也不尽相同,如果没有配置NO_HZ选项,通常每个tick的定时中断周期,do_timer会被调用一次,相反,如果配置了NO_HZ选项,可能会在好几个tick后,do_timer才会被调用一次,当然传入的参数是本次更新离上一次更新时相隔了多少个tick周期,系统会保证在clocksource的max_idle_ns时间内调用do_timer,以防止clocksource的溢出:

    void do_timer(unsigned long ticks)
    {
        jiffies_64 += ticks;
        update_wall_time();
        calc_global_load(ticks);
    }

     

    在do_timer中,jiffies_64变量被相应地累加,然后在update_wall_time中完成xtime等时间的更新操作,更新时间的核心操作就是读取关联clocksource的计数值,累加到xtime等字段中,其中还设计ntp时间的调整等代码,详细的代码就不贴了。

    5.  获取时间

    timekeeper提供了一系列的接口用于获取各种时间信息。

    • void getboottime(struct timespec *ts);    获取系统启动时刻的实时时间
    • void get_monotonic_boottime(struct timespec *ts);     获取系统启动以来所经过的时间,包含休眠时间
    • ktime_t ktime_get_boottime(void);   获取系统启动以来所经过的c时间,包含休眠时间,返回ktime类型
    • ktime_t ktime_get(void);    获取系统启动以来所经过的c时间,不包含休眠时间,返回ktime类型
    • void ktime_get_ts(struct timespec *ts) ;   获取系统启动以来所经过的c时间,不包含休眠时间,返回timespec结构
    • unsigned long get_seconds(void);    返回xtime中的秒计数值
    • struct timespec current_kernel_time(void);    返回内核最后一次更新的xtime时间,不累计最后一次更新至今clocksource的计数值
    • void getnstimeofday(struct timespec *ts);    获取当前时间,返回timespec结构
    • void do_gettimeofday(struct timeval *tv);    获取当前时间,返回timeval结构

     

    关于timekeeping的补充

    1. 更新walltime

    更新walltime有几个通道,用户空间通过stime/settimeofday;内核do_timer()更新jiffies的时候通过update_wall_time()。

    因为timekeeper.wall_to_monotonic依赖于timekeeper.xtime,所以每次更新xtime的时候都要考虑wall_to_monotonic。

    int do_settimeofday(const struct timespec *tv)
    {
    ...
        timekeeping_forward_now();---------------------------------------更新时间
    
        ts_delta.tv_sec = tv->tv_sec - timekeeper.xtime.tv_sec;
        ts_delta.tv_nsec = tv->tv_nsec - timekeeper.xtime.tv_nsec;-------计算调整的xtime差值
        timekeeper.wall_to_monotonic =
                timespec_sub(timekeeper.wall_to_monotonic, ts_delta);----将settimeofday的修改差值反映到wall_to_monotonic以达到保证monotonic递增的目的。
    ...
    }

     

    NTP调整walltime,do_adjtimex-->timekeeping_inject_offset。

    int timekeeping_inject_offset(struct timespec *ts)
    {
    ...
        timekeeping_forward_now();
    
        tmp = timespec_add(timekeeper.xtime,  *ts);
        if (!timespec_valid_strict(&tmp)) {
            ret = -EINVAL;
            goto error;
        }
    
        timekeeper.xtime = timespec_add(timekeeper.xtime, *ts);
        timekeeper.wall_to_monotonic =
                    timespec_sub(timekeeper.wall_to_monotonic, *ts);
    ...
    }

     

     

    2. 更新total_sleep_time

    维护total_sleep_time的地方有两处:一是通过RTC,在rtc_resume的时候通过timekeeping_inject_sleeptime();另一是通过timekeeping功能维护。

    2.1 RTC维护sleeptime

    RTC用于维护系统suspend时间通过rtc_suspend/rtc_resume。

    static int __init rtc_init(void)
    {
    ...
        rtc_class->suspend = rtc_suspend;
        rtc_class->resume = rtc_resume;
    ...
    }

     

    在rtc_suspend中保存old_rtc和old_system,然后在rec_resume中计算sleep_time。

    static int rtc_resume(struct device *dev)
    {
    ...
        /* snapshot the current rtc and system time at resume */
        getnstimeofday(&new_system);
        rtc_read_time(rtc, &tm);
        if (rtc_valid_tm(&tm) != 0) {
            pr_debug("%s:  bogus resume time\n", dev_name(&rtc->dev));
            return 0;
        }
        rtc_tm_to_time(&tm, &new_rtc.tv_sec);
        new_rtc.tv_nsec = 0;
    
        if (new_rtc.tv_sec < old_rtc.tv_sec) {
            pr_debug("%s:  time travel!\n", dev_name(&rtc->dev));
            return 0;
        }
    
        /* calculate the RTC time delta (sleep time)*/
        sleep_time = timespec_sub(new_rtc, old_rtc);
    
        /*
         * Since these RTC suspend/resume handlers are not called
         * at the very end of suspend or the start of resume,
         * some run-time may pass on either sides of the sleep time
         * so subtract kernel run-time between rtc_suspend to rtc_resume
         * to keep things accurate.
         */
        sleep_time = timespec_sub(sleep_time,
                timespec_sub(new_system, old_system));
    
        if (sleep_time.tv_sec >= 0)
            timekeeping_inject_sleeptime(&sleep_time);
        return 0;
    }

     

     

    2.2 timekeeping维护sleeptime

    timekeeping的suspend/resume维护了sleeptime:

    /**
     * timekeeping_resume - Resumes the generic timekeeping subsystem.
     *
     * This is for the generic clocksource timekeeping.
     * xtime/wall_to_monotonic/jiffies/etc are
     * still managed by arch specific suspend/resume code.
     */
    static void timekeeping_resume(void)
    {
        unsigned long flags;
        struct timespec ts;
    
        read_persistent_clock(&ts);---------------------------------------------------在resume再次读取persistent时间
    
        clocksource_resume();---------------------------------------------------------resume clocksource_list上的设备
    
        write_seqlock_irqsave(&timekeeper.lock, flags);
    
        if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) {
            ts = timespec_sub(ts, timekeeping_suspend_time);
            __timekeeping_inject_sleeptime(&ts);--------------------------------------计算suspend前后的时间差值,作为sleeptime,并更新到timekeeper.total_sleep_time。
        }
        /* re-base the last cycle value */
        timekeeper.clock->cycle_last = timekeeper.clock->read(timekeeper.clock);
        timekeeper.ntp_error = 0;
        timekeeping_suspended = 0;
        timekeeping_update(false);
        write_sequnlock_irqrestore(&timekeeper.lock, flags);
    
        touch_softlockup_watchdog();
    
        clockevents_notify(CLOCK_EVT_NOTIFY_RESUME, NULL);--------------------------resume clockevents设备
    
        /* Resume hrtimers */
        hrtimers_resume();-----------------------------------------------------------打开hrtimers。
    }
    
    static int timekeeping_suspend(void)
    {
        unsigned long flags;
        struct timespec        delta, delta_delta;
        static struct timespec    old_delta;-----------------------------------------注意此变量为static,在timekeeping_suspend被执行过程中会保持上一次调用值。
    
        read_persistent_clock(&timekeeping_suspend_time);----------------------------读取当前persistent时钟计数到timekeeping_suspend_time中。
    
        write_seqlock_irqsave(&timekeeper.lock, flags);
        timekeeping_forward_now();
        timekeeping_suspended = 1;
    
        /*
         * To avoid drift caused by repeated suspend/resumes,
         * which each can add ~1 second drift error,
         * try to compensate so the difference in system time
         * and persistent_clock time stays close to constant.
         */
        delta = timespec_sub(timekeeper.xtime, timekeeping_suspend_time);
        delta_delta = timespec_sub(delta, old_delta);
        if (abs(delta_delta.tv_sec)  >= 2) {
            /*
             * if delta_delta is too large, assume time correction
             * has occured and set old_delta to the current delta.
             */
            old_delta = delta;
        } else {
            /* Otherwise try to adjust old_system to compensate */
            timekeeping_suspend_time =
                timespec_add(timekeeping_suspend_time, delta_delta);
        }
        write_sequnlock_irqrestore(&timekeeper.lock, flags);
    
        clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);----------------------suspend clockevent设备
        clocksource_suspend();---------------------------------------------------将相关clocksource拉入suspend状态
    
        return 0;
    }
    
    /* sysfs resume/suspend bits for timekeeping */
    static struct syscore_ops timekeeping_syscore_ops = {
        .resume        = timekeeping_resume,
        .suspend    = timekeeping_suspend,
    };

     

     

     

    2.3 如何更新total_sleep_time

    timekeeping_inject_sleeptime-->__timekeeping_inject_sleeptime更新timekeeper.total_sleep_time。

    /**
     * __timekeeping_inject_sleeptime - Internal function to add sleep interval
     * @delta: pointer to a timespec delta value
     *
     * Takes a timespec offset measuring a suspend interval and properly
     * adds the sleep offset to the timekeeping variables.
     */
    static void __timekeeping_inject_sleeptime(struct timespec *delta)
    {
        if (!timespec_valid_strict(delta)) {
            printk(KERN_WARNING "__timekeeping_inject_sleeptime: Invalid "
                        "sleep delta value!\n");
            return;
        }
    
        timekeeper.xtime = timespec_add(timekeeper.xtime, *delta);-------------------------xtime需要加上睡眠时间
        timekeeper.wall_to_monotonic =
                timespec_sub(timekeeper.wall_to_monotonic, *delta);------------------------由于xtime加上了睡眠时间,但是monotonic不包括睡眠时间,所以wall_to_monotonic需要减去睡眠时间。
        update_sleep_time(timespec_add(timekeeper.total_sleep_time, *delta));--------------累积睡眠时间,更新到timekeeper.total_sleep_time,
    }

     

     

     

    3. cpu suspend对时间的影响

    static void timekeeping_resume(void)
    {
        unsigned long flags;
        struct timespec ts;
    
        read_persistent_clock(&ts);
    
        clocksource_resume();
    
        write_seqlock_irqsave(&timekeeper.lock, flags);
    
        if (timespec_compare(&ts, &timekeeping_suspend_time) > 0) {
            ts = timespec_sub(ts, timekeeping_suspend_time);
            __timekeeping_inject_sleeptime(&ts);
        }
        /* re-base the last cycle value */
        timekeeper.clock->cycle_last = timekeeper.clock->read(timekeeper.clock);
        timekeeper.ntp_error = 0;
        timekeeping_suspended = 0;
        timekeeping_update(false);
        write_sequnlock_irqrestore(&timekeeper.lock, flags);
    
        touch_softlockup_watchdog();
    
        clockevents_notify(CLOCK_EVT_NOTIFY_RESUME, NULL);
        printk("arnoldlu %s timekeeping_suspend_time=%ld.%09ld\n", __func__, timekeeping_suspend_time.tv_sec, timekeeping_suspend_time.tv_nsec);
    
        /* Resume hrtimers */
        hrtimers_resume();
    }
    
    static int timekeeping_suspend(void)
    {
        unsigned long flags;
        struct timespec        delta, delta_delta;
        static struct timespec    old_delta;
    
        read_persistent_clock(&timekeeping_suspend_time);
    
        write_seqlock_irqsave(&timekeeper.lock, flags);
        timekeeping_forward_now();
        timekeeping_suspended = 1;
    
        /*
         * To avoid drift caused by repeated suspend/resumes,
         * which each can add ~1 second drift error,
         * try to compensate so the difference in system time
         * and persistent_clock time stays close to constant.
         */
        delta = timespec_sub(timekeeper.xtime, timekeeping_suspend_time);
        delta_delta = timespec_sub(delta, old_delta);
        if (abs(delta_delta.tv_sec)  >= 2) {
            /*
             * if delta_delta is too large, assume time correction
             * has occured and set old_delta to the current delta.
             */
            old_delta = delta;
        } else {
            /* Otherwise try to adjust old_system to compensate */
            timekeeping_suspend_time =
                timespec_add(timekeeping_suspend_time, delta_delta);
        }
        write_sequnlock_irqrestore(&timekeeper.lock, flags);
        printk("arnoldlu %s timekeeping_suspend_time=%ld.%09ld\n", __func__, timekeeping_suspend_time.tv_sec, timekeeping_suspend_time.tv_nsec);
        clockevents_notify(CLOCK_EVT_NOTIFY_SUSPEND, NULL);
        clocksource_suspend();
    
        return 0;
    }

     

     

    persistent时间读取-->关闭tick-->打开tick-->persistent时间读取

     

    转载于:https://www.cnblogs.com/arnoldlu/p/7078250.html

    展开全文
  • 计时员 保留多个时间实例。 它包括倒数计时器,倒数计时器和当前时间。 您还可以在计时器上设置闹钟
  • 计时员 用于ranviermud的TimeManager的草稿 目标 完全可定制的时间和日期跟踪系统 昼/夜循环 根据时间/日期/季节更改房间的描述 可自定义的基于时间/日期的事件 ???
  • 语言:English 将计时器放入任何Web工具中,允许使用存储在Toggl帐户上的所有数据进行快速实时生产力跟踪 toggl按钮,用于PortaOne的计时箱:将计时器放入任何Web工具中 允许使用存储在Toggl帐户上的所有数据进行...
  • 计时员 游戏时钟服务器。
  • 计时员 适用于 Linux 的自动时间跟踪器 该项目的目标是跟踪活动窗口,以便应用一些自动规则来确定桌面应用程序上的个人使用情况分析。 这将允许一个人查看他/她使用每个程序花费了多少时间。 还有一些其他的项目是...
  • 计时员 Sailfish OS 的世界时钟应用程序。 查看不同国家和时区的时间。 从预设时区配置时钟,或设置自定义时间偏移以跟踪世界各地的时间。

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 937
精华内容 374
关键字:

timekeeper