一区二区三区日韩精品-日韩经典一区二区三区-五月激情综合丁香婷婷-欧美精品中文字幕专区

分享

Linux驅(qū)動(dòng)總結(jié)3- unlocked_ioctl和堵塞(waitqueue)讀寫函數(shù)的實(shí)現(xiàn)

 云將東游 2015-10-26
    學(xué)習(xí)了驅(qū)動(dòng)程序的設(shè)計(jì),感覺在學(xué)習(xí)驅(qū)動(dòng)的同時(shí)學(xué)習(xí)linux內(nèi)核,也是很不錯(cuò)的過程哦,做了幾個(gè)實(shí)驗(yàn),該做一些總結(jié),只有不停的作總結(jié)才能印象深刻。
我的平臺(tái)是虛擬機(jī),fedora14,內(nèi)核版本為2.6.38.1.其中較之前的版本存在較大的差別,具體的實(shí)現(xiàn)已經(jīng)在上一次總結(jié)中給出了。今天主要總結(jié)的是ioctl和堵塞讀寫函數(shù)的實(shí)現(xiàn)。

一、ioctl函數(shù)的實(shí)現(xiàn)
首先說明在2.6.36以后ioctl函數(shù)已經(jīng)不再存在了,而是用unlocked_ioctl和compat_ioctl兩個(gè)函數(shù)實(shí)現(xiàn)以前版本的ioctl函數(shù)。同時(shí)在參數(shù)方面也發(fā)生了一定程度的改變,去除了原來ioctl中的struct inode參數(shù),同時(shí)改變了返回值。
但是驅(qū)動(dòng)設(shè)計(jì)過程中存在的問題變化并不是很大,同樣在應(yīng)用程序設(shè)計(jì)中我們還是采用ioctl實(shí)現(xiàn)訪問,而并不是unlocked_ioctl函數(shù),因此我們還可以稱之為ioctl函數(shù)的實(shí)現(xiàn)。
ioctl函數(shù)的實(shí)現(xiàn)主要是用來實(shí)現(xiàn)具體的硬件控制,采用相應(yīng)的命令控制硬件的具體操作,這樣就能使得硬件的操作不再是單調(diào)的讀寫操作。使得硬件的使用更加的方便。
ioctl函數(shù)實(shí)現(xiàn)主要包括兩個(gè)部分,首先是命令的定義,然后才是ioctl函數(shù)的實(shí)現(xiàn),命令的定義是采用一定的規(guī)則。
ioctl的命令主要用于應(yīng)用程序通過該命令操作具體的硬件設(shè)備,實(shí)現(xiàn)具體的操作,在驅(qū)動(dòng)中主要是對(duì)命令進(jìn)行解析,通過switch-case語句實(shí)現(xiàn)不同命令的控制,進(jìn)而實(shí)現(xiàn)不同的硬件操作。

ioctl函數(shù)的命令定義方法:
int (*unlocked_ioctl)(struct file*filp,unsigned int cmd,unsigned long arg)
雖然其中沒有指針的參數(shù),但是通常采用arg傳遞指針參數(shù)。cmd是一個(gè)命令。每一個(gè)命令由一個(gè)整形數(shù)據(jù)構(gòu)成(32bits),將一個(gè)命令分成四部分,每一部分實(shí)現(xiàn)具體的配置,設(shè)備類型(幻數(shù))8bits,方向2bits,序號(hào)8bits,數(shù)據(jù)大小13/14bits。命令的實(shí)現(xiàn)實(shí)質(zhì)上就是通過簡單的移位操作,將各個(gè)部分組合起來而已。
一個(gè)命令的分布的大概情況如下:

|---方向位(31-30)|----數(shù)據(jù)長度(29-16)----------------|---------設(shè)備類型(15-8)------|----------序號(hào)(7-0)----------|
|----------------------------------------------------------------------------------------------------------------------------------------|

