如何重写文件内容? Linux x86_64,组装,GAS
How to rewrite a files content? Linux x86_64, assembly, GAS
嗨,我得到了这个令人兴奋的任务,实际上几乎完成了..但它在一个有趣的方面失败了。
任务是加载一个包含整数的文件,对它们进行排序并将它们写入文件。耶...好吧,我的程序正在这样做,但它保留了原始内容。即:
让我们说
45
32
应该得到订购的内容
32 45
好吧,我的程序保留了原始内容并添加了新内容:
45 32 32 45.
那么有什么解决这个问题的建议吗?虽然删除原始文件并以相同的名称创建一个新文件。但那有点失败,如果文件有非整数并且我的代码有关于那个的错误报告。
我把重要的代码放在这里:
_OpenFile:
movq , %rax # open file
movq $inputfile, %rdi # filename
movq , %rsi # read and write
movq 44, %rdx # setting proper permissions
syscall
ret
并且:
_PrintNumber: #needs rdi as numberholder
movq , %r9 # count the number of chars to print
push # store the chars on the stack, we always have '\n'
movq %rdi, %rax # things are easier with it in rax
movq , %rcx
decode_loop:
movq [=13=], %rdx
idivq %rcx # do rdx:rax / rcx
addq , %rdx # convert the remainder to an ASCII digit
pushq %rdx # and save it on the stack
addq , %r9 # while counting the number of chars
cmpq [=13=], %rax
jne decode_loop # loop until rax == 0
write_loop:
movq , %rax # write
movq , %rdi # to the file
movq %rsp, %rsi # which is stored here
movq , %rdx # a single character/byte
syscall
addq , %rsp # pop the character
addq $-1, %r9 # correct the char count
jne write_loop # loop until r9 reaches 0
ret
感谢所有愿意对此发表评论的人!
您似乎是 re-opening 带有 O_APPEND
的文件,或者您打开它 read/write 并且在重写它之前没有搜索到开头。 (所以读取整个文件后,文件描述符的位置就是文件的末尾,所以newly-written数据会到那里。)
lseek(2)
system call就是你需要移动的文件位置。 lseek(fd, 0, SEEK_SET)
倒回到开头。 (参数进入 EDI、RSI、EDX,就像 x86-64 System V system-call 约定的正常情况一样,内核接口与 libc 接口匹配。)
由于您要写出的数据具有相同的长度,因此在开始重写之前不需要 ftruncate(fd, len)
文件。您将一直覆盖所有字节到最后。
顺便说一句,您不需要 write
每个字符分开;您可以制作一个包含数字的所有 ASCII 字节的小缓冲区,然后进行一个 write
系统调用;效率更高,实际上代码更少:Printing an integer as a string with AT&T syntax, with Linux system calls instead of printf。我在那里的回答还表明你可以 #include <asm/unistd.h>
并使用像
这样的代码
mov $__NR_write, %eax # SYS_write, from unistd_64.h
如果您使用 .S
文件,那么 gcc 将 运行 它通过预处理器,而不是对 system-call 数字使用数字文字。
不幸的是,大多数 headers 像 <unistd.h>
(不是 asm/unistd.h
)也有 C 声明,所以你不能像 SEEK_SET
这样容易地获得常量的宏或 O_RDWR
让你做 mov $SEEK_SET, %edx
或 mov $O_WRONLY|O_CREAT|O_TRUNC, %esi
.
取消链接文件不会影响 already-open 文件的内容;要获得您在问题中所描绘的效果,您可以 close/reopen 文件。 (在 Unix 中,删除文件的目录条目不会影响已经打开它的程序. 不过,一旦最后一个目录条目和 file-descriptor 消失,它将从磁盘中释放。)
所以你打开它进行读取,读取所有数据,然后(在检查错误之后,当你确定你有一些有效数据要写入时),open(infile, O_CREAT|O_TRUNC|O_WRONLY, 0666)
并写出数据.您没有使用 O_APPEND
,因此新的 write-only FD 的位置将在文件的前面。 file-size 将被 t运行 归为 0。与 shell.
中的 echo foo > filename
完全一样
(它将仍然具有相同的 inode 编号并且 "the same file" 具有不同的内容,除非你 unlink(infile)
在将其打开到 re-create 一个具有该名称的新文件之前。在那种情况下O_CREAT
实际上是必需的。当重新打开一个现有文件写入+t运行cate时,当文件已经存在时不需要O_CREAT
。)
这里的关键是在做任何破坏性的事情之前检查读取错误,而不是只读取它,破坏原件,然后继续。所以当您排序时文件仍然在磁盘上。
嗨,我得到了这个令人兴奋的任务,实际上几乎完成了..但它在一个有趣的方面失败了。 任务是加载一个包含整数的文件,对它们进行排序并将它们写入文件。耶...好吧,我的程序正在这样做,但它保留了原始内容。即:
让我们说 45 32
应该得到订购的内容 32 45
好吧,我的程序保留了原始内容并添加了新内容: 45 32 32 45.
那么有什么解决这个问题的建议吗?虽然删除原始文件并以相同的名称创建一个新文件。但那有点失败,如果文件有非整数并且我的代码有关于那个的错误报告。
我把重要的代码放在这里:
_OpenFile:
movq , %rax # open file
movq $inputfile, %rdi # filename
movq , %rsi # read and write
movq 44, %rdx # setting proper permissions
syscall
ret
并且:
_PrintNumber: #needs rdi as numberholder
movq , %r9 # count the number of chars to print
push # store the chars on the stack, we always have '\n'
movq %rdi, %rax # things are easier with it in rax
movq , %rcx
decode_loop:
movq [=13=], %rdx
idivq %rcx # do rdx:rax / rcx
addq , %rdx # convert the remainder to an ASCII digit
pushq %rdx # and save it on the stack
addq , %r9 # while counting the number of chars
cmpq [=13=], %rax
jne decode_loop # loop until rax == 0
write_loop:
movq , %rax # write
movq , %rdi # to the file
movq %rsp, %rsi # which is stored here
movq , %rdx # a single character/byte
syscall
addq , %rsp # pop the character
addq $-1, %r9 # correct the char count
jne write_loop # loop until r9 reaches 0
ret
感谢所有愿意对此发表评论的人!
您似乎是 re-opening 带有 O_APPEND
的文件,或者您打开它 read/write 并且在重写它之前没有搜索到开头。 (所以读取整个文件后,文件描述符的位置就是文件的末尾,所以newly-written数据会到那里。)
lseek(2)
system call就是你需要移动的文件位置。 lseek(fd, 0, SEEK_SET)
倒回到开头。 (参数进入 EDI、RSI、EDX,就像 x86-64 System V system-call 约定的正常情况一样,内核接口与 libc 接口匹配。)
由于您要写出的数据具有相同的长度,因此在开始重写之前不需要 ftruncate(fd, len)
文件。您将一直覆盖所有字节到最后。
顺便说一句,您不需要 write
每个字符分开;您可以制作一个包含数字的所有 ASCII 字节的小缓冲区,然后进行一个 write
系统调用;效率更高,实际上代码更少:Printing an integer as a string with AT&T syntax, with Linux system calls instead of printf。我在那里的回答还表明你可以 #include <asm/unistd.h>
并使用像
这样的代码
mov $__NR_write, %eax # SYS_write, from unistd_64.h
如果您使用 .S
文件,那么 gcc 将 运行 它通过预处理器,而不是对 system-call 数字使用数字文字。
不幸的是,大多数 headers 像 <unistd.h>
(不是 asm/unistd.h
)也有 C 声明,所以你不能像 SEEK_SET
这样容易地获得常量的宏或 O_RDWR
让你做 mov $SEEK_SET, %edx
或 mov $O_WRONLY|O_CREAT|O_TRUNC, %esi
.
取消链接文件不会影响 already-open 文件的内容;要获得您在问题中所描绘的效果,您可以 close/reopen 文件。 (在 Unix 中,删除文件的目录条目不会影响已经打开它的程序. 不过,一旦最后一个目录条目和 file-descriptor 消失,它将从磁盘中释放。)
所以你打开它进行读取,读取所有数据,然后(在检查错误之后,当你确定你有一些有效数据要写入时),open(infile, O_CREAT|O_TRUNC|O_WRONLY, 0666)
并写出数据.您没有使用 O_APPEND
,因此新的 write-only FD 的位置将在文件的前面。 file-size 将被 t运行 归为 0。与 shell.
echo foo > filename
完全一样
(它将仍然具有相同的 inode 编号并且 "the same file" 具有不同的内容,除非你 unlink(infile)
在将其打开到 re-create 一个具有该名称的新文件之前。在那种情况下O_CREAT
实际上是必需的。当重新打开一个现有文件写入+t运行cate时,当文件已经存在时不需要O_CREAT
。)
这里的关键是在做任何破坏性的事情之前检查读取错误,而不是只读取它,破坏原件,然后继续。所以当您排序时文件仍然在磁盘上。