内核层与应用层jiffies同步

本文参考资料为:《linux内核设计与实现》第三版定时器和时间管理章节

下文将展示:

1.一个简单用来实现“内核层-应用层jiffies同步”的例子.
2.介绍jiffies定义

对于应用来说,它需要一个时间标尺来记录时间流逝,比如GUI进程需要周期性的给LCD刷图。而驱动的jiffies对于应用来说就一个很好的标尺。
实现要求:底层给应用层暴露一个显示jiffies接口,其中驱动要保证应用捞到的jiffies是新鲜的。
实现方案:
1.底层以系统调用支持
2.底层以驱动设备形式支持
考虑到代码灵活性、拓展性我选择了方案二。因为增加系统调用捞jiffies有点大材小用。


jiffies定义

  • jiffies 是一个无符号长整型数,在32位表现为32位长、64位表现为64位长,32位64位下jiffies的差异由编译器的链接脚本实现。
  • jiffies的更新在内核timekeeping.cdo_timer函数中实现
  • 总的来说,因为为了兼容历史版本内核开发者们更倾向使用32位长的jiffies

内核层-应用层jiffies同步(以驱动、应用形式)

驱动代码,在linux源码drivers下增加tick目录并增加下面驱动.c源码

#include <linux/kernel.h>
#include <linux/module.h>
#include <linux/fcntl.h>
#include <linux/mm.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/init.h>
#include <linux/proc_fs.h>
#include <asm/uaccess.h>
#include <linux/moduleparam.h>
#include <linux/miscdevice.h>

#define MISC_TICK_MINOR 242

extern char __iomem *tick_memy_base;

static int tick_mmap(struct file *file, struct vm_area_struct *vma)
{
    unsigned long pfn = (virt_to_phys(tick_memy_base) >> PAGE_SHIFT);
/* 应用进程的虚拟地址绑定刚分配的虚拟页存 */
    if (remap_pfn_range(vma, vma->vm_start, pfn,
        vma->vm_end - vma->vm_start,
        vma->vm_page_prot))
    return -EAGAIN;

    printk("get tick mmap\n");
    return NULL;
}

static int tick_open(struct inode *inode, struct file *file)
{
    printk("open tick\n");
    return 0;
}

static struct file_operations tick_fops ={
    .owner      = THIS_MODULE,
    .open       = tick_open,
    .mmap       = tick_mmap,
};


static struct miscdevice tick_miscdev =
{
    .minor      = MISC_TICK_MINOR,
    .name       = "tick_drv",
    .fops       = &tick_fops,
};

static int __init ktick_init(void)
{
    int rt = misc_register(&tick_miscdev);
    if (rt) {
        printk("kktick register fail\n");
        return -1;
    }
    tick_memy_base = (char __iomem *)__get_free_pages(GFP_KERNEL, 1);   /* 分配4k虚拟页存 */
    if(NULL == tick_memy_base)
        printk("tick get free pages fail\n");

    return 0;
}

#if 1
static void __exit ktick_exit(void)
{
    free_pages((unsigned long)tick_memy_base, 1);
    misc_deregister(&tick_miscdev);

    return 0;
}
#endif
module_init(ktick_init);
module_exit(ktick_exit);

MODULE_AUTHOR("jinfa");
MODULE_DESCRIPTION("kernel tick driver");
MODULE_LICENSE("GPL");

驱动的Makefile

drivers 目录Makefile
obj-y   += tick/

tick 目录Makefile
obj-y   += tick_drv.o

timekeeping.c中增加get_tick、修改do_timer

char __iomem *tick_memy_base=NULL;

inline void get_tick(void)
{
/* *((int*)tick_memy_base)直接将jiffies向tick_memy_base前四个坑里填*/
    *((int*)tick_memy_base) = jiffies; 
}

/*
 * The 64-bit jiffies value is not atomic - you MUST NOT read it
 * without sampling the sequence number in xtime_lock.
 * jiffies is defined in the linker script...
 */
void do_timer(unsigned long ticks)
{
    jiffies_64 += ticks;
    update_wall_time();
    calc_global_load(ticks);

    /*add by jinfa*/
    if(tick_memy_base !=NULL) /*如果驱动没加载时tick_memy_base为NULL*/
        get_tick();
}

测试代码

#include <string.h>
#include <stdlib.h>
#include <errno.h>
#include <unistd.h>
#include <fcntl.h>
#include <linux/types.h>
#include <sys/mman.h>

char *tick_drv = "/dev/tick_drv";
unsigned int jiffies=0;
int main()
{
    int tick_fd = open(tick_drv, O_RDWR);
    if(tick_fd <0)
        perror("open tick error");
    char *tick_buff = mmap(NULL, 4096,  //tick buff one page=4096
                    PROT_READ|PROT_WRITE,
                    MAP_SHARED, tick_fd, 0);
    while(1)
    {
        printf("jiffies:%d\n", *((int*)tick_buff));
        usleep(500000);
    }
    return 0;
}

读者可以验证下每次打印的间隔是HZ/2 ?

猜你喜欢

转载自blog.csdn.net/huang_165/article/details/80339509
今日推荐