c-linux高級編程_第1頁
c-linux高級編程_第2頁
c-linux高級編程_第3頁
c-linux高級編程_第4頁
c-linux高級編程_第5頁
已閱讀5頁,還剩12頁未讀, 繼續免費閱讀

下載本文檔

版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領

文檔簡介

1、C/linux高級編程設計報告 題目: 字符驅動設備設計 學生姓名: 馮永強 學 號: 123821003 專 業: 計算機科學與技術 2014年5月25日 字符驅動設備設計1、 課程設計目的 Linux 系統的開源性使其在嵌入式系統的開發中得到了越來越廣泛的應用,但其本身并沒有對種類繁多的硬件設備都提供現成的驅動程序,特別是由于工程應用中的靈活性,其驅動程序更是難以統一,這時就需開發一套適合于自己產品的設備驅動。對用戶而言,設備驅動程序隱藏了設備的具體細節,對各種不同設備提供了一致的接口,一般來說是把設備映射為一個特殊的設備文件,用戶程序可以像對其它文件一樣對此設備文件進行操作。通過這次課程

2、設計可以了解linux的模塊機制,懂得如何加載模塊和卸載模塊,進一步熟悉模塊的相關操作。加深對驅動程序定義和設計的了解,了解linux驅動的編寫過程,提高自己的動手能力。2、 課程設計內容與要求(1) 設計Windows XP或者Linux操作系統下的設備驅動程序;(2) 設備類型可以是字符設備、塊設備或者網絡設備;(3) 設備可以是虛擬的也可以是實際設備;3、 系統分析與設計 (1) 系統分析 系統調用是操作系統內核和應用程序之間的接口,設備驅動程序是操作系統內核和機器硬件之間的接口。設備驅動程序為應用程序屏蔽了硬件的細節,這樣在應用程序看來,硬件設備只是一個設備文件,應用程序可以象操作普通

3、文件一樣對硬件設備進行操作。設備驅動程序是內核的一部分,它完成以下的功能:1、對設備初始化和釋放;2、把數據從內核傳送到硬件和從硬件讀取數據;3、讀取應用程序傳送給設備文件的數據和回送應用程序請求的數據;4、檢測和處理設備出現的錯誤。Linux下的設備驅動程序被組織為一組完成不同任務的函數的集合,通過這些函數使得Windows的設備操作猶如文件一般。在應用程序看來,硬件設備只是一個設備文件,應用程序可以象操作普通文件一樣對硬件設備進行操作,如open ()、close ()、read ()、write () 等。Linux主要將設備分為二類:字符設備和塊設備。字符設備是指設備發送和接收數據以字

4、符的形式進行;而塊設備則以整個數據緩沖區的形式進行。字符設備提供給應用程序的是一個流控制接口,主要包括open、close(或release)、read、write、ioctl、poll和mmap等。在系統中添加一個字符設備驅動程序,實際上就是給上述操作添加對應的代碼。對于字符設備和塊設備,Linux內核對這些操作進行了統一的抽象,把它們定義在結構體file_operations中。在驅動程序中,當多個線程同時訪問相同的資源時(驅動程序中的全局變量是一種典型的共享資源),可能會引發"競態",因此我們必須對共享資源進行并發控制。Linux內核中解決并發控制的最常用方法是自旋鎖

5、與信號量(絕大多數時候作為互斥鎖使用)。自旋鎖與信號量"類似而不類",類似說的是它們功能上的相似性,"不類"指代它們在本質和實現機理上完全不一樣,不屬于一類。自旋鎖不會引起調用者睡眠,如果自旋鎖已經被別的執行單元保持,調用者就一直循環查看是否該自旋鎖的保持者已經釋放了鎖,"自旋"就是"在原地打轉"。而信號量則引起調用者睡眠,它把進程從運行隊列上拖出去,除非獲得鎖。這就是它們的"不類"。但是,無論是互斥信號量,還是自旋鎖,在任何時刻,最多只能有一個保持者,即在任何時刻最多只能有一個執行單元獲得鎖。

6、這就是它們的"類似"。鑒于自旋鎖與信號量的上述特點,一般而言,自旋鎖適合于保持時間非常短的情況,它可以在任何上下文使用;信號量適合于保持時間較長的情況,只能在進程上下文使用。如果被保護的共享資源只在進程上下文訪問,則可以以信號量來保護該共享資源,如果對共享資源的訪問時間非常短,自旋鎖也是好的選擇。但是,如果被保護的共享資源需要在中斷上下文訪問(包括底半部即中斷處理句柄和頂半部即軟中斷),就必須使用自旋鎖。阻塞操作是指,在執行設備操作時,若不能獲得資源,則進程掛起直到滿足可操作的條件再進行操作。非阻塞操作的進程在不能進行設備操作時,并不掛起。被掛起的進程進入sleep狀態,被