其中方向位主要是表示對(duì)設(shè)備的操作,比如讀設(shè)備,寫設(shè)備等操作以及讀寫設(shè)備等都具有一定的方向,2個(gè)bits只有4種方向。
數(shù)據(jù)長度表示每一次操作(讀、寫)數(shù)據(jù)的大小,一般而已每一個(gè)命令對(duì)應(yīng)的數(shù)據(jù)大小都是一個(gè)固定的值,不會(huì)經(jīng)常改變,14bits說明可以選擇的數(shù)據(jù)長度最大為16k。
設(shè)備類型類似于主設(shè)備號(hào)(由于8bits,剛好組成一個(gè)字節(jié),因此經(jīng)常采用字符作為幻數(shù),表示某一類設(shè)備的命令),用來區(qū)別不同的命令類型,也就是特定的設(shè)備類型對(duì)應(yīng)特定的設(shè)備。序號(hào)主要是這一類命令中的具體某一個(gè),類似于次設(shè)備號(hào)(256個(gè)命令),也就是一個(gè)設(shè)備支持的命令多達(dá)256個(gè)。

同時(shí)在內(nèi)核中也存在具體的宏用來定義命令以及解析命令。
但是大部分的宏都只是定義具體的方向,其他的都需要設(shè)計(jì)者定義。
主要的宏如下:
#include

_IO(type,nr)                    表示定義一個(gè)沒有方向的命令,
_IOR(type,nr,size)            表示定義一個(gè)類型為type,序號(hào)為nr,數(shù)據(jù)大小為size的讀命令
_IOW(type,nr,size)           表示定義一個(gè)類型為type,序號(hào)為nr,數(shù)據(jù)大小為size的寫命令
_IOWR(type,nr,size)         表示定義一個(gè)類型為type,序號(hào)為nr,數(shù)據(jù)大小為size的寫讀命令

通常的type可采用某一個(gè)字母或者數(shù)字作為設(shè)備命令類型。
是實(shí)際運(yùn)用中通常采用如下的方法定義一個(gè)具體的命令:
  1. //頭文件
  2. #include

  3. /*定義一系列的命令*/
  4. /*幻數(shù),主要用于表示類型*/
  5. #define MAGIC_NUM 'k'
  6. /*打印命令*/
  7. #define MEMDEV_PRINTF _IO(MAGIC_NUM,1)
  8. /*從設(shè)備讀一個(gè)int數(shù)據(jù)*/
  9. #define MEMDEV_READ _IOR(MAGIC_NUM,2,int)
  10. /*往設(shè)備寫一個(gè)int數(shù)據(jù)*/
  11. #define MEMDEV_WRITE _IOW(MAGIC_NUM,3,int)

  12. /*最大的序列號(hào)*/
  13. #define MEM_MAX_CMD 3
還有對(duì)命令進(jìn)行解析的宏,用來確定具體命令的四個(gè)部分(方向,大小,類型,序號(hào))具體如下所示:
  1. /*確定命令的方向*/
  2. _IOC_DIR(nr)                    
  3. /*確定命令的類型*/
  4. _IOC_TYPE(nr)                     
  5. /*確定命令的序號(hào)*/
  6. _IOC_NR(nr)                           
  7. /*確定命令的大小*/
  8. _IOC_SIZE(nr)    
上面的幾個(gè)宏可以用來命令,實(shí)現(xiàn)命令正確性的檢查。

ioctl的實(shí)現(xiàn)過程主要包括如下的過程:
1、命令的檢測(cè)
2、指針參數(shù)的檢測(cè)
3、命令的控制switch-case語句

1、命令的檢測(cè)主要包括類型的檢查,數(shù)據(jù)大小,序號(hào)的檢測(cè),通過結(jié)合上面的命令解析宏可以快速的確定。
  1.         /*檢查類型,幻數(shù)是否正確*/
  2.         if(_IOC_TYPE(cmd)!=MAGIC_NUM)
  3.                 return -EINVAL;
  4.         /*檢測(cè)命令序號(hào)是否大于允許的最大序號(hào)*/
  5.         if(_IOC_NR(cmd)> MEM_MAX_CMD)
  6.                 return -EINVAL;
2、主要是指針參數(shù)的檢測(cè)。指針參數(shù)主要是因?yàn)閮?nèi)核空間和用戶空間的差異性導(dǎo)致的,因此需要來自用戶空間指針的有效性。使用copy_from_user,copy_to_user,get_user,put_user之類的函數(shù)時(shí),由于函數(shù)會(huì)實(shí)現(xiàn)指針參量的檢測(cè),因此可以省略,但是采用__get_user(),__put_user()之類的函數(shù)時(shí)一定要進(jìn)行檢測(cè)。具體的檢測(cè)方法如下所示:
  1. if(_IOC_DIR(cmd) & _IOC_READ)
  2.         err = !access_ok(VERIFY_WRITE,(void *)args,_IOC_SIZE(cmd));
  3. else if(_IOC_DIR(cmd) & _IOC_WRITE)
  4.         err = !access_ok(VERIFY_READ,(void *)args,_IOC_SIZE(cmd));
  5. if(err)/*返回錯(cuò)誤*/
  6.         return -EFAULT;
