非同步IO(來自部落格園)
結合阻塞與非阻塞訪問、poll 函式可以較好地解決裝置的讀寫,但是如果有了非同步通知就更方便了。非同步通知的意思是:一旦裝置就緒,則主動通知應用程式,這樣應用程式根本就不需要查詢裝置狀態,這一點非常類似於硬體上“中斷”地概念,比較準確的稱謂是:訊號驅動(SIGIO)的非同步 I/O。可以使用signal()函式來設定對應的訊號的處理函式。函式原型是:
void (*signal(int signo,void (*func)(int))) (int)
我們先來看一個使用訊號驅動的例子,通過signal(SIGIO,input_handler) 對開啟的檔案fd 啟動訊號機制,輸入可獲得時inputhandler被呼叫,程式碼如下:
/*async_io_app.c*/
#include <sys/types.h> #include <sys/stat.h> #include <stdio.h> #include <fcntl.h> #include <signal.h> #include <unistd.h> #include <stdlib.h> #define MAX_LEN 100 int fd; void input_handler(int num) { char data[MAX_LEN]; intlen; //讀取並輸出 STDIN_FILENO 上的輸入 len = read(fd, &data, MAX_LEN); data[len] = 0; printf("input available:%s\n", data); } int main() { int oflags; //啟動訊號驅動機制 fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR); if(fd == -1)
{ printf("Device Open Failure !\n"); exit(0); } signal(SIGIO, input_handler); fcntl(fd, F_SETOWN, getpid()); oflags = fcntl(fd, F_GETFL); fcntl(fd, F_SETFL, oflags | FASYNC); //最後進入一個死迴圈,程式什麼都不幹了,只有訊號能激發 input_handler 的執行 //如果程式中沒有這個死迴圈,會立即執行完畢 while (1); return 0; }
下面來解釋一下上面的程式碼。為了一個使用者在使用者空間中能處理一個裝置釋放的訊號,它必須完成一下3份工作:
1)通過F_SETOWN控制指令設定裝置檔案的擁有者為本程序,這樣從裝置驅動中發出的訊號才能被本程序收到。
2)通過F_SETFL 控制命令設定裝置檔案支援FASYNC,即非同步通知模式。
3)通過signal()連結訊號和訊號處理函式。
有了訊號的傳送,那麼就一定得有訊號的釋放了:
在裝置驅動和應用程式的非同步通知互動中,僅僅在應用程式端捕獲訊號是不夠的,因為訊號沒有的源頭是在驅動端,因此要在適當的時機讓裝置驅動釋放訊號。
為了使裝置支援非同步通知機制,驅動程式中涉及三個操作:
1)支援F_SETOWN命令,能在這個控制命令處理中設定filp->f_owner為對應的程序ID。不過此項工作已由核心完成,裝置驅動無須處理。
2)支援F_SETFL命令的處理,每當FASYNC標誌改變時,驅動程式中fasync()函式將得以進行。因此,驅動程式必須實現fasync()函式。
3)在裝置資源可獲得時,呼叫kill_fasync()函式激發相應的訊號。
驅動程式中上面的三步是和應用程式是一一對應的。如下圖:
裝置驅動中非同步通知程式設計還是比較簡單的,主要就是一些資料結構,和兩個函式:
資料結構:fasync_struct結構體
函式:1)處理FASYNC標誌變更的函式int fasync_helper(int fd, struct file *filp, int mode ,struct fasync_struct **fa);
2) 釋放訊號用的函式void kill_fasync(struct fasync_struct **fa, int sig, int band);
和其他裝置驅動一樣,一般將fasync_struct放到裝置結構體中。
下面給出驅動程式部分實現支援非同步IO的程式碼:
/* async_io_driver.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/device.h>
#include <linux/cdev.h>
#include <linux/sched.h>
#include <linux/fcntl.h>
#include <linux/poll.h>
MODULE_LICENSE("GPL");
#define LEN 30
#define init_MUTEX(LOCKNAME) sema_init(LOCKNAME,1)
#define DEVICE_NAME "CDEV_ZHU"
static struct class *cdev_class;
struct asycIO
{
struct cdev dev_c; /*cdev結構體*/
dev_t dev;
char mem[LEN];
int flag ;
struct semaphore sem; /*併發控制用的訊號量*/
wait_queue_head_t r_wait; /*阻塞讀用的等待佇列頭*/
struct fasync_struct *async_queue; /* 非同步結構體指標,用於讀 */
};
struct asycIO asyc_device;
static int asyc_io_fasync(int fd, struct file *filp, int mode)
{
return fasync_helper(fd, filp, mode, &asyc_device.async_queue);
}
/*檔案釋放函式*/
int asyc_io_release(struct inode *inode, struct file *filp)
{
/* 將檔案從非同步通知列表中刪除 */
asyc_io_fasync( - 1, filp, 0);
return 0;
}
/*寫操作*/
static ssize_t asyc_write(struct file *filp, const char __user *buf,size_t count, loff_t *ppos)
{
int ret = count;
printk("In asyc_write! \n");
down(&asyc_device.sem); //獲取訊號量
memset(asyc_device.mem,0,LEN);
if (copy_from_user(asyc_device.mem, buf, count))
{
up(&asyc_device.sem);
return - EFAULT;
}
printk("kernel recieve: %s and the length is %d \n",asyc_device.mem,count);
up(&asyc_device.sem);
asyc_device.flag = 1;
if (asyc_device.async_queue)
kill_fasync(&asyc_device.async_queue, SIGIO, POLL_IN);
wake_up_interruptible(&asyc_device.r_wait);
return ret;
}
static ssize_t asyc_read(struct file *filp, char *buf, size_t len, loff_t *off)
{
int ret = len;
printk("In asyc_read \n");
if (wait_event_interruptible(asyc_device.r_wait, asyc_device.flag != 0))
{
return - ERESTARTSYS;
}
if (down_interruptible(&asyc_device.sem))
{
return - ERESTARTSYS;
}
asyc_device.flag = 0;
if (copy_to_user(buf, asyc_device.mem, len))
{
up(&asyc_device.sem);
return - EFAULT;
}
up(&asyc_device.sem);
return ret;
}
struct file_operations asyc_fops =
{
read: asyc_read,
write: asyc_write,
fasync: asyc_io_fasync,
release: asyc_io_release,
};
static int __init asyc_init(void)
{
int ret,err;
ret = alloc_chrdev_region(&(asyc_device.dev),0,1,DEVICE_NAME) ;
if (ret)
{
printk("globalvar register failure");
}
else
{
cdev_init(&(asyc_device.dev_c),&asyc_fops);
err = cdev_add(&(asyc_device.dev_c),asyc_device.dev,1);
if(err)
{
printk(KERN_NOTICE "error %d adding FC_dev\n",err);
unregister_chrdev_region(asyc_device.dev, 1);
return err;
}
else
{
printk("device register success! \n");
}
cdev_class = class_create(THIS_MODULE,DEVICE_NAME);
if(IS_ERR(cdev_class))
{
printk("ERR:cannot create a cdev_class\n");
unregister_chrdev_region(asyc_device.dev, 1);
return -1;
}
device_create(cdev_class, NULL, asyc_device.dev, 0, DEVICE_NAME);
asyc_device.flag = 0;
init_MUTEX(&(asyc_device.sem));
init_waitqueue_head(&(asyc_device.r_wait));
}
return ret;
}
static void __exit asyc_exit(void)
{
device_destroy(cdev_class,asyc_device.dev);
class_destroy(cdev_class);
unregister_chrdev_region(asyc_device.dev,1);
printk(" device exit! \n");
}
module_init(asyc_init);
module_exit(asyc_exit);
應用程式實現寫入功能:
/* async_io_app_w.c*/
#include <sys/types.h>
#include <sys/stat.h>
#include <stdio.h>
#include <fcntl.h>
#include <string.h>
int main()
{
int fd, num;
char buffer[100] = {0};
fd = open("/dev/CDEV_ZHU", O_RDWR, S_IRUSR | S_IWUSR);
printf("open /dev/CDEV_ZHU fd = %d \n",fd);
if (fd != -1)
{
while (1)
{
memset(buffer,0,sizeof(buffer));
printf("Please input the buffer:\n");
scanf("%s", buffer);
if (buffer[0] == '0') //如果輸入 0,退出
{
close(fd);
break;
}
write(fd, buffer, strlen(buffer));
printf("We have written: %s\n",buffer);
}
}
else
{
printf("device open failure\n");
}
return 0;
}
將上面的“async_io_app.c”、“async_io_driver.c”、“async_io_app_w.c”進行編譯,載入驅動之後,開兩個終端,分別執行async_io_app 和 async_io_app_w,當async_io_app_w有資料寫入的時候,async_io_app的終端會列印所寫入的資料,當然核心也會列印資料,下面是結果:
說明:上面圖是三個終端的列印結果,從左到右一次是async_io_app_w , async_io_app 和使用dmesg 列印核心的結果。
注:我本來也想用程式碼格式,但是感覺在vim上排版很舒服的,上來用程式碼格式反而還不好看了,於是就這樣了
相關推薦
非同步IO(來自部落格園)
結合阻塞與非阻塞訪問、poll 函式可以較好地解決裝置的讀寫,但是如果有了非同步通知就更方便了。非同步通知的意思是:一旦裝置就緒,則主動通知應用程式,這樣應用程式根本就不需要查詢裝置狀態,這一點非常類似於硬體上“中斷”地概念,比較準確的稱謂是:訊號驅動(SIGIO)的非同
BBS+ BLOG系統(仿部落格園)
一、基本要求 作業題目:開發BBS+BLOG系統 作業需求: 1 基於ajax和使用者認證元件實現登入驗證 2 基於ajax和form元件實現註冊功能 3 系統首頁文章列表的渲染 4 個人站點頁面設計5 文章詳細頁的繼承6 點贊與踩滅7 評論功能8 富文字編輯器的使用9 防止xss攻擊
jquery學習入門到高階(轉載部落格園)
什麼是jQuery jQuery是一套Javascript指令碼庫. 在我的部落格中可以找到"Javascript輕量級指令碼庫"系列文章. Javascript指令碼庫類似於.NET的類庫, 我們將一些工具方法或物件方法封裝在類庫中, 方便使用
Spring 在多執行緒中,bean的注入問題(轉部落格園)
最近碰到了一個問題,使用SSM框架,在Service層需要另開一個執行緒,這個執行緒專門用來做一些操作,並將結果寫入資料庫中。但是線上程中使用@Resource或者@Autowired注入全部為NULL,原來是Spring不能線上程中注入。網上的主要解決方法有:將需要的Bean作為執行緒的的建構函式的引數傳入
從CSDN(部落格園)搬家到wordpress
最近自己搞了個域名,想把自己在CSDN上面的部落格全部轉到wordpress上面(雖然說自己在CSDN上寫部落格的時間並不是很長,呵呵)。 第一步 CSDN是沒有匯出功能的,所以我先在部落格園註冊了一個賬號,然後把自己在CSDN上面的部落格全部轉到了部落格園上面。
線路人生EP2C5/8啟蒙板檢測(舊部落格遷移)
閒魚是個好東西,兩套CycloneⅡ的開發板只要40,關鍵還是包郵。雖然說產品升級了N代了,不過這個價格就算拆件也值了。板子框圖、實拍圖如下:
線路人生EP2C5/8啟蒙板檢測-續 (舊部落格遷移)
果然驗證了我的猜測,加上自制的244緩衝驅動隔離小板後,山寨下載器又恢復了神勇,立馬檢測到FPGA晶片了。
Lesson02:八位LED發光管操作(舊部落格遷移)
LED發光二極體是最常用的輸出指示裝置,具有操作簡單、價格低廉的優點。相應的電路原理圖如圖2.1所示:
Lesson01:W-A_51微控制器實訓板介紹(舊部落格遷移)
一、開發板介紹 微控制器實訓板是以Atmel公司的AT89S52微控制器為核心的實驗板,主要資源包括以下部分: 1、輸入單元——四個獨立按鍵/十六個矩陣按鍵(通過短路帽跳線選擇),一個PS2鍵盤/滑鼠介面 2、輸出單元——八位LED發光管/四位動態掃描數碼管/1602液晶(
TFT液晶研究(舊部落格遷移)
TFT液晶研究 淘寶上買了幾款TFT裸片,帶8080介面,價效比很高。原先專案上用到的4.3寸TFT要100多,而且手上還有一些STM32的開發板,就想給這些板子配上液晶模組。 一、TFT液晶屏引數
(python部落格開發)window7上Error loading MySQLdb module: No module named MySQLdb解決辦法
在windows環境中(win7),建立虛擬環境,django建好模型準備遷移資料,執行命令:,出現如下錯誤: (virtualenv) D:\workspace\blog_project>python manage.py makemigrations Traceba
完成埠(Completion Port)詳解 轉載來自部落格園----- By PiggyXP(小豬)
完成埠(Completion Port)詳解 ----- By PiggyXP(小豬) 前 言
部落格搬家系列(三)-爬取部落格園部落格
部落格搬家系列(三)-爬取部落格園部落格 一.前情回顧 部落格搬家系列(一)-簡介:https://blog.csdn.net/rico_zhou/article/details/83619152 部落格搬家系列(二)-爬取CSDN部落格:https://bl
如何更改自己部落格(部落格園的)的背景
首先 , 先開啟部落格園的首頁 -> 我的部落格-> 管理-> 設定 -> 找到頁面定製CSS程式碼然後貼上下文程式碼 /* 程式碼高亮開始,使用了一個叫Monokai Sublime的黑色主題面板,直接拿過來還不行,有一些樣式衝突,還要自己稍微改一些地方 Mon
python3爬蟲例子02(獲取個人部落格園的文章資訊)
#!/usr/bin/env python# -*- coding:UTF-8 -*-import requestsfrom bs4 import BeautifulSoupres=requests.get("https://www.cnblogs.com/NiceTime/")# c=res.content
比較全面的Eclipse配置詳解(包括智慧提示設定、智慧提示外掛修改,修改空格自動上屏、JDK配置、各種快捷鍵列表……) - decarl - 部落格園
Eclipse編輯器基本設定 1、新增行號 在邊緣處右鍵 2、改字型 字型的一般配置 3、去掉拼寫錯誤檢查 4、Java程式碼風格 程式碼格式化 Ctrl + Shift + F 之後點選右邊的New按鈕,新建一個風格。
Oracle database和client 安裝教程(轉自部落格園,用於備忘)
** database ** 1.先到Oracle官網上下載11g oracle Database 11g 第 2 版 (11.2.0.1.0) 標準版、標準版 1 以及企業版 適用於 Microsoft Windows (x64) 的 Oracle Datab
從csdn搬家到部落格園(無盡的藍黃)
用csdn寫部落格已經有兩年多了,可以說csdn記錄了我作為IOer兩年來的經歷。 但是用久了也覺得csdn似乎有些差強人意, 1、介面太單調了,也不能改改背景之類的,為了給讀者更好的觀博體驗,我覺
關於多執行緒的幾點總結(部落格園遷移)
關於執行緒 synchronized關鍵字: 不能用在變數和建構函式上 放在方法上面,鎖定的是物件,放在靜態方法上鎖定的是類 不應該鎖定常量,比如String等型別因為程式中這個物件難免還會用
學習部落格園開原始碼筆記(一)
index.android.js或者index.ios.js是專案的開始介面, 很簡單的一個介面,註冊了一個控制元件,需要注意的是圖中紅框的部分from後面到資料夾,而不是具體的檔案,這種情況下預設是source/index.js 檔案。 接下來我們