سؤال

ما هي أفضل طريقة لإنشاء قفل على ملف في بيرل؟

هل من الأفضل التزاحم على الملف أم إنشاء ملف قفل لوضع قفل عليه والتحقق من وجود قفل على ملف القفل؟

هل كانت مفيدة؟

المحلول

إذا انتهى بك الأمر إلى استخدام القطيع، فإليك بعض التعليمات البرمجية للقيام بذلك:

use Fcntl ':flock'; # Import LOCK_* constants

# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code.  Use a variable.
my $file = '/path/to/some/file';

# Open the file for appending.  Note the file path is quoted
# in the error message.  This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";

# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX) or die "Could not lock '$file' - $!";

# Do something with the file here...

# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above.  This could create
# a race condition.  The close() call below will unlock the
# file for you, but only after writing any buffered data.

# In a world of buffered i/o, some or all of your data may not 
# be written until close() completes.  Always, always, ALWAYS 
# check the return value of close() if you wrote to the file!
close($fh) or die "Could not write '$file' - $!";

بعض الروابط المفيدة:

ردًا على سؤالك المضاف، أود أن أقول إما وضع القفل على الملف أو إنشاء ملف تسميه "قفل" عندما يكون الملف مقفلاً، ثم احذفه عندما لا يكون مقفلاً (ثم تأكد من طاعة برامجك تلك الدلالات).

نصائح أخرى

تغطي الإجابات الأخرى قفل قطيع Perl بشكل جيد، ولكن في العديد من أنظمة Unix/Linux يوجد في الواقع نظامان قفل مستقلان:BSD قطيع () والأقفال المستندة إلى POSIX fcntl ().

ما لم توفر خيارات خاصة لتكوينها عند إنشاء لغة Perl، فإن قطيعها سيستخدم قطيع () إذا كان متاحًا.يعد هذا أمرًا جيدًا بشكل عام وربما ما تريده إذا كنت تحتاج فقط إلى القفل داخل التطبيق الخاص بك (الذي يعمل على نظام واحد).ومع ذلك، في بعض الأحيان تحتاج إلى التفاعل مع تطبيق آخر يستخدم أقفال fcntl() (مثل Sendmail، في العديد من الأنظمة) أو ربما تحتاج إلى القيام بقفل الملفات عبر أنظمة الملفات المثبتة على NFS.

في تلك الحالات، قد ترغب في إلقاء نظرة على ملف::FcntlLock أو الملف::lockf.من الممكن أيضًا إجراء قفل يعتمد على fcntl() في لغة Perl النقية (مع بعض الأجزاء المشعرة وغير المحمولة من الحزمة()).

نظرة عامة سريعة على اختلافات القطيع/fcntl/lockf:

يتم تنفيذ lockf دائمًا تقريبًا أعلى fcntl، وله قفل على مستوى الملف فقط.إذا تم التنفيذ باستخدام fcntl، فإن القيود الموضحة أدناه تنطبق أيضًا على lockf.

يوفر fcntl تأمينًا على مستوى النطاق (داخل ملف) وتأمينًا للشبكة عبر NFS، ولكن لا يتم توريث الأقفال بواسطة العمليات التابعة بعد fork().في العديد من الأنظمة، يجب أن يكون لديك مقبض الملف مفتوحًا للقراءة فقط لطلب قفل مشترك، ومقبض للقراءة والكتابة لطلب قفل خاص.

يحتوي القطيع على قفل على مستوى الملف فقط، ويتم القفل فقط داخل جهاز واحد (يمكنك قفل ملف مثبت على NFS، لكن العمليات المحلية فقط هي التي سترى القفل).يتم وراثة الأقفال من قبل الأطفال (على افتراض أن واصف الملف غير مغلق).

في بعض الأحيان تتم محاكاة قطيع (أنظمة SYSV) باستخدام lockf أو fcntl؛في بعض أنظمة BSD تتم محاكاة lockf باستخدام القطيع.بشكل عام، تعمل هذه الأنواع من المحاكاة بشكل سيئ وينصح بتجنبها.

CPAN للإنقاذ: IO::LockedFile.

كتب رايان بي:

في هذه الحالة، يتم إلغاء تأمين الملف فعليًا لفترة قصيرة من الوقت أثناء إعادة فتح الملف.

لذلك لا تفعل ذلك.بدلاً من، open ملف القراءة/الكتابة:

open my $fh, '+<', 'test.dat'
    or die "Couldn’t open test.dat: $!\n";