當(dāng)方向是讀時(shí),說明是從設(shè)備讀數(shù)據(jù)到用戶空間,因此要檢測(cè)用戶空間的指針是否可寫,采用VERIFY_WRITE,而當(dāng)方向是寫時(shí),說明是往設(shè)備中寫數(shù)據(jù),因此需要檢測(cè)用戶空間中的指針的可讀性VERIFY_READ。檢查通常采用access_ok()實(shí)現(xiàn)檢測(cè),第一個(gè)參數(shù)為讀寫,第二個(gè)為檢測(cè)的指針,第三個(gè)為數(shù)據(jù)的大小。
3、命名的控制:
命令的控制主要是采用switch和case相結(jié)合實(shí)現(xiàn)的,這于window編程中的檢測(cè)各種消息的實(shí)現(xiàn)方式是相同的。
  1. /*根據(jù)命令執(zhí)行相應(yīng)的操作*/
  2.         switch(cmd)
  3.         {
  4.                 case MEMDEV_PRINTF:
  5.                         printk("<--------CMD MEMDEV_PRINTF Done------------>\n\n");
  6.                         ...
  7.                         break;
  8.                 case MEMDEV_READ:
  9.                         ioarg = &mem_devp->data;
  10.                         ...
  11.                         ret = __put_user(ioarg,(int *)args);
  12.                         ioarg = 0;
  13.                         ...
  14.                         break;
  15.                 case MEMDEV_WRITE:
  16.                         ...
  17.                         ret = __get_user(ioarg,(int *)args);
  18.                         printk("<--------CMD MEMDEV_WRITE Done ioarg = %d--------->\n\n",ioarg);
  19.                         ioarg = 0;
  20.                         ...
  21.                         break;
  22.                 default:
  23.                         ret = -EINVAL;
  24.                         printk("<-------INVAL CMD--------->\n\n");
  25.                         break;
  26.         }
這只是基本的框架結(jié)構(gòu),實(shí)際中根據(jù)具體的情況進(jìn)行修改。這樣就實(shí)現(xiàn)了基本的命令控制。
文件操作支持的集合如下:
  1. /*添加該模塊的基本文件操作支持*/
  2. static const struct file_operations mem_fops =
  3. {
  4.         /*結(jié)尾不是分號(hào),注意其中的差別*/
  5.         .owner = THIS_MODULE,
  6.         .llseek = mem_llseek,
  7.         .read = mem_read,
  8.         .write = mem_write,
  9.         .open = mem_open,
  10.         .release = mem_release,
  11.         /*添加新的操作支持*/
  12.         .unlocked_ioctl = mem_ioctl,
  13. };
需要注意不是ioctl,而是unlocked_ioctl。


二、設(shè)備的堵塞讀寫方式實(shí)現(xiàn),通常采用等待隊(duì)列。
設(shè)備的堵塞讀寫方式,默認(rèn)情況下的讀寫操作都是堵塞型的,具體的就是如果需要讀數(shù)據(jù),當(dāng)設(shè)備中沒有數(shù)據(jù)可讀的時(shí)候應(yīng)該等待設(shè)備中有設(shè)備再讀,當(dāng)往設(shè)備中寫數(shù)據(jù)時(shí),如果上一次的數(shù)據(jù)還沒有被讀完成,則不應(yīng)該寫入數(shù)據(jù),就會(huì)導(dǎo)致進(jìn)程的堵塞,等待數(shù)據(jù)可讀寫。但是在應(yīng)用程序中也可以采用非堵塞型的方式進(jìn)行讀寫。只要在打開文件的時(shí)候添加一個(gè)O_NONBLOCK,這樣在不能讀寫的時(shí)候就會(huì)直接返回,而不會(huì)等待。
因此我們?cè)趯?shí)際設(shè)計(jì)驅(qū)動(dòng)設(shè)備的同時(shí)需要考慮讀寫操作的堵塞方式。堵塞方式的設(shè)計(jì)主要是通過等待隊(duì)列實(shí)現(xiàn),通常是將等待隊(duì)列(實(shí)質(zhì)就是一個(gè)鏈表)的頭作為設(shè)備數(shù)據(jù)結(jié)構(gòu)的一部分。在設(shè)備初始化過程中初始化等待隊(duì)列的頭。最后在設(shè)備讀寫操作的實(shí)現(xiàn)添加相應(yīng)的等待隊(duì)列節(jié)點(diǎn),并進(jìn)行相應(yīng)的控制。

