Возвращаемое значение процедуры 'show' последовательности ядра Linux

Я пытаюсь понять файлы последовательности в ядре Linux. ИМХО, они довольно непонятые звери, и с учетом этого я скомпилировал готовый модуль ядра из веб. Я воспроизвожу (измененный) код здесь для простоты доступа:

#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
#include <linux/kernel.h>
#include <linux/proc_fs.h>
#include <linux/fs.h>
#include <linux/seq_file.h>
#include <linux/slab.h>

static int limit = 10; //default value, it can be changed here or
module_param(limit, int, S_IRUGO); //transfered as a module parameter

static int* even_ptr; //we will work with dynamic memory

/**
 * start
 */
static void *ct_seq_start(struct seq_file *s, loff_t *pos) {
     printk(KERN_INFO "Entering start(), pos = %Ld, seq-file pos = %lu.\n", *pos, s->count);

     if ((*pos) >= limit) {     // are we done?
         printk(KERN_INFO "Apparently, we're done.\n");
         return NULL;
     }
     //Allocate an integer to hold our increasing even value
     even_ptr = kmalloc(sizeof(int), GFP_KERNEL);

     if (!even_ptr)     // fatal kernel allocation error
         return NULL;

     printk(KERN_INFO "In start(), even_ptr = 0x%pX.\n", even_ptr);
     *even_ptr = (*pos)*2;
     return even_ptr;
}

/**
 * show
 */
static int ct_seq_show(struct seq_file *s, void *v) {
     printk(KERN_INFO "In show(), even = %d.\n", *(int*)v);
     seq_printf(s, "The current value of the even number is %d\n", *(int*)v);
     // return (*(int *)v); // <-- Instead of a customary 'zero' I return the value of 'v' which is the actual even number that we are printing to the kernel log as well as the seq file
     return 0;
}

/**
 * next
 */
static void *ct_seq_next(struct seq_file *s, void *v, loff_t *pos) {
     printk(KERN_INFO "In next(), v = 0x%pX, *(int*)v=%d, pos=%Ld, seq-file pos=%lu.\n", v, *(int*)v, *pos, s->count);

     (*pos)++;              //increase my position counter
     if (*pos >= limit)     //are we done?
          return NULL;

     *(int*)v += 2;         //to the next even value

     return v;
 }

/**
 * stop
 */
static void ct_seq_stop(struct seq_file *s, void *v) {
     printk(KERN_INFO "Entering stop().\n");

     if (v)
         printk(KERN_INFO "v is %pX.\n", v);
     else
         printk(KERN_INFO "v is null.\n");

     printk(KERN_INFO "In stop(), even_ptr = %pX.\n", even_ptr);

     if (even_ptr) {
         printk(KERN_INFO "Freeing and clearing even_ptr.\n");
         kfree(even_ptr);
         even_ptr = NULL;
     } else
         printk(KERN_INFO "even_ptr is already null.\n");
}

/**
 * This structure gathers functions which control the sequential reading
 */
static struct seq_operations ct_seq_ops = {
     .start = ct_seq_start,
     .next  = ct_seq_next,
     .stop  = ct_seq_stop,
     .show  = ct_seq_show
};

/**
 * This function is called when a file from /proc is opened
 */
static int ct_open(struct inode *inode, struct file *file) {
     return seq_open(file, &ct_seq_ops);
};

/**
 * This structure gathers functions for a /proc-file operations
 */
static struct file_operations ct_file_ops = {
     .owner   = THIS_MODULE,
     .open    = ct_open,
     .read    = seq_read,
     .llseek  = seq_lseek,
     .release = seq_release
};

/**
 * This function is called when this module is loaded into the kernel
 */
static int __init ct_init(void) {
     proc_create("evens", 0, NULL, &ct_file_ops);
     return 0;
}

/**
 * This function is called when this module is removed from the kernel
 */
static void __exit ct_exit(void) {
     remove_proc_entry("evens", NULL);
}

module_init(ct_init);
module_exit(ct_exit);

MODULE_LICENSE("GPL");

Мой журнал ядра гласит:

