1.製作一個定時器驅動
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/device.h>
#define DEV_NAME "new_timer"
#define DEV_MAJOR 0
#define DEV_MONITOR 0
#define CLASS_NAME "cla_ntimer"
struct timer_dev {
struct cdev cdev;
struct class *pclas;
struct device *pdev;
int dev_num;
struct timer_list timer;
int count;
atomic_t atomic;
};
static struct timer_dev mytimer;
static void timer_irq_hand( unsigned long arg)
{
mytimer.count ++;
printk("time: %d\n", mytimer.count);
mod_timer( &(mytimer.timer), jiffies + HZ);
return;
}
static int timer_open(struct inode *inode, struct file *fp)
{
DEFINE_TIMER( temp, timer_irq_hand, jiffies + HZ, 0);
printk("open...KB\n");
mytimer.timer = temp;
mytimer.count = 0;
add_timer( &(mytimer.timer));
return 0;
}
static int timer_release(struct inode *inode, struct file *fp)
{
printk("open...KB\n");
del_timer( &(mytimer.timer));
return 0;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = timer_open,
.release =timer_release,
};
static int __init timer_init(void)
{
int ret = 0;
printk("keyBoard_init..........\n");
ret = alloc_chrdev_region( &(mytimer.dev_num), DEV_MONITOR, 1, DEV_NAME);
if( ret<0 )
{
printk("error:%s, %d\n", __FUNCTION__,__LINE__);
goto ERR_ALLOC_CHRDEV;
}
cdev_init( &(mytimer.cdev), &fops);
mytimer.cdev.owner = THIS_MODULE;
ret = cdev_add( &(mytimer.cdev), mytimer.dev_num, 1);
if( ret<0)
{
printk("error: %s, %d\n", __FUNCTION__, __LINE__);
goto ERR_CDEV_ADD;
}
mytimer.pclas = class_create( THIS_MODULE, CLASS_NAME);
if( NULL==mytimer.pclas )
{
printk("error: %s, %d\n", __FUNCTION__, __LINE__);
goto ERR_CLAS_CREAT;
}
mytimer.pdev = device_create( mytimer.pclas, NULL, mytimer.dev_num, NULL, DEV_NAME);
if( NULL==mytimer.pdev )
{
goto ERR;
}
atomic_set( &(mytimer.atomic) , 1);
return 0;
ERR:
class_destroy( mytimer.pclas);
ERR_CLAS_CREAT:
cdev_del( &(mytimer.cdev));
ERR_CDEV_ADD:
unregister_chrdev_region( mytimer.dev_num, 1);
ERR_ALLOC_CHRDEV:
return -1;
}
static void __exit timer_exit(void)
{
printk("keyBoard_exit..........\n");
device_destroy( mytimer.pclas, mytimer.dev_num);
class_destroy( mytimer.pclas);
cdev_del( &(mytimer.cdev));
unregister_chrdev_region( mytimer.dev_num, 1);
return;
}
MODULE_LICENSE("GPL");
module_init(timer_init);
module_exit(timer_exit);
該驅動還沒有能力和應用層傳輸數據,以及進行讀取控制。防止在中斷函數修改數值時,進程讀取數據而造成的錯誤。
嘗試用原子操作來保證驅動使用的併發數量限制。用信號量來保證臨界資源的訪問安全。並且測試了在中斷中進行進程調度的後果,it's awful, totally awful.
#include <linux/module.h>
#include <linux/types.h>
#include <linux/fs.h>
#include <linux/errno.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/cdev.h>
#include <asm/uaccess.h>
#include <asm/system.h>
#include <linux/device.h>
#define DEV_NAME "new_timer"
#define DEV_MAJOR 0
#define DEV_MONITOR 0
#define CLASS_NAME "cla_ntimer"
struct timer_dev {
struct cdev cdev;
struct class *pclas;
struct device *pdev;
int dev_num;
struct timer_list timer;
atomic_t open_cnt; //原子操作來保證併發數量限制
struct semaphore sem; //信號量操作
int counter; //用來給計時器計數用
wait_queue_head_t wqh; //用來存儲阻塞進程用
};
static struct timer_dev mytimer;
static void timer_irq_hand( unsigned long arg)
{
down( &(mytimer.sem)); //主要與read()互斥
mytimer.counter++;
up( &(mytimer.sem));
printk("time: %d\n", mytimer.counter);
mod_timer( &(mytimer.timer), jiffies + HZ);
#if 0 //測試中斷中進行進程調度的後果
struct semaphore sem;
sema_init( &sem, 0);
down( &sem); //這裏會進入阻塞
//實驗發現,由於進程調度,操作系統確實跑飛了
#endif
}
static int timer_open(struct inode *inode, struct file *fp)
{
#if 1
if( atomic_sub_return( 1, &(mytimer.open_cnt))< 0 ) //假如計數已經小於0,表明已經達到最大併發數
{
atomic_add(1, &(mytimer.open_cnt));
return -EBUSY; //不能得到資源就立即返回
}
#else
while( atomic_sub_return( 1, &(mytimer.open_cnt))< 0)
{
atomic_add( 1, &(mytimer.open_cnt));
wait_queue_t wq;
init_wait( &(wq));
add_wait_queue( &(mytimer.wqh), &wq);
set_current_state( TASK_INTERRUPTIBLE);
schedule();
if( signal_pending( current))
{
return
}
remove_wait_queue( &(mytimer.wqh), &wq);
set_current_state( TASK_RUNNING);
wake_up( &(mytimer.wqh));
}
#endif
DEFINE_TIMER( temp, timer_irq_hand, jiffies + HZ, 0);
printk("open...KB\n");
mytimer.timer = temp;
add_timer( &(mytimer.timer));
sema_init( &(mytimer.sem), 1);
return 0;
}
static int timer_release(struct inode *inode, struct file *fp)
{
printk("open...KB\n");
atomic_add( 1, &(mytimer.open_cnt)); //釋放資源
del_timer( &(mytimer.timer));
return 0;
}
static ssize_t timer_read(struct file *fp, char __user *buf, size_t sz, loff_t *off)
{
printk("read....\n");
int temp;
#if 1 //阻塞版
down( &(mytimer.sem)); //主要與中斷中的自加互斥
#else //非阻塞版
if( down_trylock( &(mytimer.sem)))
{
return -EBUSY;
}
#endif
temp = mytimer.counter; //安全的獲取數據
up( &(mytimer.sem));
copy_to_user( buf, &temp, sz);
return sz;
}
static struct file_operations fops = {
.owner = THIS_MODULE,
.open = timer_open,
.release =timer_release,
.read =timer_read,
};
static int __init timer_init(void)
{
int ret = 0;
printk("keyBoard_init..........%s\n", __TIME__);
ret = alloc_chrdev_region( &(mytimer.dev_num), DEV_MONITOR, 1, DEV_NAME);
if( ret<0 )
{
printk("error:%s, %d\n", __FUNCTION__,__LINE__);
goto ERR_ALLOC_CHRDEV;
}
cdev_init( &(mytimer.cdev), &fops);
mytimer.cdev.owner = THIS_MODULE;
ret = cdev_add( &(mytimer.cdev), mytimer.dev_num, 1);
if( ret<0)
{
printk("error: %s, %d\n", __FUNCTION__, __LINE__);
goto ERR_CDEV_ADD;
}
mytimer.pclas = class_create( THIS_MODULE, CLASS_NAME);
if( NULL==mytimer.pclas )
{
printk("error: %s, %d\n", __FUNCTION__, __LINE__);
goto ERR_CLAS_CREAT;
}
mytimer.pdev = device_create( mytimer.pclas, NULL, mytimer.dev_num, NULL, DEV_NAME);
if( NULL==mytimer.pdev )
{
goto ERR;
}
atomic_set( &(mytimer.open_cnt) , 1); //只允許打開一次
init_waitqueue_head( &(mytimer.wqh));
return 0;
ERR:
class_destroy( mytimer.pclas);
ERR_CLAS_CREAT:
cdev_del( &(mytimer.cdev));
ERR_CDEV_ADD:
unregister_chrdev_region( mytimer.dev_num, 1);
ERR_ALLOC_CHRDEV:
return -1;
}
static void __exit timer_exit(void)
{
printk("keyBoard_exit..........\n");
device_destroy( mytimer.pclas, mytimer.dev_num);
class_destroy( mytimer.pclas);
cdev_del( &(mytimer.cdev));
unregister_chrdev_region( mytimer.dev_num, 1);
return;
}
MODULE_LICENSE("GPL");
module_init(timer_init);
module_exit(timer_exit);