linux驅動程序編程學習

admin @ 2014-03-25 , reply:0

         應用程序與驅動程序通過設備文件進行通信

       每個設備文件都有主設備號與次設備號 主設備號表示設備的類型 次設備號表示具體的設備

        在內核中 就是根據主設備號來調用相應的驅動程序 驅動根據次設備號分辯具體設備以區分操作

         主設備號由linux統一分配 但是也可以使用臨時設備 在註冊驅動時把主設備號輸入為0則由內核自動分配

         設備的類型:
         主要類型有下面三種

        字元設備:它的保存是以字元流的形式保存

        塊設備:它的保存是以塊為單位保存,提供和字元驅動一樣的介面,當然有自己塊操作的介面,如mount,同時支持像字元設備那樣的訪問方式,
上面兩個都是映射到一個設備文件,設備文件通常保存在/dev/目錄下,當然你可以保存在任意地方
        
        網路介面:用於進行網路通信,可能針對某個硬體,如網卡,或是純軟體,如loopback,網路介面只是面向數據包而不是數據流,所以內核的處理也不同,沒有映射成任何設備文件,而是按unix標準給它們分配一個唯一的名字

驅動開發時注意點:
1 只提供機制,不限制策略,調用者可以按照自己的要求自己訂製策略
2 許可權的判斷, 應該盡量讓系統管理員決定而不是在驅動中指定,除非這個設備對整個系統有影響
3 安全編程,從用戶進程得到的輸入必須檢查,返回給用戶進程的內存必須初始化(如果這塊內存剛好保存口令信息 或是其它就會破壞系統安全性)
4 內核不連接libc,所以不可以使用libc裡面的函數 如printf等,必須使用內核自己提供的函數,如果你不知道有哪些函數,那看看內核頭文件是不錯的選擇
5 避免名字空間污染,不需要輸出的符號就不輸出 如果你的符號被其它模塊引用,那使用EXPORT_SYMBLE聲明被輸出,要輸出的模塊最好加上模塊名前綴 避免衝突 .不對外開放的使用static限制在本地使用
6 注意可重入性問題,必須考慮到執行到某步時任務切換或是中斷處理時應該怎樣處理.

作者: 無雙 2004-05-03 20:40:30
在2.6下開發簡單的驅動

2.6驅動的開發相對以前各版本來說有了很大的改變 先是初始化與退出函數
以前使用init_module函數來聲明模塊初始化過程 使用cleanup_module來定義模塊退出時的清除過程

而在2.6中 必須使用 module_init 標誌來說明初始化函數 使用module_exit來說明清理函數

在2.4中,可以單獨編譯一個模塊 而在2.6中,一個模塊的編譯,必須編譯全部所有模塊

下面是一個2.6內核的模板


CODE

#define MODULE
#i nclude linux/module.h>
#i nclude linux/config.h>
#i nclude linux/init.h>

static int __init name_of_initialization_routine(void) {
/*
* code here
*/
}
static void __exit name_of_cleanup_routine(void) {
/*
* code here
*/
}
module_init(name_of_initialization_routine);
module_exit(name_of_cleanup_routine);

 

MODULE宏也不再是必須的了,在編譯時可不定義

編譯過程的區別
2.4的編譯過程如下
gcc -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4.21/include -O2 -c testmod.c
要定義__KERNEL__是因為在內核頭文件中,某些符號只在定義這個宏后才會公開

2.6的編譯過程
寫一個簡單的Makefile只有一行
obj-m := testmod.o

然後使用如下命令編譯
make -C /usr/src/linux-2.6.1 SUBDIRS=$PWD modules
$PWD是你的內核模塊所在的目錄 使用如上命令時會自動重編譯系統的內核模塊與你的模塊
所以 保存內核源目錄中的版本號與配置文件與你當前運行內核相同是必要的

一個簡單的例子:
testmod.c
CODE

#define MODULE
#i nclude <linux/module.h>
#i nclude <linux/config.h>
#i nclude <linux/init.h>

static int __init testmod_init(void)
{
printk("<1>hello world\n");
return 0;
}

static void __exit testmod_cleanup(void)
{
printk("<1>exit module\n");
}

module_init(testmod_init);
module_exit(testmod_cleanup);


Makefile 編譯方法與執行結果
CODE

wushuang:~/work/kernel# cat Makefile
obj-m :=testmod.o

wushuang:~/work/kernel# cat test
testmod: module license 'unspecified' taints kernel.
hello world
wushuang:~/work/kernel# rmmod testmod
exit module
wushuang:~/work/kernel#

wushuang:~/work/kernel# make -C /usr/src/linux SUBDIRS=~/work/kernel/ modules

作者: 無雙 2004-05-03 20:41:00
一個有用的資源
CODE

http://people.netfilter.org/~rusty/unreliable-guides/kernel-hacking/lk-hacking-guide.html

定義了一些常用的內核符號定義

作者: 無雙 2004-05-04 13:08:34
第一個可用的例子

下面是一個簡單的字元設備驅動程序


CODE

 

/*  chardev.c: Creates a read-only char device that says how many
*  times
*   *  you've read from the dev file
*    */

#i nclude <linux/module.h>
#i nclude <linux/config.h>
#i nclude <linux/init.h>
#i nclude <linux/fs.h>
#i nclude <asm/uaccess.h>
#i nclude <linux/moduleparam.h>


static int __init testmod_init(void);
static void __exit testmod_exit(void);

static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);

#define SUCCESS 0
#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices   */
#define BUF_LEN 80            /* Max length of the message from the device */


