Home  >  Article  >  System Tutorial  >  Kernel interactive file system in Linux system: Detailed explanation of self-constructed proc

Kernel interactive file system in Linux system: Detailed explanation of self-constructed proc

WBOY
WBOYforward
2024-02-13 23:00:23480browse

Proc is a special file system in the Linux system. It is used to provide an interactive interface between the kernel and user space, such as displaying kernel information, modifying kernel parameters, controlling kernel functions, etc. The advantage of proc is that it is simple and easy to use and does not require additional equipment or drivers. The implementation of proc involves concepts such as the proc_dir_entry structure, proc_create function, and seq_file mechanism. In this article, we will introduce the principles and methods of self-constructing proc in Linux kernel debugging technology, including creating and deleting proc files, reading and writing proc files, using the seq_file mechanism, etc., and giving examples to illustrate their usage and precautions. .

1 Introduction

Using printk in the kernel can save debugging information in the log_buf buffer. You can use the command #cat /proc/kmsg to print out the numerical data in the buffer area. Today we will study it and write kmsg ourselves. We named this file mymsg.

2. Check how /proc/kmsg is written in the kernel!

In the Proc_misc.c (fs\proc) file:

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;
  ......................... 
}

In the Kmsg.c (fs\proc) file:

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

When using cat /proc/kmsg in user space, kmsg_open will be called, and the kmsg_read function will be called to read the data in log_buf and copy it to user space for display.

3. Before writing, we need to learn about circular queue

Ring queue is an extremely useful data structure in actual programming. It has the following characteristics.

It is a FIFO data structure connected end to end, using the linear space of the array. The data organization is simple, and you can quickly know whether the queue is full or empty. Data can be accessed very quickly.

Because of simplicity and efficiency, ring queues are even implemented in hardware.

Ring queues are widely used for network data transmission and reception, and for data exchange between different programs (such as exchanging large amounts of data between the kernel and applications, and receiving large amounts of data from hardware).

3.1. Ring queue implementation principle

There is no ring structure in the memory, so the ring queue is actually implemented by the linear space of the array. So what to do when the data reaches the end? It will turn back to position 0 for processing. This conversion is performed using a modulo operation.

<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>
Kernel interactive file system in Linux system: Detailed explanation of self-constructed proc

The key to the ring queue is to determine whether the queue is empty or full. When tail catches up with head, the queue is full, and when head catches up with tail, the queue is empty. But how to know who is catching up with whom. Some auxiliary means are also needed to judge.

How to judge whether the ring queue is empty or full? There are two ways to judge.

1. Attaching a flag tag

When head catches up with tail and the queue is empty, set tag=0,
When tail catches up with head and the queue is full, let tag=1,

2. Limit tail to catch up with head, that is, there is at least one element of space between the tail node and the head node of the team.

The queue is empty: head==tail
Queue full: (tail 1)% MAXN ==head

Kernel interactive file system in Linux system: Detailed explanation of self-constructed proc

4. Programming

#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. Test program

Note: EXPORT_SYMBOL(myprintk) is used in the above program; which means that myprintk can be used in the entire kernel space.

使用方法:①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. Test the effect in 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系统中一种有用而灵活的文件系统,它可以实现对内核的交互和反馈,也可以提升内核的可维护性和可扩展性。希望本文能够对你有所帮助和启发。

The above is the detailed content of Kernel interactive file system in Linux system: Detailed explanation of self-constructed proc. For more information, please follow other related articles on the PHP Chinese website!

Statement:
This article is reproduced at:lxlinux.net. If there is any infringement, please contact admin@php.cn delete