linux驅動中的write函數

來源:互聯網
上載者:User

 Linux下我們在使用裝置的時候,都會用到write這個函數,通過這個函數我們可以象使

      用檔案那樣向裝置傳送資料。可是為什麼使用者使用write函數就可以把資料寫到裝置裡面

      去,這個過程到底是怎麼實現的呢?

     

      這個奧秘就在於裝置驅動程式的write實現中,這裡我結合一些原始碼來解釋如何使得一

      個簡簡單單的write函數能夠完成向裝置裡面寫資料的複雜過程。

      

      這裡的原始碼主要來自兩個地方。第一是oreilly出版的《Linux device driver》中的

      執行個體,第二是Linux Kernel 2.2.14核心原始碼。我只列出了其中相關部分的內容,如果

      讀者有興趣,也可以查閱其它原始碼。不過我不是在講解如何編寫裝置驅動程式,所以不

      會對每一個細節都進行說明,再說有些地方我覺得自己還沒有吃透。

      

      由於《Linux device driver》一書中的例子對於我們還是複雜了一些,我將其中的一個

      常式簡化了一下。這個驅動程式支援這樣一個裝置:核心空間中的一個長度為10的數組

      kbuf[10]。我們可以通過使用者程式open它,read它,write它,close它。這個裝置的名

      字我稱為short_t。

     

      現在言歸正傳。

      對於一個裝置,它可以在/dev下面存在一個對應的邏輯裝置節點,這個節點以檔案的形式

      存在,但它不是普通意義上的檔案,它是裝置檔案,更確切的說,它是裝置節點。這個節

      點是通過mknod命令建立的,其中指定了主裝置號和次裝置號。主裝置號表明了某一類設

      備,一般對應著確定的驅動程式;次裝置號一般是區分是標明不同屬性,例如不同的使用

      方法,不同的位置,不同的操作。這個裝置號是從/proc/devices檔案中獲得的,所以一

      般是先有驅動程式在核心中,才有裝置節點在目錄中。這個裝置號(特指主裝置號)的主

      要作用,就是聲明裝置所使用的驅動程式。驅動程式和裝置號是一一對應的,當你開啟一

      個裝置檔案時,作業系統就已經知道這個裝置所對應的驅動程式是哪一個了。這個"知道"

      的過程後面就講。

     

      我們再說說驅動程式的基本結構吧。這裡我只介紹動態模組型驅動程式(就是我們使用

      insmod載入到核心中並使用rmmod卸載的那種),因為我只熟悉這種結構。

      模組化的驅動程式由兩個函數是固定的:int init_module(void) ;void

      cleanup_module(void)。前者在insmod的時候執行,後者在rmmod的時候執行。

      init_nodule在執行的時候,進行一些驅動程式初始化的工作,其中最主要的工作有三

      件:註冊裝置;申請I/O連接埠位址範圍;申請中斷IRQ。這裡和我們想知道的事情相關的只

      有註冊裝置。

     

      下面是一個典型的init_module函數:

     

      int init_module(void){

int result = check_region(short_base,1);/* 察看連接埠地址*/
      ……

      request_region(short_base,1,"short"); /* 申請連接埠地址*/

      ……

      result = register_chrdev(short_major, "short", &short_fops); /* 註冊裝置

      */

      ……

      result = request_irq(short_irq, short_interrupt, SA_INTERRUPT, "short",

      NULL); /* 申請IRQ */

      ……

      return 0;

      }/* init_module*/

     

      上面這個函數我只保留了最重要的部分,其中最重要的函數是

      result = register_chrdev(short_major, "short", &short_fops);

      這是一個驅動程式的精髓所在!!當你執行indmod命令時,這個函數可以完成三件大事:

      第一,申請主裝置號(short_major),或者指定,或者動態分配;第二,在核心中註冊設

    
