首先,先來了解一下設備的阻塞與非阻塞操作以及實現阻塞操作的方法:
1.設備的阻塞與非阻塞操作:
阻塞操作是指,在執行設備操作時,若不能獲得資源,則進程被掛起直到滿足可操作的條件再進行操作。非阻塞操作是指,當進程不能進行設備操作時,並不掛起,它或者放棄,或者不停地查詢,直到可以進行操作為止。
2.實現阻塞操作的方法:
在linux驅動程序中,可以使用等待隊列(wait queue)來實現阻塞訪問。
一,glob字符設備驅動程序的編寫,把文件名命名為glob.c,源代碼如下:
#include <linux/module.h>
#include <linux/init.h>
#include <linux/fs.h>
#include <asm/uaccess.h>
#include <linux/wait.h> //有關等待隊列的頭文件
#include <linux/semaphore.h> //有關信號量的頭文件
#include <linux/sched.h>
MODULE_LICENSE("GPL");
#define MAJOR_NUM 1400
#define DEVICE_NAME "glob"
static int glob_var = 0;
static struct semaphore sem; //定義信號量
static wait_queue_head_t outq; //定義一個等待隊列頭
static int flag = 0;
//*******************定義read方法****************************
static ssize_t glob_read(struct file *filp, char *buf, ssize_t len, loff_t *off)
{
//等待數據可獲得
//wait_event_interruptible的返回一個整數值,非零值表示休眠被某個信號中斷
//wait_event_interruptible中第一個參數是等待隊列頭,第二個參數是一個布爾表達式,在條件為真之前,進程會保持休眠
if (wait_event_interruptible(outq, flag != 0))
{
return - ERESTARTSYS;
}
//down_interruptible 函數返回非零值,表示操作被中斷,調用者擁有信號量失敗
if (down_interruptible(&sem))
{
return - ERESTARTSYS;
}
flag = 0;
//將內核空間中的數據移動到用戶空間
if (copy_to_user(buf, &glob_var, sizeof(int)))
{
up(&sem); //移動數據的操作不完全成功也需要釋放信號量
return - EFAULT;
}
up(&sem);//移動數據成功,釋放信號量
return sizeof(int);
}
//************************定義write方法******************************
//glob_write函數中,flip是文件指針,buf是指向用戶空間的緩沖區,len表示請求傳輸數據的長度,
//off指向一個長偏移量類型對象的指針,這個對象指明用戶在文件中進行存儲操作的位置
static ssize_t glob_write(struct file *filp, const char *buf, ssize_t len,loff_t *off)
{
if (down_interruptible(&sem))
{
return - ERESTARTSYS;
}
//將用戶空間的數據移動到內核空間
if (copy_from_user(&glob_var, buf, sizeof(int)))
{
up(&sem); //移動數據不完全成功也需要釋放信號量
return - EFAULT;
}
up(&sem); //移動數據成功,釋放信號量
flag = 1;
//通知數據可獲得
wake_up_interruptible(&outq); //喚醒休眠進程
return sizeof(int);
}
//************初始化file_operations結構體*************
struct file_operations glob_fops =
{
.owner = THIS_MODULE,
.read = glob_read,
.write = glob_write,
};
//*******模塊初始化函數*********
static int __init glob_init(void)
{
int ret;
ret = register_chrdev(MAJOR_NUM, DEVICE_NAME, &glob_fops);
if (ret)
{
printk("glob register failure");
}
else
{
printk("glob register success");
//init_MUTEX(&sem);
sema_init(&sem,1); //初始化一個互斥鎖,把信號量sem的值設置為1
init_waitqueue_head(&outq); //初始化等候隊列頭
}
return ret;
}
//************模塊卸載函數**************
static void __exit glob_exit(void)
{
unregister_chrdev(MAJOR_NUM, DEVICE_NAME);
printk("glob unregister success!\n");
}
module_init(glob_init);
module_exit(glob_exit);
二,Makefile文件的編寫,源代碼如下:
12345
obj-m:=glob.o
default:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) modules
clean:
$(MAKE) -C /lib/modules/$(shell uname -r)/build M=$(PWD) clean
怎樣在 Ubuntu 上安裝 Linux 3.11 內核 http://www.linuxidc.com/Linux/2013-09/89674.htm
Ubuntu 13.10 (Saucy Salamander) 內核已升級至 Linux Kernel 3.10 RC5 http://www.linuxidc.com/Linux/2013-06/86110.htm
Linux Kernel 3.4.62 LTS 現已經提供下載 http://www.linuxidc.com/Linux/2013-09/90368.htm
如何在Ubuntu 13.10上安裝Linux內核 3.12 http://www.linuxidc.com/Linux/2013-11/92930.htm
三,編譯模塊:
把上面的glob.c和Makefile兩個文件放在同一個文件夾下,我這裡的文件夾是“glob阻塞操作實驗”,然後進入文件夾,打開終端,登錄root,輸入指令make,便開始進行模塊的編譯了,遇到編譯錯誤,多百度,積累經驗。
更多詳情見請繼續閱讀下一頁的精彩內容: http://www.linuxidc.com/Linux/2014-08/106012p2.htm