鏈接: C/C++Linux服務(wù)器開發(fā)/后臺架構(gòu)師【零聲教育】-學習視頻教程-騰訊課堂
1 Linux 時間管理和內(nèi)核定時器簡介
1.1 內(nèi)核時間管理簡介
-
Linux 內(nèi)核中有大量的函數(shù)需要時間管理,比如周期性的調(diào)度程序、延時程序、對于驅(qū)動編寫者來說最常用的定時器。
-
硬件定時器提供時鐘源,時鐘源的頻率可以設(shè)置, 設(shè)置好以后就周期性的產(chǎn)生定時中斷,系統(tǒng)使用定時中斷來計時。中斷周期性產(chǎn)生的頻率就是系統(tǒng)頻率,也叫做節(jié)拍率(tick rate)(叫系統(tǒng)頻率),比如 1000Hz, 100Hz 等等說的就是系統(tǒng)節(jié)拍率。系統(tǒng)節(jié)拍率是可以設(shè)置的,單位是 Hz
在編譯 Linux 內(nèi)核的時候可以通過圖形化界面設(shè)置系統(tǒng)節(jié)拍率,按照如下路徑打開配置界面:
make menuconfig
-> Kernel Features
-> Timer frequency (<choice> [=y])
選中“Timer frequency”
可以看出,可選的系統(tǒng)節(jié)拍率為 100Hz、 200Hz、 250Hz、 300Hz、 500Hz 和 1000Hz,默認情況下選擇 100Hz。設(shè)置好以后打開 Linux 內(nèi)核源碼根目錄下的.config 文件,在此文件中
CONFIG_HZ 為 100, Linux 內(nèi)核會使用 CONFIG_HZ 來設(shè)置自己的系統(tǒng)時鐘。
打開文件 include/asm-generic/param.h,有如下內(nèi)容:
6 # undef HZ
7 # define HZ CONFIG_HZ
8 # define USER_HZ 100
9 # define CLOCKS_PER_SEC (USER_HZ)
第 7 行定義了一個宏 HZ,宏 HZ 就是 CONFIG_HZ,因此 HZ=100,編寫 Linux驅(qū)動的時候會用到 HZ,因為 HZ 表示一秒的節(jié)拍數(shù),也就是頻率。
- 系統(tǒng)節(jié)拍率默認為 100Hz 的時候都會有疑問,怎么這么小? 100Hz 是可選的節(jié)拍率里面最小的。為什么不選擇大一點的呢?這里就引出了一個問題:高節(jié)拍率和低節(jié)拍率的優(yōu)缺點:
①、高節(jié)拍率會提高系統(tǒng)時間精度,如果采用 100Hz 的節(jié)拍率,時間精度就是 10ms,采用1000Hz 的話時間精度就是 1ms,精度提高了 10 倍。高精度時鐘的好處有很多,對于那些對時間要求嚴格的函數(shù)來說,能夠以更高的精度運行,時間測量也更加準確。
②、高節(jié)拍率會導致中斷的產(chǎn)生更加頻繁,頻繁的中斷會加劇系統(tǒng)的負擔, 1000Hz 和 100Hz的系統(tǒng)節(jié)拍率相比,系統(tǒng)要花費 10 倍的“精力”去處理中斷。中斷服務(wù)函數(shù)占用處理器的時間增加,但是現(xiàn)在的處理器性能都很強大,所以采用 1000Hz 的系統(tǒng)節(jié)拍率并不會增加太大的負載壓力。根據(jù)自己的實際情況,選擇合適的系統(tǒng)節(jié)拍率,本教程我們?nèi)坎捎媚J的 100Hz 系統(tǒng)節(jié)拍率。
- Linux 內(nèi)核使用全局變量 jiffies 來記錄系統(tǒng)從啟動以來的系統(tǒng)節(jié)拍數(shù),系統(tǒng)啟動的時候會將 jiffies 初始化為 0, jiffies 定義在文件 include/linux/jiffies.h 中,定義如下:
76 extern u64 __jiffy_data jiffies_64;
77 extern unsigned long volatile __jiffy_data jiffies;
jiffies_64 和 jiffies 其實是同一個東西, jiffies_64 用于 64 位系統(tǒng),而 jiffies 用于 32 位系統(tǒng)。為了兼容不同的硬件, jiffies 其實就是 jiffies_64 的低 32 位, jiffies_64 和 jiffies 的結(jié)構(gòu)如圖
訪問的是 jiffies_64 的低 32 位,使用 get_jiffies_64 這個函數(shù)可以獲取 jiffies_64 的值。在 32 位的系統(tǒng)上讀取 jiffies 的值,在 64 位的系統(tǒng)上 jiffes 和 jiffies_64表示同一個變量,因此也可以直接讀取 jiffies 的值。所以不管是 32 位的系統(tǒng)還是 64 位系統(tǒng),都可以使用 jiffies。
- HZ 表示每秒的節(jié)拍數(shù), jiffies 表示系統(tǒng)運行的 jiffies 節(jié)拍數(shù),所以 jiffies/HZ 就是系統(tǒng)運行時間,單位為秒。
不管是 32 位還是 64 位的 jiffies,都有溢出的風險,溢出以后會重新從 0 開始計數(shù),相當于繞回來了,將這個現(xiàn)象也叫做繞回。 假如 HZ 為最大值 1000 的時候, 32 位的 jiffies 只需要 49.7 天就發(fā)生了繞回,對于 64 位的 jiffies 來說大概需要5.8 億年才能繞回
處理 32 位 jiffies 的繞回顯得尤為重要,Linux 內(nèi)核提供了幾個 API 函數(shù)來處理繞回
函數(shù) | 描述 |
---|---|
time_after(unkown, known) | unkown 通常為 jiffies, known 通常是需要對比的值。 |
time_before(unkown, known) | |
time_after_eq(unkown, known) | |
time_before_eq(unkown, known) |
- 如果 unkown 超過 known 的話, time_after 函數(shù)返回真 ,否則返回假。
- 果 unkown 沒有超過 known 的話 time_before 函數(shù)返回真,否則返回假。
- time_after_eq 函數(shù)和 time_after 函數(shù)類似,只是多了判斷等于這個條件。同理, time_before_eq 函數(shù)和 time_before 函數(shù)也類似。
要判斷某段代碼執(zhí)行時間有沒有超時,此時就可以使用如下所示代碼:
1 unsigned long timeout;
2 timeout = jiffies + (2 * HZ); /* 超時的時間點 */
3
4 /*************************************
5 具體的代碼
6 ************************************/
7
8 /* 判斷有沒有超時 */
9 if(time_before(jiffies, timeout)) {
10 /* 超時未發(fā)生 */
11 } else {
12 /* 超時發(fā)生 */
13 }
timeout 就是超時時間點,比如我們要判斷代碼執(zhí)行時間是不是超過了 2 秒,那么超時時間點就是 jiffies+(2*HZ),如果 jiffies 大于 timeout 那就表示超時了,否則就是沒有超時。
通過函數(shù) time_before 來判斷 jiffies 是否小于 timeout,如果小于的話就表示沒有超時。
為了方便開發(fā), Linux 內(nèi)核提供了幾個 jiffies 和 ms、 us、 ns 之間的轉(zhuǎn)換函數(shù)
函數(shù) | 描述 |
---|---|
int jiffies_to_msecs(const unsigned long j) | 將 jiffies 類型的參數(shù) j 分別轉(zhuǎn)換為對應(yīng)的毫秒、 微秒、納秒。 |
int jiffies_to_usecs(const unsigned long j) | |
u64 jiffies_to_nsecs(const unsigned long j) | |
long msecs_to_jiffies(const unsigned int m) | 將毫秒、微秒、納秒轉(zhuǎn)換為 jiffies 類型。 |
long usecs_to_jiffies(const unsigned int u) | |
unsigned long nsecs_to_jiffies(u64 n) |
1.2 內(nèi)核定時器簡介
-
定時器是一個很常用的功能,需要周期性處理的工作都要用到定時器。
-
Linux 內(nèi)核定時器使用很簡單,只需要提供超時時間(相當于定時值)和定時處理函數(shù)即可,當超時時間到了以后設(shè)置的定時處理函數(shù)就會執(zhí)行 ,使用內(nèi)核定時器不需要做一大堆的寄存器初始化工作。
-
要注意一點,內(nèi)核定時器并不是周期性運行的,超時以后就會自動關(guān)閉,因此如果想要實現(xiàn)周期性定時,那么就需要在定時處理函數(shù)中重新開啟定時器。
Linux 內(nèi)核使用 timer_list 結(jié)構(gòu)體表示內(nèi)核定時器, timer_list 定義在文件nclude/linux/timer.h 中,定義如下
struct timer_list {
struct list_head entry;
unsigned long expires; /* 定時器超時時間,單位是節(jié)拍數(shù) */
struct tvec_base *base;
void (*function)(unsigned long); /* 定時處理函數(shù) */
unsigned long data; /* 要傳遞給 function 函數(shù)的參數(shù) */
int slack;
};
-
要使用內(nèi)核定時器首先要先定義一個 timer_list 變量,表示定時器, tiemr_list 結(jié)構(gòu)體的expires 成員變量表示超時時間,單位為節(jié)拍數(shù)。
-
比如需要定義一個周期為 2 秒的定時器,那么這個定時器的超時時間就是 jiffies+(2HZ),因此 expires=jiffies+(2HZ)。
-
function 就是定時器超時以后的定時處理函數(shù),要做的工作就放到這個函數(shù)里面,需要編寫這個定時處理函數(shù)。
需要通過一系列的 API 函數(shù)來初始化此定時器,這些函數(shù)如下:
1、init_timer 函數(shù)
init_timer 函數(shù)負責初始化 timer_list 類型變量,當定義了一個 timer_list 變量以后一定要先用 init_timer 初始化一下。 init_timer 函數(shù)原型如下:
void init_timer(struct timer_list *timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要初始化定時器。
返回值: 沒有返回值。
2、add_timer 函數(shù)
add_timer 函數(shù)用于向 Linux 內(nèi)核注冊定時器,使用 add_timer 函數(shù)向內(nèi)核注冊定時器以后,定時器就會開始運行,函數(shù)原型如下:
void add_timer(struct timer_list *timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要注冊的定時器。
返回值: 沒有返回值。
3、del_timer 函數(shù)
del_timer 函數(shù)用于刪除一個定時器,不管定時器有沒有被激活,都可以使用此函數(shù)刪除。在多處理器系統(tǒng)上,定時器可能會在其他的處理器上運行,因此在調(diào)用 del_timer 函數(shù)刪除定時器之前要先等待其他處理器的定時處理器函數(shù)退出。 del_timer 函數(shù)原型如下:
int del_timer(struct timer_list * timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要刪除的定時器。
返回值: 0,定時器還沒被激活; 1,定時器已經(jīng)激活。
4、del_timer_sync 函數(shù)
del_timer_sync 函數(shù)是 del_timer 函數(shù)的同步版,會等待其他處理器使用完定時器再刪除,del_timer_sync 不能使用在中斷上下文中。
del_timer_sync 函數(shù)原型如下所示:
int del_timer_sync(struct timer_list *timer)
函數(shù)參數(shù)和返回值含義如下:
timer:要刪除的定時器。
返回值: 0,定時器還沒被激活; 1,定時器已經(jīng)激活。
5、mod_timer 函數(shù)
mod_timer 函數(shù)用于修改定時值,如果定時器還沒有激活的話, mod_timer 函數(shù)會激活定時器!函數(shù)原型如下:
int mod_timer(struct timer_list *timer, unsigned long expires)
函數(shù)參數(shù)和返回值含義如下:
timer:要修改超時時間(定時值)的定時器。
expires:修改后的超時時間。
返回值: 0,調(diào)用 mod_timer 函數(shù)前定時器未被激活; 1,調(diào)用 mod_timer 函數(shù)前定時器已被激活
- 內(nèi)核定時器一般的使用流程
struct timer_list timer; /* 定義定時器 */
/* 定時器回調(diào)函數(shù) */
void function(unsigned long arg
{
/*
* 定時器處理代碼
*/
/* 如果需要定時器周期性運行的話就使用 mod_timer
* 函數(shù)重新設(shè)置超時值并且啟動定時器。
*/
mod_timer(&dev->timertest, jiffies + msecs_to_jiffies(2000));
}
/* 初始化函數(shù) */
void init(void)
{
init_timer(&timer); /* 初始化定時器 */
timer.function = function; /* 設(shè)置定時處理函數(shù) */
timer.expires=jffies + msecs_to_jiffies(2000);/* 超時時間 2 秒 */
timer.data = (unsigned long)&dev; /* 將設(shè)備結(jié)構(gòu)體作為參數(shù) */
add_timer(&timer); /* 啟動定時器 */
}
/* 退出函數(shù) */
void exit(void)
{
del_timer(&timer); /* 刪除定時器 */
/* 或者使用 */
del_timer_sync(&timer);
}
1.3 Linux 內(nèi)核短延時函數(shù)
需要在內(nèi)核中實現(xiàn)短延時,尤其是在 Linux 驅(qū)動中。 Linux 內(nèi)核提供了毫秒、微秒和納秒延時函數(shù)
函數(shù) | 描述 |
---|---|
void ndelay(unsigned long nsecs) | 納秒、微秒和毫秒延時函數(shù)。 |
void udelay(unsigned long usecs) | |
void mdelay(unsigned long mseces) |
2 實驗程序編寫
使用內(nèi)核定時器周期性的點亮和熄滅開發(fā)板上的 LED 燈, LED 燈的閃爍周期由內(nèi)核定時器來設(shè)置,測試應(yīng)用程序可以控制內(nèi)核定時器周期。
2.1 定時器驅(qū)動程序編寫
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <linux/slab.h>
#include <linux/uaccess.h>
#include <linux/io.h>
#include <linux/cdev.h>
#include <linux/device.h>
#include <linux/of.h>
#include <linux/of_address.h>
#include <linux/of_irq.h>
#include <linux/gpio.h>
#include <linux/of_gpio.h>
#include <linux/timer.h>
#include <linux/jiffies.h>
#define TIMER_CNT 1
#define TIMER_NAME "timer"
#define CLOSE_CMD _IO(0XEF, 1) //關(guān)閉命令
#define OPEN_CMD _IO(0XEF, 2) //打開命令
#define SETPERIOD_CMD _IOW(0XEF, 3, int) //設(shè)置周期
/* timer設(shè)備結(jié)構(gòu)體 */
struct timer_dev{
dev_t devid; /* 設(shè)備號 */
struct cdev cdev; /* cdev */
struct class* class; /* 類 */
struct device* device; /* 設(shè)備 */
int major; /* 主設(shè)備號 */
int minor; /* 次設(shè)備號 */
struct device_node* nd; /* 設(shè)備節(jié)點 */
int led_gpio; /* led所使用的GPIO編號 */
struct timer_list timer; /* 定時器 */
int timeperiod; /* 定時周期ms */
};
struct timer_dev timerdev; /* led設(shè)備 */
/*
* @description : 打開設(shè)備
* @param - inode : 傳遞給驅(qū)動的inode
* @param - filp : 設(shè)備文件,file結(jié)構(gòu)體有個叫做private_data的成員變量
* 一般在open的時候?qū)rivate_data指向設(shè)備結(jié)構(gòu)體。
* @return : 0 成功;其他 失敗
*/
static int timer_open(struct inode* inode, struct file* filp)
{
filp->private_data = &timerdev;
return 0;
}
/*
* @description : 關(guān)閉/釋放設(shè)備
* @param - filp : 要關(guān)閉的設(shè)備文件(文件描述符)
* @return : 0 成功;其他 失敗
*/
static int timer_release(struct inode* inode, struct file* filp)
{
return 0;
}
static long timer_ioctl(struct file* filp, unsigned int cmd, unsigned long arg)
{
int ret = 0;
int value = 0;
struct timer_dev* dev = filp->private_data;
switch (cmd) {
case CLOSE_CMD:
del_timer_sync(&dev->timer);
break;
case OPEN_CMD:
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
break;
case SETPERIOD_CMD:
ret = copy_from_user(&value, (int *)arg, sizeof(int));
if(ret < 0) {
return -EFAULT;
}
dev->timeperiod = value;
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
break;
}
return ret;
}
/* 設(shè)備操作函數(shù) */
static struct file_operations timerdev_fops = {
.owner = THIS_MODULE,
.open = timer_open,
.unlocked_ioctl = timer_ioctl,
.release = timer_release
};
/* 定時器處理函數(shù) */
static void timer_func(unsigned long arg)
{
struct timer_dev *dev = (struct timer_dev*)arg;
static int sta = 1;
sta = !sta;
gpio_set_value(dev->led_gpio, sta);
mod_timer(&dev->timer, jiffies + msecs_to_jiffies(dev->timeperiod));
}
/*初始化LED燈*/
int led_init(struct timer_dev* dev)
{
int ret = 0;
/* 1,獲取設(shè)備節(jié)點 */
dev->nd = of_find_node_by_path("/gpioled");
if(dev->nd == NULL) {
ret = -EINVAL;
goto fail_fd;
}
/* 2, 獲取LED所對應(yīng)的GPIO */
dev->led_gpio = of_get_named_gpio(dev->nd, "led-gpio", 0);
if(dev->led_gpio < 0) {
printk("can't find led gpio\r\n");
ret = -EINVAL;
goto fail_gpio;
}
printk("led gpio num = %d\r\n", dev->led_gpio);
/* 3,申請IO
ret = gpio_request(dev->led_gpio, "led-gpio");
if (ret) {
printk("Failed to request the led gpio\r\n");
ret = -EINVAL;
goto faile_request;
}*/
/* 4,使用IO,設(shè)置為輸出 */
ret = gpio_direction_output(dev->led_gpio, 1); /* 設(shè)置輸出,默認關(guān)燈 */
if(ret < 0) {
ret = -EINVAL;
goto fail_gpioset;
}
fail_gpioset:
gpio_free(dev->led_gpio);
//faile_request:
fail_gpio:
fail_fd:
return ret;
}
/*
* @description : 驅(qū)動出口函數(shù)
* @param : 無
* @return : 無
*/
static int __init timer_init(void)
{
int ret = 0;
/* 注冊字符設(shè)備驅(qū)動 */
/* 1、創(chuàng)建設(shè)備號 */
if (timerdev.major) { /* 定義了設(shè)備號 */
timerdev.devid = MKDEV(timerdev.major, 0);
register_chrdev_region(timerdev.devid, TIMER_CNT, TIMER_NAME);
} else { /* 沒有定義設(shè)備號 */
alloc_chrdev_region(&timerdev.devid, 0, TIMER_CNT, TIMER_NAME); /* 申請設(shè)備號 */
timerdev.major = MAJOR(timerdev.devid); /* 獲取分配號的主設(shè)備號 */
timerdev.minor = MINOR(timerdev.devid); /* 獲取分配號的次設(shè)備號 */
}
if(ret < 0){
goto fail_devid;
}
printk("timerdev major=%d,minor=%d\r\n",timerdev.major, timerdev.minor);
/* 2、初始化cdev */
timerdev.cdev.owner = THIS_MODULE;
cdev_init(&timerdev.cdev, &timerdev_fops);
/* 3、添加一個cdev */
cdev_add(&timerdev.cdev, timerdev.devid, TIMER_CNT);
if (ret)
goto fail_cdevadd;
/* 4、創(chuàng)建類 */
timerdev.class = class_create(THIS_MODULE, TIMER_NAME);
if (IS_ERR(timerdev.class)) {
return PTR_ERR(timerdev.class);
goto fail_class;
}
/* 5、創(chuàng)建設(shè)備 */
timerdev.device = device_create(timerdev.class, NULL, timerdev.devid, NULL, TIMER_NAME);
if (IS_ERR(timerdev.device)) {
return PTR_ERR(timerdev.device);
goto fail_device;
}
/* 5、初始化LED燈 */
led_init(&timerdev);
if(ret < 0) {
goto fail_ledinit;
}
/* 7,初始化定時器 */
init_timer(&timerdev.timer); /* 初始化定時器 */
timerdev.timeperiod = 500; /* 設(shè)置定時周期 */
timerdev.timer.function = timer_func;; /* 設(shè)置定時處理函數(shù) */
timerdev.timer.expires = jiffies + msecs_to_jiffies(timerdev.timeperiod); /* 超時時間 500 毫秒 */
timerdev.timer.data = (unsigned long)&timerdev; /* 將設(shè)備結(jié)構(gòu)體作為參數(shù) */
add_timer(&timerdev.timer); /* 啟動定時器 */
return 0;
fail_ledinit:
fail_device:
class_destroy(timerdev.class);
fail_class:
cdev_del(&timerdev.cdev);
fail_cdevadd:
unregister_chrdev_region(timerdev.devid, TIMER_CNT);
fail_devid:
return ret;
}
/*
* @description : 驅(qū)動出口函數(shù)
* @param : 無
* @return : 無
*/
static void __exit timer_exit(void)
{
/* 關(guān)燈 */
gpio_set_value(timerdev.led_gpio, 1);
/* 刪除定時器 */
del_timer(&timerdev.timer);
/* 注銷字符設(shè)備驅(qū)動 */
cdev_del(&timerdev.cdev);
unregister_chrdev_region(timerdev.devid, TIMER_CNT);
device_destroy(timerdev.class, timerdev.devid);
class_destroy(timerdev.class);
/* 釋放IO */
//gpio_free(timerdev.led_gpio);
}
module_init(timer_init);
module_exit(timer_exit);
MODULE_LICENSE("GPL");
MODULE_AUTHOR("kaka");
- 函數(shù) timer_ioctl,對應(yīng)應(yīng)用程序的 ioctl 函數(shù),應(yīng)用程序調(diào)用 ioctl函數(shù)向驅(qū)動發(fā)送控制信息,此函數(shù)響應(yīng)并執(zhí)行。此函數(shù)有三個參數(shù): filp, cmd 和 arg,其中 filp是對應(yīng)的設(shè)備文件, cmd 是應(yīng)用程序發(fā)送過來的命令信息, arg 是應(yīng)用程序發(fā)送過來的參數(shù).
- 共有三種命令 CLOSE_CMD, OPEN_CMD 和 SETPERIOD_CMD,這三個命令分別為關(guān)閉定時器、打開定時器、設(shè)置定時周期。這三個命令的左右如下:
- CLOSE_CMD: 關(guān)閉定時器命令, 調(diào)用 del_timer_sync 函數(shù)關(guān)閉定時器。
- OPEN_CMD:打開定時器命令,調(diào)用 mod_timer 函數(shù)打開定時器,定時周期為 timerdev 的timeperiod 成員變量,定時周期默認是 1 秒。
- SETPERIOD_CMD:設(shè)置定時器周期命令,參數(shù) arg 就是新的定時周期,設(shè)置 timerdev 的timeperiod 成員變量為 arg 所表示定時周期指。并且使用 mod_timer 重新打開定時器,使定時器以新的周期運行。
2.2 編寫測試 APP
測試 APP 要實現(xiàn)的內(nèi)容如下:
①、運行 APP 以后提示我們輸入要測試的命令,輸入 1 表示關(guān)閉定時器、輸入 2 表示打開定時器,輸入 3 設(shè)置定時器周期。
②、如果要設(shè)置定時器周期的話,需要讓用戶輸入要設(shè)置的周期值,單位為毫秒。
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ioctl.h>
/*
*argc:應(yīng)用程序參數(shù)個數(shù)
*argv[]:具體的參數(shù)內(nèi)容,字符串形式
*./timerAPP <filename>
* ./timerAPP /dev/timer
*/
#define CLOSE_CMD _IO(0XEF, 1) //關(guān)閉命令
#define OPEN_CMD _IO(0XEF, 2) //打開命令
#define SETPERIOD_CMD _IOW(0xEF, 3, int) //設(shè)置周期
int main(int argc, char *argv[])
{
int fd, ret;
char *filename;
unsigned char databuf[1];
unsigned int cmd;
unsigned int arg;
unsigned char str[100];
if(argc != 2) {
printf("Error Usage!\r\n");
return -1;
}
filename = argv[1];
fd = open(filename, O_RDWR);
if(fd < 0) {
printf("file %s open failed!\r\n", filename);
return -1;
}
/* 循環(huán)讀取 */
while(1) {
printf("Input CMD:");
ret = scanf("%d", &cmd);
if(ret !=1 ) {
gets(str); /* 防止卡死 */
}
if(cmd == 1) { /* 關(guān)閉 */
ioctl(fd, CLOSE_CMD, &arg);
} else if(cmd == 2) { /* 打開 */
ioctl(fd, OPEN_CMD, &arg);
} else if(cmd == 3) { /* 設(shè)置周期 */
printf("Input Timer period:");
ret = scanf("%d", &arg);
if(ret !=1 ) {
gets(str);
}
ioctl(fd, SETPERIOD_CMD, &arg);
}
}
close(fd);
return 0;
}
3 編譯驅(qū)動程序和測試 APP
3.1 編譯驅(qū)動程序
編寫 Makefile 文件
KERNELDIR := /home/kaka/linux/IMX6ULL/linux-imx-rel_imx_4.1.15_2.1.0_ga_alientek
CURRENT_PATH := $(shell pwd)
obj-m := timer.o
build: kernel_modules
kernel_modules:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) modules
arm-linux-gnueabihf-gcc timerAPP.c -o timerAPP
clean:
$(MAKE) -C $(KERNELDIR) M=$(CURRENT_PATH) clean
輸入如下命令編譯出驅(qū)動模塊文件:
make -j32
編譯成功以后就會生成一個名為“timer.ko ”的驅(qū)動模塊文件。
3.2 編譯測試 APP
arm-linux-gnueabihf-gcc timerAPP.c -o timerAPP
4 運行測試
將編譯出來的led.ko和ledApp這兩個文件拷貝到rootfs/lib/modules/4.1.15目錄中 ,輸入如下命令加載 timer.ko 驅(qū)動模塊:
depmod //第一次加載驅(qū)動的時候需要運行此命令
modprobe timer.ko //加載驅(qū)動
驅(qū)動加載成功以后會在終端中輸出一些信息
/lib/modules/4.1.15 # modprobe timer.ko
timerdev major=249,minor=0
led gpio num = 3
驅(qū)動加載成功以后如下命令來測試:
/timerApp /dev/timer
輸入上述命令以后終端提示輸入命令
/lib/modules/4.1.15 # ./timerAPP /dev/timer
Input CMD:
輸入“2”,打開定時器,此時 LED 燈就會以默認的 1 秒周期開始閃爍。在輸入“3”來設(shè)置定時周期,根據(jù)提示輸入要設(shè)置的周期值文章來源:http://www.zghlxwxcb.cn/news/detail-433087.html
Input CMD:3
Input Timer period:1000
輸入“1000”,表示設(shè)置定時器周期值為 1000ms,設(shè)置好以后 LED 燈就會以 1000ms 為間隔,開始閃爍。最后可以通過輸入“1”來關(guān)閉定時器,如果要卸載驅(qū)動的話輸入如下命令即可:文章來源地址http://www.zghlxwxcb.cn/news/detail-433087.html
rmmod timer.ko
到了這里,關(guān)于linux驅(qū)動開發(fā) - 08_內(nèi)核定時器的文章就介紹完了。如果您還想了解更多內(nèi)容,請在右上角搜索TOY模板網(wǎng)以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持TOY模板網(wǎng)!