LINUX.ORG.RU

синхронный ввод-вывод


0

2

есть прибор с немного странным интерфейсом: в нем контроллер usb mass storage устройства, виден он как флешка на 32М (-t vfat -o sync, dirsync). Команды прибору записываются в один файл, считываются из другого. Тестовая программа

int main(){
    int wfd, rfd;
    const char wpath[] = "/mnt/ZATA.TXT";
    const char rpath[] = "/mnt/DATA.TXT";
    char buf[512];
    const char cmd_2[]  = "COMMANDLINE:2\r";
    wfd = open(wpath, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC | O_DIRECT, 0666);
    write(wfd, (const void*)cmd_2, 512)
    close(wfd);
    sync();
    rfd = open(rpath, O_RDONLY | O_SYNC);
    int bytes = read(rfd, (void*)buf, 512);
    close(rfd);
    printf("%s\n", buf);
    return 0;
}

Выяснилось, что, несмотря на флаги O_SYNC | O_DIRECT запись в прибор все равно не происходит, а данные остаются в кэше страниц. Так что для того чтобы считать показания прибора, приходится делать еще и echo 1 > /proc/sys/vm/drop_caches.

Понимаю, проблема не раз возникала, но гугл предлагает мне только этот вариант решения проблемы. Так как же правильно организовать небуферизованный ввод-вывод в linux?

★★★★★

Проблема точно в данных самого файла? Может, дёргать fsync перед close, чтобы метаданные синкнуть?

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

на «всякий пожарный» — код, работающий под windows:

int main(){
	HANDLE hRead, hWrite;
	char wfile[] = "E:\\ZATA.TXT";
	char rfile[] = "E:\\DATA.TXT";
	char buf[512];
	DWORD bytes;
	const char cmd_2[] = "COMMANDLINE:2\r";
	
	hWrite = CreateFile(wfile, GENERIC_WRITE, FILE_SHARE_WRITE, NULL, OPEN_ALWAYS, FILE_FLAG_NO_BUFFERING, 0);
	WriteFile(hWrite, (LPCVOID)cmd_2, 512, &bytes, NULL);
	CloseHandle(hWrite);

	hRead = CreateFile(rfile, GENERIC_READ, FILE_SHARE_READ, NULL, OPEN_EXISTING, FILE_FLAG_NO_BUFFERING, 0);
	ReadFile(hRead, buf, 512, &bytes, NULL);
	CloseHandle(hRead);

	printf("%s\n", buf);
	return 0;
}

Вариант с fsync'ом пробовал. Данные не пишутся.

demidrol ★★★★★
() автор топика

найди где реально на диски твои файлы через «hdparm --fibmap /mnt/ZATA.TXT» и пиши прямо в тот блок девайса. Пример для размышления:

[root@battlehummer ~]# hdparm --fibmap /boot/grub/grub.conf

/boot/grub/grub.conf:
 filesystem blocksize 1024, begins at LBA 63; assuming 512 byte sectors.
 byte_offset  begin_LBA    end_LBA    sectors
           0     175747     175748          2
        1024     175749     175750          2
[root@battlehummer ~]# dd if=/dev/sda skip=175747 count=1 2>/dev/null | head -3
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
[root@battlehummer ~]#
legolegs ★★★★★
()
Ответ на: комментарий от legolegs

не работает

# hdparm --fibmap /mnt/ZATA.TXT 

/mnt/ZATA.TXT:
 filesystem blocksize 512, begins at LBA 0; assuming 512 byte sectors.
 byte_offset  begin_LBA    end_LBA    sectors
           0        808        808          1

# dd if=out of=/dev/sdc skip=18
dd: «out»: невозможно пропустить до заданного смещения
0+0 записей считано
0+0 записей написано
скопировано 0 байт (0 B), 0.00430544 c, 0.0 kB/c

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

man dd

skip=blocks Пропускает blocks блоков длины ibs байт во входном файле перед началом копирования.

seek=blocks Пропускает в выходном файле blocks блоков длины obs байт перед началом копирования.

PS а чтение-то работает?

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

понимаете, это не совсем флешка. Видимо, не работает там seek как положено. Да и необязательно писать в какой-то конкретный сектор, прибор читает любой. Главное — чтобы точно произошла запись, а не так, что данные висят либо в дисковом, либо в страничном кэше.

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

прочитал внимательнее про seek, попробовал dd if=out of=/dev/sdc seek=808 — ничего. То есть даже после echo 1 > /proc/sys/vm/drop_caches содержимое DATA.TXT старое.

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

а, нет.

dd if=out of=/dev/sdc seek=808 #запись в ZATA.TXT
echo 1 > /proc/sys/vm/drop_caches
dd if=/dev/sdc skip=18 bs=512 count=1 # чтение DATA.TXT

но все равно без сброса кэша не работает.

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

Во-первых, вы делали skip, а во входном файле «out» скипать особо нечего, вот дд и выдал ошибку. А вот seek не работать не может, иначе бы драйвер vfat тоже не работал.

Во-вторых, ну и пишите тогда прямо в девайс. Через шелл+dd или через open+write, не важно. Раз уж есть такая возможность (не винда же) - надо скинуть с плеч слои linux VFS и собственно двайвер ФС.

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