等待隊(duì)列的操作基本如下:
1、等待隊(duì)列的頭定義并初始化的過程如下:
方法一:
struct wait_queue_head_t mywaitqueue;
init_waitqueue_head(&mywaitqueue);
方法二:
DECLARE_WAIT_QUEUE_HEAD(mywaitqueue);
以上的兩種都能實(shí)現(xiàn)定義和初始化等待隊(duì)列頭。

2、創(chuàng)建、移除一個(gè)等待隊(duì)列的節(jié)點(diǎn),并添加、移除相應(yīng)的隊(duì)列。
定義一個(gè)等待隊(duì)列的節(jié)點(diǎn):DECLARE_WAITQUEUE(wait,tsk)
其中tsk表示一個(gè)進(jìn)程,可以采用current當(dāng)前的進(jìn)程。
添加到定義好的等待隊(duì)列頭中。
add_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
即:add_wait_queue(&mywaitqueue,&wait);

移除等待節(jié)點(diǎn)
remove_wait_queue(wait_queue_head_t *q,wait_queue_t *wait);
即:remove_wait_queue(&mywaitqueue,&wait);

3、等待事件
wait_event(queue,condition);當(dāng)condition為真時(shí),等待隊(duì)列頭queue對(duì)應(yīng)的隊(duì)列被喚醒,否則繼續(xù)堵塞。這種情況下不能被信號(hào)打斷。
wait_event_interruptible(queue,condition);當(dāng)condition為真時(shí),等待隊(duì)列頭queue對(duì)應(yīng)的隊(duì)列被喚醒,否則繼續(xù)堵塞。這種情況下能被信號(hào)打斷。
4、喚醒等待隊(duì)列
wait_up(wait_queue_head_t *q),喚醒該等待隊(duì)列頭對(duì)應(yīng)的所有等待。
wait_up_interruptible(wait_queue_head_t *q)喚醒處于TASK_INTERRUPTIBLE的等待進(jìn)程。
應(yīng)該成對(duì)的使用。即wait_event于wait_up,而wait_event_interruptible與wait_up_interruptible。

  1. wait_event和wait_event_interruptible的實(shí)現(xiàn)都是采用宏的方式,都是一個(gè)重新調(diào)度的過程,如下所示:
  1. #define wait_event_interruptible(wq, condition)                \
  2. ({                                    \
  3.     int __ret = 0;                            \
  4.     if (!(condition))                        \
  5.         __wait_event_interruptible(wq, condition, __ret);    \
  6.     __ret;                                \
  7. })
  1. #define __wait_event_interruptible(wq, condition, ret)            \
  2. do {                                    \
  3.      /*此處存在一個(gè)聲明等待隊(duì)列的語句,因此不需要再重新定義一個(gè)等待隊(duì)列節(jié)點(diǎn)*/
  4.     DEFINE_WAIT(__wait);                        \
  5.                                     \
  6.     for (;;) {                            \
  7.         /*此處就相當(dāng)于add_wait_queue()操作,具體參看代碼如下所示*/
  8.         prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);    \
  9.         if (condition)                        \
  10.             break;                        \
  11.         if (!signal_pending(current)) {                \
  12.             /*此處是調(diào)度,丟失CPU,因此需要wake_up函數(shù)喚醒當(dāng)前的進(jìn)程
  13. 根據(jù)定義可知,如果條件不滿足,進(jìn)程就失去CPU,能夠跳出for循環(huán)的出口只有
  14.                 1、當(dāng)條件滿足時(shí)2、當(dāng)signal_pending(current)=1時(shí)。
  15.                 1、就是滿足條件,也就是說wake_up函數(shù)只是退出了schedule函數(shù),
  16.                 而真正退出函數(shù)還需要滿足條件
  17.                 2、說明進(jìn)程可以被信號(hào)喚醒。也就是信號(hào)可能導(dǎo)致沒有滿足條件時(shí)就喚醒當(dāng)前的進(jìn)程。 
  18.                這也是后面的代碼采用while判斷的原因.防止被信號(hào)喚醒。   
  19.    */
  20.             schedule();                    \
  21.             continue;                    \
  22.         }                            \
  23.         ret = -ERESTARTSYS;                    \
  24.         break;                            \
  25.     }                                \
  26.     finish_wait(&wq, &__wait);                    \
  27. } while (0)

