內核模塊中對文件的讀寫

平時網絡部分的東西碰的多些,這塊一開始還真不知道怎么寫,因為肯定和在用戶空間下是不同的。google過后,得到以下答案。一般可以用兩種方法:第一種是用系統調用。第二種方法是filp->open()等函數。下面分別來說下這兩種方法。


1 利用系統調用:
sys_open,sys_write,sys_read等。
其實分析過sys_open可以知道,最后調用的也是filp->open。
sys_open ==> do_sys_open ==> filp->open
在linuxsir上的一個帖子,上面一個版主說:sys_open和進程緊密相關,往往不在內核中使用。
而其實sys_open最后也是調用了filp->open。
其實好像Linux2.6.20后面就不推薦使用sys_open,那我們這里就就后者進行詳細的介紹

2 filp->open等函數。
在模塊中,用戶空間的open,read,write,llseek等函數都是不可以使用的。應該使用其在內核中對應的函數。可以使用filp->open配合struct file里的read/write來進行對文件的讀寫操作。


例子1:
  1. #include <linux/kernel.h>?
  2. #include <linux/module.h>?
  3. #include <linux/fs.h>?
  4. #include <asm/uaccess.h>?
  5. #include <linux/mm.h>?

  6. MODULE_AUTHOR("Kenthy@163.com.");?
  7. MODULE_DESCRIPTION("Kernel study and test.");?


  8. void fileread(const char * filename)?
  9. {?
  10. ??struct file *filp;?
  11. ??struct inode *inode;?
  12. ??mm_segment_t fs;?
  13. ??off_t fsize;?
  14. ??char *buf;?
  15. ??unsigned long magic;?
  16. ??printk("<1>start....\n");?
  17. ??filp=filp_open(filename,O_RDONLY,0);?
  18. ??inode=filp->f_dentry->d_inode;??
  19. ??
  20. ??magic=inode->i_sb->s_magic;?
  21. ??printk("<1>file system magic:%li \n",magic);?
  22. ??printk("<1>super blocksize:%li \n",inode->i_sb->s_blocksize);?
  23. ??printk("<1>inode %li \n",inode->i_ino);?
  24. ??fsize=inode->i_size;?
  25. ??printk("<1>file size:%i \n",(int)fsize);?
  26. ??buf=(char *) kmalloc(fsize+1,GFP_ATOMIC);?

  27. ??fs=get_fs();?
  28. ??set_fs(KERNEL_DS);?
  29. ??filp->f_op->read(filp,buf,fsize,&(filp->f_pos));?
  30. ??set_fs(fs);?

  31. ??buf[fsize]='\0';?
  32. ??printk("<1>The File Content is:\n");?
  33. ??printk("<1>%s",buf);?


  34. ??filp_close(filp,NULL);?
  35. }?

  36. void filewrite(char* filename, char* data)
  37. {
  38. ??struct file *filp;?
  39. mm_segment_t fs;
  40. filp = filp_open(filename, O_RDWR|O_APPEND, 0644);
  41. if(IS_ERR(filp))
  42. ? ? {
  43. ? ?? ?printk("open error...\n");?
  44. ? ?? ?return;
  45. ? ?? ???}? ?

  46. ??fs=get_fs();
  47. ??set_fs(KERNEL_DS);
  48. ??filp->f_op->write(filp, data, strlen(data),&filp->f_pos);
  49. ??set_fs(fs);
  50. ??filp_close(filp,NULL);
  51. }

  52. int init_module()?
  53. {?
  54. ??char *filename="/root/test1.c";?

  55. ??printk("<1>Read File from Kernel.\n");?
  56. ??fileread(filename);?
  57. ??filewrite(filename, "kernel write test\n");
  58. ??return 0;?
  59. }?

  60. void cleanup_module()?
  61. {?
  62. ??printk("<1>Good,Bye!\n");?
  63. }
復制代碼