у дд есть флаги синхронного доступа. Но вообще я привёл тут dd только для тестов и демонстрации идеи. Просто делайте

owfd = pen(«/dev/sdc», O_WRONLY | O_SYNC, 0666);

lseek(owfd, 808*512,SEEK_SET)

Возможно, O_DIRECT таки нужен, но сильно сомневаюсь, а смотреть лень.

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

размер кластера твоей ФС.

там файлы размером 512 байт.

с различными вариантами sync'а тоже пробовал. В том числе и после выполнения программы запускать в консоли sync, а потом опять читать из файла. Результат отрицательный. Работает либо так, как рассказал выше, либо после цикла отмонтирования-примонтирования.

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

> ну и пишите тогда прямо в девайс. Через шелл+dd или через open+write, не важно. Раз уж есть такая возможность (не винда же)

А заплаты - тупее вендовых.

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

>> размер кластера твоей ФС.

там файлы размером 512 байт.

У O_DIRECT могут быть довольно странные требования к выравниванию и размерам блока. Твоя cmd_2, кстати, вряд ли выровнена даже на 512 байт.

с различными вариантами sync'а тоже пробовал.

В программе, которую ты привел - sync, это в принципе неправильно. Если не работают fsync и fdatasync, ты встретил баг ядра, что крайне сомнительно.

tailgunner ★★★★★
()

а обязательно вместе с командой каждый раз ещё и всё содержимое стека в файл писать?)

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

>ты встретил баг ядра, что крайне сомнительно.

Что сомнительного? В драйвере vfat всякие фишки могли и не реализовывать. O_DIRECT - он же не для странных девайсов придуман, а для производительности. Но разве кому вообще может в здравом уме понадобиться выжимать скорость из фата?

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

Цитируем legolegs

надо скинуть с плеч слои linux VFS и собственно двайвер ФС

спасибо за совет, попробую уже с утра, о результатах отпишусь.

demidrol ★★★★★
() автор топика
Ответ на: комментарий от ei-grad

да, девайс требует, чтобы целиком был записан весь сектор.

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

>> ты встретил баг ядра, что крайне сомнительно.

Что сомнительного?

Ты часто баги ядра встречаешь?

В драйвере vfat всякие фишки могли и не реализовывать.

fsync/fdatasync - это не фишка драйвера ФС, это page cache.

O_DIRECT - он же не для странных девайсов придуман

К черту O_DIRECT, не работает fdatasync.

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

вообще, судя по выхлопу dd, синхронная запись работает, (16.6 kB/c), а вот при чтении что-то странное — 6.1 MB/c, скорее всего, это из кэша читается.

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

> вообще, судя по выхлопу dd, синхронная запись работает, (16.6 kB/c)

Попробуй выровнять данные.

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

вроде бы проблема решилась (с помощью dd)

dd if=out of=/dev/sdc seek=808 oflag=sync #ZATA.TXT
dd if=/dev/sdc skip=18 bs=512 count=1 iflag=direct


Всем большое спасибо.
demidrol ★★★★★
() автор топика

-o flush при монтировании пробовал?

ei-grad ★★★★★
()
Ответ на: комментарий от demidrol

dd без спецключей не синхронно пишет, не смотри на его показания. Лучше проспись, сделай cmd_2 размером в 512 байт (как тебе дважды намекали) и определись уже, пользуешься ты файловой системйо (как советует тейлганнер), или пишешь напрямую (как советую я). И если слушаешь хвостострелка, то и правда попробуй fdatasync (мой придворный астролог видел в хрустальном шаре, что ты ещё не делал этого).

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

о, да и просто работает все

dd if=out of=/mnt/ZATA.TXT  oflag=direct
dd if=/mnt/DATA.TXT iflag=direct


Так что теперь проблема точно решена.
demidrol ★★★★★
() автор топика
Ответ на: комментарий от demidrol

Проблема будет решена, когда ты сможешь сам сделать то, что за тебя делает dd. А пока тебе просто подсказали заплату, которая непонятно как работает.

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

сделай strace и посмотри что именно делает dd

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

Товарищ Торвальдс рекомендует вместо O_DIRECT использовать madvise() and posix_fadvise(). Но, если не работает fdatasync, то ТС'у вряд ли поможет posix_fadvise.

http://kerneltrap.org/node/7563

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

Цитируем tailgunner

У O_DIRECT могут быть довольно странные требования к выравниванию и размерам блока.

точно, без выравнивания чтение вываливалось с EINVAL. Окончательный рабочий вариант

int main(){
    int wfd, rfd;
    const char wpath[] = "/mnt/ZATA.TXT";
    const char rpath[] = "/mnt/DATA.TXT";
    char buf[512];
    char *rbuf;
    const char cmd_2[]  = "COMMANDLINE:2\r";
    wfd = open(wpath, O_WRONLY | O_CREAT | O_TRUNC | O_DIRECT, 0666);
    write(wfd, (const void*)cmd_2, 512);
    close(wfd);


    posix_memalign((void **)&rbuf, sysconf(_SC_PAGESIZE), sysconf(_SC_PAGESIZE)) != 0);
    rfd = open(rpath, O_RDONLY | O_DIRECT);
    read(rfd, rbuf, 512);
    close(rfd);
    printf("%s\n", rbuf);
    free(rbuf);
    return 0;
}

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