備的名字("short");第三,指定fops方法(&short_fops)。其中所指定的fops方法就是

      我們對裝置進行操作的方法(例如read,write,seek,dir,open,release等),如何?

      這些方法,是編寫裝置驅動程式大部分工作量所在。

     

      現在我們就要接觸關鍵區段了--如何?fops方法。

      我們都知道,每一個檔案都有一個file的結構,在這個結構中有一個file_operations的

      結構體,這個結構體指明了能夠對該檔案進行的操作。

     

      下面是一個典型的file_operations結構:

      struct file_operations {

      loff_t (*llseek) (struct file *, loff_t, int);

      ssize_t (*read) (struct file *, char *, size_t, loff_t *);

      ssize_t (*write) (struct file *, const char *, size_t, loff_t *);

      int (*readdir) (struct file *, void *, filldir_t);

      unsigned int (*poll) (struct file *, struct poll_table_struct *);

      int (*ioctl) (struct inode *, struct file *, unsigned int, unsigned

      long);

      int (*mmap) (struct file *, struct vm_area_struct *);

      int (*open) (struct inode *, struct file *);

      int (*flush) (struct file *);

      int (*release) (struct inode *, struct file *);

      int (*fsync) (struct file *, struct dentry *);

      int (*fasync) (int, struct file *, int);

      int (*check_media_change) (kdev_t dev);

      int (*revalidate) (kdev_t dev);

      int (*lock) (struct file *, int, struct file_lock *);

      };

     

      我們可以看到它實際上就是許多檔案操作的函數指標,其中就有write,其它的我們就不

      去管它了。這個write指標在實際的驅動程式中會以程式員所實現的函數名字出現,它指

      向程式員實現的裝置write操作函數。下面就是一個實際的例子,這個write函數可以向核

      心記憶體的一個數組裡輸入一個字串。

     

      int short_write (struct inode *inode, struct file *filp, const char *buf,

      int count){

      int retval = count;

      extern unsigned char kbuf[10];

 

      if(count>10)

      count=10;

      copy_from_user(kbuf, buf, count);

      return retval;

      }/* short_write */

      裝置short_t對應的fops方法是這樣聲明的:

      struct file_operations short_fops = {

      NULL, /* short_lseek */

      short_read,

      short_write,

      NULL, /* short_readdir */

      NULL, /* short_poll */

      NULL, /* short_ioctl */

      NULL, /* short_mmap */

      short_open,

      short_release,

      NULL, /* short_fsync */

      NULL, /* short_fasync */

      /* nothing more, fill with NULLs */

      };

     

      其中NULL的項目就是不提供這個功能。所以我們可以看出short_t裝置只提供了

      read,write,open,release功能。其中write功能我們在上面已經實現了,具體的實現函

      數起名為short_write。這些函數就是真正對裝置進行操作的函數,這就是驅動程式的一

    