7、從調度器的運行隊列移走,直到等待的條件被滿足。在Linux驅動程序中,我們可以使用等待隊列(wait queue)來實現阻塞操作。wait queue很早就作為一個基本的功能單位出現在Linux內核里了,它以隊列為基礎數據結構,與進程調度機制緊密結合,能夠用于實現核心的異步事件通知機制。等待隊列可以用來同步對系統資源的訪問,上節中所講述Linux信號量在內核中也是由等待隊列來實現的。結合阻塞與非阻塞訪問、poll函數可以較好地解決設備的讀寫,但是如果有了異步通知就更方便了。異步通知的意思是:一旦設備就緒,則主動通知應用程序,這樣應用程序根本就不需要查詢設備狀態,這一點非常類似于硬件上"

8、;中斷"地概念,比較準確的稱謂是"信號驅動(SIGIO)的異步I/O"。在本課程設計中主要實現一個簡單的字符驅動設備,運用信號量和自旋鎖進行相關的并發控制。(2) 系統設計 2.1 測試環境: 系統: linuxmint 15 kernel version : 3.8.0-23-generic gcc version : 4.7.3 2.2 模塊設計: 打開設備 讀操作 寫操作釋放設備 退出設備字符設備驅動2.3 數據結構說明:字符設備驅動主要應用了三種數據結構:file_operations結構,這是設備驅動程序所提供的一組用一個結構向系統進行說明的入口點;fi

9、le結構,主要用于與文件系統對應的設備驅動程序。代表一個打開的文件,它由內核在open時創建,并傳遞給在該文件上進行操作的所有函數,直到碰到最后的close函數。在文件的所有實例都被關閉之后,內核會釋放這個數據結構; inode結構,提供了關于特殊設備文件/dev/mydev的信息。使用cat /proc/kmsg &來監測Kernel中的輸出信息,即程序中的printk的輸出信息。同時通過#include<errno.h>來引入相關的錯誤定義,在對應錯誤發生的時候向內核輸出出錯信息。各個結構的定義如下:(1)file_operations結構: static const

10、struct file_operations my_fops = .owner = THIS_MODULE, .llseek = my_llseek, .read = my_read, .write = my_write, .open = my_open, .release = my_release, .unlocked_ioctl = ioctl, ;(2)file結構: 1)讀 static ssize_t my_read(struct file *filp, char _user *buf, size_t size, loff_t *ppos) 2)寫 static ssize_t my

11、_write(struct file *filp, const char _user *buf, size_t size, loff_t *ppos) 3)seek文件定位 static loff_t my_llseek(struct file *filp, loff_t offset, int whence) 4)IO控制 static int ioctl (struct file *file, unsigned int cmd, unsigned long arg)(3)inode結構: 1) 打開 int my_open(struct inode *inode, struct file

12、*filp) 2) 釋放 int my_release(struct inode *inode, struct file *filp)(4) 信號量定義:1) 定義信號量 static struct semaphore sem;2) 初始化信號量為1 sema_init ( &sem, 1 );3) 獲取信號量 up( &sem )4) 釋放信號量 down( &sem )(5) 自旋鎖定義:1) 獲得自旋鎖 spin_lock ( &spin );2) 釋放自旋鎖 spin_unlock ( &spin );(6) 模塊初始化和退出1) 模塊初始化 m

13、odule_init(mydev_init);2) 模塊退出 module_exit(mydev_exit);2.4 算法流程圖如下:結束 文件釋放函數 mydev_release 設備驅動模塊 卸載函數mydev_exit()開始設備驅動模塊加載函數ly_init()文件打開函數ly_open() 信號量 獲取 自旋鎖 獲取讀函數 mydev_read 寫函數mydev_write4、 系統調試與分析4.1 使用su用戶 4.2 對源程序進行編譯4.3 打開后臺內核輸出監控4.4 加載驅動程序并查看4.5 顯示主設4.6 創建節點并查看4.7 編譯測試程序4.8 運行測試程序4.9 打開設備

