Linux核心模組程式設計--阻塞程式(轉)
阻塞程式
當某人要求你什麼事而你當時不能時你在做什麼?如果你是人而你被別人打擾,你唯一能說的是:‘現在不行,我正忙著呢。 走開!’。但是如果你是一個核心模組而你被一個程式打擾,你有另外的可能。你可以讓那個程式睡眠直到你能為它服務。畢竟,核心可以讓程式睡眠並且可以隨時喚醒它(那就是在單CPU上呈現同一時間多個程式執行的方式)。
這個核心模組就是這樣的例子。那個檔案(被稱為 /proc/sleep)在同一時間只能被一個程式開啟。如果那個檔案已經開啟了,核心模組呼叫module_interruptible_sleep_on(保持一個檔案開啟的最簡單的辦法是用 tail -f)。這個函式改變那個任務(任何任務是包含有關程式的資訊和系統呼叫的核心的一種資料結構)的狀態為TASK_INTERRUPTIBLE,它的意思是任務不能執行,除非它被喚醒。並且該任務被加入 WaitQ-- 等待訪問該檔案的任務佇列。然後函式呼叫排程程式進行上下文轉換到一個還要使用CPU的不同的程式。
當一個程式用完該檔案,它關閉該檔案,然後module_close 被呼叫。那個函式喚醒佇列中的所有程式(沒有機制只喚醒其中的一個)。然後它返回而剛剛關閉該檔案的程式可以繼續執行。排程程式及時地決定那個程式已經用了夠多的時間而將CPU的控制權交給另一個程式。最後,佇列中的某個程式會獲得排程程式賦予的CPU的控制權。它正好在對module_interruptible_sleep_on(這意味著程式仍然在核心模式--直到程式被關照,它釋出 open 系統呼叫然而系統呼叫還沒有返回。程式不知道別人在它釋出呼叫和它返回之前的大部分時間內使用CPU)的呼叫後開始。然後它能繼續設定一個全域性變數以告訴所有其他程式該檔案仍然開啟,它們將繼續它們等待的生活。當另一個程式得到CPU時間片,它們將看到那個全域性變數而繼續去睡眠。
為了使我們的生活更有趣, module_close 沒有喚醒等待訪問該檔案的程式的壟斷權。一個訊號,例如Ctrl-C (SIGINT)也可以喚醒一個程式(這是因為我們使用 module_interruptible_sleep_on。我們不能使用module_sleep_on 作為代替,但那將導致那些他們控制的計算機被忽略的使用者的極度的憤怒)。在那種情況下,我們想立即用 -EINTR 返回。這是很重要的,例如使用者因此可以在程式收到該檔案前將它殺死。
還有一點需要記住。有時候程式不想睡眠,它們想要麼立即得到它們需要的要麼被告知它不能完成。當檔案開啟時這類程式使用 O_NONBLOCK 標誌。例如當檔案開啟時,核心被假設從阻塞操作中返回錯誤程式碼-EAGAIN作為回應。在這章的源目錄中有的程式 cat_noblock 能用O_NONBLOCK開啟檔案。
範例 sleep.c
/* sleep.c - 建立一個 /proc 檔案,而如果幾個程式同時試圖開啟它,除了一個外使其他所有的睡眠 *//* Copyright (C) 1998-99 by Ori Pomerantz *//* 必要標頭檔案 *//* 標準標頭檔案 */#include /* 核心工作 */#include /* 明確指定是模組 *//* 處理 CONFIG_MODVERSIONS */#if CONFIG_MODVERSIONS==1#define MODVERSIONS#include#endif/* 使用 proc 檔案系統所必須的 */#include/* 為了使程式睡眠和喚醒 */#include#include/* 2.2.3 版/usr/include/linux/version.h 包含該宏* 但 2.0.35 版不包含--假如以備需要 */#ifndef KERNEL_VERSION#define KERNEL_VERSION(a,b,c) ((a)*65536+(b)*256+(c))#endif#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)#include /* 為了得到 get_user 和 put_user */#endif/* 模組的檔案函式 ********************** *//* 儲存收到的上一個訊息以證明能夠處理輸入 */#define MESSAGE_LENGTH 80static char Message[MESSAGE_LENGTH];/* 既然使用檔案操作結構,我們就不能特殊的 proc 輸出 - 我們不得不使用這個標準的讀函式 */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)static ssize_t module_output(struct file *file, /* 要讀的檔案 */char *buf, /* 放置資料的緩衝區(在使用者記憶體段) */size_t len, /* 緩衝區長度 */loff_t *offset) /* 檔案偏移量 - 忽略 */#elsestatic int module_output(struct inode *inode, /* 要讀的節點 */struct file *file, /* 要讀的檔案 */char *buf, /* 放置資料的緩衝區(在使用者記憶體段) */int len) /* 緩衝區長度 */#endif{static int finished = 0;int i;char message[MESSAGE_LENGTH+30];/* 返回0指明檔案尾--沒有更多說的 */if (finished) {finished = 0;return 0;}/* 如果你到現在還不懂這個,你沒有希望成為核心程式設計師 */sprintf(message, "Last input:%s ", Message);for(i=0; i put_user(message[i], buf+i);finished = 1;return i; /* 返回‘讀’的位元組數 */}/* 當使用者向 /proc 檔案寫說這個函式接收使用者輸入 */#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)static ssize_t module_input(struct file *file, /* 檔案自己 */const char *buf, /* 帶有輸入的緩衝區 */size_t length, /* 緩衝區長度 */loff_t *offset) /* 檔案偏移量-忽略 */#elsestatic int module_input(struct inode *inode, /* 檔案的節點 */struct file *file, /* 檔案自己 */const char *buf, /* 帶有輸入的緩衝區 */int length) /* 緩衝區長度 */#endif{int i;/* 將輸入放入 Message, module_output 隨後將能使用它 */for(i=0; i #if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)get_user(Message[i], buf+i);#elseMessage[i] = get_user(buf+i);#endif/* 我們需要一個標準的以0終止的字串 */Message[i] = ';/* 返回使用過的輸入的字元數 */return i;}/* 如果檔案當前被某人開啟則為1 */int Already_Open = 0;/* 需要我們的檔案的程式佇列 */static struct wait_queue *WaitQ = NULL;/* 當 /proc 檔案被開啟時被呼叫 */static int module_open(struct inode *inode,struct file *file){/* 如果檔案的標誌包含 O_NONBLOCK 則意味著程式不想等待該檔案。* 在這種情況下,如果檔案已經開啟,我們將用返回 -EAGAIN 表示失敗,意思是“你將再試一次”* 而不讓寧願保持醒狀態的程式阻塞。 */if ((file->f_flags & O_NONBLOCK) && Already_Open)return -EAGAIN;/* 這是放置 MOD_INC_USE_COUNT 的合適的位置,因為如果一個程式處於核心模組的迴圈中,* 核心模組不應被清除。 */MOD_INC_USE_COUNT;/* 如果檔案已經開啟,等待,直到它不再如此 */while (Already_Open){#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)int i, is_sig=0;#endif/* 該函式使當前的程式睡眠,包括任何系統呼叫,例如我們的。* 執行將在函式呼叫後恢復,要麼因為某人呼叫了 wake_up(&WaitQ) (只有當檔案被關閉時* 由 module_close 做這個),要麼諸如 Ctrl-C 之類的訊號傳送到程式 */module_interruptible_sleep_on(&WaitQ);/* 如果因為得到一個訊號我們醒來,我們不再阻塞,返回 -EINTR (系統呼叫失敗)。* 這允許程式被殺死或停止。 *//** Emmanuel Papirakis:** 在 2.2.* 上有一點更新。訊號現在被包含在一個雙字中(64 位) 並被儲存在一個* 包含雙無符號長整型陣列的結構中。必要的話我們需要做兩次檢查。** Ori Pomerantz:** 沒有人向我許諾他們決不使用多於64位,或者這本書不在16位字長的 Linux 版本上使用。* 這些程式碼無論如何都可以工作。*/#if LINUX_VERSION_CODE >= KERNEL_VERSION(2,2,0)for(i=0; i<_nsig_words i="">
來自 “ ITPUB部落格 ” ,連結:http://blog.itpub.net/10752043/viewspace-940222/,如需轉載,請註明出處,否則將追究法律責任。
相關文章
- 阻塞式程式設計和非阻塞式程式設計區別程式設計
- Linux核心net模組引入了Rust程式碼 - PhoronixLinuxRust
- 玩轉 PHP 網路程式設計全套阻塞與非阻塞 IOPHP程式設計
- JS模組化程式設計JS程式設計
- javascript 模組化程式設計JavaScript程式設計
- linux非阻塞式socket程式設計之select()用法Linux程式設計
- Linux核心模組Linux
- Java 網路程式設計 —— 非阻塞式程式設計Java程式設計
- 【網路程式設計】阻塞IO程式設計的坑程式設計
- [譯] 非同步程式設計:阻塞與非阻塞非同步程式設計
- windows核心程式設計--程式Windows程式設計
- Spring-boot模組化程式設計Springboot程式設計
- 【Linux網路程式設計】位元組序Linux程式設計
- NIO非阻塞程式設計小案例程式設計
- Linux核心模組編譯Linux編譯
- Linux核心模組學習Linux
- Python中常用網路程式設計模組Python程式設計
- 簡述JavaScript模組化程式設計(二)JavaScript程式設計
- QT QML模組的程式設計藝術QT程式設計
- 【轉載】Linux核心除錯之使用模組引數Linux除錯
- windows核心程式設計--核心物件Windows程式設計物件
- Java併發程式設計:阻塞佇列Java程式設計佇列
- Java併發程式設計——阻塞佇列Java程式設計佇列
- C++模組2:物件導向程式設計C++物件程式設計
- 深入學習 Linux 核心模組Linux
- Windows核心程式設計_HookWindows程式設計Hook
- C++核心程式設計C++程式設計
- 高效學習Linux核心——核心模組編譯Linux編譯
- 用函式實現模組化程式設計二函式程式設計
- 用函式實現模組化程式設計三函式程式設計
- 用函式實現模組化程式設計一函式程式設計
- 使用面向 Aspect 的程式設計改進模組性程式設計
- Python程式設計時候,匯入模組失敗Python程式設計
- 《程式設計時間簡史系列》JavaScript 模組化的歷史程式程式設計JavaScript
- 【linux】驅動-2-核心模組Linux
- windows核心程式設計--windows程式的執行Windows程式設計
- 核心動畫程式設計(一)動畫程式設計
- 核心動畫程式設計(二)動畫程式設計
- Windows核心程式設計_磁碟加密Windows程式設計加密