搜尋
首頁系統教程LinuxLinux系統中的核心互動檔案系統:自構proc詳解

Linux系統中的核心互動檔案系統:自構proc詳解

Feb 13, 2024 pm 11:00 PM
linuxlinux教程linux系統linux指令shell腳本嵌入式linuxlinux入門linux學習

proc是Linux系統中一種特殊的檔案系統,它用來提供內核和用戶空間的交互接口,如顯示內核訊息,修改內核參數,控制內核功能等。 proc的優點是簡單易用,不需要額外的設備或驅動。 proc的實作涉及proc_dir_entry結構體,proc_create函數,seq_file機制等概念。在本文中,我們將介紹Linux內核調試技術之自構proc的原理和方法,包括創建和刪除proc文件,讀取和寫入proc文件,使用seq_file機制等,並舉例說明它們的使用方法和注意事項。

1、簡介

#在核心中使用printk可以講調試資訊保存在log_buf緩衝區中,可以使用命令#cat /proc/kmsg 將緩衝區的數區的數資料列印出來,今天我們就來研究一下,自己寫kmsg這個文件,我們取名叫做mymsg。

2、看核心中 /proc/kmsg怎麼寫的!

在Proc_misc.c (fs\proc) 檔案中:

void __init proc_misc_init(void)
{
    .........................
        struct proc_dir_entry *entry;
        //这里创建了一个proc入口kmsg
        entry = create_proc_entry("kmsg", S_IRUSR, &proc_root);
        if (entry)
       /*构造一个proc_fops结构*/
       entry->proc_fops = &proc_kmsg_operations;
  ......................... 
}

在Kmsg.c (fs\proc) 檔案中:

const struct file_operations proc_kmsg_operations = {
    .read        = kmsg_read,
    .poll        = kmsg_poll,
    .open        = kmsg_open,
    .release    = kmsg_release,
};

在使用者空間使用 cat /proc/kmsg的時候,會呼叫kmsg_open,在呼叫kmsg_read函數,讀取log_buf中的數據,並拷貝到使用者空間顯示。

3、在寫之前,我們需要來學習循環佇列

#環形佇列是在實際程式設計極為有用的資料結構,它有以下特點。

它是一個首尾相連的FIFO的資料結構,採用陣列的線性空間,資料組織簡單,很快就能知道佇列是否滿為空。能以很快速度的來存取資料。

因為有簡單且有效率的原因,甚至在硬體都實作了環形佇列。

環形佇列廣泛用於網路資料收發,和不同程式間資料交換(例如核心與應用程式大量交換數據,從硬體接收大量資料)均使用了環形佇列。

3.1.環形佇列實作原理

記憶體上沒有環形的結構,因此環形佇列實在是數組的線性空間來實現。那當數據到了尾部要如何處理呢?它將轉回到0位置來處理。這個的轉回是透過取模操作來執行的。

<code style="display: -webkit-box;font-family: Operator Mono, Consolas, Monaco, Menlo, monospace;border-radius: 0px;font-size: 12px">因此环列队列的是逻辑上将数组元素q[0]与q[MAXN-1]连接,形成一个存放队列的环形空间。

为了方便读写,还要用数组下标来指明队列的读写位置。head/tail.其中head指向可以读的位置,tail指向可以写的位置。
</code>
Linux系統中的核心互動檔案系統:自構proc詳解

環形佇列的關鍵是判斷佇列為空,還是為滿。當tail追上head時,佇列為滿時,當head追上tail時,佇列為空。但如何知道誰追上誰。還需要一些輔助的手段來判斷.

如何判斷環形隊列為空,為滿有兩種判斷方法。

一.是附加一個標誌位元tag

當head趕上tail,隊列空,則令tag=0,
當tail趕上head,隊列滿,則令tag=1,

二.限制tail趕上head,即隊尾結點與隊首結點之間至少留有一個元素的空間。

佇列空: head==tail
隊列滿: (tail 1)% MAXN ==head

Linux系統中的核心互動檔案系統:自構proc詳解

4、程式編寫

#include 
\#include
\#include
\#include
\#include
\#include
\#include
\#include
\#include
\#include
\#include

\#define MYLOG_BUF_LEN 1024
static char mylog_buf[MYLOG_BUF_LEN];
static char tmp_buf[MYLOG_BUF_LEN];
static int mylog_r = 0;
static int mylog_w = 0;
static int mylog_r_tmp = 0;

