Rumah  >  Artikel  >  Tutorial sistem  >  Sistem fail interaktif kernel dalam sistem Linux: Penjelasan terperinci tentang proc yang dibina sendiri

Sistem fail interaktif kernel dalam sistem Linux: Penjelasan terperinci tentang proc yang dibina sendiri

WBOY
WBOYke hadapan
2024-02-13 23:00:23445semak imbas

proc ialah sistem fail khas dalam sistem Linux Ia digunakan untuk menyediakan antara muka interaktif antara kernel dan ruang pengguna, seperti memaparkan maklumat kernel, mengubah suai parameter kernel, mengawal fungsi kernel, dsb. Kelebihan proc ialah ia mudah dan mudah digunakan serta tidak memerlukan peralatan atau pemandu tambahan. Pelaksanaan proc melibatkan konsep seperti struktur proc_dir_entry, fungsi proc_create, dan mekanisme seq_file. Dalam artikel ini, kami akan memperkenalkan prinsip dan kaedah membina sendiri proc dalam teknologi debugging kernel Linux, termasuk mencipta dan memadam fail proc, membaca dan menulis fail proc, menggunakan mekanisme seq_file, dsb., dan memberi contoh untuk menggambarkan penggunaannya. dan langkah berjaga-jaga.

1 Menggunakan printk dalam kernel boleh menyimpan maklumat penyahpepijatan dalam penimbal log_buf Anda boleh menggunakan perintah #cat /proc/kmsg untuk mencetak data berangka di kawasan penimbal Hari ini kita akan mengkajinya dan menulis fail kmsg sendiri. kami menamakannya mymsg.

2. Semak cara /proc/kmsg ditulis dalam kernel!

Dalam fail Proc_misc.c (fsproc):

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

Dalam fail Kmsg.c (fsproc):

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

Apabila menggunakan cat /proc/kmsg dalam ruang pengguna, kmsg_open akan dipanggil, dan fungsi kmsg_read akan dipanggil untuk membaca data dalam log_buf dan menyalinnya ke ruang pengguna untuk paparan.

3 Sebelum menulis, kita perlu belajar tentang giliran bulat

Baris gilir adalah struktur data yang sangat berguna dalam pengaturcaraan sebenar. Ia mempunyai ciri-ciri berikut.

Ia adalah struktur data FIFO yang disambungkan hujung ke hujung, menggunakan ruang linear tatasusunan Organisasi data adalah mudah, dan anda boleh mengetahui dengan cepat sama ada baris gilir penuh atau kosong. Data boleh diakses dengan sangat cepat.

Oleh kerana kesederhanaan dan kecekapan, baris gilir cincin juga dilaksanakan dalam perkakasan.

Baris gilir dering digunakan secara meluas untuk penghantaran dan penerimaan data rangkaian, dan untuk pertukaran data antara program yang berbeza (seperti menukar sejumlah besar data antara kernel dan aplikasi, dan menerima sejumlah besar data daripada perkakasan).

3.1 Prinsip pelaksanaan baris gilir

Tiada struktur cincin dalam ingatan, jadi baris gilir cincin sebenarnya dilaksanakan oleh ruang linear tatasusunan. Jadi apa yang perlu dilakukan apabila data mencapai penghujung? Ia akan kembali ke kedudukan 0 untuk diproses. Pembalikan ini dilakukan melalui operasi modulo.

<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>

Kunci kepada baris gilir pekeliling adalah untuk menentukan sama ada baris gilir kosong atau penuh. Apabila ekor mengejar kepala, barisan penuh, dan apabila kepala mengejar ekor, barisan kosong. Tetapi bagaimana untuk mengetahui siapa yang mengejar siapa. Beberapa cara tambahan juga diperlukan untuk menilai.Sistem fail interaktif kernel dalam sistem Linux: Penjelasan terperinci tentang proc yang dibina sendiri

Bagaimana untuk menilai sama ada barisan pekeliling kosong atau penuh? Terdapat dua cara untuk menilai.

1. Lampirkan tanda bendera

Apabila kepala mengejar ekor dan baris gilir kosong, tetapkan tag=0,

Apabila ekor mengejar kepala dan barisan penuh, biarkan tag=1,


2 Hadkan ekor untuk mengejar kepala, iaitu terdapat sekurang-kurangnya satu elemen ruang antara nod ekor dan nod kepala pasukan.

Barisan kosong: kepala==ekor

Barisan penuh: (ekor+1)% MAXN ==head


Sistem fail interaktif kernel dalam sistem Linux: Penjelasan terperinci tentang proc yang dibina sendiri4
#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. Program ujian

Nota: EXPORT_SYMBOL(myprintk) digunakan dalam program di atas; yang bermaksud myprintk boleh digunakan dalam keseluruhan ruang kernel.
使用方法:①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 Uji kesan dalam 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系统中一种有用而灵活的文件系统,它可以实现对内核的交互和反馈,也可以提升内核的可维护性和可扩展性。希望本文能够对你有所帮助和启发。

Atas ialah kandungan terperinci Sistem fail interaktif kernel dalam sistem Linux: Penjelasan terperinci tentang proc yang dibina sendiri. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:lxlinux.net. Jika ada pelanggaran, sila hubungi admin@php.cn Padam