Есть ли потенциальная ошибка в моей функции `cat`? - PullRequest
0 голосов
/ 12 февраля 2019

Я запускаю эту программу на компьютере Mac.Прошло 7 месяцев после создания этой функции, и кто-то утверждает, что я неправильно воссоздаю функцию своей кошки.Я хотел знать, почему не работает, если я вызываю другие функции.Мой вывод верен, но я не помню, почему причины не работают.

Некоторое время назад я решил воссоздать некоторые функции, но в NASM.Чтобы проверить свои знания, я хотел воссоздать вызов cat, который получает дескриптор файла из пути к исходному файлу, используя функцию open.Затем вызовите мою cat(fd) функцию сборки.Вывод кажется удовлетворительным, но маркировка не в мою пользу.

Вот мой файл функции cat cat cat.s:

[bits 64]

global cat
%define SYS_READ 0x2000003
%define SYS_WRITE 0x2000004
%define STDOUT 0x01
%define BUFF_SIZE 0xff

section         .bss
    buffer      resb        BUFF_SIZE       ; unitialised storage space, basically reserving BUFF_SIZE bytes

section         .text

; int               my_cat(int fd);
_my_cat:
    xor     rax, rax
    push    rbp
    mov     rbp, rsp

    .read:
        push    rdi                 ; push rdi stack first before we start reading
        lea     rsi, [rel buffer]
        mov     rdx, BUFF_SIZE
        mov     rax, SYS_READ       ; read
        syscall
        jc      end                 ; jump carry
        cmp     rax, 0
        jle     end

    .write:                         ; write the message indicating end of file write
        mov     rdi, STDOUT         ; output fd
        mov     rdx, rax            ; store the destination of rax
        mov     rax, SYS_WRITE      ; write
        syscall
        pop     rdi                 ; take out our initial rdi stack
        jmp     .read               ; read again

    end:
        mov     rsp, rbp
        pop     rbp
        ret

Тогда это тестовый файл, который я запускаю после open() в моем hello.s файле:

[bits 64]

global my_hello

section .data
    hello_world db 'Hello World!', 0x0a

section .text

%define SYS_WRITE 0x2000004

%define SYS_EXIT 0x2000001

_my_hello:
    mov     rax, SYS_WRITE      ; syscall write
    mov     rdi, 1              ; stdout fd (where will it write?)
    mov     rsi, hello_world    ; string address (where does it start?)
    mov     rdx, 20             ; string length in bytes (how many bytes to write?)
    syscall                     ; system call

    mov     rax, SYS_EXIT       ; exit system call
    xor     rdi, 0              ; 0 can be replaced with rdi
    syscall

main.c file:

extern  int             my_hello(void);
extern  int             my_cat(int fd);

int main(void)
{
    printf("testing my_cat: \n");
    fd = open("src/my_hello.s", O_RDONLY);
    ft_cat(fd);

    return (0);
}

У меня правильное ожидаемое значение, которым должно быть содержимое файла hello.s.Единственная часть, которую я не понимаю, это то, что я не использую функцию записи должным образом, потому что я не беру 3 параметра (дескриптор, содержимое строки, nbuff_size).Помоги мне научиться.Например: как я могу увидеть, что происходит за кулисами, когда он компилируется (как, например, использование lldb для моей кошки?). Перемещение моих регистров не помогает мне понять больше.

1 Ответ

0 голосов
/ 12 февраля 2019

Ваш крошечный буфер делает этот супер неэффективным (ядро / пользовательский переход когда-либо 255 байтов).По крайней мере, 8киБ будет намного лучше, может быть, от 32 до 64 тысяч.(Соответствует размеру кэша L2 на типичном современном x86.)

Вам также может понадобиться обработать чтение или запись, возвращающие EINTR (прерывается сигналом перед чтением любых данных), для корректности в случае, если пользователь нажимает на элементг в нужный момент.Я не уверен точно, как семантика системных вызовов работает здесь, или это только для сигналов с обработчиками.Но если предполагается, что это функция, а не целая программа, вы не можете предполагать, что вызывающая сторона не установила никаких обработчиков.

По крайней мере, вы используете возвращаемое значение read в качестведлина для write, поэтому короткие чтения (которые возвращаются рано, но с ненулевой длиной) обрабатываются правильно.

Я думаю, можно с уверенностью предположить / потребовать, чтобы входные FD и stdout не былиоткрывается с O_NONBLOCK, потому что вы не обрабатываете EWOULDBLOCK / EAGAIN.Но это нормально, и это не ошибка в cat(), а лишь часть его контракта.


Качество кода: как указывало @phuclv, вы тратите байты кода с xor rax,rax вместо xor eax,eax.И это все равно бессмысленно, потому что mov eax, SYS_READ уже перезаписывает весь RAX.

push rdi тоже странно.Используйте регистр, например r8 или что-то еще, чтобы спрятать функцию fd arg.syscall только клобберы RCX, R11 и возвращаемое значение в RAX.Вам не нужно трогать память стека пользовательского пространства между системными вызовами;это может привести к дополнительным пропускам TLB или ошибкам страниц в зависимости от стратегии смягчения последствий Meltdown ядра.(push / pop - это наименьшая опция размера кода.)


Разве OS X не имеет системного вызова для копирования с fd-на-fd для этого?

Linux имеет sendfile(2), который копирует между FD в пространстве ядра, избегая копирования данных в пространство пользователя и обратно.(Первоначально он был разработан для веб-файловых серверов с нулевым копированием для отправки данных из файлов в сокеты, и в действительно старых ядрах (до 2.6.33) out_fd должен был быть сокетом. Но это было много лет назад.) В любом случае,произвольно большие размеры копий без выделения / сбоя страницы любой пользовательской памяти и сохранения копии.

Для файлов, в частности, есть также copy_file_range(2), который предоставляет драйверы ядра / файловой системывозможность делать такие вещи, как NFS-копирование на стороне сервера, или создавать ссылки для копирования при записи с системами, которые поддерживают это.(Sendfile также может сделать это, но на странице руководства это не упоминается.)

ssize_t sendfile(int out_fd, int in_fd, off_t *offset, size_t count);

ssize_t copy_file_range(int fd_in, loff_t *off_in,
                           int fd_out, loff_t *off_out,
                           size_t len, unsigned int flags);
...