/*休眠队列初始化*/
static DECLARE_WAIT_QUEUE_HEAD(mymsg_waitq);

/*
*判断环形队列是否为空
*返回0:表示不空 返回1:表示空
*/
static int is_mylog_empty(void)
{
  return (mylog_r == mylog_w);
}

/*
*判断环形队列是否满
*返回0:表示不满 返回1:表示满
*/
static int is_mylog_full(void)
{
  return((mylog_w + 1)% MYLOG_BUF_LEN == mylog_r);
}

/*
*在读取的时候,判断环形队列中数据是否为空
*返回0:表示不空 返回1:表示空
*/
static int is_mylog_empty_for_read(void)
{
  return (mylog_r_tmp == mylog_w);
}

/*
*往循环队列中存字符
*输入:c字符 单位:1byte
*输出:无
*/
static void mylog_putc(char c)
{

  if(is_mylog_full())
  {
    /*如果检测到队列已经满了,则丢弃该数据*/
    mylog_r= (mylog_r + 1) % MYLOG_BUF_LEN;
    
    /*mylog_r_tmp不能大于mylog_r*/
    if((mylog_r_tmp + 1)% MYLOG_BUF_LEN == mylog_r)
      mylog_r_tmp= mylog_r;
    
  }
  mylog_buf[mylog_w]= c;
  /*当mylog_w=1023的时候 (mylog_w+1) % MYLOG_BUF_LEN =0,回到队列头,实现循环*/
  mylog_w= (mylog_w + 1) % MYLOG_BUF_LEN;
  /* 唤醒等待数据的进程*/  
  wake_up_interruptible(&mymsg_waitq); 
}

/*
*从循环队列中读字符
*输入:*p 单位:1byte
*输出:1表示成功
*/
static int mylog_getc(char *p)
{
  /*判断数据是否为空*/
  if (is_mylog_empty_for_read())
  {
    return 0;
  }
  *p = mylog_buf[mylog_r_tmp ];
  mylog_r_tmp = (mylog_r_tmp + 1) % MYLOG_BUF_LEN;
  return 1;
}

