如何重写文件内容? Linux x86_64,程序集,GAS

时间:2018-11-02 20:02:07

标签: linux x86-64 gas

我的人完成了这项激动人心的任务,实际上几乎完成了..但是它在一个有趣的方面失败了。 任务是使用整数加载文件,对它们进行排序并将其写入文件。是的...我的程序正在执行此操作,但是它保留了原始内容。即:

说 45 32

应获得订购的内容 32 45

我的程序保留了原始内容并添加了新内容: 45 32 32 45。

那么解决这个问题有什么建议吗?尽管删除了原始文件并以相同的名称创建了一个新文件。但是,如果文件中包含非整数文件,并且我的代码中有关于此问题的错误报告,那也算是一个失败。

在这里给出重要的代码:

_OpenFile:
    movq $2, %rax           # open file
    movq $inputfile, %rdi   # filename
    movq $2, %rsi           # read and write
    movq $0644, %rdx        # setting proper permissions
    syscall
    ret

并且:

_PrintNumber: #needs rdi as numberholder
    movq $1, %r9            # count the number of chars to print
    push $10                # store the chars on the stack, we always have '\n'
    movq %rdi, %rax         # things are easier with it in rax
    movq $10, %rcx
    decode_loop:
    movq $0, %rdx
    idivq %rcx              # do rdx:rax / rcx
    addq $48, %rdx          # convert the remainder to an ASCII digit
    pushq %rdx              # and save it on the stack
    addq $1, %r9            # while counting the number of chars
    cmpq $0, %rax
    jne decode_loop         # loop until rax == 0
    write_loop:
    movq $1, %rax           # write
    movq $3, %rdi           # to the file
    movq %rsp, %rsi         # which is stored here
    movq $1, %rdx           # a single character/byte
    syscall

    addq $8, %rsp           # pop the character
    addq $-1, %r9           # correct the char count

    jne write_loop          # loop until r9 reaches 0
    ret

感谢所有想对此发表评论的人!

1 个答案:

答案 0 :(得分:0)

似乎您正在使用O_APPEND重新打开文件,或者您以可读/可写方式打开了文件,并且在重新写入之前没有寻找开始。 (因此,在读取了整个文件之后,文件描述符的位置是文件的末尾,因此新写入的数据将到达该位置。)

lseek(2) system call是移动文件位置所需要的。 lseek(fd, 0, SEEK_SET)后退到开头。 (与x86-64 System V系统调用约定一样,Args进入EDI,RSI,EDX,并且内核接口与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将通过预处理器运行它,而不是使用数字文字作为系统调用号码。

不幸的是,大多数标头,例如<unistd.h>(不是asm/unistd.h)也具有C声明,因此,您无法像SEEK_SETO_RDWR这样的常量获取宏可以让您执行mov $SEEK_SET, %edxmov $O_WRONLY|O_CREAT|O_TRUNC, %esi


取消链接文件不会对已经打开的文件的内容产生任何影响;要获得类似问题的效果,可以关闭/重新打开文件。(在Unix中,删除文件的目录条目不会影响已经打开该文件的程序。它将不过,一旦最后一个目录条目和文件描述符消失了,就可以从磁盘上将其释放。)

因此,您将打开它进行读取,读取所有数据,然后(检查错误后,当您确定要写入一些有效数据时),open(infile, O_CREAT|O_TRUNC|O_WRONLY, 0666)并写出数据。您没有使用O_APPEND,因此新的只写FD的位置将位于文件的开头。而且文件大小将被截断为0。与外壳中的echo foo > filename完全一样。

(除非您在打开unlink(infile)来重新创建该名称的新文件之前,它仍将具有相同的inode编号,并且是具有不同内容的“同一文件”。在这种情况下,{{1} }实际上是必要的。重新打开现有文件进行写+截断时,O_CREAT在文件已经存在时就不需要。)

这里的关键是在进行破坏性操作之前先检查读取错误,而不仅仅是读取,销毁原始文件,然后继续进行。因此,排序时文件仍在磁盘上。