大好處:不管你實現的時候是多麼的複雜,但對使用者來看,就是那些常用的檔案操作函數。

      

      但是我們可以看到,驅動程式裡的write函數有四個參數,函數格式如下:

      short_write (struct inode *inode, struct file *filp, const char *buf, int count)

      而使用者程式中的write函數只有三個參數,函數格式如下:

      write(inf fd, char *buf, int count)

      那他們兩個是怎麼聯絡在一起的呢?這就要靠作業系統核心中的函數sys_write了,下面

      是Linux Kernel 2.2.14中sys_write中的原始碼:

      asmlinkage ssize_t sys_write(unsigned int fd, const char * buf, size_t count)

      {

      ssize_t ret;

      struct file * file;

      struct inode * inode;

      ssize_t (*write)(struct file *, const char *, size_t, loff_t *); /* 指向

      驅動程式中的wirte函數的指標*/

 

      lock_kernel();

      ret = -EBADF;

      file = fget(fd); /* 通過檔案描述符得到檔案指標 */

      if (!file)

      goto bad_file;

      if (!(file->f_mode & FMODE_WRITE))

      goto out;

      inode = file->f_dentry->d_inode; /* 得到inode資訊 */

      ret = locks_verify_area(FLOCK_VERIFY_WRITE, inode, file, file->f_pos,

      count);

      if (ret)

      goto out;

      ret = -EINVAL;

      if (!file->f_op || !(write = file->f_op->write)) /* 將函數開始時聲明的

      write函數指標指向fops方法中對應的write函數 */

      goto out;

      down(&inode->i_sem);

      ret = write(file, buf, count, &file->f_pos); /*
使用驅動程式中的write函數

      將資料輸入裝置,注意看,這裡就是四個參數了 */

      up(&inode->i_sem);

      out:

      fput(file);

      bad_file:

      unlock_kernel();

      return ret;

      }

 

      我寫了一個簡單的程式來測試這個驅動程式,該程式原始碼節選如下(該省的我都省了):

     

      main(){

      int fd,count=0;

      unsigned char buf[10];

      fd=open("/dev/short_t",O_RDWR);

      printf("input string:");

      scanf("%s",buf);

      count=strlen(buf);

      if(count>10)

      count=10;

      count=write(fd,buf,count);

      close(fd);

      return 1;

      }

 

      現在我們就示範一下使用者使用write函數將資料寫到裝置裡面這個過程到底是怎麼實現的:

    
1,insmod驅動程式。驅動程式申請裝置名稱和主裝置號,這些可以在/proc/devieces中獲得。

      2,從/proc/devices中獲得主裝置號,並使用mknod命令建立裝置節點檔案。這是通過主

      裝置號將裝置節點檔案和裝置驅動程式聯絡在一起。裝置節點檔案中的file屬性中指明了

      驅動程式中fops方法實現的函數指標。

      3,使用者程式使用open開啟裝置節點檔案,這時作業系統核心知道該驅動程式工作了,就

      調用fops方法中的open函數進行相應的工作。open方法一般返回的是檔案標示符,實際

      上並不是直接對它進行操作的,而是有作業系統的系統調用在背後工作。

      4,當使用者使用write函數操作裝置檔案時,作業系統調用sys_write函數,該函數首先通

      過檔案標示符得到裝置節點檔案對應的inode指標和flip指標。inode指標中有裝置號信

      息,能夠告訴作業系統應該使用哪一個裝置驅動程式,flip指標中有fops資訊,可以告訴

      作業系統相應的fops方法函數在那裡可以找到。

      5,然後這時sys_write才會調用驅動程式中的write方法來對裝置進行寫的操作。

      其中1-3都是在使用者空間進行的,4-5是在核心空間進行的。使用者的write函數和作業系統

      的write函數通過系統調用sys_write聯絡在了一起。

      注意:

      對於塊裝置來說,還存在寫的模式的問題,這應該是由GNU C庫來解決的,這裡不予討

      論,因為我沒有看過GNU C庫的原始碼。

      另外,這是一個測試版的文章,請各位朋友們多提意見和建議,非常感謝

相關文章

聯繫我們

該頁面正文內容均來源於網絡整理,並不代表阿里雲官方的觀點,該頁面所提到的產品和服務也與阿里云無關,如果該頁面內容對您造成了困擾,歡迎寫郵件給我們,收到郵件我們將在5個工作日內處理。

如果您發現本社區中有涉嫌抄襲的內容,歡迎發送郵件至: info-contact@alibabacloud.com 進行舉報並提供相關證據,工作人員會在 5 個工作天內聯絡您,一經查實,本站將立刻刪除涉嫌侵權內容。

A Free Trial That Lets You Build Big!

Start building with 50+ products and up to 12 months usage for Elastic Compute Service

  • Sales Support

    1 on 1 presale consultation

  • After-Sales Support

    24/7 Technical Support 6 Free Tickets per Quarter Faster Response

  • Alibaba Cloud offers highly flexible support services tailored to meet your exact needs.