/*
*调用myprintk,和printf用法相同
*/
int myprintk(const char *fmt, ...)
{
  va_list args;
  int i;
  int j;

  va_start(args, fmt);
  i= vsnprintf(tmp_buf, INT_MAX, fmt, args);
  va_end(args);
  
  for (j = 0; j return i;
}


static ssize_t mymsg_read(struct file *file, char __user *buf,
      size_t count, loff_t*ppos)
{
  int error=0;
  size_t i=0;
  char c;
  /* 把mylog_buf的数据copy_to_user, return*/

  /*非阻塞 和 缓冲区为空的时候返回*/
  if ((file->f_flags & O_NONBLOCK) && is_mylog_empty())
    return -EAGAIN;
  
  /*休眠队列wait_event_interruptible(xxx,0)-->休眠*/
  error= wait_event_interruptible(mymsg_waitq, !is_mylog_empty_for_read());
  
  /* copy_to_user*/
  while (!error && (mylog_getc(&c)) && i if (!error)
    error= i;
  /*返回实际读到的个数*/
  return error;
}

static int mymsg_open(struct inode * inode, struct file * file)
{
  mylog_r_tmp= mylog_r;
  return 0;
}


const struct file_operations proc_mymsg_operations = {
  .read= mymsg_read,
  .open= mymsg_open,
  };
static int mymsg_init(void)
{
  struct proc_dir_entry *myentry; kmsg
  myentry= create_proc_entry("mymsg", S_IRUSR, &proc_root);
  if (myentry)
    myentry->proc_fops = &proc_mymsg_operations;
  return 0;
}

static void mymsg_exit(void)
{
  remove_proc_entry("mymsg", &proc_root);
}

module_init(mymsg_init);
module_exit(mymsg_exit);

/*声名到内核空间*/
EXPORT_SYMBOL(myprintk);

MODULE_LICENSE("GPL");

5、測試程式

#注意:在上面程式中 使用了 EXPORT_SYMBOL(myprintk);意思是把myprintk可以在整個核心空間使用。

使用方法:①extern int myprintk(const char *fmt, ...);声明

      ② myprintk("first_drv_open : %d\n", ++cnt);使用

\#include 
\#include
\#include
\#include
\#include
\#include
\#include
\#include
\#include
\#include

static struct class *firstdrv_class;
static struct class_device  *firstdrv_class_dev;

volatile unsigned long *gpfcon = NULL;
volatile unsigned long *gpfdat = NULL;

extern int myprintk(const char *fmt, ...);

static int first_drv_open(struct inode *inode, struct file *file)
{
  static int cnt = 0;
  myprintk("first_drv_open : %d\n", ++cnt);
  /* 配置GPF4,5,6为输出*/
  *gpfcon &= ~((0x3return 0;
}

static ssize_t first_drv_write(struct file *file, const char __user *buf,
size_t count, loff_t * ppos)
{
  int val;
  static int cnt = 0;

  myprintk("first_drv_write : %d\n", ++cnt);

  copy_from_user(&val, buf, count); //  copy_to_user();

  if (val == 1)
  {
    // 点灯
    *gpfdat &= ~((1else
  {
    // 灭灯
    *gpfdat |= (1return 0;
}

static struct file_operations first_drv_fops = {
  .owner = THIS_MODULE,  /* 这是一个宏,推向编译模块时自动创建的__this_module变量*/
  .open = first_drv_open,  
  .write  =  first_drv_write,   
};


int major;
static int first_drv_init(void)
{
  myprintk("first_drv_init\n");
  major= register_chrdev(0, "first_drv", &first_drv_fops); // 注册, 告诉内核

  firstdrv_class= class_create(THIS_MODULE, "firstdrv");

  firstdrv_class_dev= class_device_create(firstdrv_class, NULL, MKDEV(major, 0),
 NULL, "xyz"); /* /dev/xyz*/

  gpfcon= (volatile unsigned long *)ioremap(0x56000050, 16);
  gpfdat= gpfcon + 1;

  return 0;
}

static void first_drv_exit(void)
{
  unregister_chrdev(major,"first_drv"); // 卸载

  class_device_unregister(firstdrv_class_dev);
  class_destroy(firstdrv_class);
  iounmap(gpfcon);
}

module_init(first_drv_init);
module_exit(first_drv_exit);


MODULE_LICENSE("GPL");

6、在tty中測試效果

# insmod my_msg.ko``# insmod first_drv.ko``# cat /proc/mymsg``mymsg_open mylog_r_
tmp=0``first_drv_init

通过本文,我们了解了Linux内核调试技术之自构proc的原理和方法,它们可以用来实现对内核的调试和控制。我们应该根据实际需求选择合适的方法,并遵循一些基本原则,如使用正确的文件名,使用正确的读写函数,使用正确的seq_file操作等。proc是Linux系统中一种有用而灵活的文件系统,它可以实现对内核的交互和反馈,也可以提升内核的可维护性和可扩展性。希望本文能够对你有所帮助和启发。

以上是Linux系統中的核心互動檔案系統:自構proc詳解的詳細內容。更多資訊請關注PHP中文網其他相關文章!

陳述
本文轉載於:良许Linux教程网。如有侵權,請聯絡admin@php.cn刪除
Linux和Windows之間的用戶帳戶管理有什麼區別?Linux和Windows之間的用戶帳戶管理有什麼區別?May 02, 2025 am 12:02 AM

Linux和Windows在用戶賬戶管理上的主要區別在於權限模型和管理工具。 Linux使用基於Unix的權限模型和命令行工具(如useradd、usermod、userdel),而Windows採用自己的安全模型和圖形用戶界面(GUI)管理工具。

Linux的命令行環境如何使其比Windows更安全?Linux的命令行環境如何使其比Windows更安全?May 01, 2025 am 12:03 AM

Linux'scommandlinecanbemoresecurethanWindowsifmanagedcorrectly,butrequiresmoreuserknowledge.1)Linux'sopen-sourcenatureallowsforquicksecurityupdates.2)Misconfigurationcanleadtovulnerabilities.Windows'commandlineismorecontrolledbutlesscustomizable,with

如何在Linux中自動製作USB驅動器安裝如何在Linux中自動製作USB驅動器安裝Apr 30, 2025 am 10:04 AM

本指南說明瞭如何在Linux的啟動下自動安裝USB驅動器,從而節省了時間和精力。 步驟1:確定您的USB驅動器 使用LSBLK命令列出所有塊設備。 您的USB驅動器可能會標記為 /dev /sdb1, /dev /sdc1等

2025年,最佳Linux,Windows和Mac的最佳跨平台應用程序2025年,最佳Linux,Windows和Mac的最佳跨平台應用程序Apr 30, 2025 am 09:57 AM

跨平台應用程序已徹底改變了軟件開發,從而在Linux,Windows和MacOS等操作系統上實現了無縫功能。 這消除了根據您的設備切換應用程序的需求,提供一致的體驗

2025年AI和機器學習的最佳Linux工具2025年AI和機器學習的最佳Linux工具Apr 30, 2025 am 09:44 AM

人工智能(AI)正在迅速改變許多部門,從醫療保健和金融到藝術和音樂等創意領域。 Linux具有開源性,適應性和性能功能,已成為首要的Platfo

5最佳輕巧的Linux發行版,不帶GUI5最佳輕巧的Linux發行版,不帶GUIApr 30, 2025 am 09:38 AM

尋找沒有圖形用戶界面(GUI)的快速,最小和高效的Linux分佈? 輕巧,無GUI-Linux發行版非常適合較舊的硬件或服務器和嵌入式系統(例如服務器和嵌入式系統)。他們消耗較少的res

如何在Redhat發行中安裝葡萄酒10.0如何在Redhat發行中安裝葡萄酒10.0Apr 30, 2025 am 09:32 AM

Wine 10.0穩定版發布:在Linux上運行Windows應用更上一層樓 Wine,這款開源免費的應用程序,讓Linux用戶能夠在Unix/Linux類操作系統上運行Windows軟件和遊戲,迎來了10.0穩定版的發布!此版本已提供源代碼和二進制包下載,支持Linux、Windows和Mac等多種發行版。 這一版本凝聚了一年的辛勤工作和超過8600項改進,帶來了諸多令人興奮的提升。主要亮點包括: 增強對藍牙設備的支持。 提升對HID輸入設備的支持。 優化了32位和64位應用程序的運行性能。

如何在RHEL上安裝和配置SQL Server如何在RHEL上安裝和配置SQL ServerApr 30, 2025 am 09:27 AM

該教程通過在RHEL 8.x或9.x上安裝SQL Server 2022,通過SQLCMD命令行工具,數據庫創建和基本查詢連接。 先決條件 開始之前,請確保: 支持的RHEL版本(RHEL 8或9)。 Sudo

See all articles

熱AI工具

Undresser.AI Undress

Undresser.AI Undress

人工智慧驅動的應用程序,用於創建逼真的裸體照片

AI Clothes Remover

AI Clothes Remover

用於從照片中去除衣服的線上人工智慧工具。

Undress AI Tool

Undress AI Tool

免費脫衣圖片

Clothoff.io

Clothoff.io

AI脫衣器

Video Face Swap

Video Face Swap

使用我們完全免費的人工智慧換臉工具,輕鬆在任何影片中換臉!

熱工具

SecLists

SecLists

SecLists是最終安全測試人員的伙伴。它是一個包含各種類型清單的集合,這些清單在安全評估過程中經常使用,而且都在一個地方。 SecLists透過方便地提供安全測試人員可能需要的所有列表,幫助提高安全測試的效率和生產力。清單類型包括使用者名稱、密碼、URL、模糊測試有效載荷、敏感資料模式、Web shell等等。測試人員只需將此儲存庫拉到新的測試機上,他就可以存取所需的每種類型的清單。

記事本++7.3.1

記事本++7.3.1

好用且免費的程式碼編輯器

DVWA

DVWA

Damn Vulnerable Web App (DVWA) 是一個PHP/MySQL的Web應用程序,非常容易受到攻擊。它的主要目標是成為安全專業人員在合法環境中測試自己的技能和工具的輔助工具,幫助Web開發人員更好地理解保護網路應用程式的過程,並幫助教師/學生在課堂環境中教授/學習Web應用程式安全性。 DVWA的目標是透過簡單直接的介面練習一些最常見的Web漏洞,難度各不相同。請注意,該軟體中

Dreamweaver CS6

Dreamweaver CS6

視覺化網頁開發工具

MinGW - Minimalist GNU for Windows

MinGW - Minimalist GNU for Windows

這個專案正在遷移到osdn.net/projects/mingw的過程中,你可以繼續在那裡關注我們。 MinGW:GNU編譯器集合(GCC)的本機Windows移植版本,可自由分發的導入函式庫和用於建置本機Windows應用程式的頭檔;包括對MSVC執行時間的擴展,以支援C99功能。 MinGW的所有軟體都可以在64位元Windows平台上運作。