eg2:
  1. #include<linux/module.h>
  2. #include<linux/kernel.h>
  3. #include<linux/init.h>

  4. #include<linux/types.h>

  5. #include<linux/fs.h>
  6. #include<linux/string.h>
  7. #include<asm/uaccess.h> /* get_fs(),set_fs(),get_ds() */

  8. #define FILE_DIR "/root/test.txt"

  9. MODULE_LICENSE("GPL");
  10. MODULE_AUTHOR("kenthy@163.com");

  11. char *buff = "module read/write test";
  12. char tmp[100];

  13. static struct file *filp = NULL;

  14. static int __init wr_test_init(void)
  15. {
  16. ? ? mm_segment_t old_fs;
  17. ? ? ssize_t ret;
  18. ? ??
  19. ? ? filp = filp_open(FILE_DIR, O_RDWR | O_CREAT, 0644);
  20. ? ??
  21. ? ? //? ? if(!filp)

  22. ? ? if(IS_ERR(filp))
  23. ? ?? ???printk("open error...\n");
  24. ? ??
  25. ? ? old_fs = get_fs();
  26. ? ? set_fs(get_ds());
  27. ? ??
  28. ? ? filp->f_op->write(filp, buff, strlen(buff), &filp->f_pos);
  29. ? ??
  30. ? ? filp->f_op->llseek(filp,0,0);
  31. ? ? ret = filp->f_op->read(filp, tmp, strlen(buff), &filp->f_pos);
  32. ? ??
  33. ? ? set_fs(old_fs);
  34. ? ??
  35. ? ? if(ret > 0)
  36. ? ?? ???printk("%s\n",tmp);
  37. ? ? else if(ret == 0)
  38. ? ?? ???printk("read nothing.............\n");
  39. ? ? else?
  40. ? ?? ???{
  41. ? ?? ?? ?? ?printk("read error\n");
  42. ? ?? ?? ?? ?return -1;
  43. ? ?? ???}

  44. ? ? return 0;
  45. }

  46. static void __exit wr_test_exit(void)
  47. {
  48. ? ? if(filp)
  49. ? ?? ???filp_close(filp,NULL);
  50. }

  51. module_init(wr_test_init);
  52. module_exit(wr_test_exit);
復制代碼




3.Makefile

  1. obj-m := os_attack.o

  2. KDIR := /lib/modules/$(uname -r)/build/
  3. PWD := $(shell pwd)

  4. all:module

  5. module:
  6. ? ? ? ? $(MAKE) -C $(KDIR) M=$(PWD) modules


  7. clean:
  8. ? ? ? ? rm -rf *.ko *.mod.c *.o Module.* modules.* .*.cmd .tmp_versions
復制代碼




注意:
在調用filp->f_op->read和filp->f_op->write等對文件的操作之前,應該先設置FS。
默認情況下,filp->f_op->read或者filp->f_op->write會對傳進來的參數buff進行指針檢查。如果不是在用戶空間會拒絕訪問。因為是在內核模塊中,所以buff肯定不在用戶空間,所以要增大其尋址范圍。

拿filp->f_op->write為例來說明:
filp->f_op->write最終會調用access_ok ==> range_ok.
而range_ok會判斷訪問的地址是否在0 ~ addr_limit之間。如果在,則ok,繼續。如果不在,則禁止訪問。而內核空間傳過來的buff肯定大于addr_limit。所以要set_fs(get_ds())。
這些函數在asm/uaccess.h中定義。以下是這個頭文件中的部分內容:

#define MAKE_MM_SEG(s)? ? ((mm_segment_t) { (s) })

#define KERNEL_DS? ? MAKE_MM_SEG(-1UL)
#define USER_DS? ?? ???MAKE_MM_SEG(PAGE_OFFSET)

#define get_ds()? ? (KERNEL_DS)
#define get_fs()? ? (current_thread_info()->addr_limit)
#define set_fs(x)? ? (current_thread_info()->addr_limit = (x))

#define segment_eq(a, b)? ? ((a).seg == (b).seg)


可以看到set_fs(get_ds())改變了addr_limit的值。這樣就使得從模塊中傳遞進去的參數也可以正常使用了。

在寫測試模塊的時候,要實現的功能是寫進去什么,然后讀出來放在tmp數組中。但寫完了以后filp->f_ops已經在末尾了,這個時候讀是什么也讀不到的,如果想要讀到數據,則應該改變filp->f-ops的值,這就要用到filp->f_op->llseek函數了。上網查了下,其中的參數需要記下筆記:


系統調用:
off_t sys_lseek(unsigned int fd, off_t offset, unsigned int origin)
offset是偏移量。
若origin是SEEK_SET(0),則將該文件的位移量設置為距文件開始處offset 個字節。
若origin是SEEK_CUR(1),則將該文件的位移量設置為其當前值加offset, offset可為正或負。
若origin是SEEK_END(2),則將該文件的位移量設置為文件長度加offset, offset可為正或負。

ok,that's all.

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/385020.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/385020.shtml
英文地址,請注明出處:http://en.pswp.cn/news/385020.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Makefile文件試錯

1成功&#xff1a; src $(wildcard ./*cpp) obj $(patsubst %.cpp,%.o ,$(src))target test$(target) : $(obj)g $(obj) -o $(target) -I/usr/include/mysql -L/usr/lib/mysql/ -lmysqlclient %.o: %.cppg -c $< -o $ -I/usr/include/mysql -L/usr/lib/mysql/ -lmysql…

內核定時器timer_list使用

Linux內核中提供了timer使用的API&#xff0c;做一個簡單的記要。 1. 包含的頭文件&#xff1a;linux/timer.h 2. 數據類型&#xff1a;struct timer_list; 包含的主要成員&#xff1a; a. data:傳遞到超時處理函數的參數&#xff0c;主要在多個定時器同時使用時&#xff0c;區…

內存四區

1.代碼區&#xff1a; 代碼區Code&#xff0c;程序被操作系統加載到內存的時候&#xff0c;所有的可執行代碼都加載到代碼區&#xff0c;也叫代碼段&#xff0c;這塊內存是不可以在運行期間修改的。 2. 靜態區 所有的全局變量以及程序中的靜態變量都存儲在靜態區。 #include<…

最高效的進(線)程間通信機制--eventfd

我們常用的進程&#xff08;線程&#xff09;間通信機制有管道&#xff0c;信號&#xff0c;消息隊列&#xff0c;信號量&#xff0c;共享內存&#xff0c;socket等等&#xff0c;其中主要作為進程&#xff08;線程&#xff09;間通知/等待的有管道pipe和socketpair。線程還有特…

malloc,calloc,realloc

與堆操作相關的兩個函數 malloc #include<stdio.h> #include<stdlib.h> #include<string.h>int main() {char *p malloc(10); //內存隨機&#xff0c;未做處理int i;for(i 0; i < 10: i){printf(“%d “,p[i]);} free(p);return 0; } 運行結果&…

Linux內核同步機制之completion

內核編程中常見的一種模式是&#xff0c;在當前線程之外初始化某個活動&#xff0c;然后等待該活動的結束。這個活動可能是&#xff0c;創建一個新的內核線程或者新的用戶空間進程、對一個已有進程的某個請求&#xff0c;或者某種類型的硬件動作&#xff0c;等等。在這種情況下…

可變參數函數(一)

一個函數可以接受不定數的參數個數&#xff0c;這就是可變參數函數&#xff0c;比較常見的比如printf(),scanf()&#xff1b; printf(const char* format,…); printf(“%d”,i); printf(“%s”,s); printf(“the number is %d,stirng is :%s”,i,s); 變量參數函數的簡單實現&a…

Linux內核線程kernel thread詳解--Linux進程的管理與調度