عندما تكون مستعدًا لكتابة العداد، فقط seek العودة إلى بداية الملف.لاحظ أنه إذا قمت بذلك، فيجب عليك ذلك truncate فقط قبل close, ، حتى لا يُترك الملف مع بيانات غير صحيحة إذا كانت محتوياته الجديدة أقصر من محتوياته السابقة.(عادةً ما يكون الموضع الحالي في الملف في نهايته، لذا يمكنك الكتابة فقط truncate $fh, tell $fh.)

لاحظ أيضًا أنني استخدمت الوسيطة الثلاثة open ومقبض ملف معجمي، كما تأكدت من نجاح العملية.يرجى تجنب مقابض الملفات العامة (المتغيرات العامة سيئة، mmkay؟) والوسيطة السحرية open (والذي كان مصدرًا للعديد من الأخطاء (القابلة للاستغلال) في كود Perl)، واختبر دائمًا ما إذا كان لديك openنجح.

أعتقد أنه سيكون من الأفضل إظهار ذلك مع المتغيرات المعجمية كمعالجات للملفات ومعالجة الأخطاء.من الأفضل أيضًا استخدام الثوابت من وحدة Fcntl بدلاً من استخدام الرمز الثابت للرقم السحري 2 والذي قد لا يكون الرقم الصحيح في جميع أنظمة التشغيل.

    use Fcntl ':flock'; # import LOCK_* constants

    # open the file for appending
    open (my $fh, '>>', 'test.dat') or die $!;

    # try to lock the file exclusively, will wait till you get the lock
    flock($fh, LOCK_EX);

    # do something with the file here (print to it in our case)

    # actually you should not unlock the file
    # close the file will unlock it
    close($fh) or warn "Could not close file $!";

تحقق من كامل توثيق القطيع و ال تعليم قفل الملفات على PerlMonks على الرغم من أنه يستخدم أيضًا النمط القديم لاستخدام مقبض الملف.

في الواقع ، عادةً ما أتخطى معالجة الخطأ على Close () لأنه لا يوجد الكثير يمكنني فعله إذا فشل على أي حال.

فيما يتعلق بما يجب قفله، إذا كنت تعمل في ملف واحد، فقم بقفل هذا الملف.إذا كنت بحاجة إلى قفل عدة ملفات في وقت واحد - لتجنب الأقفال الميتة - فمن الأفضل اختيار ملف واحد تقوم بقفله.لا يهم حقًا ما إذا كان هذا أحد الملفات العديدة التي تحتاج حقًا إلى قفلها أو ملف منفصل تقوم بإنشائه لغرض القفل فقط.

هل فكرت في استخدام LockFile::وحدة بسيطة؟يقوم بمعظم العمل نيابةً عنك بالفعل.

في تجربتي السابقة، وجدت أنه سهل الاستخدام وقوي جدًا.

use strict;

use Fcntl ':flock'; # Import LOCK_* constants

# We will use this file path in error messages and function calls.
# Don't type it out more than once in your code.  Use a variable.
my $file = '/path/to/some/file';

# Open the file for appending.  Note the file path is in quoted
# in the error message.  This helps debug situations where you
# have a stray space at the start or end of the path.
open(my $fh, '>>', $file) or die "Could not open '$file' - $!";

# Get exclusive lock (will block until it does)
flock($fh, LOCK_EX);


# Do something with the file here...


# Do NOT use flock() to unlock the file if you wrote to the
# file in the "do something" section above.  This could create
# a race condition.  The close() call below will unlock it
# for you, but only after writing any buffered data.

# In a world of buffered i/o, some or all of your data will not 
# be written until close() completes.  Always, always, ALWAYS 
# check the return value on close()!
close($fh) or die "Could not write '$file' - $!";

كان هدفي في هذا السؤال هو قفل ملف يتم استخدامه كمخزن بيانات لعدة نصوص برمجية.في النهاية استخدمت رمزًا مشابهًا لما يلي (من كريس):

