高級字符驅動程序操作[(2)阻塞型I/O和休眠]

高級字符驅動程序操作[(2)阻塞型I/O和休眠]

 

這一部分主要討論:如果驅動程序無法立即滿足請求,該如何響應?(65865346)

 


 

一、休眠

進程被置爲休眠,意味着它被標識爲處於一個特殊的狀態並且從調度器的運行隊列中移走。這個進程將不被在任何 CPU 上調度,即將不會運行。 直到發生某些事情改變了那個狀態。安全地進入休眠的兩條規則:

(1) 永遠不要在原子上下文中進入休眠,即當驅動在持有一個自旋鎖、seqlock或者 RCU 鎖時不能睡眠;關閉中斷也不能睡眠。持有一個信號量時休眠是合法的,但你應當仔細查看代碼:如果代碼在持有一個信號量時睡眠,任何其他的等待這個信號量的線程也會休眠。因此發生在持有信號量時的休眠必須短暫,而且決不能阻塞那個將最終喚醒你的進程。

(2)當進程被喚醒,它並不知道休眠了多長時間以及休眠時發生什麼;也不知道是否另有進程也在休眠等待同一事件,且那個進程可能在它之前醒來並獲取了所等待的資源。所以不能對喚醒後的系統狀態做任何的假設,並必須重新檢查等待條件來確保正確的響應

除非確信其他進程會在其他地方喚醒休眠的進程,否則也不能睡眠。使進程可被找到意味着:需要維護一個稱爲等待隊列的數據結構。它是一個進程鏈表,其中飽含了等待某個特定事件的所有進程。在 Linux 中, 一個等待隊列由一個wait_queue_head_t 結構體來管理,其定義在<linux/wait.h>中。wait_queue_head_t 類型的數據結構非常簡單:

struct __wait_queue_head {
    spinlock_t lock;
    struct list_head task_list;
};
typedef struct __wait_queue_head wait_queue_head_t;

它包含一個自旋鎖和一個鏈表。這個鏈表是一個等待隊列入口,它被聲明做 wait_queue_t。wait_queue_head_t包含關於睡眠進程的信息和它想怎樣被喚醒。

 


 

 

 

簡單休眠(其實是高級休眠的宏)

Linux 內核中最簡單的休眠方式是稱爲 wait_event的宏(及其變種),它實現了休眠和進程等待的條件的檢查。形式如下:

wait_event(queue, condition)/*不可中斷休眠,不推薦*/
wait_event_interruptible(queue, condition)/*推薦,返回非零值意味着休眠被中斷,且驅動應返回 -ERESTARTSYS*/
wait_event_timeout(queue, condition, timeout)
wait_event_interruptible_timeout(queue, condition, timeout)
/*有限的時間的休眠;若超時,則不管條件爲何值返回0,*/

喚醒休眠進程的函數稱爲 wake_up,形式如下:

void wake_up(wait_queue_head_t *queue);
void wake_up_interruptible(wait_queue_head_t *queue);

慣例:用 wake_up 喚醒 wait_event ;用 wake_up_interruptible 喚醒wait_event_interruptible。

簡單休眠實驗
 模塊程序鏈接:sleepy
模塊測試程序鏈接sleepy-test

實驗現象:

[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod sleepy.ko
[Tekkaman2440@SBC2440V4]#cd /dev/
[Tekkaman2440@SBC2440V4]#cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
252 sleepy
253 usb_endpoint
254 rtc

Block devices:
  1 ramdisk
256 rfd
  7 loop
 31 mtdblock
 93 nftl
 96 inftl
179 mmc
[Tekkaman2440@SBC2440V4]#mknod -m 666 sleepy c 252 0
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./sleepy_testr&
[Tekkaman2440@SBC2440V4]#./sleepy_testr&
[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  783 root 1744 S init
  787 root 1744 S init
  799 root 1336 S ./sleepy_testr
  800 root 1336 S ./sleepy_testr
  802 root 1744 R ps
[Tekkaman2440@SBC2440V4]#./sleepy_testw
read code=0
write code=0
[2] + Done ./sleepy_testr
[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  783 root 1744 S init
  787 root 1744 S init
  799 root 1336 S ./sleepy_testr
  804 root 1744 R ps
[Tekkaman2440@SBC2440V4]#./sleepy_testw
write code=0
[Tekkaman2440@SBC2440V4]#read code=0

[1] + Done ./sleepy_testr
[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  783 root 1744 S init
  787 root 1744 S init
  806 root 1744 R ps

 


阻塞和非阻塞操作

 

全功能的 read 和 write 方法涉及到進程可以決定是進行非阻塞 I/O還是阻塞 I/O操作。明確的非阻塞 I/O 由 filp->f_flags 中的 O_NONBLOCK 標誌來指示(定義再 <linux/fcntl.h> ,被 <linux/fs.h>自動包含)。瀏覽源碼,會發現O_NONBLOCK 的另一個名字:O_NDELAY ,這是爲了兼容 System V 代碼。O_NONBLOCK 標誌缺省地被清除,因爲等待數據的進程的正常行爲只是睡眠.

其實不一定只有read 和 write 方法有阻塞操作,open也可以有阻塞操作。後面會見到。而我的項目有一個和CPLD的接口的驅動,我決定要在ioctl 中使用阻塞。

以下是後面的scullpipe實驗的有關阻塞的代碼,我覺得寫得很好,先結合書上的介紹看看吧:

while (dev->rp == dev->wp)      { /* nothing to read */
     up(&dev->sem); /* release the lock */
     if (filp->f_flags & O_NONBLOCK)
         return -EAGAIN;
     PDEBUG("/"%s/" reading: going to sleep/n", current->comm);
     if (wait_event_interruptible(dev->inq, (dev->rp != dev->wp)))
        return -ERESTARTSYS; /* signal: tell the fs layer to handle it */ /* otherwise loop, but first reacquire the lock */
     if (down_interruptible(&dev->sem))
        return -ERESTARTSYS;

}        /* ok, data is there, return something */
......

高級休眠

步驟:

(1)分配和初始化一個 wait_queue_t 結構, 隨後將其添加到正確的等待隊列。

(2)設置進程狀態,標記爲休眠。在 <linux/sched.h> 中定義有幾個任務狀態:TASK_RUNNING 意思是進程能夠運行。有 2 個狀態指示一個進程是在睡眠: TASK_INTERRUPTIBLE 和 TASK_UNTINTERRUPTIBLE。2.6 內核的驅動代碼通常不需要直接操作進程狀態。但如果需要這樣做使用的代碼是:

void set_current_state(int new_state);

在老的代碼中, 你常常見到如此的東西:current->state = TASK_INTERRUPTIBLE; 但是象這樣直接改變 current 是不推薦的,當數據結構改變時這樣的代碼將會失效。通過改變 current 狀態,只改變了調度器對待進程的方式,但進程還未讓出處理器。

(3) 最後一步是放棄處理器。 但必須先檢查進入休眠的條件。如果不做檢查會引入競態: 如果在忙於上面的這個過程時有其他的線程剛剛試圖喚醒你,你可能錯過喚醒且長時間休眠。因此典型的代碼下:

if (!condition)
    schedule();

如果代碼只是從 schedule 返回,則進程處於TASK_RUNNING 狀態。 如果不需睡眠而跳過對 schedule 的調用,必須將任務狀態重置爲 TASK_RUNNING,還必要從等待隊列中去除這個進程,否則它可能被多次喚醒。

 

手工休眠

/* (1)創建和初始化一個等待隊列。常由宏定義完成:*/
DEFINE_WAIT(my_wait);
/*name 是等待隊列入口項的名字. 也可以用2步來做:*/
wait_queue_t my_wait;
init_wait(&my_wait);
/*常用的做法是放一個 DEFINE_WAIT 在循環的頂部,來實現休眠。*/


/* (2)添加等待隊列入口到隊列,並設置進程狀態:*/
void prepare_to_wait(wait_queue_head_t *queue, wait_queue_t *wait, int state);
/*queue 和 wait 分別地是等待隊列頭和進程入口。state 是進程的新狀態:TASK_INTERRUPTIBLE(可中斷休眠,推薦)或TASK_UNINTERRUPTIBLE(不可中斷休眠,不推薦)。*/

/* (3)在檢查確認仍然需要休眠之後調用 schedule*/

schedule();

/* (4)schedule 返回,就到了清理時間:*/
void finish_wait(wait_queue_head_t *queue, wait_queue_t *wait);

 

認真地看簡單休眠中的 wait_event(queue, condition) wait_event_interruptible(queue, condition) 底層源碼會發現,其實他們只是手工休眠中的函數的組合。所以怕麻煩的話還是用wait_event比較好。

獨佔等待

當一個進程調用 wake_up 在等待隊列上,所有的在這個隊列上等待的進程被置爲可運行的。 這在許多情況下是正確的做法。但有時,可能只有一個被喚醒的進程將成功獲得需要的資源,而其餘的將再次休眠。這時如果等待隊列中的進程數目大,這可能嚴重降低系統性能。爲此,內核開發者增加了一個“獨佔等待”選項。它與一個正常的睡眠有 2 個重要的不同:

(1)當等待隊列入口設置了 WQ_FLAG_EXCLUSEVE 標誌,它被添加到等待隊列的尾部;否則,添加到頭部。

(2)當 wake_up 被在一個等待隊列上調用, 它在喚醒第一個有 WQ_FLAG_EXCLUSIVE 標誌的進程後停止喚醒.但內核仍然每次喚醒所有的非獨佔等待。

採用獨佔等待要滿足 2 個條件:

(1)希望對資源進行有效競爭;

(2)當資源可用時,喚醒一個進程就足夠來完全消耗資源。

使一個進程進入獨佔等待,可調用:

void prepare_to_wait_exclusive(wait_queue_head_t *queue, wait_queue_t *wait, int state);

 

注意:無法使用 wait_event 和它的變體來進行獨佔等待.

喚醒的相關函數

很少會需要調用wake_up_interruptible 之外的喚醒函數,但爲完整起見,這裏是整個集合:

wake_up(wait_queue_head_t *queue);
wake_up_interruptible(wait_queue_head_t *queue);
/*wake_up 喚醒隊列中的每個非獨佔等待進程和一個獨佔等待進程。wake_up_interruptible 同樣, 除了它跳過處於不可中斷休眠的進程。它們在返回之前, 使一個或多個進程被喚醒、被調度(如果它們被從一個原子上下文調用, 這就不會發生).*/ wake_up_nr(wait_queue_head_t *queue, int nr);
wake_up_interruptible_nr(wait_queue_head_t *queue, int nr);
/*這些函數類似 wake_up, 除了它們能夠喚醒多達 nr 個獨佔等待者, 而不只是一個. 注意傳遞 0 被解釋爲請求所有的互斥等待者都被喚醒*/

 

 

wake_up_all(wait_queue_head_t *queue);
wake_up_interruptible_all(wait_queue_head_t *queue);
/*這種 wake_up 喚醒所有的進程, 不管它們是否進行獨佔等待(可中斷的類型仍然跳過在做不可中斷等待的進程)*/

 

 

wake_up_interruptible_sync(wait_queue_head_t *queue);
/*一個被喚醒的進程可能搶佔當前進程, 並且在 wake_up 返回之前被調度到處理器。 但是, 如果你需要不要被調度出處理器時,可以使用 wake_up_interruptible 的"同步"變體. 這個函數最常用在調用者首先要完成剩下的少量工作,且不希望被調度出處理器時。*/

 

 

 

 

 

poll 和 select

當應用程序需要進行對多文件讀寫時,若某個文件沒有準備好,則系統會處於讀寫阻塞的狀態,並影響了其他文件的讀寫。爲了避免這種情況,在必須使用多輸入輸出流又不想阻塞在它們任何一個上的應用程序常將非阻塞 I/O 和 poll(System V)、select(BSD Unix)、 epoll(linux2.5.45開始)系統調用配合使用。當poll函數返回時,會給出一個文件是否可讀寫的標誌,應用程序根據不同的標誌讀寫相應的文件,實現非阻塞的讀寫。這些系統調用功能相同: 允許進程來決定它是否可讀或寫一個或多個文件而不阻塞。這些調用也可阻塞進程直到任何一個給定集合的文件描述符可用來讀或寫。這些調用都需要來自設備驅動中poll 方法的支持,poll返回不同的標誌,告訴主進程文件是否可以讀寫,其原型(定義在<linux/poll.h> ):

unsigned int (*poll) (struct file *filp, poll_table *wait);

實現這個設備方法分兩步:

1. 在一個或多個可指示查詢狀態變化的等待隊列上調用 poll_wait. 如果沒有文件描述符可用來執行 I/O, 內核使這個進程在等待隊列上等待所有的傳遞給系統調用的文件描述符. 驅動通過調用函數 poll_wait增加一個等待隊列到 poll_table 結構,原型:

void poll_wait (struct file *, wait_queue_head_t *, poll_table *);

2. 返回一個位掩碼:描述可能不必阻塞就立刻進行的操作,幾個標誌(通過 <linux/poll.h> 定義)用來指示可能的操作:

 標誌

 含義

 POLLIN

 如果設備無阻塞的讀,就返回該值

 POLLRDNORM

 通常的數據已經準備好,可以讀了,就返回該值。通常的做法是會返回(POLLLIN|POLLRDNORA)

 POLLRDBAND

 如果可以從設備讀出帶外數據,就返回該值,它只可在linux內核的某些網絡代碼中使用,通常不用在設備驅動程序中

 POLLPRI

 如果可以無阻塞的讀取高優先級(帶外)數據,就返回該值,返回該值會導致select報告文件發生異常,以爲select八帶外數據當作異常處理

 POLLHUP

 當讀設備的進程到達文件尾時,驅動程序必須返回該值,依照select的功能描述,調用select的進程被告知進程時可讀的。

 POLLERR

 如果設備發生錯誤,就返回該值。

 POLLOUT

 如果設備可以無阻塞地些,就返回該值

 POLLWRNORM

 設備已經準備好,可以寫了,就返回該值。通常地做法是(POLLOUT|POLLNORM)

 POLLWRBAND

 於POLLRDBAND類似

考慮 poll 方法的 scullpipe 實現:

static unsigned int scull_p_poll(struct file *filp, poll_table *wait)
{
        struct scull_pipe *dev = filp->private_data;
        unsigned int mask = 0;
        
/*
        * The buffer is circular; it is considered full
        * if "wp" is right behind "rp" and empty if the
        * two are equal.
        */

        down(&dev->sem);
        poll_wait(filp, &dev->inq, wait);
        poll_wait(filp, &dev->outq, wait);
        if (dev->rp != dev->wp)
                mask |= POLLIN | POLLRDNORM; /* readable */
        if (spacefree(dev))
                mask |= POLLOUT | POLLWRNORM; /* writable */
        up(&dev->sem);
        return mask;
}

與read 和write 的交互

正確實現poll調用的規則:

從設備讀取數據:

(1)如果在輸入緩衝中有數據,read 調用應當立刻返回,即便數據少於應用程序要求的,並確保其他的數據會很快到達。 如果方便,可一直返回小於請求的數據,但至少返回一個字節。在這個情況下,poll 應當返回 POLLIN|POLLRDNORM。

(2)如果在輸入緩衝中無數據,read默認必須阻塞直到有一個字節。若O_NONBLOCK 被置位,read 立刻返回 -EAGIN 。在這個情況下,poll 必須報告這個設備是不可讀(清零POLLIN|POLLRDNORM)的直到至少一個字節到達。

(3)若處於文件尾,不管是否阻塞,read 應當立刻返回0,且poll 應該返回POLLHUP。

向設備寫數據

(1)若輸出緩衝有空間,write 應立即返回。它可接受小於調用所請求的數據,但至少必須接受一個字節。在這個情況下,poll應返回 POLLOUT|POLLWRNORM。

(2)若輸出緩衝是滿的,write默認阻塞直到一些空間被釋放。若 O_NOBLOCK 被設置,write 立刻返回一個 -EAGAIN。在這些情況下, poll 應當報告文件是不可寫的(清零POLLOUT|POLLWRNORM). 若設備不能接受任何多餘數據, 不管是否設置了 O_NONBLOCK,write 應返回 -ENOSPC("設備上沒有空間")。

(3)永遠不要讓write在返回前等待數據的傳輸結束,即使O_NONBLOCK 被清除。若程序想保證它加入到輸出緩衝中的數據被真正傳送, 驅動必須提供一個 fsync 方法。

刷新待處理輸出
若一些應用程序需要確保數據被髮送到設備,就實現必須fsync 方法。對 fsync 的調用只在設備被完全刷新時(即輸出緩衝爲空)才返回,不管 O_NONBLOCK 是否被設置,即便這需要一些時間。其原型是:

int (*fsync) (struct file *file, struct dentry *dentry, int datasync);

 

底層數據結構

只要用戶應用程序調用 poll、select、或epoll_ctl,內核就會調用這個系統調用所引用的所有文件的 poll 方法,並向他們傳遞同一個poll_table。 poll_table 結構只是構成實際數據結構的簡單封裝:

struct poll_table_struct;
/*
 * structures and helpers for f_op->poll implementations
 */

typedef void (*poll_queue_proc)(struct file *, wait_queue_head_t *, struct poll_table_struct *);

typedef struct poll_table_struct {
    poll_queue_proc qproc;
} poll_table;

對於 poll和 select系統調用,poll_table 是一個包含 poll_table_entry 結構內存頁鏈表。

struct poll_table_entry {
    struct file * filp;
    wait_queue_t wait;
    wait_queue_head_t * wait_address;
};

對 poll_wait 的調用有時還會將進程添加到給定的等待隊列。整個的結構必須由內核維護,在 poll 或者 select 返回前,進程可從所有的隊列中去除, .

如果被輪詢的驅動沒有一個驅動程序指明可進行非阻塞I/O,poll 調用會簡單地睡眠,直到一個它所在的等待隊列(可能許多)喚醒它.

當 poll 調用完成,poll_table 結構被重新分配, 所有的之前加入到 poll 表的等待隊列入口都會從表和它們的等待隊列中移出.

struct poll_wqueues {
    poll_table pt;
    struct poll_table_page * table;
    int error;
    int inline_index;
    struct poll_table_entry inline_entries[N_INLINE_POLL_ENTRIES];
};

struct poll_table_page {
    struct poll_table_page * next;
    struct poll_table_entry * entry;
    struct poll_table_entry entries[0];
};

poll 背後的數據結構

 

 

 


 

異步通知

 

通過使用異步通知,應用程序可以在數據可用時收到一個信號,而無需不停地輪詢。

啓用步驟:

(1)它們指定一個進程作爲文件的擁有者:使用 fcntl 系統調用發出 F_SETOWN 命令,這個擁有者進程的 ID 被保存在 filp->f_owner。目的:讓內核知道信號到達時該通知哪個進程。

(2)使用 fcntl 系統調用,通過 F_SETFL 命令設置 FASYNC 標誌。

內核操作過程

1.F_SETOWN被調用時filp->f_owner被賦值。

2. 當 F_SETFL 被執行來打開 FASYNC, 驅動的 fasync 方法被調用.這個標誌在文件被打開時缺省地被清除。

3. 當數據到達時,所有的註冊異步通知的進程都會被髮送一個 SIGIO 信號。

Linux 提供的通用方法是基於一個數據結構和兩個函數,定義在<linux/fs.h>。

數據結構:

struct fasync_struct {
    int    magic;
    int    fa_fd;
    struct    fasync_struct    *fa_next; /* singly linked list */
    struct    file         *fa_file;
};

驅動調用的兩個函數的原型:

int fasync_helper(int fd, struct file *filp, int mode, struct fasync_struct **fa);
void kill_fasync(struct fasync_struct **fa, int sig, int band);

當一個打開的文件的FASYNC標誌被修改時,調用fasync_helper 來從相關的進程列表中添加或去除文件。除了最後一個參數, 其他所有參數都時被提供給 fasync 方法的相同參數並被直接傳遞。 當數據到達時,kill_fasync 被用來通知相關的進程,它的參數是被傳遞的信號(常常是 SIGIO)和 band(幾乎都是 POLL_IN)。

這是 scullpipe 實現 fasync 方法的:

static int scull_p_fasync(int fd, struct file *filp, int mode)
{
 struct scull_pipe *dev = filp->private_data;
 return fasync_helper(fd, filp, mode, &dev->async_queue);
}

當數據到達, 下面的語句必須被執行來通知異步讀者. 因爲對 sucllpipe 讀者的新數據通過一個發出 write 的進程被產生, 這個語句出現在 scullpipe 的 write 方法中:

if (dev->async_queue)
 kill_fasync(&dev->async_queue, SIGIO, POLL_IN); /* 注意, 一些設備也針對設備可寫而實現了異步通知,在這個情況,kill_fasnyc 必須以 POLL_OUT 模式調用.*/

當文件被關閉時必須調用fasync 方法,來從活動的異步讀取進程列表中刪除該文件。儘管這個調用僅當 filp->f_flags 被設置爲 FASYNC 時才需要,但不管什麼情況,調用這個函數不會有問題,並且是普遍的實現方法。 以下是 scullpipe 的 release 方法的一部分:

/* remove this filp from the asynchronously notified filp's */
scull_p_fasync(-1, filp, 0);

異步通知使用的數據結構和 struct wait_queue 幾乎相同,因爲他們都涉及等待事件。區別異步通知用 struct file 替代 struct task_struct. 隊列中的 file 用獲取 f_owner, 一邊給進程發送信號。

 


scullpipe的實驗(poll和fasync方法的實現):

 

 

 

 

模塊程序鏈接:scullpipe

模塊測試程序鏈接scullpipe-test

ARM9實驗板的實驗現象是:

[Tekkaman2440@SBC2440V4]#cd /lib/modules/
[Tekkaman2440@SBC2440V4]#insmod pipe.ko
[Tekkaman2440@SBC2440V4]#cat /proc/devices
Character devices:
  1 mem
  2 pty
  3 ttyp
  4 /dev/vc/0
  4 tty
  4 ttyS
  5 /dev/tty
  5 /dev/console
  5 /dev/ptmx
  7 vcs
 10 misc
 13 input
 14 sound
 81 video4linux
 89 i2c
 90 mtd
116 alsa
128 ptm
136 pts
180 usb
189 usb_device
204 s3c2410_serial
252 pipe
253 usb_endpoint
254 rtc

Block devices:
  1 ramdisk
256 rfd
  7 loop
 31 mtdblock
 93 nftl
 96 inftl
179 mmc
[Tekkaman2440@SBC2440V4]#cd /dev/
[Tekkaman2440@SBC2440V4]#
[Tekkaman2440@SBC2440V4]#cd /tmp/
[Tekkaman2440@SBC2440V4]#./pipe_test &
[Tekkaman2440@SBC2440V4]#open scullpipe0 !
open scullpipe1 !
 SCULL_P_IOCTSIZE : scull_p_buffer0=21 !
 SCULL_P_IOCTSIZE : scull_p_buffer1=21 !
close pipetest0 !
close pipetest1 !
reopen scullpipe0 !
reopen scullpipe1 !

[Tekkaman2440@SBC2440V4]#echo 12345678901234567890 > /dev/scullpipe0
[Tekkaman2440@SBC2440V4]#read from pipetest0 code=20
[0]=1 [1]=2 [2]=3 [3]=4 [4]=5
[5]=6 [6]=7 [7]=8 [8]=9 [9]=0
[10]=1 [11]=2 [12]=3 [13]=4 [14]=5
[15]=6 [16]=7 [17]=8 [18]=9 [19]=0
read from pipetest0 code=1
[0]=
 [1]=2 [2]=3 [3]=4 [4]=5
[5]=6 [6]=7 [7]=8 [8]=9 [9]=0
[10]=1 [11]=2 [12]=3 [13]=4 [14]=5
[15]=6 [16]=7 [17]=8 [18]=9 [19]=0

[Tekkaman2440@SBC2440V4]#echo 12345678901234 > /dev/scullpipe1
[Tekkaman2440@SBC2440V4]#read from pipetest1 code=15
[0]=1 [1]=2 [2]=3 [3]=4 [4]=5
[5]=6 [6]=7 [7]=8 [8]=9 [9]=0
[10]=1 [11]=2 [12]=3 [13]=4 [14]=

[15]=6 [16]=7 [17]=8 [18]=9 [19]=0

[Tekkaman2440@SBC2440V4]#ps
  PID Uid VSZ Stat Command
    1 root 1744 S init
    2 root SW< [kthreadd]
    3 root SWN [ksoftirqd/0]
    4 root SW< [watchdog/0]
    5 root SW< [events/0]
    6 root SW< [khelper]
   59 root SW< [kblockd/0]
   60 root SW< [ksuspend_usbd]
   63 root SW< [khubd]
   65 root SW< [kseriod]
   77 root SW [pdflush]
   78 root SW [pdflush]
   79 root SW< [kswapd0]
   80 root SW< [aio/0]
  707 root SW< [mtdblockd]
  708 root SW< [nftld]
  709 root SW< [inftld]
  710 root SW< [rfdd]
  742 root SW< [kpsmoused]
  751 root SW< [kmmcd]
  769 root SW< [rpciod/0]
  778 root 1752 S -sh
  779 root 1744 S init
  781 root 1744 S init
  783 root 1744 S init
  785 root 1744 S init
  796 root 1344 S ./pipe_test
  797 root 1744 R ps
[Tekkaman2440@SBC2440V4]#./asynctest &
[Tekkaman2440@SBC2440V4]#echo 12345678901234 > /dev/scullpipe0
[Tekkaman2440@SBC2440V4]#12345678901234
close pipetest0 !
exit !

[2] + Done ./asynctest
[Tekkaman2440@SBC2440V4]#cat /proc/scullpipe
Default buffersize is 21

Device 0: c3e18494
   Queues: c04f1c34 c04f1c34
   Buffer: c3dc6c08 to c3dc6c1d (21 bytes)
   rp c3dc6c17 wp c3dc6c17
   readers 1 writers 0

Device 1: c3e18528
   Queues: c04f1c6c c04f1c6c
   Buffer: c3dc6b38 to c3dc6b4d (21 bytes)
   rp c3dc6b47 wp c3dc6b47
   readers 1 writers 0

Device 2: c3e185bc
   Queues: c3e185bc c3e185bc
   Buffer: 00000000 to 00000000 (0 bytes)
   rp 00000000 wp 00000000
   readers 0 writers 0

Device 3: c3e18650
   Queues: c3e18650 c3e18650
   Buffer: 00000000 to 00000000 (0 bytes)
   rp 00000000 wp 00000000
   readers 0 writers 0

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章