內核線程為什么需要內核線程Linux內核可以看作一個服務進程(管理軟硬件資源&#xff0c;響應用戶進程的種種合理以及不合理的請求)。 內核需要多個執行流并行&#xff0c;為了防止可能的阻塞&#xff0c;支持多線程是必要的。 內核線程就是內核的分身&#xff0c;一個分身可以處…

可變參數函數(二)

函數樣例&#xff1a; #include<stdio.h> #include<stdlib.h> #include<stdarg.h>double add(int n,...) {int i 0;double sum 0;va_list argptr;va_start(argptr,n);for(i 0 ; i < n; i){double d va_arg(argptr,double);printf("%d argument …

Linux 內核網絡協議棧 ------sk_buff 結構體 以及 完全解釋 (2.6.16)

在2.6.24之后這個結構體有了較大的變化&#xff0c;此處先說一說2.6.16版本的sk_buff&#xff0c;以及解釋一些問題。一、先直觀的看一下這個結構體~~~~~~~~~~~~~~~~~~~~~~在下面解釋每個字段的意義~~~~~~~~~~~[cpp] view plaincopyprint?struct sk_buff { /* These…

可變參數輸出(三)

Linux C關于輸出函數的定義&#xff1a; int printf(const char *format,…); int vprintf(const char * format,va_list ap); int vfprintf(FILE *stream,cosnt char *format,va_list ap); int vsprintf(char *str,const char *format,va_list ap); int vsnprintf(char *str,s…

最常用的設計模式---適配器模式(C++實現)

適配器模式屬于結構型的設計模式&#xff0c;它是結構型設計模式之首&#xff08;用的最多的結構型設計模式&#xff09;。 適配器設計模式也并不復雜&#xff0c;適配器它是主要作用是將一個類的接口轉換成客戶希望的另外一個接口這樣使得原本由于接口不兼容而不能一起工作的那…

Linux 簡單打印日志(二)

#include<stdio.h> #include<stdlib.h> #include<string.h> #include<time.h> //#include<windows.h> #include <unistd.h> // linux下頭文件#define FILE_MAX_SIZE (1024*1024)void get_local_time(char* buffer){time_t rawtime;struct …

程序隨筆——C++實現的一個線程池

1.線程池簡介 我們知道在線程池是一種多線程處理形式&#xff0c;處理過程中我們將相應的任務提交給線程池&#xff0c;線程池會分配對應的工作線程執行任務或存放在任務隊列中&#xff0c;等待執行。 面向對象編程中&#xff0c;創建和銷毀對象是需要消耗一定時間的&#xff0…

線程池原理及創建并C++實現

本文給出了一個通用的線程池框架&#xff0c;該框架將與線程執行相關的任務進行了高層次的抽象&#xff0c;使之與具體的執行任務無關。另外該線程池具有動態伸縮性&#xff0c;它能根據執行任務的輕重自動調整線程池中線程的數量。文章的最后&#xff0c;我們給出一個簡單示例…

Linux 打印簡單日志(一)

簡單日志輸出&#xff1a; #include<stdio.h> #include<string.h> #include<stdlib.h>void write(char* filename,char* szStr){FILE* fp;fp fopen(filename,"at");if(fp ! NULL){fwrite(szStr,256,1,fp); //fclose(fp);fp NULL;} }int main(int…

c++簡單線程池實現

線程池&#xff0c;簡單來說就是有一堆已經創建好的線程&#xff08;最大數目一定&#xff09;&#xff0c;初始時他們都處于空閑狀態&#xff0c;當有新的任務進來&#xff0c;從線程池中取出一個空閑的線程處理任務&#xff0c;然后當任務處理完成之后&#xff0c;該線程被重…

Linux 打印可變參數日志

實現了傳輸進去的字符串所在的文檔&#xff0c;函數和行數顯示功能。 實現了將傳入的可變參數打印到日志功能。 #include<stdio.h> #include<stdarg.h> #include<string.h>const char * g_path "/home/exbot/wangqinghe/log.txt"; #define LOG(fm…

C++強化之路之線程池開發整體框架(二)

一.線程池開發框架 我所開發的線程池由以下幾部分組成&#xff1a; 1.工作中的線程。也就是線程池中的線程&#xff0c;主要是執行分發來的task。 2.管理線程池的監督線程。這個線程的創建獨立于線程池的創建&#xff0c;按照既定的管理方法進行管理線程池中的所有線程&#xf…

vfprintf()函數

函數聲明&#xff1a;int vfprintf(FILE *stream, const char *format, va_list arg) 函數參數&#xff1a; stream—這是指向了FILE對象的指針&#xff0c;該FILE對象標識了流。 format—c語言字符串&#xff0c;包含了要被寫入到流stream中的文本。它可以包含嵌入的format標簽…