首页 > 系统相关 >Linux驱动开发之ioctl控制定时器并实现任意整数级秒计时器

Linux驱动开发之ioctl控制定时器并实现任意整数级秒计时器

时间:2024-09-19 20:24:32浏览次数:13  
标签:定时器 struct int dev1 timer ioctl cdev Linux test

在IO模型中,I和O分别代表系统的输入和输出,在计算机中可以直观地理解为输入设备和输出设备,例如鼠标、键盘、显示器等。由于Linux中运行于用户空间的应用程序不能直接对硬件进行操作,需要应用程序向操作系统内核发起调用,将进程切换到内核空间,才能进行 IO 操作。IO 模型根据功能不同可划分为阻塞 IO、非阻塞 IO、信号驱动 IO,IO 多路复用和异步 IO。ioctl则是设备驱动程序中用来控制设备的接口函数,一个字符设备驱动通常需要实现设备的打开、关闭、读取、写入等功能,而在一些需要功能细分的场合下,就需要增设ioctl()命令来实现。

此次实验在之前定时器实验:《Linux驱动开发之使用内核定时器实现可重置的秒计时器》的基础上通过增加信号驱动IO模型和ioctl操作函数实现对定时器的控制和配置。

一、驱动程序编写

在上一次内核定时器实验的基础上,采用全新的原子变量操作函数,并通过添加信号驱动IO以及Poll轮询实现任意整数秒的秒计时器,最后添加ioctl操作函数实现对内核定时器的打开、关闭、设置等。

<头文件包含>

#define TIMER_OPEN _IO('L',0)

#define TIMER_CLOSE _IO('L',1)

#define TIMER_SET _IOW('L',2,int)

struct device_test{

    dev_t dev_num; 

    int major ;

    int minor ; 

    struct cdev cdev_test; 

    struct class *class; 

    struct device *device; 

    int counter;

    int sec;

    int flag;

    struct fasync_struct *fasync;

};

DECLARE_WAIT_QUEUE_HEAD(read_wq); //定义并初始化等待队列头

atomic64_t v = ATOMIC_INIT(0);

static struct device_test dev1;

static void fnction_test(struct timer_list *t);

DEFINE_TIMER(timer_test,fnction_test);

void fnction_test(struct timer_list *t)

{

    int add_num;

    add_num = dev1.counter/1000;

    atomic64_add_negative(add_num,&v); //原子变量增加相应计时值

    dev1.sec = atomic64_read(&v);

    dev1.flag=1;

    wake_up_interruptible(&read_wq);

    kill_fasync(&dev1.fasync,SIGIO,POLLIN);

    mod_timer(&timer_test,jiffies_64 + msecs_to_jiffies(dev1.counter));

}

static int cdev_test_open(struct inode *inode, struct file *file)

{

    file->private_data=&dev1;//设置私有数据

    return 0;

}

static ssize_t cdev_test_read(struct file *file, char __user *buf, size_t size, loff_t *off)

{

    if(copy_to_user(buf,&dev1.sec,sizeof(dev1.sec))){

        printk("copy_to_user error \n");

        return -1;

    }

    return 0;

}

static int cdev_test_release(struct inode *inode, struct file *file)

{

    file->private_data=&dev1;

    return 0;

}

static long cdev_test_ioctl(struct file *file, unsigned int cmd, unsigned long arg)

{

    struct device_test *test_dev = (struct device_test *)file->private_data;

    switch(cmd){

        case TIMER_OPEN:

            dev1.sec = 0;

            atomic64_set(&v,0);

            add_timer(&timer_test);

            printk("timer add successed\n");

            break;

        case TIMER_CLOSE:

            dev1.sec = 0;

            atomic64_set(&v,0);

            del_timer(&timer_test);

            printk("timer delete successed\n");

            break;

        case TIMER_SET:

            test_dev->counter = arg;

            timer_test.expires = jiffies_64 + msecs_to_jiffies(test_dev->counter);//设置定时时间

            printk("timer set successed\n");

            break;

        default:

            break;

    }

    return 0;

}

static __poll_t cdev_test_poll(struct file *file, struct poll_table_struct *p){

    struct device_test *test_dev=(struct device_test *)file->private_data; 

    __poll_t mask=0;

    poll_wait(file,&read_wq,p);

    if (test_dev->flag == 1)

    {

        mask |= POLLIN;

    }

    return mask;

}

static int cdev_test_fasync (int fd, struct file *file, int on)

{

    struct device_test *test_dev=(struct device_test *)file->private_data; 

    return fasync_helper(fd,file,on,&test_dev->fasync);

}

struct file_operations cdev_test_fops = {

    .owner = THIS_MODULE, /

    .open = cdev_test_open,

    .read = cdev_test_read,

    .release = cdev_test_release,

    .unlocked_ioctl = cdev_test_ioctl,

