Что означают параметры в реализации чтения с помощью scull? - PullRequest
0 голосов
/ 28 мая 2019

В LDD3 (книга «Драйверы устройств для Linux 3-е издание») scullpipe, что означают параметры в static int scull_read_p_mem(char *buf, char **start, off_t offset, int count, int *eof, void *data)? В частности, я не понимаю разницу между start, page и offset.

У меня есть ряд вопросов относительно фактической реализации (которые можно найти ниже):

struct scull_pipe {
        wait_queue_head_t inq, outq;       /* read and write queues */
        char *buffer, *end;                /* begin of buf, end of buf */
        int buffersize;                    /* used in pointer arithmetic */
        char *rp, *wp;                     /* where to read, where to write */
        int nreaders, nwriters;            /* number of openings for r/w */
        struct fasync_struct *async_queue; /* asynchronous readers */
        struct semaphore sem;              /* mutual exclusion semaphore */
        struct cdev cdev;                  /* Char device structure */
};

int scull_p_nr_devs;            /* the number of scull_pipe devices */
scull_pipe *scull_p_devices;    /* scull_pipe devices to be malloc'ed */

/* ...... */

/* our proc read implementation */
static int scull_read_p_mem(char *buf, char **start, off_t offset, int count,
        int *eof, void *data)
{
    int i, len;
    struct scull_pipe *p;

#define LIMIT (PAGE_SIZE-200)   /* don't print any more after this size */
    *start = buf;
    len = sprintf(buf, "Default buffersize is %i\n", scull_p_buffer);
    for(i = 0; i < scull_p_nr_devs && len <= LIMIT; i++) {
        p = &scull_p_devices[i];
        if (down_interruptible(&p->sem))
            return -ERESTARTSYS;
        len += sprintf(buf+len, "\nDevice %i: %p\n", i, p);
        len += sprintf(buf+len, "   Buffer: %p to %p (%i bytes)\n", 
                                    p->buffer, p->end, p->buffersize);
        len += sprintf(buf+len, "   rp %p   wp %p\n", p->rp, p->wp);
        len += sprintf(buf+len, "   readers %i   writers %i\n", 
                                    p->nreaders, p->nwriters);
        up(&p->sem);
        scullp_proc_offset(buf, start, &offset, &len);
    }
    *eof = (len <= LIMIT);
    return len;
}


static void scullp_proc_offset(char *buf, char **start, off_t *offset, int *len)
{
    /* QUESTION: what does this function do? */
    if (*offset == 0)
        return;
    if (*offset >= *len) {
        *offset -= *len;    /* QUESTION: what is the purpose of this? */
        *len = 0;
    }
    else {
        *start = buf + *offset; /* QUESTION: why do you need to change "start"? */
        *offset = 0;
    }
}

1 Ответ

1 голос
/ 28 мая 2019

Функция scull_read_p_mem используется для создания записи процедуры здесь с использованием create_proc_read_entry функции.5-минутный поиск в Google дал этой странице , которая объясняет параметры в указателе функции, переданной функции create_proc_read_entry.С фиксированным форматированием это:

Аргументы:
* buf: Ядро выделяет страницу памяти любому процессу, который пытается прочитать запись процесса.Указатель страницы указывает на тот буфер памяти, в который записываются данные.
** start: этот указатель используется, когда чтение файла proc должно начинаться не с начала файла, а с определенного смещения.Для небольших операций чтения обычно устанавливается значение NULL.
off: смещение от начала файла, в котором указатель файла в данный момент указывает на
count: число байтов данных, которые нужно прочитать
data:данные, переданные из вызова функции create_read_proc_entry.
eof: установлено в 1, чтобы указать конец файла

