Может ли Perl sysopen открыть файл для атомарной записи? - PullRequest
0 голосов
/ 31 января 2019

Читая книгу APUE (3-е издание), я столкнулся с системным вызовом open и его возможностью разрешить пользователю открывать файл для write атомарной операции в режиме O_APPEND, что означает, что несколько процессов могут записывать в дескриптор файлаи ядро ​​гарантирует, что данные, записанные в один файл несколькими процессами, не перекрываются и все строки не повреждены.

После успешных экспериментов с открытым системным вызовом с помощью программы на C / C ++ я смог проверитьто же самое, и это работает так же, как книга описывает.Я смог запустить несколько процессов, которые записали в один файл, и все строки могли быть учтены относительно их PID процессов.

Я надеялся наблюдать такое же поведение с perl sysopen, так как у меня есть некоторыезадачи на работе, которые могли бы извлечь выгоду из этого поведения.Пробовал, но на самом деле это не сработало.Когда я проанализировал выходной файл, я смог увидеть признаки состояния гонки (возможно), поскольку во нем много раз чередуются строки.

Вопрос: Разве вызов perl sysopen не совпадает с системным вызовом Linux в open?Можно ли выполнить этот тип атомарной операции записи несколькими процессами в один файл?

РЕДАКТИРОВАТЬ: добавление кода C и кода perl, используемого для тестирования.

C / C ++код

int main(void)
{
  if ((fd = open("outfile.txt",O_WRONLY|O_CREAT|O_APPEND)) == -1) { 
    printf ("failed to create outfile! exiting!\n");
    return -1;
  }

  for (int counter{1};counter<=MAXLINES;counter++)
  { /* write string 'line' for MAXLINES no. of times */
    std::string line = std::to_string(ACE_OS::getpid())
      + " This is a sample data line ";
    line += std::to_string(counter) + " \n";
    if ((n = write(fd,line.c_str(),strlen(line.c_str()))) == -1) {
      printf("Failed to write to outfile!\n";
    }
  }
  return 0;
}

Perl-код

#!/usr/bin/perl

use Fcntl;
use strict;
use warnings;

my $maxlines = 100000;

sysopen (FH, "testfile", O_CREAT|O_WRONLY|O_APPEND) or die "failed sysopen\n";
while ($maxlines != 0) {
  print FH "($$) This is sample data line no. $maxlines\n";
  $maxlines--;
}
close (FH);
__END__

Обновление ( после первоначального устранения неполадок ):

Благодаря информации, предоставленной в ответе ниже, я смог заставить его работать.Хотя я столкнулся с проблемой пропущенных строк, которая была вызвана тем, что я открывал файл с каждым процессом с O_TRUNC, чего я не должен был делать, но сначала пропустил его.После некоторого тщательного анализа - я обнаружил проблему и исправил ее.Как всегда - linux никогда не подводит вас:).

Вот скрипт bash, который я использовал для запуска процессов:

#!/bin/bash

# basically we spawn "$1" instances of the same 
# executable which should append to the same output file.

max=$1
[[ -z $max ]] && max=6
echo "creating $max processes for appending into same file"

# this is our output file collecting all
# the lines from all the processes.
# we truncate it before we start
>testfile

for i in $(seq 1 $max)
do
    echo $i && ./perl_read_write_with_syscalls.pl 2>>_err & 
done

# end.

Проверка из выходного файла:

[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  ls -lrth testfile 
-rw-rw-r--. 1 compuser compuser 252M Jan 31 22:52 testfile
[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  wc -l testfile 
6000000 testfile
[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  cat testfile |cut -f1 -d" "|sort|uniq -c
1000000 (PID: 21118)
1000000 (PID: 21123)
1000000 (PID: 21124)
1000000 (PID: 21125)
1000000 (PID: 21126)
1000000 (PID: 21127)
[compuser@lenovoe470:07-multiple-processes-append-to-a-single-file]$  

Наблюдения:

К моему удивлению, в системе не было средней нагрузки ожидания - вообще.Я не ожидал этого.Я полагаю, что ядро ​​как-то позаботилось об этом, но не знаю, как это работает.Мне было бы интересно узнать больше об этом.

Какие могут быть возможные применения этого?

Я делаю много файлов для согласования файлов,и мы ( на работе ) всегда должны анализировать огромные файлы данных (например, 30 - 50 ГБ каждый).С этой работой - теперь я мог бы выполнять параллельные операции вместо моего предыдущего подхода, который включал в себя: хэширование file1, затем хэширование file2, затем сравнение пар ключей и значений из 2 файлов.Теперь я могу параллельно выполнять хеширование и сокращать время, которое требуется - еще больше.

Спасибо

1 Ответ

0 голосов
/ 31 января 2019

Неважно, если вы open или sysopen;ключ использует syswrite и sysread вместо print / printf / say / и т. д. и readline / read / eof / и т. д.

syswriteсопоставляется с одним write(2) вызовом, в то время как print / printf / say / etc может привести к нескольким вызовам на write(2) (даже если автозапуск включен). [1]

sysread отображается на один read(2) вызов, в то время как readline / read / eof / etc может привести к нескольким вызовам на read(2).

. Таким образом,используя syswrite и sysread, вы подчиняетесь всем гарантиям, которые дает POSIX в отношении этих вызовов (какими бы они ни были), если вы работаете в системе POSIX.


  1. Есливы используете print / printf / say / etc и ограничивает ваши записи меньше размера буфера между (явными или автоматическими) сбрасываниями, вы получите один write(2) вызов.Размер буфера в старых версиях Perl составлял 4 КиБ, а в новых версиях Perl по умолчанию он равен 8 КиБ.(Размер определяется при создании perl.)
...