    .poll = cdev_test_poll, 

    .fasync = cdev_test_fasync, 

    };

static int __init timer_dev_init(void)

{

    int ret;

    ret = alloc_chrdev_region(&dev1.dev_num, 0, 1, "alloc_name"); 

    if (ret < 0)

    {

        goto err_chrdev;

    }

    printk("alloc_chrdev_region is ok\n");

    dev1.major = MAJOR(dev1.dev_num); 

    dev1.minor = MINOR(dev1.dev_num); 

    printk("major is %d \r\n", dev1.major); 

    printk("minor is %d \r\n", dev1.minor); 

    dev1.cdev_test.owner = THIS_MODULE;

    cdev_init(&dev1.cdev_test, &cdev_test_fops);

    ret = cdev_add(&dev1.cdev_test, dev1.dev_num, 1);

    if(ret<0)

    {

        goto err_chr_add;

    }

    dev1.class = class_create(THIS_MODULE, "test");

    if(IS_ERR(dev1.class))

    {

        ret=PTR_ERR(dev1.class);

        goto err_class_create;

    }

    dev1.device = device_create(dev1.class, NULL, dev1.dev_num, NULL, "test");

    if(IS_ERR(dev1.device))

    {

        ret=PTR_ERR(dev1.device);

        goto err_device_create;

    }

    return 0;

    err_device_create:

        class_destroy(dev1.class); //删除类

    err_class_create:

        cdev_del(&dev1.cdev_test); 

    err_chr_add:

        unregister_chrdev_region(dev1.dev_num, 1); //注销设备号

    err_chrdev:

        return ret;

}

static void __exit timer_dev_exit(void) 

{

    unregister_chrdev_region(dev1.dev_num, 1); 

    cdev_del(&dev1.cdev_test); 

    device_destroy(dev1.class, dev1.dev_num); 

    class_destroy(dev1.class); 

    printk("exit byebye\n");

}

module_init(timer_dev_init);

module_exit(timer_dev_exit);

MODULE_LICENSE("GPL v2");

MODULE_AUTHOR("tester");

二、测试程序编写

由于此次实验依旧没有涉及到硬件操作,为了使实验现象更明显,编写读取并打印定时器计时值的函数,通过信号驱动IO模型实现任意整数秒计时器秒数的打印。

<头文件包含>

int fd;

int count;

//信号处理函数

static void func(int signum)

{

    read(fd,&count,sizeof(count));

    printf("num is %d\n",count);

}

int main(int argc,char *argv[])

{

    int flags;

    fd = open("/dev/test",O_RDWR);

    if (fd < 0)

    {

        perror("open error \n");

        return fd;

    }

    signal(SIGIO,func); //注册信号处理函数

    fcntl(fd,F_SETOWN,getpid());

    flags = fcntl(fd,F_GETFD); //获取文件描述符标志

    //开启信号驱动 IO ,打开 FASYNC 标志

    fcntl(fd,F_SETFL,flags| FASYNC);

    while(1);

    close(fd);

    return 0;

}

编写测试程序,通过ioctl操作函数实现对定时器的打开,关闭和设置。其中,设置定时器定时时间的输入参数单位为毫秒,定时间隔可以为任意整数。

<头文件包含>

#define TIME_OPEN _IO('L',0)

#define TIME_CLOSE _IO('L',1)

#define TIME_SET _IOW('L',2,int)

int main(int argc,char *argv[]){

    int fd;

    int ret;

    fd = open("/dev/test",O_RDWR,0777);

    if(fd < 0){

        printf("file open error \n");

    }

    if(strcmp(argv[1],"timer_open") == 0)

    {

        ret = ioctl(fd,TIME_OPEN);

        if(ret < 0){

            printf("ioctl open error \n");

            return -1;

        }

        return ret;

    }

    else if(strcmp(argv[1],"timer_set") == 0)

    {

        ret = ioctl(fd,TIME_SET,atoi(argv[2]));

        if(ret < 0){

            printf("ioctl set error \n");

            return -1;

        }

        return ret;

    }

    else if(strcmp(argv[1],"timer_close") == 0)

    {

        ret = ioctl(fd,TIME_CLOSE);

        if(ret < 0){

            printf("ioctl close error \n");

            return -1;

        }

        return ret;

    }

    close(fd);

}

三、运行测试

Makefile沿用之前编写好Makefile的即可,注意更改驱动文件名称。编译驱动程序及应用程序,得到驱动模块文件及可执行应用程序文件。

加载驱动并运行read_timer程序。

此时定时器还未启动,故没有计时器值的打印。设置定时器定时时间为1秒,打开定时器,观察read_timer打印现象。

可以看出,定时器成功实现了1秒时间间隔的计时。关闭定时器,重新设置定时器定时时间为3秒,打开定时器,观察read_timer打印现象。