#define DEFINE_WAIT(name) DEFINE_WAIT_FUNC(name, autoremove_wake_function)
#define DEFINE_WAIT_FUNC(name, function) \
wait_queue_t name = { \
.private = current, \
.func = function, \
.task_list = LIST_HEAD_INIT((name).task_list), \
}

  1. void prepare_to_wait(wait_queue_head_t *q, wait_queue_t *wait, int state)
  2. {
  3. unsigned long flags;

  4. wait->flags &= ~WQ_FLAG_EXCLUSIVE;
  5. spin_lock_irqsave(&q->lock, flags);
  6. if (list_empty(&wait->task_list))
  7.                /*添加節(jié)點(diǎn)到等待隊(duì)列*/
  8. __add_wait_queue(q, wait);
  9. set_current_state(state);
  10. spin_unlock_irqrestore(&q->lock, flags);
  11. }
  12. 喚醒的操作也是類似的。
  13. #define wake_up_interruptible(x) __wake_up(x, TASK_INTERRUPTIBLE, 1, NULL)
 
  void __wake_up(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, void *key)
{
unsigned long flags;

spin_lock_irqsave(&q->lock, flags);
__wake_up_common(q, mode, nr_exclusive, 0, key);
spin_unlock_irqrestore(&q->lock, flags);
}

static void __wake_up_common(wait_queue_head_t *q, unsigned int mode,
int nr_exclusive, int wake_flags, void *key)
{
wait_queue_t *curr, *next;

list_for_each_entry_safe(curr, next, &q->task_list, task_list) {
unsigned flags = curr->flags;

if (curr->func(curr, mode, wake_flags, key) &&
(flags & WQ_FLAG_EXCLUSIVE) && !--nr_exclusive)
break;
}
}
等待隊(duì)列通常用在驅(qū)動(dòng)程序設(shè)計(jì)中的堵塞讀寫操作,并不需要手動(dòng)的添加節(jié)點(diǎn)到隊(duì)列中,直接調(diào)用即可實(shí)現(xiàn),具體的實(shí)現(xiàn)方法如下:
1、在設(shè)備結(jié)構(gòu)體中添加等待隊(duì)列頭,由于讀寫都需要堵塞,所以添加兩個(gè)隊(duì)列頭,分別用來堵塞寫操作,寫操作。
  1. #include<linux/wait.h>

  2. struct mem_dev
  3. {
  4.         char *data;
  5.         unsigned long size;
  6.         /*添加一個(gè)并行機(jī)制*/
  7.         spinlock_t lock;

  8.         /*添加一個(gè)等待隊(duì)列t頭*/
  9.         wait_queue_head_t rdqueue;
  10.         wait_queue_head_t wrqueue;
  11. };
2、然后在模塊初始化中初始化隊(duì)列頭:
  1. /*初始化函數(shù)*/
  2. static int memdev_init(void)
  3. {
  4.        ....
  5.         for(i = 0; i < MEMDEV_NR_DEVS; i)
  6.         {
  7.                 mem_devp[i].size = MEMDEV_SIZE;
  8.                 /*對(duì)設(shè)備的數(shù)據(jù)空間分配空間*/
  9.                 mem_devp[i].data = kmalloc(MEMDEV_SIZE,GFP_KERNEL);
  10.                 /*問題,沒有進(jìn)行錯(cuò)誤的控制*/
  11.                 memset(mem_devp[i].data,0,MEMDEV_SIZE);

  12.                 /*初始化定義的互信息量*/
  13.                 //初始化定義的自旋鎖ua
  14.                 spin_lock_init(&(mem_devp[i].lock));
  15.                 /*初始化兩個(gè)等待隊(duì)列頭,需要注意必須用括號(hào)包含起來,使得優(yōu)先級(jí)正確*/
  16.                 init_waitqueue_head(&(mem_devp[i].rdqueue));
  17.                 init_waitqueue_head(&(mem_devp[i].wrqueue));
  18.         }
  19.       ...
  20. }
