版權說明:本文檔由用戶提供并上傳,收益歸屬內容提供方,若內容存在侵權,請進行舉報或認領
文檔簡介
1324第3章字符設備驅動程序scull的設計主設備號與次設備號一些重要的數(shù)據(jù)結構字符設備的注冊open和releasescull的內存使用read和write567scull的設計scull0~scull3由全局且持久的內存區(qū)組成,可被多次打開,關閉后再打開,仍能保持原數(shù)據(jù)scullpipe0toscullpipe3FIFO(先進先出)設備,類似管道,可由一個進程讀,另一個進程寫Scullsingle、scullpriv、sculluid、scullwuid與scull0類似,但在open操作方面有些限制1324第3章字符設備驅動程序scull的設計主設備號與次設備號一些重要的數(shù)據(jù)結構字符設備的注冊open和releasescull的內存使用read和write567主設備號與次設備號$ls–l/devcrw-rw-rw-1rootroot1,3Apr112002nullcrw1rootroot10,1Apr112002psauxcrw1rootroot4,1Oct2803:04tty1crw-rw-rw-1roottty4,64Apr112002ttys0crw-rw1rootuucp4,65Apr112002ttyS1crw--w1vcsatty7,1Apr112002vcs1crw--w1vcsatty7,129Apr112002vcsa1crw-rw-rw-1rootroot1,5Apr112002zero
主設備號通常用于標識設備對應的驅動程序,號相同的設備共用一個驅動程序;次設備號用于標識驅動程序所服務的具體設備主設備號次設備號主設備號與次設備號設備編號的內部表達內核中,設備編號數(shù)據(jù)類型為dev_t,定義在<linux/types.h>中操作設備編號的宏MAJOR(dev_tdev);由dev_t數(shù)得到主設備號MINOR(dev_tdev);由dev_t數(shù)得到次設備號MKDEV(intmajor,intminor);由主、次設備號得到dev_t數(shù)主設備號次設備號12位20位dev_t主設備號與次設備號靜態(tài)分配設備編號intregister_chrdev_region(dev_tfirst,unsignedintcount,char*name);first:主設備號范圍起始值,次設備號通常為0count:連續(xù)設備號的個數(shù)name:設備編號范圍關聯(lián)的設備名稱,將出現(xiàn)在/proc/devices和/sysfs中成功返回0,失敗返回負數(shù)靜態(tài)分配需要預先知道可使用的設備號,如何知道?內核源代碼樹的Documenttation/devices.text可查到尚有哪些號可用主設備號與次設備號動態(tài)分配設備編號intalloc_chrdev_region(dev_t*dev,unsignedintfirstminor,unsignedintcount,char*name);dev:調用成功后保存分配到的設備編號范圍的第一個數(shù)firstminor:第一個次設備號,通常用是0count與name與靜態(tài)分配時相同釋放設備編號voidunregister_chrdev_region(dev_tfirst,unsignedintcount);
主設備號與次設備號scull設備號分配if(scull_major){dev=MKDEV(scull_major,scull_minor);result=register_chrdev_region(dev,scull_nr_devs,"scull");}else{result=alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull");scull_major=MAJOR(dev);}
if(result<0){printk(KERN_WARNING"scull:can'tgetmajor%d\n",scull_major);returnresult;}scull_major為全局變量,初始化值為SCULL_MAJOR,定義在scull.h中,設為0時為動態(tài)分配(默認),否則為靜態(tài)分配。主設備號與次設備號scull_load靜態(tài)分配的問題是:若驅動程序僅自己使用,選擇一個未用的號來用沒什么問題,但若驅動程序被廣泛使用,則可能造成沖突動態(tài)分配的問題是:由于分配的主設備號不能始終一至,所以無法預先創(chuàng)建設備節(jié)點一旦分配了設備號,就可從/proc/devices中讀到,因此可寫一腳本代替insmod,在載入模塊后讀出設備號并創(chuàng)建節(jié)點。即用一腳本完成加載模塊、讀出主設備號及創(chuàng)建設備節(jié)點等操作。scull設備的這一腳本叫scull_load主設備號與次設備號/proc/devices文件如下:Characterdevices: 1mem 2pty 3ttyp 4ttyS 6lp 7vcs 10misc 13input 14sound 21sg 180usbBlockdevices:2fd8sd11sr65sd66sd
主設備號與次設備號/*Scull_load*/#!/bin/shmodule="scull"device="scull"mode="664"/sbin/insmod./$module.ko$*||exit1rm-f/dev/${device}[0-3]major=$(awk"\$2==\"$module\"{print\$1}"/proc/devices)mknod/dev/${device}0c$major0mknod/dev/${device}1c$major1mknod/dev/${device}2c$major2mknod/dev/${device}3c$major3group="staff“grep-q'^staff:'/etc/group||group="wheel“chgrp$group/dev/${device}[0-3]chmod$mode/dev/${device}[0-3]#awk‘條件類型1{動作1}條件類型2{動作2}…’filename#grep[參數(shù)]‘搜索字符串’filename主設備號與次設備號也可編寫一個init腳本放在/etc/init.d下,可在系統(tǒng)初始化時加載模塊、并根據(jù)主設備號創(chuàng)建節(jié)點若只涉及單個驅動,因動態(tài)設備號的分配并不是真正隨機生成的,故只需在第一次加載時創(chuàng)建節(jié)點,以后加載時無需再創(chuàng)節(jié)點1324第3章字符設備驅動程序scull的設計主設備號與次設備號一些重要的數(shù)據(jù)結構字符設備的注冊open和releasescull的內存使用read和write567一些重要的數(shù)據(jù)結構大部分的驅動程序操作涉及到3個重要的內核數(shù)據(jù)結構,分別是:file_operations:文件操作結構,保存文件操作方法file:文件結構,對應一個打開的文件inode:節(jié)點結構,對應一個文件在編寫真正的驅動程序前,需要對這3個結構有一個基本的認識。一些重要的數(shù)據(jù)結構file_operations,在<linux/fs.h>中定義structfile_operations{ structmodule*owner; loff_t(*llseek)(structfile*,loff_t,int); ssize_t(*read)(structfile*,char__user*,size_t,loff_t*); ssize_t(*aio_read)(structkiocb*,char__user*,size_t,loff_t); ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*); ssize_t(*aio_write)(structkiocb*,constchar__user*,size_t,loff_t*); int(*readdir)(structfile*,void*,filldir_t); unsignedint(*poll)(structfile*,structpoll_table_struct*); int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong); int(*mmap)(structfile*,structvm_area_struct*);一些重要的數(shù)據(jù)結構 int(*open)(structinode*,structfile*); int(*flush)(structfile*); int(*release)(structinode*,structfile*); int(*fsync)(structfile*,structdentry*,int); int(*aio_fsync)(structkiocb*,int); int(*fasync)(int,structfile*,int); int(*lock)(structfile*,int,structfile_lock*); ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*); ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*); ssize_t(*sendfile)(structfile*,loff_t*,size_t,read_actor_t,void*); ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int); unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong); int(*check_flags)(int); int(*dir_notify)(structfile*,unsignedlong);};一些重要的數(shù)據(jù)結構structmodule*owner第一個file_operations成員不是一個操作,是一個指向擁有這個結構的模塊的指針。這個成員用來在它的操作還在被使用時阻止模塊被卸載。幾乎所有時間中,它被初始化為THIS_MODULE,一個在<linux/module.h>中定義的宏。loff_t(*llseek)(structfile*,loff_t,int);llseek方法用于改變文件中的當前讀/寫位置,并且將新位置作為(正的)返回值。loff_t參數(shù)是一個“l(fā)ongoffset”,并且就算在32位平臺上也至少有64位寬。出錯時返回一個負的返回值。如果這個函數(shù)指針是NULL,seek調用會以無法預測的方式修改file結構中的位置計數(shù)器(在"file結構"一節(jié)中描述)。一些重要的數(shù)據(jù)結構ssize_t(*read)(structfile*,char__user*,size_t,loff_t*);用來從設備中獲取數(shù)據(jù)。非負返回值代表成功讀取的字節(jié)數(shù)(返回值是一個“signedsize”類型,常常是目標平臺本地的整數(shù)類型)。該函數(shù)指針為NULL時將導致調用失敗并返回-EINVAL(“Invalidargument”)ssize_t(*aio_read)(structkiocb*,char__user*,size_t,loff_t);初始化一個異步讀--在函數(shù)返回前可能不會結束的讀操作。如果這個方法是NULL,所有的讀操作會由read代替進行(同步地)。一些重要的數(shù)據(jù)結構ssize_t(*write)(structfile*,constchar__user*,size_t,loff_t*);發(fā)送數(shù)據(jù)給設備。非負返回值代表成功發(fā)送的字節(jié)數(shù).ssize_t(*aio_write)(structkiocb*,constchar__user*,size_t,loff_t*);設備上的一個異步寫入方法。int(*readdir)(structfile*,void*,filldir_t);對于設備文件,這個成員應當為NULL;它用來讀取目錄,僅對文件系統(tǒng)有用一些重要的數(shù)據(jù)結構unsignedint(*poll)(structfile*,structpoll_table_struct*);poll方法是poll,epoll和select3個系統(tǒng)調用的后端實現(xiàn),用來查詢對一個或多個文件描述符的讀或寫是否會被阻塞。poll應當返回一個位掩碼,用于指示非阻塞的讀或寫是否可能,并且也會向內核提供將調用進程置于睡眠狀態(tài)直到I/O變?yōu)榭赡軙r的信息。如果一個驅動的poll方法為NULL,則設備會被認為可讀可寫,且不會被阻塞。int(*ioctl)(structinode*,structfile*,unsignedint,unsignedlong);ioctl方法提供了操作設備的非讀寫方法(如格式化軟盤)。另外,內核還能識別一些ioctl命令而不必調用fops表的ioctl。如果設備不提供ioctl方法,對于內核未預先定義的請求,ioctl系統(tǒng)調用將返回錯誤(-ENOTTY,即設備無這樣的ioctl)一些重要的數(shù)據(jù)結構int(*mmap)(structfile*,structvm_area_struct*);mmap用來請求將設備內存映射到進程的地址空間。如果這個方法是NULL,mmap系統(tǒng)調用返回-ENODEV。int(*open)(structinode*,structfile*);通常是對設備文件進行的第一個操作,但并不要求驅動一定要聲明一個對應的方法。如果這個項是NULL,設備的打開永遠成功,但系統(tǒng)不會通知驅動程序int(*release)(structinode*,structfile*);在file結構被釋放時調用這個操作。如同open,release可以為NULL一些重要的數(shù)據(jù)結構int(*fsync)(structfile*,structdentry*,int);這個方法是fsync系統(tǒng)調用的后端實現(xiàn),用戶調用它來刷新任何待處理的數(shù)據(jù)。如果為NULL,fsync系統(tǒng)調用返回-EINVAL。int(*aio_fsync)(structkiocb*,int);這是fsync方法的異步版本int(*fasync)(int,structfile*,int);這個操作用來通知設備其FASYNC標志發(fā)生了改變。異步通知是一個高級的主題,在第6章中描述。如果驅動不支持異步通知,這個成員可以是NULL。一些重要的數(shù)據(jù)結構int(*lock)(structfile*,int,structfile_lock*);lock方法用來實現(xiàn)文件加鎖;加鎖對常規(guī)文件是必不可少的特性,但是設備驅動幾乎從不實現(xiàn)它。ssize_t(*readv)(structfile*,conststructiovec*,unsignedlong,loff_t*);ssize_t(*writev)(structfile*,conststructiovec*,unsignedlong,loff_t*);這些方法用來實現(xiàn)分散/匯聚型的讀寫操作。應用程序有時需要進行涉及多個內存區(qū)的單個讀或寫操作,這些系統(tǒng)調用允許它們這樣做而不必額外增加數(shù)據(jù)拷貝操作。如果這些函數(shù)指針為NULL,就會調用read和write方法(可能多于一次)。一些重要的數(shù)據(jù)結構ssize_t(*sendfile)(structfile*,loff_t*,size_t,read_actor_t,void*);這個方法實現(xiàn)sendfile系統(tǒng)調用的讀。其用最少的拷貝操作從一個文件描述符搬移數(shù)據(jù)到另一個。例如,web服務器可以利用這個方法將某一個文件的內容發(fā)送到網(wǎng)絡連接。設備驅動常常使sendfile為NULL。ssize_t(*sendpage)(structfile*,structpage*,int,size_t,loff_t*,int);sendpage是sendfile的另一半;它由內核調用來發(fā)送數(shù)據(jù)到對應的文件,一次一頁。設備驅動實際上不實現(xiàn)sendpage。一些重要的數(shù)據(jù)結構unsignedlong(*get_unmapped_area)(structfile*,unsignedlong,unsignedlong,unsignedlong,unsignedlong);這個方法的目的是在進程的地址空間找一個合適的位置,以便將底層設備中的內存段映射到該位置。該任務通常由內存管理代碼完成,但該方法的存在可允許驅動程序強制滿足特定設備需要的任何對齊需求。大部分驅動可以置這個方法為NULL。int(*check_flags)(int)這個方法允許模塊檢查傳遞給fnctl(F_SETFL...)調用的標志一些重要的數(shù)據(jù)結構int(*dir_notify)(structfile*,unsignedlong);這個方法在應用程序使用fcntl來請求目錄改變通知時調用。只對文件系統(tǒng)有用;驅動不需要實現(xiàn)dir_notify。一些重要的數(shù)據(jù)結構scull設備驅動只實現(xiàn)最重要的設備方法。它的file_operations結構是如下初始化的:structfile_operationsscull_fops={ .owner=THIS_MODULE, .llseek=scull_llseek, .read=scull_read, .write=scull_write, .ioctl=scull_ioctl, .open=scull_open, .release=scull_release,};
一些重要的數(shù)據(jù)結構structfile,definedin<linux/fs.h>文件結構代表一個打開的文件,由內核在open時創(chuàng)建,并傳遞給在文件上操作的任何函數(shù),直到最后關閉在文件的所有實例都關閉后,內核釋放這個數(shù)據(jù)結構在內核源碼中,指向structfile的指針常常稱為file或者filp(“filepointer”)。本書用filp表示這個指針,以避免和結構自身混淆。因此,file指的是結構,而filp是結構指針一些重要的數(shù)據(jù)結構structfile{ mode_tf_mode; loff_tf_pos; unsignedintf_flags; structfile_operations*f_op; void*private_data; structdentry*f_dentry;};一些重要的數(shù)據(jù)結構mode_tf_mode;文件模式。它通過FMODE_READ和FMODE_WRITE位來標示文件是否可讀或可寫(或兩者都是)loff_tf_pos;當前讀寫位置。loff_t在所有平臺下都是64位(在gcc術語里是longlong)。驅動如果需要知道文件中的當前位置,可以讀這個值,但不應該修改它。read/write會使用它們接收到的最后那個指針參數(shù)來更新這一位置,而不是直接對filp->f_pos進行操作。這一規(guī)則的一個例外是llseek,它的目的就是修改文件位置一些重要的數(shù)據(jù)結構unsignedintf_flags;這些是文件標志,例如O_RDONLY,O_NONBLOCK,和O_SYNC。為了檢查用戶請求的是否非阻塞操作,驅動程序應當檢查O_NONBLOCK標志。而其他標志很少用到。注意,檢查讀/寫權限應該查看f_mode,而非f_flags。所有的標志在頭文件<linux/fcntl.h>中定義。一些重要的數(shù)據(jù)結構structfile_operations*f_op;與文件關聯(lián)的操作。內核在執(zhí)行open操作時對指針賦值,以后需要處理這些操作時就讀取這個指針。filp->f_op中的值決不會為方便引用而保存起來。這意味著你可在需要的時候修改文件關聯(lián)的操作,在返回調用者之后新方法就會生效。例如,對應于主設備號1(/dev/null,/dev/zero,等等)的open代碼根據(jù)要打開的次設備號替代filp->f_op中的操作。這種技巧允許相同主設備號下的設備實現(xiàn)多種操作行為,而不會增加系統(tǒng)調用的負擔。這種替換文件操作的能力在面向對象編程中稱為"方法重載"。一些重要的數(shù)據(jù)結構void*private_data;open系統(tǒng)調用在調用驅動程序的open方法之前將這個指針設置為NULL。驅動程序可以將這個字段用于任何目的或者忽略它。驅動程序可以用這個成員來指向已分配的數(shù)據(jù),但是一定要在內核銷毀file結構之前,在release方法中釋放內存。private_data是一個有用的資源,可用于在系統(tǒng)調用間保存狀態(tài)信息,本書大部分例子模塊都使用它。structdentry*f_dentry;文件對應的目錄項(dentry)結構。除了用filp->f_dentry->d_inode的方式來訪問索引節(jié)點inode結構之外,設備驅動開發(fā)者一般無需要關心dentry結構一些重要的數(shù)據(jù)結構structinode,<linux/fs.h>內核用inode結構在內部表示文件,因此它和file結構不同,后者表示打開的文件。對于單個文件,可能會有許多個表示打開的文件描述符file結構,但它們都指向單個inode結構。inode結構包含了大量有關文件的信息。作為一個通用的規(guī)則,這個結構只有2個成員對于編寫驅動代碼有用:structinode{ dev_ti_rdev; structcdev*i_cdev; …};一些重要的數(shù)據(jù)結構dev_ti_rdev;對于表示設備文件的節(jié)點inode結構,這個成員包含了實際的設備編號structcdev*i_cdev;structcdev是內核的內部結構,代表字符設備;當inode指向一個字符設備文件時,這個成員包含一個指向structcdev結構的指針內核開發(fā)者已經(jīng)增加了2個宏,可用來從一個inode中獲取主次編號:unsignedintiminor(structinode*inode);unsignedintimajor(structinode*inode);1324第3章字符設備驅動程序scull的設計主設備號與次設備號一些重要的數(shù)據(jù)結構字符設備的注冊open和releasescull的內存使用read和write567字符設備的注冊內核字符設備結構與接口(在<linux/cdev.h>中)structcdev{ structkobjectkobj; structmodule*owner; conststructfile_operations*ops; structlist_headlist; dev_tdev; unsignedintcount;};structcdev*cdev_alloc(void);voidcdev_init(structcdev*,conststructfile_operations*);intcdev_add(structcdev*,dev_t,unsigned);voidcdev_del(structcdev*);字符設備的注冊分配structcdev結構(動態(tài)):structcdev*my_cdev=cdev_alloc();my_cdev->ops=&my_fops;初始化structcdev結構voidcdev_init(structcdev*cdev,structfile_operations*fops);將structcdev結構的信息告訴內核intcdev_add(structcdev*dev,dev_tnum,unsignedintcount);num:該設備對應的第一個設備編號count:與該設備關聯(lián)的設備編號的數(shù)量字符設備的注冊cdev_add使用注意事項:這個調用可能失敗。如果它返回一個負的錯誤碼,則設備并沒有增加到系統(tǒng)中,故需要檢測它的返回值cdev_add一返回,設備就“活”了,它的操作就會被內核調用。因此,如果你的驅動程序沒有完全準備好處理設備上的操作,你不應當調用cdev_add.要從系統(tǒng)中去除一個字符設備時,調用:voidcdev_del(structcdev*dev);字符設備的注冊Scull設備注冊先定義自己的設備結構scull_dev,并包含內核字符設備結構cdevstructscull_dev{structscull_qset*data;/*Pointertofirstquantumset*/intquantum;/*thecurrentquantumsize*/intqset;/*thecurrentarraysize*/unsignedlongsize;/*amountofdatastoredhere*/unsignedintaccess_key;/*usedbysculluidandscullpriv*/structsemaphoresem;/*mutualexclusionsemaphore*/
structcdevcdev;/*Chardevicestructure*/};字符設備的注冊初始化structcdevstaticvoidscull_setup_cdev(structscull_dev*dev,intindex){interr,devno=MKDEV(scull_major,scull_minor+index);cdev_init(&dev->cdev,&scull_fops);dev->cdev.owner=THIS_MODULE;dev->cdev.ops=&scull_fops;err=cdev_add(&dev->cdev,devno,1);/*Failgracefullyifneedbe*/if(err)printk(KERN_NOTICE"Error%daddingscull%d",err,index);}字符設備的注冊intscull_init_module(void){ intresult,i; dev_tdev=0; if(scull_major){ dev=MKDEV(scull_major,scull_minor); result=register_chrdev_region(dev,scull_nr_devs,"scull"); }else{ result=alloc_chrdev_region(&dev,scull_minor,scull_nr_devs,"scull"); scull_major=MAJOR(dev); } if(result<0){ printk(KERN_WARNING"scull:can'tgetmajor%d\n",scull_major); returnresult; }字符設備的注冊scull_devices=kmalloc(scull_nr_devs*sizeof(structscull_dev),GFP_KERNEL);if(!scull_devibces){ result=-ENOMEM; gotofail; } memset(scull_devices,0,scull_nr_devs*sizeof(structscull_dev));
for(i=0;i<scull_nr_devs;i++){scull_devices[i].quantum=scull_quantum;scull_devices[i].qset=scull_qset;init_MUTEX(&scull_devices[i].sem);scull_setup_cdev(&scull_devices[i],i);}structscull_dev*scull_devices;#defineSCULL_NR_DEVS4intscull_nr_devs=SCULL_NR_DEVSstructscull_dev{structscull_qset*data;intquantum;intqset;unsignedlongsize;unsignedintaccess_key;structsemaphoresem;structcdevcdev;};字符設備的注冊return0;fail: scull_cleanup_module();returnresult;}module_init(scull_init_module);module_exit(scull_cleanup_module);字符設備的注冊voidscull_cleanup_module(void){ inti;dev_tdevno=MKDEV(scull_major,scull_minor);if(scull_devices){ for(i=0;i<scull_nr_devs;i++){ scull_trim(scull_devices+i); cdev_del(&scull_devices[i].cdev); } kfree(scull_devices); }unregister_chrdev_region(devno,scull_nr_devs);}字符設備的注冊注冊一個字符設備的早期(2.6前)方法intregister_chrdev(unsignedintmajor,constchar*name,structfile_operations*fops);major:主設備號,可以對應256個次設備號,主次設備號均不能大于255name:驅動程序名稱fops:默認的操作結構該調用將為主設備號注冊0~255作為次號,并為設備建立一個cdev結構intunregister_chrdev(unsignedintmajor,constchar*name);
1324第3章字符設備驅動程序scull的設計主設備號與次設備號一些重要的數(shù)據(jù)結構字符設備的注冊open和releasescull的內存使用read和write567open和releaseopen方法進行設備的初始化,在大部分驅動程序中,完成如下工作檢查設備特定的錯誤初始化設備(若是首次打開)更新f_op指針(若有必要)分配并填寫filp->private_data里的數(shù)據(jù)結構open原型int(*open)(structinode*inode,structfile*filp);inode中包含有cdev結構,但是需要的是scull_devstructinode{dev_ti_rdev;structcdev*i_cdev;…};open和release利用container_of(pointer,container_type,container_field);可由cdev結構得到包含它的scull_dev結構
structscull_dev*dev; dev=container_of(inode->i_cdev,structscull_dev,cdev);得到scull_dev后,將其保存在file結構中,方便以后訪問filp->private_data=dev;structscull_dev{structscull_qset*data;intquantum;intqset;unsignedlongsize;unsignedintaccess_key;structsemaphoresem;
structcdevcdev;};open和releasescull的open如下intscull_open(structinode*inode,structfile*filp){structscull_dev*dev;dev=container_of(inode->i_cdev,structscull_dev,cdev);filp->private_data=dev;
if((filp->f_flags&O_ACCMODE)==O_WRONLY){scull_trim(dev);/*刪除舊量子及量子集(若有),并初始化scull_dev}return0;}open和releaserelease方法釋放由open分配的,保存在filp->private_data中的內容關閉設備(在最后一次調用時)scull的release如下:intscull_release(structinode*inode,structfile*filp){return0;}1324第3章字符設備驅動程序scull的設計主設備號與次設備號一些重要的數(shù)據(jù)結構字符設備的注冊open和releasescull的內存使用read和write567structscull_dev{structscull_qset*data;intquantum,qset;unsignedlongsize;…structcdevcdev;};structscull_qset{void**data;structscull_qset*next;};對應一內存區(qū),量子數(shù)組,量子集SCULL_QUANTUM=4000BSCULL_QSET=1000scull的內存使用intscull_trim(structscull_dev*dev){structscull_qset*next,*dptr;intqset=dev->qset;inti;for(dptr=dev->data;dptr;dptr=next){if(dptr->data){for(i=0;i<qset;i++)kfree(dptr->data[i]);kfree(dptr->data);dptr->data=NULL;}next=dptr->next;kfree(dptr);}
dev->size=0;dev->quantum=scull_quantum;dev->qset=scull_qset;dev->data=NULL;return0;}該函數(shù)刪除舊量子及量子集(若有),并初始化設備結構structscull_dev{structscull_qset*data;intquantum;intqset;unsignedlongsize;…};1324第3章字符設備驅動程序scull的設計主設備號與次設備號一些重要的數(shù)據(jù)結構字符設備的注冊open和releasescull的內存使用read和write567read和writeread從設備讀取數(shù)據(jù)到用戶空間,write從用戶空間寫數(shù)據(jù)到設備,原型如下:ssize_tread(structfile*filp,char__user*buff,size_tcount,loff_t*offp);ssize_twrite(structfile*filp,constchar__user*buff,size_tcount,loff_t*offp);filp:文件指針count:請求傳輸?shù)淖止?jié)數(shù)offp:指明用戶在文件中進行存取操作的位置buff:指向用戶空間緩沖區(qū)read和writebuff是用戶空間指針,內核代碼不能直接引用read和write內核空間與用戶空間進行數(shù)據(jù)交換的函數(shù)是:unsignedlongcopy_to_user(void__user*to,constvoid*from,unsignedlongcount);unsignedlongcopy_from_user(void*to,constvoid__user*from,unsignedlongcount);用一些特殊的與架構相關的方法來實現(xiàn),確保在內核和用戶空間安全、正確地交換數(shù)據(jù)拷貝前對指針進行檢查,若指針無效,則不拷貝;另外,若在拷貝過程中遇到無效地址,則僅會復制部分數(shù)據(jù),返回值為還需要拷貝的內存數(shù)值。read和writessize_tscull_read(structfile*filp,char__user*buf,size_tcount,loff_t*f_pos){structscull_dev*dev=filp->private_data;structscull_qset*dptr;intquantum=dev->quantum,qset=dev->qset;intitemsize=quantum*qset;intitem,s_pos,q_pos,rest;ssize_tretval=0;if(down_interruptible(&dev->sem))return-ERESTARTSYS;if(*f_pos>=dev->size)gotoout;if(*f_pos+count>dev->size)count=dev->size-*f_pos;…item=(long)*f_pos/itemsize;rest=(long)*f_pos%itemsize;s_pos=rest/quantum;q_pos=rest%quantum;dptr=scull_follow(dev,item);if(dptr==NULL||!dptr->data||!dptr->data[s_pos])gotoout;if(count>quantum-q_pos)count=quantum-q_pos;if(copy_to_user(buf,dptr->data[s_pos]+q_pos,count)){retval=-EFAULT;gotoout;}*f_pos+=count;retval=count;out:up(&dev->sem);returnretval;}read和writessize_tscull_write(structfile*filp,constchar__user*buf,size_tcount,loff_t*f_pos){structscull_dev*dev=filp->private_data;structscull_qset*dptr;intquantum=dev->quantum,qset=dev->qset;intitemsize=quantum*qset;intitem,s_pos,q_pos,rest;ssize_tretval=-ENOMEM;
if(down_interruptible(&dev->sem))
return-ERESTARTSYS;item=(long)*f_pos/itemsize;rest=(long)*f_pos%itemsize;
s_pos=rest/quantum;q
溫馨提示
- 1. 本站所有資源如無特殊說明,都需要本地電腦安裝OFFICE2007和PDF閱讀器。圖紙軟件為CAD,CAXA,PROE,UG,SolidWorks等.壓縮文件請下載最新的WinRAR軟件解壓。
- 2. 本站的文檔不包含任何第三方提供的附件圖紙等,如果需要附件,請聯(lián)系上傳者。文件的所有權益歸上傳用戶所有。
- 3. 本站RAR壓縮包中若帶圖紙,網(wǎng)頁內容里面會有圖紙預覽,若沒有圖紙預覽就沒有圖紙。
- 4. 未經(jīng)權益所有人同意不得將文件中的內容挪作商業(yè)或盈利用途。
- 5. 人人文庫網(wǎng)僅提供信息存儲空間,僅對用戶上傳內容的表現(xiàn)方式做保護處理,對用戶上傳分享的文檔內容本身不做任何修改或編輯,并不能對任何下載內容負責。
- 6. 下載文件中如有侵權或不適當內容,請與我們聯(lián)系,我們立即糾正。
- 7. 本站不保證下載資源的準確性、安全性和完整性, 同時也不承擔用戶因使用這些下載資源對自己和他人造成任何形式的傷害或損失。
最新文檔
- 2025中信銀行金融市場及同業(yè)部社會招聘模擬筆試試題及答案解析
- 2026浙江臺州溫嶺市衛(wèi)生事業(yè)單位招聘醫(yī)學衛(wèi)生類高學歷人才17人備考考試題庫及答案解析
- 2025內蒙古鄂爾多斯市東勝區(qū)殘疾人輔助性就業(yè)服務中心招聘模擬筆試試題及答案解析
- 2025江蘇連云港碩項湖水務集團有限公司第二次招聘2人備考考試題庫及答案解析
- 2025年水口關出入境邊防檢查站警務輔助人員招聘備考題庫及答案詳解1套
- 2025年現(xiàn)代中藥制劑教育部重點實驗室科研助理招聘補報通知備考題庫及答案詳解參考
- 2025年河北省交通規(guī)劃設計研究院有限公司面向社會公開招聘工作人員的備考題庫及參考答案詳解1套
- 山東省聊城市茌平區(qū)振興中學2025-2026學年八年級上學期第三次月考生物試題(含答案)
- 天津2025年九年級上學期語文期末試卷附答案
- 安徽省部分學校大聯(lián)考2025-2026學年高二上學期12月月考政治(B)試卷(含答案)
- 云南省曲靖市麒麟?yún)^(qū)2023年小升初數(shù)學試卷
- 電子承兌支付管理辦法
- 學堂在線 知識產(chǎn)權法 章節(jié)測試答案
- 全檢員考試試題及答案
- 提高住院患者圍手術期健康宣教知曉率品管圈活動報告
- 應急救援個體防護
- 黨建陣地日常管理制度
- 車間醫(yī)藥箱管理制度
- 食葉草種植可行性報告
- 落葉清掃壓縮機設計答辯
- 《高血壓、2型糖尿病、高脂血癥、肥胖癥膳食運動基層指導要點》解讀課件
評論
0/150
提交評論