我正在Mac电脑上运行此程序。创建此函数已经7个月了,有人在争论我没有正确地创建cat函数。我想知道如果我调用其他函数为什么会不起作用。我的输出是正确的,但是我不记得为什么不起作用的原因。
前一段时间,我决定重新创建某些功能,但是要在NASM中。为了测试我的知识,我想使用open
函数重新创建从源文件路径接收文件描述符的cat调用。然后调用我的cat(fd)
汇编函数。输出看起来令人满意,但在我的辩护中,标签似乎不正确。
这是我的汇编猫功能文件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文件:
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
文件的内容。我唯一不了解的部分是我没有正确使用write函数,因为我没有使用3个参数(描述符,字符串的内容,nbuff_size)。帮我学习。例如:编译后如何查看幕后情况(例如为我的cat.s使用lldb?)移动寄存器并不能帮助我了解更多。
答案 0 :(得分:1)
您的微小缓冲区使此超级低效(内核/用户转换曾经为255个字节)。至少8kiB会更好,也许是32至64k。 (适合典型的现代x86的L2缓存大小。)
您可能还需要处理返回EINTR
(在读取任何数据之前被信号中断)的读或写操作,以确保正确性,以防用户在正确的时机击中control-z。我不确定系统调用语义在这里是如何工作的,或者仅用于带有处理程序的信号。但是,如果这应该是一个函数,而不是整个程序,则不能假定调用方没有设置任何处理程序。
至少您将read
的返回值用作write
的长度,所以正确处理了短读(返回早,但长度非零)。
我想可以安全地假设/要求未使用O_NONBLOCK
打开输入FD和stdout,因为您不处理EWOULDBLOCK / EAGAIN。但这是正常现象,而不是cat()
中的错误,只是合同中的一部分。
代码质量:正如@phuclv所指出的那样,您浪费的是xor rax,rax
而不是xor eax,eax
的代码字节。而且这毫无意义,因为mov eax, SYS_READ
已经覆盖了整个RAX。
push rdi
也很奇怪。使用r8
之类的寄存器或其他东西来存放fd函数arg。 syscall
仅阻塞RCX,R11和RAX中的返回值。您无需在系统调用之间接触用户空间堆栈内存;根据内核的Meltdown缓解策略,这可能导致额外的TLB丢失或页面错误。 (不过,push / pop是最小的代码大小选项。)
为此,OS X是否没有从fd到fd的复制系统调用?
Linux具有sendfile(2)
,可以在内核空间的FD之间进行复制,从而避免了将数据复制到用户空间和返回到用户空间。 (它最初是为零拷贝Web /文件服务器设计的,用于将文件中的数据发送到套接字,并且在真正的旧内核(2.6.33之前)中,out_fd
必须是套接字。但这是几年前的事情。)无论如何,任意大的副本大小都不会分配/页面破坏任何用户空间内存,也不会保存副本。
特别是对于文件,还有copy_file_range(2)
,它使内核/文件系统驱动程序有机会进行诸如NFS服务器端复制之类的操作,或与支持该功能的系统进行写时复制的reflink。 (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);