3、確定一個(gè)具體的條件,比如數(shù)據(jù)有無,具體的條件根據(jù)實(shí)際的情況設(shè)計(jì)。
/*等待條件*/
static bool havedata = false;

4、在需要堵塞的讀函數(shù),寫函數(shù)中分別實(shí)現(xiàn)堵塞,首先定義等待隊(duì)列的節(jié)點(diǎn),并添加到隊(duì)列中去,然后等待事件的喚醒進(jìn)程。但是由于讀寫操作的兩個(gè)等待隊(duì)列都是基于條件havedata的,所以在讀完成以后需要喚醒寫,寫完成以后需要喚醒讀操作,同時(shí)更新條件havedata,最后還要移除添加的等待隊(duì)列節(jié)點(diǎn)。
  1. /*read函數(shù)的實(shí)現(xiàn)*/
  2. static ssize_t mem_read(struct file *filp,char __user *buf, size_t size,loff_t *ppos)
  3. {
  4.         unsigned long p = *ppos;
  5.         unsigned int count = size;
  6.         int ret = 0;

  7.         struct mem_dev *dev = filp->private_data;

  8.         /*參數(shù)的檢查,首先判斷文件位置*/
  9.         if(p >= MEMDEV_SIZE)
  10.                 return 0;
  11.         /*改正文件大小*/
  12.         if(count > MEMDEV_SIZE - p)
  13.                 count = MEMDEV_SIZE - p;
         #if 0
  1.         /*添加一個(gè)等待隊(duì)列節(jié)點(diǎn)到當(dāng)前進(jìn)程中*/
  2.         DECLARE_WAITQUEUE(wait_r,current);

  3.         /*將節(jié)點(diǎn)添加到等待隊(duì)列中*/
  4.         add_wait_queue(&dev->rdqueue,&wait_r);

  5.         /*添加等待隊(duì)列,本來采用if即可,但是由于信號(hào)等可能導(dǎo)致等待隊(duì)列的喚醒,因此采用循環(huán),確保不會(huì)出現(xiàn)誤判*/
  6.         #endif

  7.         while(!havedata)
  8.         {
  9.                 /*判斷用戶是否設(shè)置為非堵塞模式讀,告訴用戶再讀*/
  10.                 if(filp->f_flags & O_NONBLOCK)
  11.                         return -EAGAIN;

  12.                 /*依據(jù)條件havedata判斷隊(duì)列的狀態(tài),防止進(jìn)程被信號(hào)喚醒*/
  13.                 wait_event_interruptible(dev->rdqueue,havedata);
  14.         }

  15.         spin_lock(&dev->lock);
  16.         /*從內(nèi)核讀數(shù)據(jù)到用戶空間,實(shí)質(zhì)就通過private_data訪問設(shè)備*/
  17.         if(copy_to_user(buf,(void *)(dev->data p),count))
  18.         {
  19.                 /*出錯(cuò)誤*/
  20.                 ret = -EFAULT;
  21.         }
  22.         else
  23.         {
  24.                 /*移動(dòng)當(dāng)前文件光標(biāo)的位置*/

  25.                 *ppos = count;
  26.                 ret = count;

  27.                 printk(KERN_INFO "read %d bytes(s) from %d\n",count,p);
  28.         }
  29.       
  30.         spin_unlock(&dev->lock);
 #if 0
  1.         /*將等待隊(duì)列節(jié)點(diǎn)從讀等待隊(duì)列中移除*/
  2.         remove_wait_queue(&dev->rdqueue,&wait_r);
  3. #endif 

  4.         /*更新條件havedate*/
  5.         havedata = false;
  6.         /*喚醒寫等待隊(duì)列*/
  7.         wake_up_interruptible(&dev->wrqueue);

  8.         return ret;
  9. }

  1. /*write函數(shù)的實(shí)現(xiàn)*/
  2. static ssize_t mem_write(struct file *filp,const char __user *buf,size_t size,loff_t *ppos)
  3. {
  4.         unsigned long p = *ppos;
  5.         unsigned int count = size;
  6.         int ret = 0;

  7.         /*獲得設(shè)備結(jié)構(gòu)體的指針*/
  8.         struct mem_dev *dev = filp->private_data;

  9.         /*檢查參數(shù)的長度*/
  10.         if(p >= MEMDEV_SIZE)
  11.                 return 0;
  12.         if(count > MEMDEV_SIZE - p)
  13.                 count = MEMDEV_SIZE - p;
  #if 0
  1.         /*定義并初始化一個(gè)等待隊(duì)列節(jié)點(diǎn),添加到當(dāng)前進(jìn)程中*/
  2.         DECLARE_WAITQUEUE(wait_w,current);
  3.         /*將等待隊(duì)列節(jié)點(diǎn)添加到等待隊(duì)列中*/
  4.         add_wait_queue(&dev->wrqueue,&wait_w);
  5.         #endif

  6.         /*添加寫堵塞判斷*/
  7.         /*為何采用循環(huán)是為了防止信號(hào)等其他原因?qū)е聠拘?/
  8.         while(havedata)
  9.         {
  10.                 /*如果是以非堵塞方式*/
  11.                 if(filp->f_flags & O_NONBLOCK)
  12.                         return -EAGAIN;
  13.         /*分析源碼發(fā)現(xiàn),wait_event_interruptible 中存在DECLARE_WAITQUEUE和add_wait_queue的操作,因此不需要手動(dòng)添加等待隊(duì)列節(jié)點(diǎn)*/
  14.                 wait_event_interruptible(&dev->wrqueue,(!havedata));
  15.         }

  16.         spin_lock(&dev->lock);

  17.         if(copy_from_user(dev->data p,buf,count))
  18.                 ret = -EFAULT;
  19.         else
  20.         {
  21.                 /*改變文件位置*/
  22.                 *ppos = count;
  23.                 ret = count;
  24.                 printk(KERN_INFO "writted %d bytes(s) from %d\n",count,p);
  25.         }

  26.         spin_unlock(&dev->lock);
  27. #if 0
  28.         /*將該等待節(jié)點(diǎn)移除*/
  29.         remove_wait_queue(&dev->wrqueue,&wait_w);
  30. #endif 

  31.         /*更新條件*/
  32.         havedata = true;
  33.         /*喚醒讀等待隊(duì)列*/
  34.         wake_up_interruptible(&dev->rdqueue);

  35.         return ret;
  36. }