14、4.11 啟動另一個終端,打開設備4.10 讀設備(創建設備的時候已經在緩存區設置數據)4.11 寫入數據4.12 繼續讀數據(MAX_BUFF是20)4.13 釋放設備4.14 退出測試程序5、 程序清單mydev.c :#include <linux/init.h>#include <linux/module.h>#include <linux/kernel.h>#include <linux/fs.h>#include <linux/cdev.h>#include <asm/uaccess.h>#include &l

15、t;linux/semaphore.h>#define DEFAULT_MSG "Hello,Welcome to OS course design" /*默認字符設備數據*/#define DEVICE_NAME "mydev" /*設備名*/#define MAXBUF 100 /*設備數據緩沖區大小*/static unsigned char mydev_bufMAXBUF; /*設備內存數據緩沖區*/static struct semaphore sem; /* 定義互斥信號量 */static int globalvar_count =

16、0; /* 定義設備計數 */static spinlock_t spin = SPIN_LOCK_UNLOCKED; #SPIN_LOCK_UNLOCKED has been deprecated since 2.6.19static DEFINE_SPINLOCK ( spin ); /* 定義自旋鎖 */*定義讀寫釋放打開*/static int mydev_open ( struct inode *inode, struct file *file );static int mydev_release ( struct inode *inode, struct file *file );

17、static ssize_t mydev_read ( struct file *file, char _user *buf, size_t count, loff_t *pos );static ssize_t mydev_write ( struct file *file, const char _user *buf, size_t count, loff_t *pos );static int mydev_open ( struct inode *inode, struct file *file ) /獲得自選鎖 spin_lock ( &spin ); /臨界資源訪問 if (

18、 globalvar_count ) spin_unlock ( &spin ); return -EBUSY; globalvar_count+; /釋放自選鎖 spin_unlock ( &spin ); return 0;static int mydev_release ( struct inode *inode, struct file *file ) globalvar_count-; printk ( "設備資源釋放!n" ); return 0;static ssize_t mydev_read ( struct file *file, cha

19、r _user *buf, size_t count, loff_t *pos ) /*從設備讀取count個數據到用戶數據區buf中*/ int size = count < MAXBUF ? count : MAXBUF; /*檢測讀取的數據大小count是否比設備數據緩沖區大,如何大則截取MAXBUF的大小*/ printk ( "mydev: This is my device!n" ); /*把設備內存mydev_buf中的數據拷貝到用戶空間buf中,數量為size*/ if ( copy_to_user ( buf, mydev_buf, size ) )

20、 up ( &sem ); return -ENOMEM; /*內存不足錯誤*/ up ( &sem ); return size;static ssize_t mydev_write ( struct file *filp, const char _user *buf, size_t count, loff_t *pos ) /*把buf中count個數據寫入設備內存空間中*/ int size = count < MAXBUF ? count : MAXBUF; /*檢測寫入的數據大小count是否比設備數據緩沖區大*/ /獲得信號量 if ( down_interr

21、uptible ( &sem ) ) return - ERESTARTSYS; printk ( "mydev:This is my device!n" ); memset ( mydev_buf, 0, sizeof ( mydev_buf ) ); /*將設備內存清空*/ /*把buf中的用戶數據寫入到設備內存mydev_buf中,數量為size*/ if ( copy_from_user ( mydev_buf, buf, size ) ) up ( &sem ); return -ENOMEM; up ( &sem ); return si

22、ze;static struct file_operations mydev_fops = .read = mydev_read, .write = mydev_write, .open = mydev_open, .release = mydev_release,;static struct cdev *mydev_cdev; /*新設備指針*/static int _init mydev_init ( void ) /*模塊初始化*/ dev_t dev; /*設備號*/ int error; error = alloc_chrdev_region ( &dev, 0, 2, DE

23、VICE_NAME ); /*動態分配一個設備號*/ if ( error ) /*返回值不為0表示分配失敗*/ printk ( "動態分配設備號失??!n" ); return error; mydev_cdev = cdev_alloc(); /*新分配一個字符設備對象*/ if ( mydev_cdev = NULL ) printk ( "動態分配字符設備對象失??!n" ); unregister_chrdev_region ( dev, 2 ); /*注銷一個分配的設備號區域*/ return -ENOMEM; mydev_cdev->o

24、ps = &mydev_fops; /*設定字符設備操作函數指針*/ mydev_cdev->owner = THIS_MODULE; /*設備的屬主*/ error = cdev_add ( mydev_cdev, dev, 1 ); /*將設備添加到內核中去*/ if ( error ) printk ( "設備添加失??!n" ); unregister_chrdev_region ( dev, 2 ); /*注銷一個分配的設備號區域*/ cdev_del ( mydev_cdev ); /*刪除字符設備對象*/ return error; memset