/* Global variables are declared as
* static, so are global within the file.
* */

static int Major;            /* Major number assigned to our device driver */
static int Device_Open = 0;  /* Is device open?  Used to prevent multiple  */
static char msg[BUF_LEN];    /* The msg the device will give when asked    */
static char *msg_Ptr;

static struct file_operations fops = {
   .read = device_read,
   .write = device_write,
   .open = device_open,
   .release = device_release
};//注意這裡需要是靜態變數, 不然會出現內核訪問錯誤情況
//.open = device_read,對fops裡面的open變數賦值為device_read,
//,這是c99;裡面定義的新標準,表示定義結構變數時同時對它進行子元素的賦值
// gcc擴展使用的是open:device_read,


/*                   Functions
*                       */

static int __init testmod_init(void)
{
   Major = register_chrdev(240, DEVICE_NAME, &fops);
//註冊字元驅動 第一個參數是主設備號 如果第一個參數是0 表示由內核自動分配
//第二個是設備名 可以在/proc/devices裡面看到
//第三個是對此設備的操作函數 具體操作可以看file_operations結構定義 在linux/fs.h裡面

   if (Major < 0) {
       printk ("Registering the character device failed with %d\n", Major);
       return Major;
   }

   Major   = 240;
   printk("<1>I was assigned major number %d.  To talk to\n", Major);
   printk("<1>the driver, create a dev file with\n");
   printk("'mknod /dev/hello c %d 0'.\n", Major);
   printk("<1>Try various minor numbers.  Try to cat and echo to\n");
   printk("the device file.\n");
   printk("<1>Remove the device file and module when done.\n");

   return 0;
}