Jun 30 19:10:22 mintab kernel: [ 8213.539959] Entering start(), pos = 0, seq-file pos = 0.
Jun 30 19:10:22 mintab kernel: [ 8213.539960] In start(), even_ptr = 0xffff9241772a8000.
Jun 30 19:10:22 mintab kernel: [ 8213.539960] In show(), even = 0.
Jun 30 19:10:22 mintab kernel: [ 8213.539962] In next(), v = 0xffff9241772a8000, *(int*)v=0, pos=0, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539962] In show(), even = 2.
Jun 30 19:10:22 mintab kernel: [ 8213.539963] In next(), v = 0xffff9241772a8000, *(int*)v=2, pos=1, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539963] In show(), even = 4.
Jun 30 19:10:22 mintab kernel: [ 8213.539964] In next(), v = 0xffff9241772a8000, *(int*)v=4, pos=2, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539964] In show(), even = 6.
Jun 30 19:10:22 mintab kernel: [ 8213.539965] In next(), v = 0xffff9241772a8000, *(int*)v=6, pos=3, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539965] In show(), even = 8.
Jun 30 19:10:22 mintab kernel: [ 8213.539966] In next(), v = 0xffff9241772a8000, *(int*)v=8, pos=4, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539966] In show(), even = 10.
Jun 30 19:10:22 mintab kernel: [ 8213.539967] In next(), v = 0xffff9241772a8000, *(int*)v=10, pos=5, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539967] In show(), even = 12.
Jun 30 19:10:22 mintab kernel: [ 8213.539968] In next(), v = 0xffff9241772a8000, *(int*)v=12, pos=6, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539969] In show(), even = 14.
Jun 30 19:10:22 mintab kernel: [ 8213.539969] In next(), v = 0xffff9241772a8000, *(int*)v=14, pos=7, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539970] In show(), even = 16.
Jun 30 19:10:22 mintab kernel: [ 8213.539970] In next(), v = 0xffff9241772a8000, *(int*)v=16, pos=8, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539971] In show(), even = 18.
Jun 30 19:10:22 mintab kernel: [ 8213.539971] In next(), v = 0xffff9241772a8000, *(int*)v=18, pos=9, seq-file pos=42.
Jun 30 19:10:22 mintab kernel: [ 8213.539972] Entering stop().
Jun 30 19:10:22 mintab kernel: [ 8213.539972] v is null.
Jun 30 19:10:22 mintab kernel: [ 8213.539972] In stop(), even_ptr = ffff9241772a8000.
Jun 30 19:10:22 mintab kernel: [ 8213.539973] Freeing and clearing even_ptr.
Jun 30 19:10:22 mintab kernel: [ 8213.539996] Entering start(), pos = 10, seq-file pos = 0.
Jun 30 19:10:22 mintab kernel: [ 8213.539996] Apparently, we're done.
Jun 30 19:10:22 mintab kernel: [ 8213.539997] Entering stop().
Jun 30 19:10:22 mintab kernel: [ 8213.539997] v is null.
Jun 30 19:10:22 mintab kernel: [ 8213.539997] In stop(), even_ptr =           (null).
Jun 30 19:10:22 mintab kernel: [ 8213.539998] even_ptr is already null.

И файл proc выдает только следующий вывод, а затем останавливается: Текущее значение четного числа равно 0

Я исхожу из того, что подпрограмма «show» должна возвращать 0, чтобы считаться безошибочной для базовой файловой экосистемы seq. Однако разве маленькое положительное целое число не указывало бы на то же самое?? или они используются как-то по-другому??

UPDATE1: Как упоминалось в ответе нашего друга Цыварева, явно требуется вернуть 0 из подпрограммы show, как и все остальное считается неудачным, поэтому возвращается обратно соответственно. Сказав это, может ли кто-нибудь объяснить, почему мы снова вводим start после того, как исчерпали все числа, и v, наконец, NULL ?? Мне особенно интересно знать, почему мы это делаем?

Jul  1 11:16:59 mintab kernel: [31765.915542] v is null.
Jul  1 11:16:59 mintab kernel: [31765.915542] In stop(), even_ptr = ffff9c7df619b340.
Jul  1 11:16:59 mintab kernel: [31765.915542] Freeing and clearing even_ptr.
Jul  1 11:16:59 mintab kernel: [31765.915551] Entering start(), pos = 200, seq-file pos = 0.
Jul  1 11:16:59 mintab kernel: [31765.915551] Apparently, we're done.
Jul  1 11:16:59 mintab kernel: [31765.915552] Entering stop().
Jul  1 11:16:59 mintab kernel: [31765.915552] v is null.
Jul  1 11:16:59 mintab kernel: [31765.915553] In stop(), even_ptr =           (null).

Кроме того, приветствуются дополнительные ссылки на более подробное и (надеюсь) более простое объяснение механизма файла последовательности.


person HighOnMeat    schedule 30.06.2019    source источник


Ответы (1)


Последовательные файлы описаны в документации ядра в разделе Documentation/ файловые системы/seq_file.txt. Согласно документам, функция show должна возвращать:

  • 0 при успехе
  • -ERR (отрицательный код ошибки) при сбое
  • SEQ_SKIP для отмены любого вывода для текущего элемента

Текущая реализация обрабатывает любое положительное возвращаемое значение show как SEQ_SKIP:

// (from traverse() function in fs/seq_file.c)
// ...
error = m->op->show(m, p);

if (error < 0) // <-- negative return value is an error indicator
    break;
if (unlikely(error)) { // <-- this is for positive return values
    error = 0; // <-- as if 'show' returns 0...
    m->count = 0; // <-- ... but with output dropped
}

Итак, если вы хотите сообщить об успешном выполнении show, просто верните 0.

person Tsyvarev    schedule 30.06.2019