现象表明,在定时器关闭之后,read_timer停止了打印。在重新开启定时器后,read_timer也重新开始打印,并且打印间隔时间为设定的3秒。关闭定时器并卸载驱动,使用dmesg查看内核打印信息。

内核打印信息与手动操作一一对应,证明该驱动运行完全正常。

总结:通过添加信号驱动IO模型实现了任意整数秒的秒计时器功能,并使用ioctl操作函数实现了对定时器的打开、关闭和设置。

标签:定时器,struct,int,dev1,timer,ioctl,cdev,Linux,test
From: https://blog.csdn.net/woaidandanhou/article/details/142368281

相关文章

  • 《现代操作系统》第10章——实例研究1:UNIX、Linux和Android
    《现代操作系统》第10章——实例研究1:UNIX、Linux和Android10.1UNIX与Linux的历史第一次使UNIX的两种流派一致的严肃尝试来源于IEEE(它是一个得到高度尊重的中立组织)标准委员会的赞助。有上百名来自业界、学界以及政府的人员参加了此项工作。他们共同决定将这个项目......
  • linux使用yum命令报错Cannot find a valid baseurl for repo: base/7/x86_64
    【问题】在VMware上安装搭建centOS7虚拟机,配置好网络后,尝试通过yum命令进行安装docker容器。执行命令报错:已加载插件:fastestmirror,langpacksLoadingmirrorspeedsfromcachedhostfileCouldnotretrievemirrorlisthttp://mirrorlist.centos.org/?release=7&arch=x86_......
  • 从方法、操作流程等方面对Windows和Linux的命令进行对比
    Windows和Linux是两个常见的操作系统,它们都有自己的命令行接口。尽管两者的目的都是相同的——执行特定的任务,但它们的命令之间存在一些差异。下面将从方法、操作流程等方面对Windows和Linux的命令进行对比。一、文件和目录操作:列出目录中的文件:–Windows命令:dir–Linux命......
  • Linux系统下安装MegaCli64工具查看和管理raid卡
    命令使用:#/opt/MegaRAID/MegaCli/MegaCli64-LDInfo-Lall-aALL查raid级别#/opt/MegaRAID/MegaCli/MegaCli64-AdpAllInfo-aALL查raid卡信息#/opt/MegaRAID/MegaCli/MegaCli64-PDList-aALL查看硬盘信息#/opt/MegaRAID/MegaCli/MegaCli64-AdpBbuCmd-aAll查看电池信息......
  • Linux命令查看服务器的硬件信息
    Linux命令查看服务器的硬件信息###1、查看服务器产品名称[root@localhost~]#dmidecode|grep"SystemInformation"-A9|egrep"Manufacturer|Product|Serial"Manufacturer:Huawei#厂商ProductName:TaiShan200(Model2280)#......
  • linux监控命令
    分享一部分我写的监控命令,如下:1.cpu监控top-b-n1|grepCpu|awk'{print$2}'|cut-f1-d'%'这个命令返回的是cpu的用户那部分的使用率,就是us那一项,一般情况下我们监控这个指标就可以这个使用率一般超90%需要警告2.磁盘空间监控df-h|awk'{print$NF,$5}'|sed......
  • 如何快速创建一台Linux云服务器?
    轻量应用服务器(TencentCloudLighthouse)是新一代开箱即用、面向轻量应用场景的云服务器产品,助力中小企业和开发者便捷高效的在云端构建网站、小程序/小游戏、电商、云盘/图床以及各类开发测试和学习环境,相比普通云服务器更加简单易用,提供高带宽流量包并以套餐形式整体售卖基础云资......
  • Linux查看日志各种方式
    我发现有很多小伙伴都不会正确地查看日志,总喜欢拿tail和cat去查询,这里我分享一些查看日志的命令。先说结论,less是最适合的一、less方式命令格式1.命令格式:less[参数]文件2.命令功能:less与more类似,但使用less可以随意浏览文件,而more仅能向前移动,却不能向后移动,而且l......
  • Professional Linux Kernel Architecture(一)
    基于linux内核2.6.24版本,书籍:ProfessionalLinuxKernelArchitecture英文版(可在https://github.com/welldef/os_books.git下载)1一些概念1.1微内核和单体内核微内核:只有最基本的功能直接在中央内核(微内核)中实现。所有其他功能都委托给各自独立的进程,这些进程通过通信接口与......
  • Linux 基础入门操作 第九章 进程之间通讯信号量
    systemV的进程间通信之信号量9.3信号量信号量与其他进程间通信方式不大相同,它主要提供对进程间共享资源访问控制机制。相当于内存中的标志,进程可以根据它判定是否能够访问某些共享资源,同时,进程也可以修改该标志。除了用于访问控制外,还可用于进程同步。信号量有以下两......