GNU/Linux >> LINUX-Kenntnisse >  >> Linux

Warum werden Daten, die in eine mit O_APPEND-Flag geöffnete Datei geschrieben werden, immer am Ende geschrieben, sogar mit `lseek`?

Wenn Sie eine Datei mit O_APPEND öffnen , werden alle Daten an das Ende geschrieben, unabhängig davon, was der aktuelle Dateizeiger seit dem letzten Aufruf von lseek(2) ist oder die letzte Lese-/Schreiboperation. Ab open(2) Dokumentation:

O_APPEND
Die Datei wird im Append-Modus geöffnet. Vor jedem write(2) , wird der Datei-Offset wie bei lseek(2) am Ende der Datei positioniert .

Wenn Sie Daten an das Ende der Datei und später an den Anfang schreiben möchten, öffnen Sie sie ohne O_APPEND , verwenden Sie fstat(2) um die Dateigröße zu erhalten (st_size Mitglied innerhalb von struct stat ), und suchen Sie dann nach diesem Offset, um das Ende zu schreiben.


Tatsächlich beeinflusst O_APPEND nur das Verhalten von write , aber nicht die von read . Unabhängig davon, wie die aktuelle Position einer Datei durch lseek geändert wird , write wird immer append-only .

Wenn Sie open eine Datei mit O_RDWR | O_APPEND , read beginnt immer noch am Anfang der Datei.

Im Handbuch von open (man 2 open ),

O_APPENDDie Datei wird im Append-Modus geöffnet. Vor jedem schreiben (2) wird der Datei-Offset am Ende der Datei positioniert.

Im Handbuch von write (man 2 write ),

Wenn das O_APPEND-Flag der Dateistatus-Flags gesetzt ist, soll der Datei-Offset vor jedem Schreiben auf das Ende der Datei gesetzt werden .

Im Linux-Kernel fs/ext4 syscall write -> vfs_write -> ext4_file_write_iter ,die ext4_file_write_iter ruft ext4_write_checks auf

Rufen Sie dann generic_write_checks an

Sie finden die Stelle, an der Sie den pos einstellen =file.size

/* FIXME: this is for backwards compatibility with 2.4 */
if (iocb->ki_flags & IOCB_APPEND)
    iocb->ki_pos = i_size_read(inode);
pos = iocb->ki_pos;

Die folgende Demo kann dies verifizieren.

cat open_append.cc
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
#include <errno.h>
#include <string.h>

#include <string>
#include <iostream>

int main(int argc, char *argv[]) {
  std::string path = "./test.txt";
  std::string content = "hello_world";
  std::string read_buf(content.size(), 0x0);
  struct stat st_buf;
  ssize_t bytes_read = -1;
  ssize_t bytes_write = -1;
  int ret = -1;
  off_t cur_off = -1;
  int fd = ::open(path.c_str(), O_CREAT | O_RDWR | O_TRUNC, 0644);
  if (fd < 0) {
    std::cerr << "open err path " << path
              << " errno " << errno << std::endl;
    return -1;
  }
  std::cout << "open ok path " << path
            << " fd " << fd << std::endl;

  // Step 1 write some data into an empty file
  bytes_write = ::write(fd, content.data(), content.size());
  if (bytes_write < 0) {
    std::cerr << "write err fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "write ok fd " << fd
            << " data " << content
            << " nbytes " << bytes_write << std::endl;
  ::close(fd);

  // Step 2 open the file again with O_APPEND
  fd = -1;
  fd = ::open(path.c_str(), O_CREAT | O_RDWR | O_APPEND, 0644);
  if (fd < 0) {
    std::cerr << "open again err path " << path
              << " errno " << errno << std::endl;
    return -1;
  }
  std::cout << "open again ok path " << path
            << " fd " << fd << std::endl;

  // Step 3 the current position of the file NOT affected by O_APPEND
  cur_off = ::lseek(fd, 0, SEEK_CUR);
  if (cur_off < 0) {
    std::cerr << "lseek err SEEK_CUR fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  // cur_off expected to be 0
  std::cout << "lseek ok SEEK_CUR fd " << fd
            << " cur_off " << cur_off << std::endl;

  // Step 4  the read will start from the beginning of the file
  bytes_read = read(fd, (char*)read_buf.data(), content.size());
  if (bytes_read < 0) {
    std::cerr << "read err fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "read ok fd " << fd
            << " data " << read_buf
            << " nbytes " << bytes_read << std::endl;

  // Step 5 change the position to the half of the file size
  cur_off = ::lseek(fd, content.size() / 2, SEEK_SET);
  if (cur_off < 0) {
    std::cerr << "lseek err SEEK_SET fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  // cur_off expected to be content.size() / 2
  std::cout << "lseek ok SEEK_SET fd " << fd
            << " cur_off " << cur_off << std::endl;

  // Step 6 write will append data from the end of the file
  // the current position is ignored
  bytes_write = ::write(fd, content.data(), content.size());
  if (bytes_write < 0) {
    std::cerr << "append write err fd " << fd
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "append write ok fd " << fd
            << " append data " << content
            << " append nbytes " << bytes_write << std::endl;

  // Step 7 the file size is double content.size()
  memset((void*)&st_buf, 0x0, sizeof(struct stat));
  ret = lstat(path.c_str(), &st_buf);
  if (ret < 0) {
    std::cerr << "lstat err path " << path
              << " errno " << errno << std::endl;
    goto out;
  }
  std::cout << "lstat ok path " << path
            << " st_size " << st_buf.st_size << std::endl;
  ret = 0;

out:
  if (fd >= 0) {
    close(fd);
  }
  return ret;
}

Ergebnis ausgeben

open ok path ./test.txt fd 3
write ok fd 3 data hello_world nbytes 11
open again ok path ./test.txt fd 3
lseek ok SEEK_CUR fd 3 cur_off 0
read ok fd 3 data hello_world nbytes 11
lseek ok SEEK_SET fd 3 cur_off 5
append write ok fd 3 append data hello_world append nbytes 11
lstat ok path ./test.txt st_size 22

Linux
  1. Erste Schritte mit dem Linux-tac-Befehl

  2. Warum erkennt das Bash-Skript keine Aliase?

  3. Warum wird der Dateideskriptor nur einmal geöffnet und gelesen?

  4. Zum Anfang oder Ende der Datei in Vim gehen [Schneller Tipp]

  5. Warum schlägt das Herunterfahren von Net RPC mit den richtigen Anmeldeinformationen fehl?

Finden Sie Dateien und Verzeichnisse unter Linux mit dem Befehl find

Lesen und schreiben Sie Daten von überall mit Umleitung im Linux-Terminal

Erstellen Sie mit dem Linux MLVWM einen Retro-Apple-Desktop

Sichern Sie Linux mit der Sudoers-Datei

Was bedeutet ein + am Ende der Berechtigungen von ls -l?

Feststellen, ob gerade auf die Datei geschrieben wird?