open (FILE, '>>', test.dat') ; # open the file 
flock FILE, 2; # try to lock the file 
# do something with the file here 
close(FILE); # close the file

في مثاله قمت بإزالة ملف القطيع، 8 حيث أن الإغلاق (FILE) ينفذ هذا الإجراء أيضًا.كانت المشكلة الحقيقية هي أنه عندما يبدأ البرنامج النصي، يجب عليه الاحتفاظ بالعداد الحالي، وعندما ينتهي، يجب عليه تحديث العداد.هذا هو المكان الذي يواجه فيه Perl مشكلة، لقراءة الملف:

 open (FILE, '<', test.dat');
 flock FILE, 2;

الآن أريد أن أكتب النتائج وبما أنني أريد الكتابة فوق الملف أحتاج إلى إعادة فتحه واقتطاعه مما يؤدي إلى ما يلي:

 open (FILE, '>', test.dat'); #single arrow truncates double appends
 flock FILE, 2;

في هذه الحالة، يتم إلغاء تأمين الملف فعليًا لفترة قصيرة من الوقت أثناء إعادة فتح الملف.يوضح هذا حالة ملف القفل الخارجي.إذا كنت ستقوم بتغيير سياقات الملف، فاستخدم ملف القفل.الكود المعدل:

open (LOCK_FILE, '<', test.dat.lock') or die "Could not obtain lock";
flock LOCK_FILE, 2;
open (FILE, '<', test.dat') or die "Could not open file";
# read file
# ...
open (FILE, '>', test.dat') or die "Could not reopen file";
#write file
close (FILE);
close (LOCK_FILE);

تم تطويره من http://metacpan.org/pod/File::FcntlLock

use Fcntl qw(:DEFAULT :flock :seek :Fcompat);
use File::FcntlLock;
sub acquire_lock {
  my $fn = shift;
  my $justPrint = shift || 0;
  confess "Too many args" if defined shift;
  confess "Not enough args" if !defined $justPrint;

  my $rv = TRUE;
  my $fh;
  sysopen($fh, $fn, O_RDWR | O_CREAT) or LOGDIE "failed to open: $fn: $!";
  $fh->autoflush(1);
  ALWAYS "acquiring lock: $fn";
  my $fs = new File::FcntlLock;
  $fs->l_type( F_WRLCK );
  $fs->l_whence( SEEK_SET );
  $fs->l_start( 0 );
  $fs->lock( $fh, F_SETLKW ) or LOGDIE  "failed to get write lock: $fn:" . $fs->error;
  my $num = <$fh> || 0;
  return ($fh, $num);
}

sub release_lock {
  my $fn = shift;
  my $fh = shift;
  my $num = shift;
  my $justPrint = shift || 0;

  seek($fh, 0, SEEK_SET) or LOGDIE "seek failed: $fn: $!";
  print $fh "$num\n" or LOGDIE "write failed: $fn: $!";
  truncate($fh, tell($fh)) or LOGDIE "truncate failed: $fn: $!";
  my $fs = new File::FcntlLock;
  $fs->l_type(F_UNLCK);
  ALWAYS "releasing lock: $fn";
  $fs->lock( $fh, F_SETLK ) or LOGDIE "unlock failed: $fn: " . $fs->error;
  close($fh) or LOGDIE "close failed: $fn: $!";
}

بديل واحد للقفل ملف النهج هو استخدام القفل قابس كهرباء.يرى قفل::مقبس على CPAN لمثل هذا التنفيذ.الاستخدام بسيط كما يلي:

use Lock::Socket qw/lock_socket/;
my $lock = lock_socket(5197); # raises exception if lock already taken

هناك بعض المزايا لاستخدام المقبس:

  • ضمان (من خلال نظام التشغيل) عدم وجود تطبيقين يحملان نفس القفل:لا توجد حالة السباق.
  • مضمون (مرة أخرى من خلال نظام التشغيل) للتنظيف بدقة عند إنهاء العملية، لذلك لا توجد أقفال قديمة للتعامل معها.
  • يعتمد على وظيفة مدعومة جيدًا بأي شيء يعمل عليه Perl:لا توجد مشاكل مع دعم القطيع (2) على Win32 على سبيل المثال.

العيب الواضح هو بالطبع أن مساحة اسم القفل عالمية.من الممكن حدوث نوع من رفض الخدمة إذا قررت عملية أخرى قفل المنفذ الذي تحتاجه.

[إفشاء:أنا مؤلف الوحدة المذكورة أعلاه]

استخدم ال قطيع لوقا.

يحرر: هذا هو تفسير جيد.

ينشئ قطيع أقفال ملفات بنمط Unix، وهو متاح في معظم أنظمة التشغيل التي تعمل بنظام Perl.لكن أقفال القطيع هي استشارية فقط.

يحرر:وشدد على أن القطيع محمول

إليك الحل للقراءة والكتابة في قفل واحد...

open (TST,"+< readwrite_test.txt") or die "Cannot open file\n$!";
flock(TST, LOCK_EX);
# Read the file:
@LINES=<TST>;
# Wipe the file:
seek(TST, 0, 0); truncate(TST, 0);
# Do something with the contents here:
push @LINES,"grappig, he!\n";
$LINES[3]="Gekke henkie!\n";
# Write the file:
foreach $l (@LINES)
{
   print TST $l;
}
close(TST) or die "Cannot close file\n$!";
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top