static void __exit testmod_exit(void)
{
   /* Unregister the device */
   int ret = unregister_chrdev(Major, DEVICE_NAME);//取消註冊 取消了后模塊可以unload
   if (ret < 0) printk("Error in unregister_chrdev: %d\n", ret);


/*                   Methods
*                       */

/* Called when a process tries to open the device file, like
*   * "cat /dev/mycharfile"
*       */
static int device_open(struct inode *inode, struct file *file)
{
   static int counter = 0;
   if (Device_Open) return -EBUSY;
   Device_Open++;
   sprintf(msg,"I already told you %d times Hello world!\n", counter++);
   msg_Ptr = msg;
   //MOD_INC_USE_COUNT;在2.6中,會自動維護引用計數,所以不再需要這個宏,但是如果要考慮兼容性那最好保存這個宏

   printk("<1>device_open call\n");
   return SUCCESS;
}


/* Called when a process closes the device file.
*   */
static int device_release(struct inode *inode, struct file *file)
{
   Device_Open --;     /* We're now ready for our next caller */

   printk("<1>device_release call\n");
   /* Decrement the usage count, or else once you
    * opened the file, you'll
    *           never get get rid of the module.
    *           */
   //MOD_DEC_USE_COUNT;

   return 0;
}


/* Called when a process, which already opened the dev file,
* attempts to
*     read from it.
*      */
static ssize_t device_read(struct file *filp,
       char *buffer,    /* The buffer to fill with data */
       size_t length,   /* The length of the buffer     */
       loff_t *offset)  /* Our offset in the file       */
{
   /* Number of bytes actually written to the buffer
    * */
   int bytes_read = 0;

   /* If we're at the end of the message, return 0
    * signifying end of file */
   if (*msg_Ptr == 0) return 0;

   /* Actually put the data into the buffer */
   while (length && *msg_Ptr)  {
//內核空間與用戶空間內存是不同的,所以需要使用內存操作函數 而不可以直接賦值
       /* The buffer is in the user data
        * segment, not the kernel
        * segment;
        *       * assignment won't work.
        *       We have to use put_user
        *       which copies data from
        *               * the kernel data
        *               segment to the
        *               user data
        *               segment. */
       put_user(*(msg_Ptr++), buffer++);

       length--;
       bytes_read++;
   }

   /* Most read functions return the
    * number of bytes put into the buffer
    * */
   return bytes_read;
}


/*  Called when a process writes to dev file: echo "hi" >
*  /dev/hello */
static ssize_t device_write(struct file *filp,
       const char *buff,
       size_t len,
       loff_t *off)
{
   printk ("<1>Sorry, this operation isn't supported.\n");
   return -EINVAL;
}
module_init(testmod_init);
module_exit(testmod_exit);

 

上面是一個簡單的字元設備驅動例子
使用的makefile如下

CODE

subdir='/root/work/kernel/'
obj-m :=testmod.o
all:
make -C /usr/src/linux SUBDIRS=$(subdir) modules
clean:
-rm testmod.[^c]*
reset:clean all
-rmmod testmod
-rm *wushuang*
insmod testmod.ko
mknod testmod_wushuang0 c 240 0

 

使用時make reset就OK了

簡單的測試辦法
echo"hello">testmod_mushuang0

 

第一次自己寫時沒有把fops定義成靜態的 結果每次都報錯 很是鬱悶 後面從網上拿到了這個程序改了改

舊的內核需要自己維護模塊引用計數 所以使用MOD_INC_USE_COUNT和MOD_DEC_USE_COUNT
新內核中自動維護 這兩個已經不再使用

file_operations定義了字元設備的介面 你可以根據自己的需要添加自己要處理的函數 不需要處理的函數將為NULL 由內核自動處理


如果需要傳參數 那使用module_param宏
如下

CODE

subdir='/root/work/kernel/'
obj-m :=testmod.o
all:
make -C /usr/src/linux SUBDIRS=$(subdir) modules
clean:
-rm testmod.[^c]*
reset:clean all
-rmmod testmod
-rm *wushuang*
insmod testmod.ko
mknod testmod_wushuang0 c 240 0


作者: 無雙 2004-05-04 15:12:47
內核內可使用符號的查找

第一個辦法是看/proc/kallsyms

第二個是看內核文檔或是代碼
http://www.kernelnewbies.org/documents/ 這裡列出了很多連接 包括內核api文檔
第三個是看郵件列表 文檔總不可能及時更新 這時而相同的問題可能別人已經碰到過並處理完 這時就沒有必要再重複一次以體現自己的偉大了

另外需要注意的是 開發時必須include正確的頭文件 不然會報錯 雖然為些錯有些莫名奇妙(可能因為使用的宏類型比較多吧 )

頭文件在linux/目錄下 asm/目錄下也有


如果你在linux下工作 那建議使用cscope 來幫助查找
使用方法
cd /usr/include/linux
cscope -R

然後就出現了cscope的界面
CODE

Global definition: get_user

File Line
0 uaccess.h 171 #define get_user(x,ptr) \

 

Find this C symbol: 查找這個符號
Find this global definition: 查找這個定義
Find functions called by this function:
Find functions calling this function:
Find this text string:
Change this text string:
Find this egrep pattern:
Find this file:
Find files #including this file:


使用tab來跳轉 想退出時按ctrl-d就可以 或是ctrl-c
它可以反向查找和正向查找 很是方便 查找到后按數字或是在選中項上按回車就使用默認編輯器打開到指定頁

另外已經集成在了vim中 只是vim默認編譯時不打開cscope支持 如果想在vim中使用還需要自己另外編譯

作者: 無雙 2004-05-04 15:15:07
得到調用驅動者信息

在內核中有一個全局變數current
它是一個task_strut的指針 定義在asm/current.h
task_struct定義在linux/scked.h

這個變數指向了當前調用者信息

如果你想取調用者信息的話 可以取出它的值

下面是一個簡單的例子

 

CODE

static ssize_t device_read(struct file *filp,
       char *buffer,    /* The buffer to fill with data */
       size_t length,   /* The length of the buffer     */
       loff_t *offset)  /* Our offset in the file       */
{
       char outbuf[2048];
       int len;
       if(!buffer || length <1)
          return 0;
       len = dump_processinfo(outbuf,2047);
       outbuf[2047] = 0;
       if(len >length)
           len = length;
   copy_to_user(buffer,outbuf,len);
   return len;
}

static size_t dump_processinfo(char *buf,int len)
{
   if(!buf || len <1 )
       return 0;
   return snprintf(buf,len,"stat:%d actvated:%d pid:%d cmd:%s\n",
          current->state,current->activated,current->pid,current->comm );
}

 

把上面的device_read代替上一貼中的device_read然後重編譯

簡單測試方法就是
cat testmod_mushuang0
使用ctrl-c停止

注意到裡面使用了copy_to_user 內核空間中的內存與用戶空間的不一樣 所以需要使用內存操作函數把內核中信息複製到用戶空間去 同理還有上面的put_user 也是一樣的功能

對應的,內核空間需要讀用戶空間數據時,使用copy_from_user ,get_user


get_user和put_user只是指定了源和目的 沒有使用長度 那它是如何複製的呢 是不是只複製一個位元組或是指定的位元組 這個開始時容易讓人感到困惑
它們複製的長度是根據第二個數的類型來判斷的

也就是 put_user(x,y) = copy_to_user(x,y,sizeof(*y)) 只是性能更快 注意y 應該是內建類型 也就是如char short int類型

內存具體信息可以在網上查找
KernelHacking101.pdf

裡面講解的不錯

作者: carol 2004-05-06 16:41:56
學習

作者: 無間道 2004-05-07 20:01:58
linux驅動編程原來學過字元設備編程,不過有一段時間沒用過了,沒機會練手。

作者: 無雙 2004-05-08 12:04:52
偶只是隨意寫的 想到哪裡寫到哪裡


模塊在內核中的實例個數

在應用開發時 每個進程的地址空間都是獨立的 改變一個進程的值 不會影響其它進程

但是在內核中是不是也會這樣呢


寫一個簡單的驅動程序

如下

 

 

/* chardev.c: Creates a read-only char device that says how many
* times
* * you've read from the dev file
* */

#i nclude <linux/module.h>
#i nclude <linux/config.h>
#i nclude <linux/init.h>
#i nclude <linux/fs.h>
#i nclude <asm/uaccess.h>
#i nclude <linux/moduleparam.h>


static int __init testmod_init(void);
static void __exit testmod_exit(void);
static int device_open(struct inode *, struct file *);
static int device_release(struct inode *, struct file *);
static ssize_t device_read(struct file *, char *, size_t, loff_t *);
static ssize_t device_write(struct file *, const char *, size_t, loff_t *);
static size_t dump_processinfo(char *buf,int len);

#define SUCCESS 0
#define DEVICE_NAME "chardev" /* Dev name as it appears in /proc/devices */
#define BUF_LEN 80 /* Max length of the message from the device */


/* Global variables are declared as
* static, so are global within the file.
* */

static int Major; /* Major number assigned to our device driver */
static int Device_Open = 0; /* Is device open? Used to prevent multiple */
static char msg[BUF_LEN]; /* The msg the device will give when asked */
static char *msg_Ptr;

static struct file_operations fops = {
.read = device_read,
.write = device_write,
.open = device_open,
.release = device_release
};


/* Functions
* */

static int __init testmod_init(void)
{
Major = register_chrdev(240, DEVICE_NAME, &fops);

if (Major < 0) {
printk ("Registering the character device failed with %d\n", Major);
return Major;
}

Major = 240;
printk("<1>I was assigned major number %d. To talk to\n", Major);
printk("<1>the driver, create a dev file with\n");
printk("'mknod /dev/hello c %d 0'.\n", Major);
printk("<1>Try various minor numbers. Try to cat and echo to\n");
printk("the device file.\n");
printk("<1>Remove the device file and module when done.\n");

return 0;
}


static void __exit testmod_exit(void)
{
/* Unregister the device */
int ret = unregister_chrdev(Major, DEVICE_NAME);
if (ret < 0) printk("Error in unregister_chrdev: %d\n", ret);
}


/* Methods
* */

/* Called when a process tries to open the device file, like
* * "cat /dev/mycharfile"
* */
static int device_open(struct inode *inode, struct file *file)
{
static int counter = 0;
if (Device_Open) return -EBUSY;
Device_Open++;
sprintf(msg,"I already told you %d times Hello world!\n", counter++);
msg_Ptr = msg;
//MOD_INC_USE_COUNT;

printk("<1>device_open call\n");
return SUCCESS;
}


/* Called when a process closes the device file.
* */
static int device_release(struct inode *inode, struct file *file)
{
Device_Open --; /* We're now ready for our next caller */

printk("<1>device_release call\n");
/* Decrement the usage count, or else once you
* opened the file, you'll
* never get get rid of the module.
* */
//MOD_DEC_USE_COUNT;

return 0;
}


/* Called when a process, which already opened the dev file,
* attempts to
* read from it.
* */
static ssize_t device_read(struct file *filp,
char *buffer, /* The buffer to fill with data */
size_t length, /* The length of the buffer */
loff_t *offset) /* Our offset in the file */
{
char outbuf[2048];
int len = dump_processinfo(outbuf,2047);
outbuf[2047] = 0;
if(len >length)
len = length;
copy_to_user(buffer,outbuf,len);
return len;
/* Most read functions return the
* number of bytes put into the buffer
* */
//return bytes_read;
}


/* Called when a process writes to dev file: echo "hi" >
* /dev/hello */
static ssize_t device_write(struct file *filp,
const char *buff,
size_t len,
loff_t *off)
{
printk ("<1>Sorry, this operation isn't supported.\n");
return -EINVAL;
}

static size_t dump_processinfo(char *buf,int len)
{
static int i = 0;
if(!buf || len <1 )
return 0;

printk(KERN_INFO"%dth dump\n",i++);
return 0;
}


module_init(testmod_init);
module_exit(testmod_exit);

 

 

這只是在上面的例子基礎上進行了一些修改 注意在dump_processinfo裡面使用了一個靜態變數

測試步驟如下:
編譯上面的模塊 然後使用insmod testmod.ko載入模塊

mknod testmod_wushuang c 240 0
mknod testmod_wushuang1 c 240 1

cat testmod_wushuang
cat testmod_wushuang
cat testmod_wushuang1
cat testmod_wushuang1

看上面的輸出結果 發現雖然是不同的進程 對不同的文件進行操作 但是序號還是在增加中

rmmod testmod
insmod testmod

重載入內核模塊

然後重複上面的步驟

發現重載入后 計數器從頭開始了

可見驅動模塊在整個系統中是唯一的 另外 每次模塊unload時 內核都會把它的內存空間翻譯

所以 在模塊開發中要慎用靜態變數

作者: 無雙 2004-05-09 21:07:45
可重入的進一步研究

即然模塊在內存中只有一份 那想操作多個文件時什麼辦

這時考慮的就是看open操作會不會被調用了

對任何操作 都是先調用open 再進行讀寫操作 最後調用close(ioctl還沒江過)

如果你的程序中只調用open而沒有調用close時 那會怎樣呢

答案是 你沒有調用close時 在進程退出時操作系統也會調用close 所以不會引起資源泄漏

在這裡可以補充一下c中進程三種退出方式的區別
第一個是普通的return 這種與exit相同
第二個是使用exit,刷新所有打開文件並close,調用使用atexit 註冊的退出清理函數進行退出清理
第三個是_exit,只是關閉文件 ,不調用atexit註冊的退出函數,也不刷新IO,這可能會引起數據的不同步 但是速度比exit快

作者: 無雙 2004-05-09 21:12:57
每次對設備的操作都會調用open
並且最後也會調用close 所以 可以考慮在open中對不同的設備進行區分

open傳入兩個參數 第一個是inode類型第二個是file類型
我們知道 每個文件系統上的I節點值是唯一的 從整個系統來講 也就是inode與主\次設備號的組合唯一
這時可以考慮使用inode信息來對每個文件生成一個唯一的信息

對設備來說 可能是把同一主設備次設備號的文件看成同一個設備 對它共用操作 這時可以只看inode的主次設備號

 

另外在open時會傳入一個inode的值 裡面就是這個文件的inode值

寫一個簡單的程序測試上面的知識:
測試內核文件 在上面的基礎上修改 增加列印inode節點值功能


CODE

static int device_open(struct inode *inode, struct file *file)
{
   static int counter = 0;
   if (Device_Open) return -EBUSY;
   Device_Open++;
   sprintf(msg,"I already told you %d times Hello world!\n", counter++);
   msg_Ptr = msg;
   //MOD_INC_USE_COUNT;

   printk("<1>device_open call inode info:%d\n",inode->i_ino);
   return SUCCESS;
}


/* Called when a process closes the device file.
*   */
static int device_release(struct inode *inode, struct file *file)
{
   Device_Open --;     /* We're now ready for our next caller */

   printk("<1>device_release call inode:%d\n",inode->i_ino);
   /* Decrement the usage count, or else once you
    * opened the file, you'll
    *           never get get rid of the module.
    *           */
   //MOD_DEC_USE_COUNT;

   return 0;
}

 


測試文件

CODE

#i nclude <unistd.h>
#i nclude <stdio.h>
#i nclude <sys/stat.h>

int main()
{
   char buf[4096];
   int len = sizeof(buf);
   int fp = open("../kernel/testmod_wushuang0",S_IREAD);

   return 0;
}

 

編譯上面的程序並執行
看看輸出 發現執行了release
另外
使用ls -i ../kernel/testmod_wushuang0
看到輸出是一樣的 說明i_ino保存的就是i節點值

下面是列印出inode信息的一個例子

作者: 無雙 2004-05-11 22:05:22
inode信息 後面補充完全
現在從這裡得到參考 我想更好的理解還是多編程
http://www.win.tue.nl/~aeb/linux/lk/lk-10.html#ss8.6

 

CODE

struct inode {
         struct hlist_node       i_hash;散列 為了更快的查找到這個inode
         struct list_head        i_list;inode鏈表,使用inode_lock保護
         struct list_head        i_dentry;目錄入口,可以從這個訪問到上一級目錄 及文件名等相關信息 還有鏈接
         unsigned long           i_ino;i節點值 在一個文件系統中唯一 也就是在同一主次設備號的設備上唯一
         atomic_t                i_count;使用數目
         umode_t                 i_mode;訪問模式
         unsigned int            i_nlink;引用數目
         uid_t                   i_uid;用戶id
         gid_t                   i_gid;組id
         dev_t                   i_rdev;設備號 通過MAJOR與MIRROR得到主設備與從設備
         loff_t                  i_size;
         struct timespec         i_atime;最後訪問時間
         struct timespec         i_mtime;修改時間
         struct timespec         i_ctime;創建時間
         unsigned int            i_blkbits;
         unsigned long           i_blksize;
         unsigned long           i_version;
         unsigned long           i_blocks;
         unsigned short          i_bytes;
        spinlock_t              i_lock; /* i_blocks, i_bytes, maybe i_size */
         struct semaphore        i_sem;
         struct inode_operations *i_op;
         struct file_operations  *i_fop; /* former ->i_op->default_file_ops */
         struct super_block      *i_sb;
         struct file_lock        *i_flock;
         struct address_space    *i_mapping;
         struct address_space    i_data;
         struct dquot            *i_dquot[MAXQUOTAS];
         /* These three should probably be a union */
         struct list_head        i_devices;inode屬於某個塊設備 這裡得到設備列表 有bdev_lock保護
         struct pipe_inode_info  *i_pipe;
         struct block_device     *i_bdev;
         struct cdev             *i_cdev;
         int                     i_cindex;
 
         unsigned long           i_dnotify_mask; /* Directory notify events */
         struct dnotify_struct   *i_dnotify; /* for directory notifications */
 
         unsigned long           i_state;
 
         unsigned int            i_flags;
       unsigned char           i_sock;
 
         atomic_t                i_writecount;
         void                    *i_security;
         __u32                   i_generation;
         union {
                 void            *generic_ip;
         } u;
 #ifdef __NEED_I_SIZE_ORDERED
         seqcount_t              i_size_seqcount;
 #endif
 };

 


作者: 無雙 2004-05-1:14:48
file結構的定義
file結構在字元類型的驅動中也是重要的一個結構
所以 想開發驅動 我們還是有必要了解它的內容


CODE

struct file {
         struct list_head        f_list;
         struct dentry           *f_dentry;//目錄樹入口
         struct vfsmount         *f_vfsmnt;//指向vfs的入口
         struct file_operations  *f_op;//文件操作函數指針,一般不修改,除非你要根據不同的文件指定不同的操作
         atomic_t                f_count;//文件的引用計數
         unsigned int            f_flags;//文件的標誌
         mode_t                  f_mode;//文件的模式
         loff_t                  f_pos;//當前文件的訪問偏移
         struct fown_struct      f_owner;//文件的擁有者(打開進程)
         unsigned int            f_uid, f_gid;//文件屬主
         int                     f_error;//文件的錯誤碼
         struct file_ra_state    f_ra;//預取信息
 
         unsigned long           f_version;//
         void                    *f_security;
 
         /* needed for tty driver, and maybe others */
         void                    *private_data;
        /* Used by fs/eventpoll.c to link all the hooks to this file */
         struct list_head        f_ep_links;
         spinlock_t              f_ep_lock;
 };

 

下面是網上資料

CODE

5.1 File Structure

The file structure is defined in linux/fs.h to be:

struct fown_struct {
       int pid;                /* pid or -pgrp where SIGIO should be sent */
       uid_t uid, euid;        /* uid/euid of process setting the owner */
       int signum;             /* posix.1b rt signal to be delivered on IO */
};

struct file {
       struct list_head        f_list;
       struct dentry           *f_dentry;
       struct file_operations  *f_op;
       atomic_t                f_count;
       unsigned int            f_flags;
       mode_t                  f_mode;
       loff_t                  f_pos;
       unsigned long           f_reada, f_ramax, f_raend, f_ralen, f_rawin;
       struct fown_struct      f_owner;
       unsigned int            f_uid, f_gid;
       int                     f_error;

       unsigned long           f_version;

       /* needed for tty driver, and maybe others */
       void                    *private_data;//驅動自己使用 內核不修改它 當第一次創建時內核會把它設置成NULL 在驅動中可以用它來保存某個狀態
};

 

剛剛找到了這個站點 上面有這個結構的進一步說明 高興
http://cgi.cse.unsw.edu.au/~neilb/oss/linux-commentary/vfs-5.html

CODE

The fields have the following meaning:

f_list

   This field links files together into one of a number of lists. There is one list for each active file-system, starting at the s_files pointer in the super-block. There is one for free file structures (free_list in fs/file_table.c). And there is one for anonymous files (anon_list in fs/file_table.c) such as pipes.

f_dentry

   This field records the dcache entry that points to the inode for this file. If the inode refers to an object, such as a pipe, which isn't in a regular file-system, the dentry is a root dentry created with d_alloc_root.

f_op

   This field points to the methods to use on this file.

f_count

   The number of references to this file. One for each different user-process file descriptor, plus one for each internal usage.

f_flags

   This field stores the flags for this file such as access type (read/write), nonblocking, appendonly etc. These are defined in the per-architecture include file asm/fcntl.h. Some of these flags are only relevant at the time of opening, and are not stored in f_flags. These excluded flags are O_CREAT, O_EXCL, O_NOCTTY, O_TRUNC. This list is from filp_open in fs/open.c.

f_mode

   The bottom two bits of f_flags encode read and write access in a way that it is not easy to extract the individual read and write access information. f_mode stores the read and write access as two separate bits.

f_pos

   This records the current file position which will be the address used for the next read request, and for the next write request if the file does NOT have the O_APPEND flag.

f_reada, f_remax, f_raend, f_ralen, f_rawin

   These five fields are used to keeping track of sequential access patterns on the file, and determining how much read-ahead to do. There may be a separate section on read-ahead.

f_owner

   This structure stores a process id and a signal to send to the process when certain events happen with the file, such as new data being available. Currently, keyboards, mice, serial ports and network sockes seem to be the only files which is this feature (via kill_fasync).

f_uid, f_gid

   These fields get set to the owner and group of the process which opened the file. They don't seem to be used at all.

f_error

   This is used by the NFS client file-system code to return write errors. It is set in fs/nfs/write.c and checked in fs/nfs/file.c, and used in mm/filemap.c:generic_file_write

f_version

   This field is available to be used by the underlying file-system to help cache state, and check for the cache being invalid. It is changed whenever the file has its f_pos value changed.

   For example, the ext2 file-system uses it in conjuction with the i_version field in the inode to detect when a directory may have changed. If neither the directory nor the file position has changed, then ext2 can be sure that the current file position is the start of a valid directory entry, otherwise it much re-check from the start of the block.

private_data

   This is used by many device drivers, and even a few file-systems, to store extra per-open-file information (such as credentials in coda).

 


作者: 無雙 2004-05-1:29:41
file結構是在內核中存在 由內核維護 它與c庫裡面的FILE沒有關係

一個file文件對應一個打開的文件 由內核在打開文件前創建並置各值 如f_ops
一個file結構可能被多個進程引用 如在父子進程中
也可能多個file結構指向同一個文件 如不同進程間打開同一個文件

上面的inode也只是一個內存中的inode 而不是磁碟上的inode
磁碟上有很多inode 而它們平台多數都不會使用到
所以內核江不會啟動時就把它們讀到內存 而只是在一個inode需要時才讀到內存中
這時它會創建一個inode,初始化.
當這個inode被改變時,把它寫到磁碟上去

進一步閱讀資料
http://www.jhpu.net/wsktang/operating/

我想這些東西並不是三言二語可以講清楚的 所以參考上面的資料是了解操作系統實現的更好辦法

作者: qinxj 2004-05-18 17:20:50
無雙兄的技術一流,沒想到寫作水平也很高啊,佩服佩服!

作者: 無雙 2004-05-18 21:36:47
我都不懂我在寫什麼

 

內核中的同步機制

如果多個進程或是線程同時想改變一個變數 或說程序間的運行結果精確依賴於它的執行順序 這種情況就叫做競態
為了讓程序的運行結果可控 於是發明了同步機制

在unix下
進程間的同步方式有管道,消息隊列,信號量,信號,共享內存
線程間的同步工有互斥鎖,條件變數,

那在內核開發中 也需要有同樣的同步機制

內核開發中與應用開發的區別是
內核開發更重視高效率 並且 對可重入性的要求更高
操作系統中只有一份內核的映象 內核模塊可以訪問系統所有資源並修改 影響整個系統
而應用程序則每個進程都有自己的地址空間,每個進程的活動不影響其它進程
內核是中斷式驅動的 每一步都需要考慮可重入性
進程除非指定信號處理程序 否則是可以自己控制程序的運行
另外對多cpu的情況 必須保證內核中變數的同步 而多CPU對進程來說已經被抽像了 它不必了解

下面把內核中使用的內核鎖機制寫一下

第一個是關中斷與開中斷
disable_irq(unsigned int irq)//等待到irq的處理已經完成才返回
disable_irq_nodync(unsigned int irq)//返回前並不保證irq的處理函數已經完成
enable_irq(unsigned int irq)

原子化操作
如果指令的執行是一次完成 中間沒有中斷 我們把它叫做原子化操作
原子操作是一個atomic_t類型的變數,這個類型的具體實現是什麼對外面來說並不需要關心
因為內核已經定義了一組操作函數
atomic_t類型的有效值是24bit,注意
所有函數的定義都在asm/atomic.h


CODE

atomic_read — read atomic variable
atomic_set — set atomic variable
atomic_add — add integer to atomic variable
atomic_sub — subtract the atomic variable
atomic_sub_and_test — subtract value from variable and test result
atomic_inc — increment atomic variable
atomic_dec — decrement atomic variable
atomic_dec_and_test — decrement and test
atomic_inc_and_test — increment and test
atomic_add_negative — add and test if negative//如果<0返回true,>=0為false


帶test後綴的函數,判斷操作后結果是不是==0,如果結果==0那返回true
不然返回false


自旋鎖
自旋鎖為需要快速完成的操作提供了一個簡單的機制,當它發現鎖已被鎖定時,會循環等待一直到可用,為變數的同步訪問提供了機制
與信號量相比,自旋鎖的速度更快 開銷更少
它的定義在asm/spinlock.h
類型為spinlock_t
有一個快速初始化宏SPINLOCK_UNLICKED;
它提供的函數如下

CODE

void spin_lock_init(spinlock_t *sl); //可使用sl = SPINLOCK_UNLICKED;代替
int spin_is_locked(spinlock_t *sl); //檢測是不是已被鎖定
void spin_lock(spinlock_t *sl);//鎖定
void spin_unlock(spinlock_t *sl);//解鎖
int spin_trylock(spinlock_t *sl); /* returns 0 if succeeded */
//下面在需要禁止irq時使用,當然速度會降低
spin_lock_irqsave(spinlock_t*sl,unsigned long flags)
spin_lock_irqrestore(spinlock_t*sl,unsigned long flags)

 


讀寫鎖
讀寫鎖用於更細的鎖定
類型為rwlock_t
也定義在spilock.h
初始化變數為RELOCK_UNLOCKED
函數

CODE

read_lock(rwlock_t*)
read_lock_irqsave(rwlock_t*,unsigned long flags)
read_unlock(rwlock_t*)
read_unlock_irqrestore(rwlock_t(,unsigned long)
write_lock...


功能與spinlock的操作相似


信號量
信號量的功能與應用程序開發中的差不多
類型為semaphore
定義在semaphore.h

CODE

void sema_init(struct semaphore *sem, int val); /* alternative to DECLARE_... */
void down(struct semaphore *sem); /* may sleep */
int down_interruptible(struct semaphore *sem); /* may sleep; returns -EINTR on interrupt */
int down_trylock(struct semaphone *sem); /* returns 0 if succeeded; will no sleep */
void up(struct semaphore *sem);

 

內核鎖
內核鎖用於smp系統中,保證當前只有一個cpu運行 現在它的功能已被細分到各子鎖了 也不再那麼有用 但是在需要的時候 還是應該想到它
用法很簡單 就兩個函數

CODE

lock_kernel();
/* critical region ... */
unlock_kernel();

 

可搶佔式鎖定
新內核的進程調度方式增加了可搶佔性 它充許更高優先順序的進程運行,甚至當前進程仍在內核中運行
這時給鎖定帶來了一定的困難 因為鎖只是針對某個cpu而言的
如果你希望使進程不可以搶佔運行 那可以使用下面兩個函數
preempt_disable()
preempt_enable()
它們可以調用多次,當然你加了n次鎖也必須解n次鎖

作者: 無雙 2004-05-18 21:38:17
補充
信號量是 == 0 時阻塞 >0時可運行
up是 ++
down 是--

作者: sutao772 2004-06-05 15:41:38
請教無雙兄一個的驅動程序版本倚賴的問題:偶剛開始學驅動,謝謝!在RH9.0上
hello.c:
#define MODULE
#i nclude <linux/module.h>
int init_module(void) { printk("<1>Hello, world!\n"); return 0; }
void cleanup_module(void) { printk("<1>Goodbye, cruel world!\n"); }
編譯\運行后
#gcc -c hello.c
#insmod ./hello.o
hello.o: kernel-module version mismatch
hello.o was compiled for kernel version 2.4.20
while this kernel is version 2.4.20-8.
請無雙兄指教 ,謝謝!急

作者: 無間道 2004-06-05 23:54:11
insmod ./hello.o -f

作者: 無雙 2004-06-06 07:08:37
同意無間道的

這是你打開了內核模塊版本驗證引起的

添加下面一行
#i nclude "linux/version.h"
看看

作者: sutao772 2004-06-07 09:14:00
我試了一下,還是不行,情況照舊.

作者: 無雙 2004-06-07 11:40:21


你的內核源碼版本是不是與當前內核版本不一致

作者: sutao772 2004-06-08 11:25:20
我專程看了一下,頭文件所在的內核源碼版本和系統(RH9)的內核版本是一致的,均為2.4.20-8.
以下是makefile:

CC=gcc
MODCFLAGS := -O2 -D__KERNEL__ -I /usr/src/linux-2.4.20-8/include/linux

hello.o: hello.c
$(CC) $(MODCFLAGS) -c hello.c

clean :
rm -rf *.o


(hello.c中已添加了version.h頭文件)
情況還是照舊,肯定是內核版本驗證引起的(為兼容不同內核版本,我不得不這樣),但我現在真的不知如何解決?還請高手多多指點,大家可以自己試試

作者: 無雙 2004-06-08 13:16:50
google吧
google
kernel-module version mismatch

下面是一個資料 看看是不是對自己有用

我使用的2.6內核

--------------------------------------------------------------------------------
[Date Prev][Date Next][Thread Prev][Thread Next][Date Index][Thread Index]
Re: kernel-module version mismatch

--------------------------------------------------------------------------------

To: Andrew D Dixon <dixon99@alumni.middlebury.edu>
Subject: Re: kernel-module version mismatch
From: "John H. Robinson, IV" <jhriv@ucsd.edu>
Date: Tue, 13 Mar 2001 15:10:39 -0800
Cc: debian-mentors@lists.debian.org
In-Reply-To: <005601c0ac0f$afc034b0$0110ac@Seranoa.com>; from andrew_dwight_dixon@yahoo.com on Tue, Mar 13, 2001 at 05:48:13PM -0500
Mail-Followup-To: Andrew D Dixon <dixon99@alumni.middlebury.edu>,debian-mentors@lists.debian.org
References: <005601c0ac0f$afc034b0$0110ac@Seranoa.com>
User-Agent: Mutt/1.2.5i

--------------------------------------------------------------------------------

On Tue, Mar 13, 2001 at 05:48:13PM -0500, Andrew D Dixon wrote:
> Hi All,
> I'm writing my first module and when I try to run it I get this error:
>
> #insmod hello.o
> hello.o: kernel-module version mismatch
> hello.o was compiled for kernel version 2.2.15
> while this kernel is version 2.2.18pre21.
>
> I believe that I need to upgrade linux/module.h and I was wondering what the
> preferred method of doing this was.

/usr/include/linux/module.h correctly refers to the kernel that the
libraries were built against.

if you know where your current kernel headers are, you can include the
path to them with the -I flag

gcc -c hello.c -I/usr/src/linux/include

-john

 


--------------------------------------------------------------------------------

References:
kernel-module version mismatch
From: "Andrew D Dixon" <andrew_dwight_dixon@yahoo.com>
Prev by Date: One source package, multiple binary packages with different version numbers - courier
Next by Date: Re: Looking for Advocate
Prev by thread: kernel-module version mismatch
Next by thread: One source package, multiple binary packages with different version numbers - courier
Index(es):
Date
Thread

作者: hyzable 2004-06-12 10:49:42
斑竹,請教一個問題,如果在一塊嵌入式的板子里希望實現自己的中斷機制,應該如何處理自己寫好的模塊呢?是不是要將原來的板子的linux中斷機制覆蓋掉,要的話應該怎麼覆蓋?

作者: 無雙 2004-06-13 16:05:06
不用吧
我現在正在出差
手邊沒有資料

不過
可以查查linux中斷的編寫方法

linux裡面已經提供了安裝中斷的辦法與介面 不用自己修改內核

作者: 無雙 2004-06-13 16:05:43
看這裡
http://people.netfilter.org/~rusty/unreliable-guides/kernel-hacking/lk-hacking-guide.html

作者: hyzable 2004-06-14 13:45:38
我就想知道,如果我想申請一個自己的中斷,我是不是要知道具體操作哪個地址或者寄存器,不知道斑竹有沒有這方面的例子教程?如果不用intel的晶元,又應該怎麼做?

作者: hyzable 2004-06-19 09:19:43
模仿斑竹的簡單的字元設備驅動例子寫了一下,當insmod的時候,會報unresolved unregister_chrdev和register_chrdev,我用的是2.4的內核
還發現一個情況,如果定義了MODULE
gcc -D__KERNEL__ -DMODULE -I/usr/src/linux-2.4.21/include -O2 -c testmod.c會說MODULE重定義
問題找到了,我把chrdev寫成了chardev,奇怪,為什麼在編譯的時候不指出這個錯誤,反而在載入進去的時候才出錯呢?

作者: hyzable 2004-06-28 17:56:19
我insmod成功了,但是有這個
Warning: loading test.o will taint the kernel: no license
See http://www.tux.org/lkml/#export-tainted for information about tainted modules
Module test loaded, with warnings

還有就是我用open(“/dev/mychar”)不成功,不知道是不是路徑沒有設對

作者: hyzable 2004-06-28 17:56:32
我insmod成功了,但是有這個
Warning: loading test.o will taint the kernel: no license
See http://www.tux.org/lkml/#export-tainted for information about tainted modules
Module test loaded, with warnings
網站的英文說:modules without available source code under a free software license). As the source is not freely available, any bugs uncovered whilst such modules are loaded cannot be investigated by the kernel hackers.

還有就是我用open(“/dev/mychar”)不成功,不知道是不是路徑沒有設對

作者: 無雙 2004-06-28 18:04:57
Warning: loading test.o will taint the kernel: no license

2.6內核增加了 MODULE_LICENSE 用來說明內核版本信息

open不成功 是在內核中還是應用程序中

作者: hyzable 2004-06-29 09:05:34
應用程序,我是按照你的寫法,寫完了test。o之後,直接寫了個應用程序open(/dev/mychar),是不是還需要request religion之類的?

作者: 無雙 2004-06-29 13:20:55
/dev/mychar這個文件有沒有
並且主次設備號正確

 

http://www.unix366.com/hgcxs/vngbosudqw/28170.htm




[admin via 研發互助社區 ] linux驅動程序編程學習已經有1856次圍觀

http://cocdig.com/docs/show-post-42771.html