精华内容
下载资源
问答
  • Linux时间子系统.pdf

    2017-12-11 22:43:19
    Linux时间子系统Linux时间子系统Linux时间子系统Linux时间子系统
  • Linux时间子系统(一) -- 原理

    千次阅读 2017-08-19 16:34:18
    转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter1 内核概述内核2.6之前的时间子系统主要是围绕低分辨定时器和基于它实现的tick时钟周期展开的,随着嵌入式应用的发展,时间子系统引入了两个主要...

    转载请标明出处floater的csdn blog,http://blog.csdn.net/flaoter

    1 内核概述

    内核2.6之前的时间子系统主要是围绕低分辨定时器和基于它实现的tick时钟周期展开的,随着嵌入式应用的发展,时间子系统引入了两个主要功能: 高分辨率定时器, tickless系统。 1. tickless系统。为了满足嵌入式设备的低功耗需求,会停用周期性的时钟,使系统长时间地进入省电模式。 2.高分辨率定时器。为了满足多媒体的应用程序需要的非常精确的计时功能。

    原来的定时器是由固定的栅栏所框定的,而高分辨率事件在本质上可以发生在任意时刻,如下图。高分辨率时钟信号只在某些事件可能发生时才会发生。
    这里写图片描述

    下图是来自wowotech的一幅关于当前时间子系统的框架图。
    ■ 最底层是硬件和驱动层,每个cpu core都有自己的cpu local timer,此外SOC内部肯定会有一个用于全局的global counter。

    ■ 中间层是linux内核层,内核抽象出了时钟源(clocksource), 时钟事件设备(clock_event_device), tick设备(tick_device)用于时间管理。分为左右两部分,
    • 右边实现计时功能。linux内核有各种time line, 包括real time clock, monotonic clock, monotonic raw clock等。clocksource提供了一个单调增加的计时器产生tick,为timeline提供时钟源。timekeeper是内核提供时间服务的基础模块,负责选择并维护最优的clocksource。
    • 左边实现定时功能。clock event管理可产生event或是触发中断的定时器, 一般而言,每个CPU形成自己的一个小系统,也就要管理自己的clock event。tick device是基于clock event设备进行工作的,cpu管理自己的调度、进程统计等是基于tick设备的。低精度timer和高精度timer都是基于tick device生成的定时器设备,关于它们的事件和周期信号的关系在上面的图中有一个大体的介绍。

    ■ 最上层是linux应用层。基于timekeeping设备的是时间管理的库time lib,基于定时器设备的是定时管理的库timer lib。

    这里写图片描述

    2 时钟硬件介绍

    ARM在自己的design中加入了时钟模块的设计,方便SOC开发者进行设计和管理。下图是来自与Arm Architecutre Reference Manual Armv8的arm generic timer的框图。

    这里写图片描述

    ARM Generic Timer的主要特点,
    • System Counter, 就是计数器,计量real-time单调递增,全局共享。
    • Timer,就是定时器,每个Processor Element都有一个,当定时是时间结束时会触发system events。可进行增加计数和减少计数,可工作在real-time和virtual-time
    • Virtual Counter,需要virtualization extension的系统支持,virtualization并不是本节关注的内容,所以关于这方面的设计在此不进行展开。

    每一个clock cycle,System counter会加一。每个timer可以通过Counter interface获得System counter的值,并且所有timer看到的counter值都是一致的。
    此外,System counter需要工作在一个Always-powered的domain,不能进入睡眠。timer会和processor一起会进入休眠模式。

    1. System Counter

    • Width, 至少56 bits, 64-bit 读操作返回的值0扩展到64bit.
    • Frequency, 1-50MHz,低功耗时支持以更低频率计数并每次增长大于1的数,例如10MHz的counter可以在10M频率下每次加一,也可以在低功耗的50K频率下每次加200
    • Roll-over , 40 years.
    • Accuracy, 推荐24小时10s误差
    • Start-up, 0
      此外,system counter还支持event stream功能,ARM处理器会进入WFE状态等待event进行唤醒,这个唤醒操作可能来自与其他处理器或者SOC上的其它硬件block,比如system counter,它能产生event stream生成周期性的wakeup event。

    2. Timers

    ARMv8 每个processor都有如下四个timer,
    • A Non-secure EL1 physical timer.
    • A Secure EL1 physical timer.
    • A Non-secure EL2 physical timer.
    • A virtual timer.
      每个timer都有输出信号输出到GIC,可产生中断。并且由于timer都是cpu私有的,所以应该使用PPI类型的中断。

    此外关于时钟硬件的寄存器访问支持两种方式,
    • CP15协处理访问
    • memory map到IO地址空间,需要SOC设计时进行定义
    armv8中读取syscnt的函数arch_counter_get_cntvct,最终的寄存器读取是通过mrs实现的。

    arch_counter_get_cntvct
     arch_timer_reg_read_stable(cntvct_el0);
      read_sysreg(reg)
       asm volatile("mrs %0, " __stringify(r) : "=r" (__val));
    

    具体使用依赖于具体的SOC设计。

    展开全文
  • Linux时间子系统之时间的表示

    千次阅读 2020-05-11 14:56:46
    为了实现以上功能,Linux 实现了多种与时间相关但用于不同目的的数据结构。 1)jiffies和jiffies_64 内核用jiffies_64全局变量记录系统自启动以来经过了多少次Tick。它的声明如下(代码位于kernel/time/timer.c中...

    在Linux内核中,为了兼容原有的代码,或者符合某种规范,并且还要满足当前精度日益提高的要求,实现了多种与时间相关但用于不同目的的数据结构:

    1)jiffies和jiffies_64

    内核用jiffies_64全局变量记录系统自启动以来经过了多少次Tick。它的声明如下(代码位于kernel/time/timer.c中):

    __visible u64 jiffies_64 __cacheline_aligned_in_smp = INITIAL_JIFFIES;
    
    EXPORT_SYMBOL(jiffies_64);

    可以看出来jiffies_64被定义成了64位无符号整数。但是,由于历史的原因,内核源代码中还包含了另一个叫做jiffies的变量。jiffies的引用(代码位于include/linux/jiffies.h中)申明为:

    extern u64 __cacheline_aligned_in_smp jiffies_64;
    extern unsigned long volatile __cacheline_aligned_in_smp __jiffy_arch_data jiffies;

    因此,jiffies变量是一个unsigned long类型的全局变量,如果在32位处理器上只有4个字节长(32位)。但是,如果在64位处理器上也有8个字节长(64位),这时候jiffies和jiffies_64两个全局变量是完全等价的。

    但是翻遍所有代码你也找不到全局变量jiffies的定义,最终在内核的链接脚本中(对于Arm64架构来说脚本位于arch/arm64/kernel/vmlinux.lds.S中)找到了下面这行:

    jiffies = jiffies_64;

    玄机在这里,原来在链接的时候指定了符号jiffies和jiffies_64指向同一个地址。也就是说,在32位机器上,jiffies和jiffies_64的低4个字节是一样的。

    一般情况下,无论在32位或64位机器上,我们都可以直接访问jiffies全局变量,但如果要获得jiffies_64全局变量,则需要调用get_jiffies_64函数。对于64位系统来说,两者一样,而且jiffies被申明成了volatile的且是Cache对齐的,因此只需要直接返回jiffies就好了:

    static inline u64 get_jiffies_64(void)
    {
    	return (u64)jiffies;
    }

    而对于32位系统来说,由于其对64位读写不是原子的,所以还需要持有jiffies_lock读顺序锁:

    u64 get_jiffies_64(void)
    {
    	unsigned int seq;
    	u64 ret;
    
    	do {
    		seq = read_seqbegin(&jiffies_lock);
    		ret = jiffies_64;
    	} while (read_seqretry(&jiffies_lock, seq));
    	return ret;
    }

    jiffies基本上是每一次Tick到来都会加1的,而Tick的周期HZ是由内核编译选项配置的。在32位系统中,我们假设HZ被设置成了250,那么每个Tick的周期就是4毫秒,那么该计数器将在不到200天后达到最大值后溢出。如果HZ被设置的更高,那这个溢出时间会更短。当然,如果在64位系统中,则完全不用考虑这个问题。因此,在用jiffies进行时间比较的时候,需要用系统已经定义好的几个宏:

    time_after(a,b)
    time_before(a,b)
    time_after_eq(a,b)
    time_before_eq(a,b)
    time_in_range_open(a,b,c)
    time_is_before_jiffies(a)
    time_is_after_jiffies(a)
    time_is_before_eq_jiffies(a)
    time_is_after_eq_jiffies(a)

    为了保险起见,内核也提供了对应的64位版本。这些宏可以有效的解决回绕问题,不过也不是无限制的。具体是怎么做到的呢?我们挑一个time_after宏来看看就知道了:

    #define time_after(a,b)		\
    	(typecheck(unsigned long, a) && \
    	 typecheck(unsigned long, b) && \
    	 ((long)((b) - (a)) < 0))

    先是对两个变量做类型检查,必须都是unsigned long型的。最重要的是后面,先将两个无符号长整形相减,然后将他们变成有符号的长整型,再判断其是否为负数,也就是32位的最高位是否为1。

    为什么这样可以部分解决所谓回绕的问题呢?我们可以举个例子,为了简单起见,以8位无符号整数为例,其取值范围是0到255(0xFF)。假设当前时间是250,那么过5个Tick之后,就是255了,已经到达了能表达的最大值。这时,如果再过一个Tick,也就是6个Tick之后,就将会溢出变成0了。此时,如果简单的通过对两个值的比较来判断哪个时间再后面的话,显然就要出错了,因为过了6个Tick之后的时间是0,反而小于当前的时间,这个问题就是所谓的回绕。但是,如果我们先将这两个数相减,也就是0-250(0-0xFA),也会产生溢出,最终得到的数刚好是6。但这也是有限制的,两个比较的时间之间的差值不能超过最大表示范围的一半。假设现在的时间还是250,而过了128个Tick之后,时间值将变成122,再将两者相减的话就是122-250(0x86-0xFA),减出来的数字就是128了,此时转成有符号数就变成负数了,结果就错了。

    另外,jiffies是每个Tick更新一次的,而Tick的周期又是编译的时候定义好的,所以可以将jiffies的数值转换成具体过了多少时间,反之亦然。因此,内核提供了如下转换函数:

    unsigned int jiffies_to_msecs(const unsigned long j);
    unsigned int jiffies_to_usecs(const unsigned long j);
    unsigned long msecs_to_jiffies(const unsigned int m);
    unsigned long usecs_to_jiffies(const unsigned int u);

    2)timespec和timespec64

    timespec由秒和纳秒组成,其定义如下(代码位于include/uapi/linux/time.h):

    struct timespec {
    	__kernel_time_t	tv_sec;
    	long		tv_nsec;
    };
    • tv_sec:存放自1970年1月1日0时(UTC时间)以来经过的秒数。__kernel_time_t最终定义成了long型,也就是在32位系统上是32位长,而在64位系统上是64位长。
    • tv_nsec:存放自上一秒开始经过的纳秒(ns)数。

    timespec还有一个64位的扩展结构,其定义如下(代码位于include/linux/time64.h):

    typedef __s64 time64_t;
    
    ......
    
    struct timespec64 {
    	time64_t	tv_sec;
    	long		tv_nsec;
    };

    这个结构体中的变量定义和timespec一样,只不过tv_sec的类型一定是64位无符号数。所以,也就是说在64位系统上,timespec和timespec64结构体是一模一样的。

    3)ktime_t

    在Linux的时间子系统内,一般使用ktime_t来表示时间,其定义如下(代码位于include/linux/ktime.h):

    typedef s64	ktime_t;

    就是一个非常简单的64位带符号整数,表示的时间单位是纳秒。

    4)timeval

    gettimeofday和settimeofday函数使用timeval作为时间单位:

    struct timeval {
    	__kernel_time_t		tv_sec;
    	__kernel_suseconds_t	tv_usec;
    };
    • tv_sec:存放自1970年1月1日0时(UTC时间)以来经过的秒数。__kernel_time_t最终定义成了long型,也就是在32位系统上是32位长,而在64位系统上是64位长。
    • tv_usec:__kernel_suseconds_t实际最终也被定义成了long型,存放自上一秒开始经过的微秒(us)数。

    所以,这个结构体其实和timespec结构体大同小异,tv_sec存的值是一样的,而只需要将timespec中的tv_nsec除以1000就是timeval中的tv_usec。

    展开全文
  • Windows10下安装Linux子系统

    万次阅读 2019-07-09 22:45:29
    Windows10下安装Linux子系统 版本说明 版本 作者 日期 备注 0.1 ZY 2019.7.9 初稿 目录 文章目录Windows10下安装Linux子系统版本说明目录一、初衷二、资料收集三、官方安装说明1、准备2、安装所选的 ...

    Windows10下安装Linux子系统


    版本说明

    版本作者日期备注
    0.1ZY2019.7.9初稿

    目录

    一、初衷

    我目前开发需要Windows和Linux双系统,在之前都是通过安装虚拟机然后安装Ubuntu等的方式来做的,但是安装虚拟机和系统的时候比较耗时和麻烦的,正好Windows支持安装Linux子系统已经出来很长一段时间了,所以这里就安装并总结一下。

    二、资料收集

    既然是Windows提供的,那就去微软的中国官方网站查一下:https://www.microsoft.com/zh-cn/

    结果直接找到这篇文章:

    https://docs.microsoft.com/zh-cn/windows/wsl/install-win10

    三、官方安装说明

    以下内容来自上面所说的网站:

    1、准备

    在之前安装 WSL 任何 Linux 发行版,您必须确保"Windows 子系统为 Linux"已启用可选功能:

    1、以管理员身份打开 PowerShell 并运行:
    PowerShell

    Enable-WindowsOptionalFeature -Online -FeatureName Microsoft-Windows-Subsystem-Linux
    

    2、重新启动计算机时提示。

    2、安装所选的 Linux 分发版

    若要下载并安装你首选的 distro(s),您具有三个选项:

    Windows 10 Fall Creators Update 及更高版本:从 Microsoft Store 安装

    本部分是为 Windows 生成 16215 或更高版本。 请按照这些步骤检查你的生成

    打开 Microsoft Store,然后选择你喜爱的 Linux 分发

    在 Windows 应用商店中的 Linux 发行版的视图

    在这里插入图片描述

    以下链接将打开每个分布区的 Windows 应用商店页:

    • Ubuntu 16.04 LTS
    • Ubuntu 18.04 LTS
    • OpenSUSE Leap 15
    • OpenSUSE Leap 42
    • SUSE Linux Enterprise Server 12
    • SUSE Linux Enterprise Server 15
    • Kali Linux
    • Debian GNU/Linux
    • WSL 的 fedora Remix
    • WLinux
    • WLinux 企业
    • Alpine WSL

    从发行版的页上,选择"Get"
    在 Windows 应用商店中的 Linux 发行版的视图

    3、完成初始化的发行版

    安装 Linux 发行版后,您必须初始化新的发行版实例一次,然后才能使用。

    4、疑难解答:

    以下是相关的错误的建议修补程序。 请参阅WSL故障排除页其他常见的错误和其解决方案。

    (1)、安装失败,出现错误 0x80070003
    • 系统驱动器上仅运行于 Linux 的 Windows 子系统 (通常这是你C:驱动器)。 请确保发行版都存储在您的系统驱动器上:
    • 打开设置 -> 存储 -> 更多的存储设置:保存新内容的更改
      [外链图片转存失败(img-HUtbcL1p-1562683286922)(59C8CC81AC794DF6840F02F91EE67141)]
    (2)、失败,出现错误 0x8007019e WslRegisterDistribution
    • 未启用 Linux 可选组件的 Windows 子系统:
    • 打开Control Panel -> 程序和功能-> 打开或关闭 Windows 功能-> 检查适用于 Linux 的 Windows 子系统或使用在本文开头所述的 PowerShell cmdlet。

    四、经验总结

    以下是我安装及使用win10子系统的一些经验:
    1、安装win10专业版,不要安装企业版和教育版等,这些版本的很多破解版可能不行,下面可以提供一个专业版的镜像,如果你的win10破解版不行可以试试这个;2、安装Ubuntu失败的时候可以先安装Debian,然后再安装Ubuntu,这个不保证成功,但是可以试试;3、kali-Linux不适合作为win10子系统用,很多工具装不了,使用的意义就不大了,可以安装到移动硬盘上作为独立的系统使用;

    win10专业版百度网盘地址(仅供学习,有能力者请支持正版):

    链接:https://pan.baidu.com/s/1M6jIi3kcbWaMxhixW5lYyQ

    提取码:m5dl

    展开全文
  • 而随着内核的不断演进,大牛们已经对这种低分辨率定时器的精度不再满足,而且,硬件也在不断地发展,系统中的定时器硬件的精度也越来越高,这也给高分辨率定时器的出现创造了条件。内核从2.6.16开始加入了高精度...

    上一篇文章,我介绍了传统的低分辨率定时器的实现原理。而随着内核的不断演进,大牛们已经对这种低分辨率定时器的精度不再满足,而且,硬件也在不断地发展,系统中的定时器硬件的精度也越来越高,这也给高分辨率定时器的出现创造了条件。内核从2.6.16开始加入了高精度定时器架构。在实现方式上,内核的高分辨率定时器的实现代码几乎没有借用低分辨率定时器的数据结构和代码,内核文档给出的解释主要有以下几点:

    • 低分辨率定时器的代码和jiffies的关系太过紧密,并且默认按32位进行设计,并且它的代码已经经过长时间的优化,目前的使用也是没有任何错误,如果硬要基于它来实现高分辨率定时器,势必会打破原有的时间轮概念,并且会引入一大堆#if--#else判断;
    • 虽然大部分时间里,时间轮可以实现O(1)时间复杂度,但是当有进位发生时,不可预测的O(N)定时器级联迁移时间,这对于低分辨率定时器来说问题不大,可是它大大地影响了定时器的精度;
    • 低分辨率定时器几乎是为“超时”而设计的,并为此对它进行了大量的优化,对于这些以“超时”未目的而使用定时器,它们大多数期望在超时到来之前获得正确的结果,然后删除定时器,精确时间并不是它们主要的目的,例如网络通信、设备IO等等。

    为此,内核为高精度定时器重新设计了一套软件架构,它可以为我们提供纳秒级的定时精度,以满足对精确时间有迫切需求的应用程序或内核驱动,例如多媒体应用,音频设备的驱动程序等等。以下的讨论用hrtimer(high resolution timer)表示高精度定时器。
    /*****************************************************************************************************/
    声明:本博内容均由http://blog.csdn.net/droidphone原创,转载请注明出处,谢谢!
    /*****************************************************************************************************/

    1.  如何组织hrtimer?

    我们知道,低分辨率定时器使用5个链表数组来组织timer_list结构,形成了著名的时间轮概念,对于高分辨率定时器,我们期望组织它们的数据结构至少具备以下条件:

    • 稳定而且快速的查找能力;
    • 快速地插入和删除定时器的能力;
    • 排序功能;

    内核的开发者考察了多种数据结构,例如基数树、哈希表等等,最终他们选择了红黑树(rbtree)来组织hrtimer,红黑树已经以库的形式存在于内核中,并被成功地使用在内存管理子系统和文件系统中,随着系统的运行,hrtimer不停地被创建和销毁,新的hrtimer按顺序被插入到红黑树中,树的最左边的节点就是最快到期的定时器,内核用一个hrtimer结构来表示一个高精度定时器:

    struct hrtimer {
    	struct timerqueue_node		node;
    	ktime_t				_softexpires;
    	enum hrtimer_restart		(*function)(struct hrtimer *);
    	struct hrtimer_clock_base	*base;
    	unsigned long			state;
            ......
    };
    定时器的到期时间用ktime_t来表示,_softexpires字段记录了时间,定时器一旦到期,function字段指定的回调函数会被调用,该函数的返回值为一个枚举值,它决定了该hrtimer是否需要被重新激活:

    enum hrtimer_restart {
    	HRTIMER_NORESTART,	/* Timer is not restarted */
    	HRTIMER_RESTART,	/* Timer must be restarted */
    };
    state字段用于表示hrtimer当前的状态,有几下几种位组合:

    #define HRTIMER_STATE_INACTIVE	0x00  // 定时器未激活
    #define HRTIMER_STATE_ENQUEUED	0x01  // 定时器已经被排入红黑树中
    #define HRTIMER_STATE_CALLBACK	0x02  // 定时器的回调函数正在被调用
    #define HRTIMER_STATE_MIGRATE	0x04  // 定时器正在CPU之间做迁移
    hrtimer的到期时间可以基于以下几种时间基准系统:

    enum  hrtimer_base_type {
    	HRTIMER_BASE_MONOTONIC,  // 单调递增的monotonic时间,不包含休眠时间
    	HRTIMER_BASE_REALTIME,   // 平常使用的墙上真实时间
    	HRTIMER_BASE_BOOTTIME,   // 单调递增的boottime,包含休眠时间
    	HRTIMER_MAX_CLOCK_BASES, // 用于后续数组的定义
    };
    和低分辨率定时器一样,处于效率和上锁的考虑,每个cpu单独管理属于自己的hrtimer,为此,专门定义了一个结构hrtimer_cpu_base:

    struct hrtimer_cpu_base {
            ......
    	struct hrtimer_clock_base	clock_base[HRTIMER_MAX_CLOCK_BASES];
    };
    其中,clock_base数组为每种时间基准系统都定义了一个hrtimer_clock_base结构,它的定义如下:

    struct hrtimer_clock_base {
    	struct hrtimer_cpu_base	*cpu_base;  // 指向所属cpu的hrtimer_cpu_base结构
            ......
    	struct timerqueue_head	active;     // 红黑树,包含了所有使用该时间基准系统的hrtimer
    	ktime_t			resolution; // 时间基准系统的分辨率
    	ktime_t			(*get_time)(void); // 获取该基准系统的时间函数
    	ktime_t			softirq_time;// 当用jiffies
    	ktime_t			offset;      // 
    };
    active字段是一个timerqueue_head结构,它实际上是对rbtree的进一步封装:
    struct timerqueue_node {
    	struct rb_node node;  // 红黑树的节点
    	ktime_t expires;      // 该节点代表队hrtimer的到期时间,与hrtimer结构中的_softexpires稍有不同
    };
    
    struct timerqueue_head {
    	struct rb_root head;          // 红黑树的根节点
    	struct timerqueue_node *next; // 该红黑树中最早到期的节点,也就是最左下的节点
    };
    timerqueue_head结构在红黑树的基础上,增加了一个next字段,用于保存树中最先到期的定时器节点,实际上就是树的最左下方的节点,有了next字段,当到期事件到来时,系统不必遍历整个红黑树,只要取出next字段对应的节点进行处理即可。timerqueue_node用于表示一个hrtimer节点,它在标准红黑树节点rb_node的基础上增加了expires字段,该字段和hrtimer中的_softexpires字段一起,设定了hrtimer的到期时间的一个范围,hrtimer可以在hrtimer._softexpires至timerqueue_node.expires之间的任何时刻到期,我们也称timerqueue_node.expires为硬过期时间(hard),意思很明显:到了此时刻,定时器一定会到期,有了这个范围可以选择,定时器系统可以让范围接近的多个定时器在同一时刻同时到期,这种设计可以降低进程频繁地被hrtimer进行唤醒。经过以上的讨论,我们可以得出以下的图示,它表明了每个cpu上的hrtimer是如何被组织在一起的:

                                                           图 1.1  每个cpu的hrtimer组织结构

    总结一下:

    • 每个cpu有一个hrtimer_cpu_base结构;
    • hrtimer_cpu_base结构管理着3种不同的时间基准系统的hrtimer,分别是:实时时间,启动时间和单调时间;
    • 每种时间基准系统通过它的active字段(timerqueue_head结构指针),指向它们各自的红黑树;
    • 红黑树上,按到期时间进行排序,最先到期的hrtimer位于最左下的节点,并被记录在active.next字段中;
    • 3中时间基准的最先到期时间可能不同,所以,它们之中最先到期的时间被记录在hrtimer_cpu_base的expires_next字段中。

    2.  hrtimer如何运转

    hrtimer的实现需要一定的硬件基础,它的实现依赖于我们前几章介绍的timekeeper和clock_event_device,如果你对timekeeper和clock_event_device不了解请参考以下文章:Linux时间子系统之三:时间的维护者:timekeeperLinux时间子系统之四:定时器的引擎:clock_event_device。hrtimer系统需要通过timekeeper获取当前的时间,计算与到期时间的差值,并根据该差值,设定该cpu的tick_device(clock_event_device)的下一次的到期时间,时间一到,在clock_event_device的事件回调函数中处理到期的hrtimer。现在你或许有疑问:前面在介绍clock_event_device时,我们知道,每个cpu有自己的tick_device,通常用于周期性地产生进程调度和时间统计的tick事件,这里又说要用tick_device调度hrtimer系统,通常cpu只有一个tick_device,那他们如何协调工作?这个问题也一度困扰着我,如果再加上NO_HZ配置带来tickless特性,你可能会更晕。这里我们先把这个疑问放下,我将在后面的章节中来讨论这个问题,现在我们只要先知道,一旦开启了hrtimer,tick_device所关联的clock_event_device的事件回调函数会被修改为:hrtimer_interrupt,并且会被设置成工作于CLOCK_EVT_MODE_ONESHOT单触发模式。

    2.1  添加一个hrtimer

    要添加一个hrtimer,系统提供了一些api供我们使用,首先我们需要定义一个hrtimer结构的实例,然后用hrtimer_init函数对它进行初始化,它的原型如下:

    void hrtimer_init(struct hrtimer *timer, clockid_t which_clock,
    			 enum hrtimer_mode mode);
    which_clock可以是CLOCK_REALTIME、CLOCK_MONOTONIC、CLOCK_BOOTTIME中的一种,mode则可以是相对时间HRTIMER_MODE_REL,也可以是绝对时间HRTIMER_MODE_ABS。设定回调函数:
    timer.function = hr_callback;

    如果定时器无需指定一个到期范围,可以在设定回调函数后直接使用hrtimer_start激活该定时器:

    int hrtimer_start(struct hrtimer *timer, ktime_t tim,
    			 const enum hrtimer_mode mode);
    如果需要指定到期范围,则可以使用hrtimer_start_range_ns激活定时器:

    hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
    			unsigned long range_ns, const enum hrtimer_mode mode);
    要取消一个hrtimer,使用hrtimer_cancel:

    int hrtimer_cancel(struct hrtimer *timer);
    以下两个函数用于推后定时器的到期时间:

    extern u64
    hrtimer_forward(struct hrtimer *timer, ktime_t now, ktime_t interval);
    
    /* Forward a hrtimer so it expires after the hrtimer's current now */
    static inline u64 hrtimer_forward_now(struct hrtimer *timer,
    				      ktime_t interval)
    {
    	return hrtimer_forward(timer, timer->base->get_time(), interval);
    }
    以下几个函数用于获取定时器的当前状态:

    static inline int hrtimer_active(const struct hrtimer *timer)
    {
    	return timer->state != HRTIMER_STATE_INACTIVE;
    }
    
    static inline int hrtimer_is_queued(struct hrtimer *timer)
    {
    	return timer->state & HRTIMER_STATE_ENQUEUED;
    }
    
    static inline int hrtimer_callback_running(struct hrtimer *timer)
    {
    	return timer->state & HRTIMER_STATE_CALLBACK;
    }
    hrtimer_init最终会进入__hrtimer_init函数,该函数的主要目的是初始化hrtimer的base字段,同时初始化作为红黑树的节点的node字段:

    static void __hrtimer_init(struct hrtimer *timer, clockid_t clock_id,
    			   enum hrtimer_mode mode)
    {
    	struct hrtimer_cpu_base *cpu_base;
    	int base;
    
    	memset(timer, 0, sizeof(struct hrtimer));
    
    	cpu_base = &__raw_get_cpu_var(hrtimer_bases);
    
    	if (clock_id == CLOCK_REALTIME && mode != HRTIMER_MODE_ABS)
    		clock_id = CLOCK_MONOTONIC;
    
    	base = hrtimer_clockid_to_base(clock_id);
    	timer->base = &cpu_base->clock_base[base];
    	timerqueue_init(&timer->node);
            ......
    }

    hrtimer_start和hrtimer_start_range_ns最终会把实际的工作交由__hrtimer_start_range_ns来完成:

    int __hrtimer_start_range_ns(struct hrtimer *timer, ktime_t tim,
    		unsigned long delta_ns, const enum hrtimer_mode mode,
    		int wakeup)
    {
            ......        
            /* 取得hrtimer_clock_base指针 */
            base = lock_hrtimer_base(timer, &flags); 
            /* 如果已经在红黑树中,先移除它: */
            ret = remove_hrtimer(timer, base); ......
            /* 如果是相对时间,则需要加上当前时间,因为内部是使用绝对时间 */
            if (mode & HRTIMER_MODE_REL) {
                    tim = ktime_add_safe(tim, new_base->get_time());
                    ......
            } 
            /* 设置到期的时间范围 */
            hrtimer_set_expires_range_ns(timer, tim, delta_ns);
            ...... 
            /* 把hrtime按到期时间排序,加入到对应时间基准系统的红黑树中 */
            /* 如果该定时器的是最早到期的,将会返回true */
            leftmost = enqueue_hrtimer(timer, new_base);
            /* 
            * Only allow reprogramming if the new base is on this CPU. 
            * (it might still be on another CPU if the timer was pending) 
            * 
            * XXX send_remote_softirq() ?
            * 定时器比之前的到期时间要早,所以需要重新对tick_device进行编程,重新设定的的到期时间
            */
            if (leftmost && new_base->cpu_base == &__get_cpu_var(hrtimer_bases))
                    hrtimer_enqueue_reprogram(timer, new_base, wakeup);
            unlock_hrtimer_base(timer, &flags);
            return ret;
    }
    

    2.2  hrtimer的到期处理

    高精度定时器系统有3个入口可以对到期定时器进行处理,它们分别是:

    • 没有切换到高精度模式时,在每个jiffie的tick事件中断中进行查询和处理;
    • 在HRTIMER_SOFTIRQ软中断中进行查询和处理;
    • 切换到高精度模式后,在每个clock_event_device的到期事件中断中进行查询和处理;

    低精度模式  因为系统并不是一开始就会支持高精度模式,而是在系统启动后的某个阶段,等待所有的条件都满足后,才会切换到高精度模式,当系统还没有切换到高精度模式时,所有的高精度定时器运行在低精度模式下,在每个jiffie的tick事件中断中进行到期定时器的查询和处理,显然这时候的精度和低分辨率定时器是一样的(HZ级别)。低精度模式下,每个tick事件中断中,hrtimer_run_queues函数会被调用,由它完成定时器的到期处理。hrtimer_run_queues首先判断目前高精度模式是否已经启用,如果已经切换到了高精度模式,什么也不做,直接返回:

    void hrtimer_run_queues(void)
    {
    
    	if (hrtimer_hres_active())
    		return;
    
    如果hrtimer_hres_active返回false,说明目前处于低精度模式下,则继续处理,它用一个for循环遍历各个时间基准系统,查询每个hrtimer_clock_base对应红黑树的左下节点,判断它的时间是否到期,如果到期,通过__run_hrtimer函数,对到期定时器进行处理,包括:调用定时器的回调函数、从红黑树中移除该定时器、根据回调函数的返回值决定是否重新启动该定时器等等:

    	for (index = 0; index < HRTIMER_MAX_CLOCK_BASES; index++) {
    		base = &cpu_base->clock_base[index];
    		if (!timerqueue_getnext(&base->active))
    			continue;
    
    		if (gettime) {
    			hrtimer_get_softirq_time(cpu_base);
    			gettime = 0;
    		}
    
    		raw_spin_lock(&cpu_base->lock);
    
    		while ((node = timerqueue_getnext(&base->active))) {
    			struct hrtimer *timer;
    
    			timer = container_of(node, struct hrtimer, node);
    			if (base->softirq_time.tv64 <=
    					hrtimer_get_expires_tv64(timer))
    				break;
    
    			__run_hrtimer(timer, &base->softirq_time);
    		}
    		raw_spin_unlock(&cpu_base->lock);
    	}
    上面的timerqueue_getnext函数返回红黑树中的左下节点,之所以可以在while循环中使用该函数,是因为__run_hrtimer会在移除旧的左下节点时,新的左下节点会被更新到base->active->next字段中,使得循环可以继续执行,直到没有新的到期定时器为止。

    高精度模式  切换到高精度模式后,原来给cpu提供tick事件的tick_device(clock_event_device)会被高精度定时器系统接管,它的中断事件回调函数被设置为hrtimer_interrupt,红黑树中最左下的节点的定时器的到期时间被编程到该clock_event_device中,这样每次clock_event_device的中断意味着至少有一个高精度定时器到期。另外,当timekeeper系统中的时间需要修正,或者clock_event_device的到期事件时间被重新编程时,系统会发出HRTIMER_SOFTIRQ软中断,软中断的处理函数run_hrtimer_softirq最终也会调用hrtimer_interrupt函数对到期定时器进行处理,所以在这里我们只要讨论hrtimer_interrupt函数的实现即可。

    hrtimer_interrupt函数的前半部分和低精度模式下的hrtimer_run_queues函数完成相同的事情:它用一个for循环遍历各个时间基准系统,查询每个hrtimer_clock_base对应红黑树的左下节点,判断它的时间是否到期,如果到期,通过__run_hrtimer函数,对到期定时器进行处理,所以我们只讨论后半部分,在处理完所有到期定时器后,下一个到期定时器的到期时间保存在变量expires_next中,接下来的工作就是把这个到期时间编程到tick_device中:

    void hrtimer_interrupt(struct clock_event_device *dev)
    {
            ......
    	for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++) {
                    ......
    		while ((node = timerqueue_getnext(&base->active))) {
                            ......
    			if (basenow.tv64 < hrtimer_get_softexpires_tv64(timer)) {
    				ktime_t expires;
    
    				expires = ktime_sub(hrtimer_get_expires(timer),
    						    base->offset);
    				if (expires.tv64 < expires_next.tv64)
    					expires_next = expires;
    				break;
    			}
    
    			__run_hrtimer(timer, &basenow);
    		}
    	}
    
    	/*
    	 * Store the new expiry value so the migration code can verify
    	 * against it.
    	 */
    	cpu_base->expires_next = expires_next;
    	raw_spin_unlock(&cpu_base->lock);
    
    	/* Reprogramming necessary ? */
    	if (expires_next.tv64 == KTIME_MAX ||
    	    !tick_program_event(expires_next, 0)) {
    		cpu_base->hang_detected = 0;
    		return;
    	}
    如果这时的tick_program_event返回了非0值,表示过期时间已经在当前时间的前面,这通常由以下原因造成:

    • 系统正在被调试跟踪,导致时间在走,程序不走;
    • 定时器的回调函数花了太长的时间;
    • 系统运行在虚拟机中,而虚拟机被调度导致停止运行;
    为了避免这些情况的发生,接下来系统提供3次机会,重新执行前面的循环,处理到期的定时器:

    	raw_spin_lock(&cpu_base->lock);
    	now = hrtimer_update_base(cpu_base);
    	cpu_base->nr_retries++;
    	if (++retries < 3)
    		goto retry;
    如果3次循环后还无法完成到期处理,系统不再循环,转为计算本次总循环的时间,然后把tick_device的到期时间强制设置为当前时间加上本次的总循环时间,不过推后的时间被限制在100ms以内:

    	delta = ktime_sub(now, entry_time);
    	if (delta.tv64 > cpu_base->max_hang_time.tv64)
    		cpu_base->max_hang_time = delta;
    	/*
    	 * Limit it to a sensible value as we enforce a longer
    	 * delay. Give the CPU at least 100ms to catch up.
    	 */
    	if (delta.tv64 > 100 * NSEC_PER_MSEC)
    		expires_next = ktime_add_ns(now, 100 * NSEC_PER_MSEC);
    	else
    		expires_next = ktime_add(now, delta);
    	tick_program_event(expires_next, 1);
    	printk_once(KERN_WARNING "hrtimer: interrupt took %llu ns\n",
    		    ktime_to_ns(delta));
    }

    3.  切换到高精度模式

    上面提到,尽管内核配置成支持高精度定时器,但并不是一开始就工作于高精度模式,系统在启动的开始阶段,还是按照传统的模式在运行:tick_device按HZ频率定期地产生tick事件,这时的hrtimer工作在低分辨率模式,到期事件在每个tick事件中断中由hrtimer_run_queues函数处理,同时,在低分辨率定时器(时间轮)的软件中断TIMER_SOFTIRQ中,hrtimer_run_pending会被调用,系统在这个函数中判断系统的条件是否满足切换到高精度模式,如果条件满足,则会切换至高分辨率模式,另外提一下,NO_HZ模式也是在该函数中判断并切换。

    void hrtimer_run_pending(void)
    {
    	if (hrtimer_hres_active())
    		return;
            ......
    	if (tick_check_oneshot_change(!hrtimer_is_hres_enabled()))
    		hrtimer_switch_to_hres();
    }
    因为不管系统是否工作于高精度模式,每个TIMER_SOFTIRQ期间,该函数都会被调用,所以函数一开始先用hrtimer_hres_active判断目前高精度模式是否已经激活,如果已经激活,则说明之前的调用中已经切换了工作模式,不必再次切换,直接返回。hrtimer_hres_active很简单:

    DEFINE_PER_CPU(struct hrtimer_cpu_base, hrtimer_bases) = {
            ......
    }
    
    static inline int hrtimer_hres_active(void)
    {
    	return __this_cpu_read(hrtimer_bases.hres_active);
    }
    hrtimer_run_pending函数接着通过tick_check_oneshot_change判断系统是否可以切换到高精度模式,

    int tick_check_oneshot_change(int allow_nohz)
    {
    	struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
    
    	if (!test_and_clear_bit(0, &ts->check_clocks))
    		return 0;
    
    	if (ts->nohz_mode != NOHZ_MODE_INACTIVE)
    		return 0;
    
    	if (!timekeeping_valid_for_hres() || !tick_is_oneshot_available())
    		return 0;
    
    	if (!allow_nohz)
    		return 1;
    
    	tick_nohz_switch_to_nohz();
    	return 0;
    }
    函数的一开始先判断check_clock标志的第0位是否被置位,如果没有置位,说明系统中没有注册符合要求的时钟事件设备,函数直接返回,check_clock标志由clocksource和clock_event_device系统的notify系统置位,当系统中有更高精度的clocksource被注册和选择后,或者有更精确的支持CLOCK_EVT_MODE_ONESHOT模式的clock_event_device被注册时,通过它们的notify函数,check_clock标志的第0为会置位。

    如果tick_sched结构中的nohz_mode字段不是NOHZ_MODE_INACTIVE,表明系统已经切换到其它模式,直接返回。nohz_mode的取值有3种:

    • NOHZ_MODE_INACTIVE    // 未启用NO_HZ模式
    • NOHZ_MODE_LOWRES    // 启用NO_HZ模式,hrtimer工作于低精度模式下
    • NOHZ_MODE_HIGHRES   // 启用NO_HZ模式,hrtimer工作于高精度模式下
    接下来的timerkeeping_valid_for_hres判断timekeeper系统是否支持高精度模式,tick_is_oneshot_available判断tick_device是否支持CLOCK_EVT_MODE_ONESHOT模式。如果都满足要求,则继续往下判断。allow_nohz是函数的参数,为true表明可以切换到NOHZ_MODE_LOWRES 模式,函数将进入tick_nohz_switch_to_nohz,切换至NOHZ_MODE_LOWRES 模式,这里我们传入的allow_nohz是表达式:

    (!hrtimer_is_hres_enabled())

    所以当系统不允许高精度模式时,将会在tick_check_oneshot_change函数内,通过tick_nohz_switch_to_nohz切换至NOHZ_MODE_LOWRES 模式,如果系统允许高精度模式,传入的allow_nohz参数为false,tick_check_oneshot_change函数返回1,回到上面的hrtimer_run_pending函数,hrtimer_switch_to_hres函数将会被调用,已完成切换到NOHZ_MODE_HIGHRES高精度模式。好啦,真正的切换函数找到了,我们看一看它如何切换:

    首先,它通过hrtimer_cpu_base中的hres_active字段判断该cpu是否已经切换至高精度模式,如果是则直接返回:

    static int hrtimer_switch_to_hres(void)
    {
    	int i, cpu = smp_processor_id();
    	struct hrtimer_cpu_base *base = &per_cpu(hrtimer_bases, cpu);
    	unsigned long flags;
    
    	if (base->hres_active)
    		return 1;
    接着,通过tick_init_highres函数接管tick_device关联的clock_event_device:

    	local_irq_save(flags);
    
    	if (tick_init_highres()) {
    		local_irq_restore(flags);
    		printk(KERN_WARNING "Could not switch to high resolution "
    				    "mode on CPU %d\n", cpu);
    		return 0;
    	}
    tick_init_highres函数把tick_device切换到CLOCK_EVT_FEAT_ONESHOT模式,同时把clock_event_device的回调handler设置为hrtimer_interrupt,这样设置以后,tick_device的中断回调将由hrtimer_interrupt接管,hrtimer_interrupt在上面已经讨论过,它将完成高精度定时器的调度和到期处理。

    接着,设置hres_active标志,以表明高精度模式已经切换,然后把3个时间基准系统的resolution字段设为KTIME_HIGH_RES:

    	base->hres_active = 1;
    	for (i = 0; i < HRTIMER_MAX_CLOCK_BASES; i++)
    		base->clock_base[i].resolution = KTIME_HIGH_RES;
    最后,因为tick_device被高精度定时器接管,它将不会再提供原有的tick事件机制,所以需要由高精度定时器系统模拟一个tick事件设备,继续为系统提供tick事件能力,这个工作由tick_setup_sched_timer函数完成。因为刚刚完成切换,tick_device的到期时间并没有被正确地设置为下一个到期定时器的时间,这里使用retrigger_next_event函数,传入参数NULL,使得tick_device立刻产生到期中断,hrtimer_interrupt被调用一次,然后下一个到期的定时器的时间会编程到tick_device中,从而完成了到高精度模式的切换:
    	tick_setup_sched_timer();
    	/* "Retrigger" the interrupt to get things going */
    	retrigger_next_event(NULL);
    	local_irq_restore(flags);
    	return 1;
    }

    整个切换过程可以用下图表示:


                                                                                 图3.1  低精度模式切换至高精度模式

    4.  模拟tick事件

    根据上一节的讨论,当系统切换到高精度模式后,tick_device被高精度定时器系统接管,不再定期地产生tick事件,我们知道,到目前的版本为止(V3.4),内核还没有彻底废除jiffies机制,系统还是依赖定期到来的tick事件,供进程调度系统和时间更新等操作,大量存在的低精度定时器也仍然依赖于jiffies的计数,所以,尽管tick_device被接管,高精度定时器系统还是要想办法继续提供定期的tick事件。为了达到这一目的,内核使用了一个取巧的办法:既然高精度模式已经启用,可以定义一个hrtimer,把它的到期时间设定为一个jiffy的时间,当这个hrtimer到期时,在这个hrtimer的到期回调函数中,进行和原来的tick_device同样的操作,然后把该hrtimer的到期时间顺延一个jiffy周期,如此反复循环,完美地模拟了原有tick_device的功能。下面我们看看具体点代码是如何实现的。

    在kernel/time/tick-sched.c中,内核定义了一个per_cpu全局变量:tick_cpu_sched,从而为每个cpu提供了一个tick_sched结构, 该结构主要用于管理NO_HZ配置下的tickless处理,因为模拟tick事件与tickless有很强的相关性,所以高精度定时器系统也利用了该结构的以下字段,用于完成模拟tick事件的操作:

    struct tick_sched {
    	struct hrtimer			sched_timer;
    	unsigned long			check_clocks;
    	enum tick_nohz_mode		nohz_mode;
            ......
    };
    
    sched_timer就是要用于模拟tick事件的hrtimer,check_clock上面几节已经讨论过,用于notify系统通知hrtimer系统需要检查是否切换到高精度模式,nohz_mode则用于表示当前的工作模式。

    上一节提到,用于切换至高精度模式的函数是hrtimer_switch_to_hres,在它的最后,调用了函数tick_setup_sched_timer,该函数的作用就是设置一个用于模拟tick事件的hrtimer:

    void tick_setup_sched_timer(void)
    {
    	struct tick_sched *ts = &__get_cpu_var(tick_cpu_sched);
    	ktime_t now = ktime_get();
    
    	/*
    	 * Emulate tick processing via per-CPU hrtimers:
    	 */
    	hrtimer_init(&ts->sched_timer, CLOCK_MONOTONIC, HRTIMER_MODE_ABS);
    	ts->sched_timer.function = tick_sched_timer;
    
    	/* Get the next period (per cpu) */
    	hrtimer_set_expires(&ts->sched_timer, tick_init_jiffy_update());
    
    	for (;;) {
    		hrtimer_forward(&ts->sched_timer, now, tick_period);
    		hrtimer_start_expires(&ts->sched_timer,
    				      HRTIMER_MODE_ABS_PINNED);
    		/* Check, if the timer was already in the past */
    		if (hrtimer_active(&ts->sched_timer))
    			break;
    		now = ktime_get();
    	}
    
    #ifdef CONFIG_NO_HZ
    	if (tick_nohz_enabled)
    		ts->nohz_mode = NOHZ_MODE_HIGHRES;
    #endif
    }
    
    该函数首先初始化该cpu所属的tick_sched结构中sched_timer字段,把该hrtimer的回调函数设置为tick_sched_timer,然后把它的到期时间设定为下一个jiffy时刻,返回前把工作模式设置为NOHZ_MODE_HIGHRES,表明是利用高精度模式实现NO_HZ。

    接着我们关注一下hrtimer的回调函数tick_sched_timer,我们知道,系统中的jiffies计数,时间更新等是全局操作,在smp系统中,只有一个cpu负责该工作,所以在tick_sched_timer的一开始,先判断当前cpu是否负责更新jiffies和时间,如果是,则执行更新操作:

    static enum hrtimer_restart tick_sched_timer(struct hrtimer *timer)
    {
            ......
    
    #ifdef CONFIG_NO_HZ
    	if (unlikely(tick_do_timer_cpu == TICK_DO_TIMER_NONE))
    		tick_do_timer_cpu = cpu;
    #endif
    
    	/* Check, if the jiffies need an update */
    	if (tick_do_timer_cpu == cpu)
    		tick_do_update_jiffies64(now);
    
    然后,利用regs指针确保当前是在中断上下文中,然后调用update_process_timer:

    	if (regs) {
                    ......
    		update_process_times(user_mode(regs));
    		......
    	}
    最后,把hrtimer的到期时间推进一个tick周期,返回HRTIMER_RESTART表明该hrtimer需要再次启动,以便产生下一个tick事件。

    	hrtimer_forward(timer, now, tick_period);
    
    	return HRTIMER_RESTART;
    }
    关于update_process_times,如果你你感兴趣,回看一下本系列关于clock_event_device的那一章: Linux时间子系统之四:定时器的引擎:clock_event_device中的第5小节,对比一下模拟tick事件的hrtimer的回调函数tick_sched_timer和切换前tick_device的回调函数 tick_handle_periodic,它们是如此地相像,实际上,它们几乎完成了一样的工作。

    展开全文
  • 在前面章节的讨论中,我们一直基于一个假设:Linux中的时钟事件都是由一个周期时钟提供,不管系统中的clock_event_device是工作于周期触发模式,还是工作于单触发模式,也不管定时器系统是工作于低分辨率模式,还是...
  • Linux时间子系统之一:clock source(时钟源)

    万次阅读 多人点赞 2012-09-13 22:25:12
    clock source用于为linux内核提供一个时间基线,如果你用linux的date命令获取当前时间,内核会读取当前的clock source,转换并返回合适的时间单位给用户空间。在硬件层,它通常实现为一个由固定时钟频率驱动的计数器...
  • Linux input子系统 Linux input子系统(一)input设备应用编程 Linux input子系统(二)input子系统驱动 Linux input子系统(一)input设备应用编程 文章目录Linux input子系统(一)input设备应用编程一、使用hex...
  • win10上linux子系统的开启、升级及使用

    万次阅读 多人点赞 2018-06-16 12:24:35
    win10上linux子系统开启、升级及使用 在16年时看到网上有所win10上可以运行ubuntu子系统,就试着下载了试用了下,利用Linux子系统可以直接访问win10的各种文件,相比使用虚拟机很是方便,而且速度也是杠杠的。 ...
  • WIN10安装LINUX子系统教程

    千次阅读 2017-06-29 10:46:20
    Windows10提供了ubantu Linux子系统,终于可以把卡卡的虚拟机扔在一边了,下面介绍一下win10安装linux子系统的过程。 打开Windows功能,在这里把勾打上 打开设置–>更新和安全–>针对开发人员,选择开发人员...
  • 人们习惯用于表示时间的方法是:年、月、日、时、分、秒、毫秒、星期等等,但是在内核中,为了软件逻辑和代码的方便性,它使用了一些不同的时间表示方法,并为这些表示方法定义了相应的变量和数据结构,本节的内容...
  • Windows10安装Linux子系统Ubuntu

    万次阅读 多人点赞 2017-05-14 13:12:29
    相信大部分同学都跟我一样都是使用Windows...在Windows10后,我们再也不用那么麻烦了,因为可以直接在Windows上安装子系统。 首先我们要打开Windows功能 在这里把勾打上 然后打开设置》》更新和安全》》针对开发
  • Win10 系统安装 Linux 子系统(WSL)教程1. WSL 简介1.1 什么是 WSL ?1.2 WSL1 与 WSL22. 安装 WSL2.1 安装 WSL1(1)启用“适用于 Linux 的 Windows 子系统”可选功能(2)重启电脑(3)安装 Linux 发行版,如 ...
  • 如何在Windows下部署Linux子系统

    千次阅读 2018-12-24 22:10:30
    如何在Windows下部署Linux子系统?双系统&amp;虚拟机&amp;子系统的那些事大功告成,好好享受你的Linux子系统吧~~~ 双系统&amp;虚拟机&amp;子系统的那些事 在自己的笔记本中安装Windows与Linux双...
  • 本系列文章的前两节讨论了用于计时的时钟源:clocksource,以及内核内部时间的一些表示方法,但是对于真实的用户来说,我们感知的是真实世界的真实时间,也就是所谓的墙上时间,clocksource只能提供一个按给定频率...
  • Linux下led子系统 --- 分析篇

    千次阅读 2018-01-11 19:13:42
    前言: 什么叫做驱动框架? 内核中驱动部分维护者针对每个种类的驱动设计一套成熟的、标准的、典型的驱动实现,并把不同厂家的同类...led子系统驱动框架: 所有led共性:  有和用户通信的设备节点  亮和灭 不同
  • Linux内核子系统
  • 痛点: 在电脑上想要使用linux又想使用windows系统只能安装双系统,因为虚拟机的性能差且使用麻烦,双系统使用起来倒是也还行,ubuntu...Linux子系统 WSL(Windows Subsystem for Linux)详细介绍见官网:https://d...
  • Windows 的 linux 子系统出来挺长时间了,你体验过了吗? 今天就带你折腾一下吧,毕竟想甩掉超占用硬件资源的大块头VM,比如VMware 本文献给爱折腾的你,折腾吧,后浪! 简单说下这篇文章的重点: 安装 WSL 的全...
  • 我们已经在前面几章介绍了低分辨率定时器和高精度定时器的实现原理,内核为了方便其它子系统,在时间子系统中提供了一些用于延时或调度的API,例如msleep,hrtimer_nanosleep等等,这些API基于低分辨率定时器或高...
  • 使用于Windows的Linux子系统"和在windows商店下载并安装ubuntu的过程就跳过不谈了, 细节可以参考其他大佬的博客, 他们提供了很多较为完成的教程, 比如我随便找了一篇, &lt;Windows10上使用Linux子系统(WSL)...
  • Linux的IRQ中断子系统分析

    千次阅读 2016-02-01 15:45:59
    本文以Linux中断子系统架构为视角,旨在提供一个对Linux中断系统的全局认识,不涉及具体实现细节。
  • Windows安装Linux子系统--安装GUI界面

    万次阅读 多人点赞 2018-03-25 12:52:45
    前段时间发现Windows可以安装Linux子系统了,恰逢电脑换了固态,还没装Linux,不如趁机体验一番!1、准备工作1.1、打开Windows的子系统功能1.2、打开Windows开发人员模式打开设置–&gt;更新和安全–&gt;针对...
  • Windows10内置Linux子系统初体验

    千次阅读 2018-06-12 15:31:25
    不过之前听闻这个版本已经支持内置的linux子系统,于是就怀着好奇心试玩了一把。虽然期间遇到了很多问题,但总体来说这个子系统体验还不错,在这里就分享一些关键步骤和遇到的问题,剩下的大家自己折腾吧。。启用...
  • 本篇介绍密钥管理子系统,只涉及内核如何管理密钥,不涉及内核加密算法的实现。密钥本质上是一段数据,内核对它的管理有些类似对文件的管理。但是因为Linux内核不愿意让密钥像文件那样“静态”存储在磁盘或者其他...
  • Linux

    千次阅读 多人点赞 2020-02-11 21:22:11
    Linux 介绍1.1 Linux 简介1.2 Linux 历史1.3 Linux 版本2. Linux 安装2.1 安装虚拟机2.2 安装 CentOS3. 远程操作 Linux3.1 SecureCRT 介绍3.2 SecureCRT 登录 Linux4. Linux 的目录结构5. Linux 常用命令5.1 列出...
  • Linux内核中的pinctrl子系统应用实例

    万次阅读 多人点赞 2017-05-31 10:48:23
     由于近期在做一个项目用到了pinctrl子系统,但是对pinctrl子系统了解又不是很多,所以遇到了麻烦,但是找度娘发现很少有同行对pinctrl的具体用法做出说明,所以只能自己去搞了,在经过一段时间Linux内核源码的...
  • 我对linux驱动 输入子系统的理解

    千次阅读 2017-04-24 22:40:40
    前言:学习驱动有一段...operations结构体直接定义在驱动定义的C代码里的,当然还有它里面的一些函数操作,可是我看内核自带的驱动代码的时候我发现里面是用另外一种机制来实现这些的,那便是子系统!初次接触到子系
  • 初探linux子系统集之led子系统(一)

    千次阅读 2014-07-08 19:54:55
    就像学编程第一个范例helloworld一样,学嵌入式,单片机、...不过,既然linux系统自己本来就带有led子系统,那么就可以好好利用之。好处不用多说了,主要对于应用层来说,不同平台都用linux的led子系统,那么应用程序不

空空如也

空空如也

1 2 3 4 5 ... 20
收藏数 320,811
精华内容 128,324
关键字:

linux时间子系统

linux 订阅