5、應(yīng)用程序采用兩個(gè)不同的進(jìn)程分別進(jìn)行讀、寫,然后檢測(cè)順序是否可以調(diào)換,檢查等待是否正常。

    本站是提供個(gè)人知識(shí)管理的網(wǎng)絡(luò)存儲(chǔ)空間,所有內(nèi)容均由用戶發(fā)布,不代表本站觀點(diǎn)。請(qǐng)注意甄別內(nèi)容中的聯(lián)系方式、誘導(dǎo)購買等信息,謹(jǐn)防詐騙。如發(fā)現(xiàn)有害或侵權(quán)內(nèi)容,請(qǐng)點(diǎn)擊一鍵舉報(bào)。
    轉(zhuǎn)藏 分享 獻(xiàn)花(0

    0條評(píng)論

    發(fā)表

    請(qǐng)遵守用戶 評(píng)論公約

    類似文章 更多

    男人把女人操得嗷嗷叫| 亚洲品质一区二区三区| 嫩呦国产一区二区三区av| 久久精品国产亚洲av久按摩| 欧美人禽色视频免费看| 麻豆蜜桃星空传媒在线观看| 亚洲中文字幕乱码亚洲| 国产福利一区二区久久| 亚洲欧美国产中文色妇| 在线免费国产一区二区| 五月的丁香婷婷综合网| 成人综合网视频在线观看| 日韩欧美国产三级在线观看| 四季精品人妻av一区二区三区 | 欧美日韩欧美国产另类| 日本福利写真在线观看| 欧美日韩精品一区免费 | 久久福利视频这里有精品| 中文字幕91在线观看| 99久久国产精品免费| 国产亚洲不卡一区二区| 国产精品流白浆无遮挡| 欧美亚洲91在线视频| 国产精品白丝一区二区| 99久久精品国产日本| 欧美日韩综合免费视频| 东京热男人的天堂一二三区| 午夜福利国产精品不卡| 日本东京热视频一区二区三区| 日本午夜乱色视频在线观看| 日韩精品日韩激情日韩综合| 国产一区麻豆水好多高潮| 99热在线精品视频观看| 在线免费视频你懂的观看 | 亚洲天堂国产精品久久精品| 亚洲国产四季欧美一区| 中文字幕一区二区三区大片| 久久偷拍视频免费观看| 国产一区二区三区四区中文| 国产精品午夜福利在线观看 | 欧美中文日韩一区久久|