Но через некоторое время я также нашел несколько документов в kenel fs / proc/generic.c.Это немного долго, но я думаю, что это единственный источник для суммирования параметра start:

        /*
         * How to be a proc read function
         * ------------------------------
         * Prototype:
         *    int f(char *buffer, char **start, off_t offset,
         *          int count, int *peof, void *dat)
         *
         * Assume that the buffer is "count" bytes in size.
         *
         * If you know you have supplied all the data you
         * have, set *peof.
         *
         * You have three ways to return data:
         * 0) Leave *start = NULL.  (This is the default.)
         *    Put the data of the requested offset at that
         *    offset within the buffer.  Return the number (n)
         *    of bytes there are from the beginning of the
         *    buffer up to the last byte of data.  If the
         *    number of supplied bytes (= n - offset) is 
         *    greater than zero and you didn't signal eof
         *    and the reader is prepared to take more data
         *    you will be called again with the requested
         *    offset advanced by the number of bytes 
         *    absorbed.  This interface is useful for files
         *    no larger than the buffer.
         * 1) Set *start = an unsigned long value less than
         *    the buffer address but greater than zero.
         *    Put the data of the requested offset at the
         *    beginning of the buffer.  Return the number of
         *    bytes of data placed there.  If this number is
         *    greater than zero and you didn't signal eof
         *    and the reader is prepared to take more data
         *    you will be called again with the requested
         *    offset advanced by *start.  This interface is
         *    useful when you have a large file consisting
         *    of a series of blocks which you want to count
         *    and return as wholes.
         *    (Hack by Paul.Russell@rustcorp.com.au)
         * 2) Set *start = an address within the buffer.
         *    Put the data of the requested offset at *start.
         *    Return the number of bytes of data placed there.
         *    If this number is greater than zero and you
         *    didn't signal eof and the reader is prepared to
         *    take more data you will be called again with the
         *    requested offset advanced by the number of bytes
         *    absorbed.
         */

Мы можем увидеть start, использованного позже внутри copy_to_user -Этот параметр используется для оптимизации чтения записей proc в файлах biiiig.Пользователь может передать очень небольшую переменную count, но у вас есть файл biig для чтения.Таким образом, вы возвращаете размер этого файла из функции чтения proc с параметром *start, который говорит, сколько байтов нужно прочитать.Таким образом, ядро ​​может даже передавать count=0, но функция proc_read может возвращать как 5000 с действительным адресом *start, позже она будет использоваться в вызове copy_to_user для ускорения чтения.

Итак:

что означают параметры в static int scull_read_p_mem (char * buf, char ** start, off_t offset, int count, int * eof, void * data)?

  • buf - целевой буфер для копирования результата в
  • start - магический указатель, описанный в комментарии выше, используется для ускорения чтения процесса.
  • offset - смещение в файле для чтения с
  • count - количество байтов для чтения
  • eof - указатель на int, который должен быть установлен на ненулевое значение, в случаевесь файл читается
  • data - пользовательский контекст, переданный в качестве последнего параметра в функции create_proc_entry.

У меня есть ряд вопросовотносительно самой реализации (которую можно найти ниже):

scullp_proc_offset манипулирует выключениями lenet внутри buf буфера.Если offset != 0, тогда scull_read_p_mem не нужно читать с первого байта, а должен содержать некоторый байт offset.Поскольку он написан лениво, то вызовы snprintf выполняются в любом случае, вам нужно «сдвинуть» буфер.

what does this function do? - На самом деле я вижу забавный способ подсчитать, сколько байтов было / нужно скопировать пользователю.

what is the purpose of this? - Понятия не имею.Выглядит глючно, так как *offset станет отрицательным.Комментарий над функцией /* FIXME this should use seq_file */ говорит, что что-то осталось исправить.Я думаю, что иды возвращают точно информацию об одном scull_p_devices[i] в одном вызове.

why do you need to change "start"? - что приходит к этому.Если *offset отличается от 0 и если у нас есть несколько байтов для чтения, мы должны вернуть указатель на buf + offset, чтобы сообщить ядру, откуда читать.Обратите внимание, что *start = buf уже инициализирован, поэтому ядро ​​будет делать copy_to_user(... *start, len).

...