LINUX.ORG.RU

Вопрос по LDD, модулю ядра scull.

 , ,


0

1

Пытался написать его сам(и написал каким-то чудом). Но совершенно не понимаю как что-то дописывать в конец файла, и должно ли оно так работать, вот к примеру:

root@test-VirtualBox:~# echo 1 > /dev/mynull 
root@test-VirtualBox:~# cat /dev/mynull 
1
root@test-VirtualBox:~# echo 2 >> /dev/mynull 
root@test-VirtualBox:~# cat /dev/mynull 
2

Меня вот что интересует - оно так и должно работать? Или оно должно писаться в конец как и положено(при работе с обычными файлами), то есть в примере выше должно быть «12» после таких операций.

Чтобы долго не искали, вот пример кода: https://github.com/sergei-krainov/cs_dev

в функции «write» параметры «count» и «*fpos» передаются всегда одинаково, вне зависимости от способа записи(>/>>).

Ну и вопрос, оно вообще должно так работать или в коде где-то ошибка?

совершенно не понимаю как что-то дописывать в конец файла

Сначала сделать lseek. Попробуй dd.

А еще лучше - наставь в коде printk и посмотри.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Весь код уже заставил этими printk. В read он всегда передается равным «131072», во write всегда равным «0».

Через реализацию lseek наверное можно и сделать, но просто в реализации write/read это сделать невозможно? Судя по тому что пишут оно не должно так работать

Kronick ()
Ответ на: комментарий от Kronick

В read он всегда передается равным «131072», во write всегда равным «0».

«Он» - это filp->f_pos?

но просто в реализации write/read это сделать невозможно?

Можно. Но тогда оба вызова write должны использовать один и тот же открытый файл (в твоем примере он закрывается между вызовами). Это на правильно работающем драйвере, естественно.

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Вот как примерно это выглядит:

root@test-VirtualBox:~# echo 1 > /dev/mynull 
root@test-VirtualBox:~# echo 2 > /dev/mynull 
root@test-VirtualBox:~# echo 3 >> /dev/mynull 
root@test-VirtualBox:~# cat /dev/mynull 
3

И dmesg:

[ 1878.191618] Driver: write()
[ 1878.191620] off = 0, count = 2
[ 1878.191628] Driver: write completed
[ 1878.191636] Driver: close()
[ 1882.214811] Driver: open()
[ 1882.214845] Driver: write()
[ 1882.214847] off = 0, count = 2
[ 1882.214852] Driver: write completed
[ 1882.214860] Driver: close()
[ 1888.247681] Driver: open()
[ 1888.247714] Driver: write()
[ 1888.247717] off = 0, count = 2
[ 1888.247724] Driver: write completed
[ 1888.247734] Driver: close()
[ 1897.465489] Driver: open()
[ 1897.465518] Driver: read()
[ 1897.465521] off = 0, count = 131072
[ 1897.465531] Driver: read completed
[ 1897.465545] Driver: read()
[ 1897.465546] off = 2, count = 131072
[ 1897.465567] Driver: close()

Получаю это вот из такого принта:

static ssize_t my_write(struct file *f, const char __user *buf, size_t len,
    loff_t *off)
{
    struct cs_dev *dev = f->private_data;
    struct cs_qset *dptr;
    
    int item, qpos, spos, rest;    
    int quantum = dev->quantum, qset = dev->qset;
    int item_size = qset * quantum;
    
    printk(KERN_INFO "Driver: write()\n");
    printk(KERN_INFO "off = %llu, count = %zu\n", *off, len);"

Kronick ()
Ответ на: комментарий от Kronick

То есть разницы от способа записи(>/>>) вообще никакой. Причем в «read» оффсет равен 2. То есть это не «read» некорректно считывает, а «write» каждый раз пишет в начало, но я могу ошибаться.

Kronick ()
Ответ на: комментарий от Kronick

То есть разницы от способа записи(>/>>) вообще никакой

По твоему фрагменту печати такой вывод сделать невозможно.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Ну то есть я правильно понимаю, что код который в ОП так и должен писать каждый раз в начало? Ну и соответственно весь код который в книжке описан в 3 главе пишет всегда в начало и не может дописывать данные в конец?

Kronick ()
Ответ на: комментарий от Kronick

код который в ОП так и должен писать каждый раз в начало?

Я не помню точно интерфейс файла - *off может быть out-параметром, а запись должна начинаться на filp->f_pos, значение которого задается lseek. Но, поскольку в твоей печати нет ни того, ни другого, никаких выводов сделать невозможно.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

Добавил еще и этот принт, он всегда равен нулю:

printk(KERN_INFO "Driver: write()\n");
    printk(KERN_INFO "off = %llu, count = %zu\n", *off, len);
    printk(KERN_INFO "fpos = %llu\n", f->f_pos);

[ 2612.278836] Driver: write()
[ 2612.278837] off = 0, count = 2
[ 2612.278838] fpos = 0
[ 2612.278842] Driver: write completed
[ 2612.278846] Driver: close()
[ 2614.431446] Driver: open()
[ 2614.431492] Driver: write()
[ 2614.431494] off = 0, count = 2
[ 2614.431495] fpos = 0
[ 2614.431529] Driver: write completed
[ 2614.431540] Driver: close()
[ 2617.839522] Driver: open()
[ 2617.839569] Driver: write()
[ 2617.839572] off = 0, count = 2
[ 2617.839573] fpos = 0
[ 2617.839580] Driver: write completed
[ 2617.839590] Driver: close()
[ 2621.184944] Driver: open()
[ 2621.184968] Driver: read()
[ 2621.184971] off = 0, count = 131072
[ 2621.184972] fpos = 0
[ 2621.184980] Driver: read completed
[ 2621.184992] Driver: read()
[ 2621.184993] off = 2, count = 131072
[ 2621.184993] fpos = 2
[ 2621.185010] Driver: close()
Kronick ()

С чего бы это ты ожидаешь ненулевой off? >>/> никаких сиков не делают, они отличаются флагами для open. Не знаю как там в драйверах оно обрабатывается, но по идее у тебя в обработчике open должно быть доступно O_APPEND и O_TRUNK, и в зависимости от них ты решашь, дописывать буффер или заменять.

slovazap ★★★★★ ()
Ответ на: комментарий от Kronick

Очевидно же, что f_pos должна увеличиваться после успешного write, но ты печатаешь ее на входе в write. И упорно не печатаешь llseek.

tailgunner ★★★★★ ()
Ответ на: комментарий от tailgunner

llseek это сама функция имеется в виду? Она не реализована у меня, ну и похоже это именно то что мне нужно было.

Спасибо.

Kronick ()
Ответ на: комментарий от Kronick

llseek это сама функция имеется в виду?

Да.

Она не реализована у меня,

Хм. Она же есть на github?

ну и похоже это именно то что мне нужно было.

Возможно. Или, как сказано выше, флаг O_APPEND (не знаю, правда, отрабатывается он на уровне ядра или libc).

tailgunner ★★★★★ ()
Последнее исправление: tailgunner (всего исправлений: 1)
Ответ на: комментарий от tailgunner

Или, как сказано выше, флаг O_APPEND (не знаю, правда, отрабатывается он на уровне ядра или libc).

В ядре, на уровне отдельных файловых систем. Если ФС использует generic_perform_write() или generic_file_write_iter(), то ядро сдвинет позицию записи в конец файла. Если нет, то ФС может делать, а может и не делать это сама.

ilammy ★★★ ()
Ответ на: комментарий от tailgunner

Да, на гитхаб она есть, но у меня реализована не была. Это все старые примеры, им лет по 7, они даже не компилятся особо, поэтому я часть переписывал сам, оставляя логику такой же. Вот сейчас залил текущую версию, свою, на гитхаб, вот адрес:

https://github.com/sergei-krainov/cs_dev

Кстати реализовал llseek, но он просто не вызывается и все работает точно так же как и раньше.

И возвращаясь к моему изначальному вопросу - оно так и должно каждый раз перезаписывать все данные или я что-то сделал не так? В книге написано вот так:

Once you are equipped with the four methods just described, the driver can be compiled and tested; it retains any data you write to it until you overwrite it with new
data. The device acts like a data buffer whose length is limited only by the amount of
real RAM available. You can try using cp, dd, and input/output redirection to test out
the driver.

Мои примеры из книги, и я никак не могу понять где же я накосячил и почему данные всегда переписываются, ясно же написано «it retains any data you write to it until you overwrite it with new data»

Kronick ()
Ответ на: комментарий от Kronick

Так, в общем код всегда считал вот это истиной:

if ( (f->f_flags & O_ACCMODE) == O_WRONLY)

Добавил вот такое и теперь работает как надо:

    dev = container_of(i->i_cdev, struct cs_dev, cdev);
    f->private_data = dev;
      
 -    if ( (f->f_flags & O_ACCMODE) == O_WRONLY) {
 +    if (((f->f_flags & O_ACCMODE) == O_WRONLY) && 
 +			(f->f_flags & O_TRUNC)) {
  		cs_trim(dev);
  	}

Ну и в целом наверное есть какая-то причина на то, почему запускается TRUNCATE на каждую попытку записи.

Отдельное спасибо slovazap. Я правда тогда совершенно не понимал что имелось ввиду, а сейчас все встало на свои места.

Kronick ()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.