25、( mydev_buf, 0, sizeof ( mydev_buf ) ); /*清空設備緩沖區數據*/ memcpy ( mydev_buf, DEFAULT_MSG, sizeof ( DEFAULT_MSG ) ); /*設定設備緩沖區默認數據*/ printk ( "設備添加成功,設備緩沖區默認數據: Hello,Welcome to OS course design!n" ); sema_init ( &sem, 1 ); return 0;static void _exit mydev_exit ( void ) /*模塊卸載*/ unregister

26、_chrdev_region ( mydev_cdev->dev, 2 ); cdev_del ( mydev_cdev ); printk ( "設備刪除成功!n" );module_init ( mydev_init );module_exit ( mydev_exit );MODULE_LICENSE ( "GPL" );test.c:#include <stdio.h>#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>#d

27、efine MAXBUF 20int main() int testdev; int i; int len; int t; char sel; int flag; char bufMAXBUF, tmpMAXBUF; printf ( "1、打開設備n2、寫操作n3、讀操作n4、釋放設備n5、退出n" ); while ( 1 ) printf ( "請輸入要執行的操作:" ); sel = getchar(); getchar(); switch ( sel ) case '1': testdev = open ( "/dev

28、/mydev", O_RDWR ); if ( testdev < 0 ) printf ( "設備打開失敗 n" ); break; flag = 0; printf ( "設備打開成功!n" ); break; case '2': if ( flag ) printf ( "請先打開設備!n" ); continue; printf ( "請輸入要寫入的字符串:" ); gets ( tmp ); len = sizeof ( tmp ); /strlen(tmp); t = w

29、rite ( testdev, tmp, len ); if ( t < 0 ) perror ( "寫操作失?。" ); exit ( -1 ); printf ( "字符串:%s 寫入成功!n", tmp ); break; case '3': if ( flag ) printf ( "請先打開設備!n" ); continue; lseek ( testdev, 0, SEEK_SET ); t = read ( testdev, buf, MAXBUF ); if ( t < 0 ) perro

30、r ( "讀操作失??!n" ); exit ( -1 ); printf ( "讀操作成功!結果為:%sn", buf ); break; case '4': if ( flag ) printf ( "請先打開設備!n" ); break; /release(testdev); close ( testdev ); printf ( "設備釋放成功!n" ); flag = 1; break; case '5': close ( testdev ); exit ( 0 ); def

31、ault: printf ( "輸入有誤!n" ); break; makefile:DEBFLAGS = -O2EXTRA_CFLAGS += $(DEBFLAGS)# CFLAGS += -I$(LDDINC)ifneq ($(KERNELRELEASE),)# call from kernel build systemxbrige-objs := mydev.o obj-m:= mydev.oelseKERNELDIR ?= /lib/modules/$(shell uname -r)/buildPWD := $(shell pwd)modules:$(MAKE) -

32、C $(KERNELDIR) M=$(PWD) modules#$(MAKE) -C $(KERNELDIR) M=$(PWD) LDDINC=$(PWD)/./include modulesendifclean:rm -rf *.o * core .depend .*.cmd *.ko *.mod.c .tmp_versionsdepend .depend dep:$(CC) $(CFLAGS) $(EXTRA_CFLAGS) -M *.c > .dependifeq (.depend,$(wildcard .depend)include .dependendif6、 課設總結在這次課程設計之前就一直在使用linux系統,在筆記本電腦上使用的是linuxmint15,所以在進行本次課程設計的時候選題為第二個會比較容易搭建環境。但是使用linux也主要是在應用層面進行一些編程和娛樂活動,并沒有涉及到驅動或者內核層次的hack,所以在本次課程設計中選擇驅動開發也可以鍛煉我相關的能力驅動相較于Linux系統是更加熟悉的一個名詞,每次重裝系統都要安裝各種各樣的驅動,不然

溫馨提示

  • 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
  • 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯系上傳者。文件的所有權益歸上傳用戶所有。
  • 3. 本站RAR壓縮包中若帶圖紙,網頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
  • 4. 未經權益所有人同意不得將文件中的內容挪作商業或盈利用途。
  • 5. 人人文庫網僅提供信息存儲空間,僅對用戶上傳內容的表現方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
  • 6. 下載文件中如有侵權或不適當內容,請與我們聯系,我們立即糾正。
  • 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。

評論

0/150

提交評論