GNU/Linux >> Tutoriels Linux >  >> Linux

Pourquoi les données sont-elles écrites dans un fichier ouvert avec le drapeau O_APPEND, toujours écrit à la fin, même avec `lseek` ?

Lorsque vous ouvrez un fichier avec O_APPEND , toutes les données sont écrites jusqu'à la fin, quel que soit le pointeur de fichier actuel depuis le dernier appel à lseek(2) ou la dernière opération de lecture/écriture. À partir du open(2) documents :

O_APPEND
Le fichier est ouvert en mode ajout. Avant chaque write(2) , l'offset du fichier est positionné à la fin du fichier, comme avec lseek(2) .

Si vous souhaitez écrire des données à la fin du fichier, puis au début de celui-ci plus tard, ouvrez-le sans O_APPEND , utilisez fstat(2) pour obtenir la taille du fichier (st_size membre dans struct stat ), puis recherchez ce décalage pour écrire la fin.


En effet, O_APPEND n'affecte que le comportement de write , mais pas celui de read . Quelle que soit la manière dont la position actuelle d'un fichier est modifiée par lseek , write sera toujours append-only .

Lorsque vous open un fichier avec O_RDWR | O_APPEND , read commencera toujours depuis le début du fichier.

Dans le manuel de open (man 2 open ),

O_APPENDLe fichier est ouvert en mode ajout. Avant chaque écriture (2), le décalage du fichier est positionné à la fin du fichier.

Dans le manuel de write (man 2 write ),

Si l'indicateur O_APPEND des indicateurs d'état de fichier est défini, le décalage de fichier doit être défini à la fin du fichier avant chaque écriture .

Dans le noyau Linux fs/ext4 syscall write -> vfs_write -> ext4_file_write_iter ,le ext4_file_write_iter appellera le ext4_write_checks

puis appelez le generic_write_checks

vous trouverez l'endroit où mettre le pos =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;

La démo suivante peut le vérifier.

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;
}

Résultat de sortie

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. Premiers pas avec la commande Linux tac

  2. Comment ajouter une nouvelle ligne à la fin d'un fichier ?

  3. Pourquoi le descripteur de fichier est-il ouvert et lu une seule fois ?

  4. Aller au début ou à la fin du fichier dans Vim [Quick Tip]

  5. Comment combiner la commande 'tar' avec 'find'

Rechercher des fichiers et des répertoires sous Linux avec la commande find

Lire et écrire des données de n'importe où avec la redirection dans le terminal Linux

Construisez un bureau Apple rétro avec Linux MLVWM

Sécurisez Linux avec le fichier Sudoers

Que signifie un + à la fin des autorisations de ls -l ?

Déterminez si le fichier est en cours d'écriture ?