fcntl --- فراخوانی‌های سیستمی fcntl و ioctl


این ماژول کنترل پرونده و ورودی/خروجی را روی توصیف‌گرهای پرونده انجام می‌دهد. این ماژول رابطی به روتین‌های fcntl() و ioctl() یونیکس است. برای جزئیات کامل، صفحه‌های راهنمای یونیکس fcntl(2) و ioctl(2) را ببینید.

دسترس‌پذیری: Unix, not WASI.

تمام توابع این ماژول یک توصیف‌گر پرونده fd را به‌عنوان نخستین آرگومان خود می‌پذیرند. این می‌تواند یک توصیف‌گر پرونده از نوع عدد صحیح باشد، مانند آنچه sys.stdin.fileno() برمی‌گرداند، یا یک شیء io.IOBase، مانند خود sys.stdin، که متد fileno() را فراهم می‌کند و این متد یک توصیف‌گر پرونده واقعی برمی‌گرداند.

تغییر یافته در نسخه‌ی 3.3: عملیات این ماژول پیش‌تر IOError را پرتاب می‌کردند، در حالی که اکنون OSError را پرتاب می‌کنند.

تغییر یافته در نسخه‌ی 3.8: ماژول fcntl اکنون شامل ثابت‌های F_ADD_SEALS، F_GET_SEALS و F_SEAL_* برای مهر و موم کردن (sealing) توصیف‌گرهای پرونده os.memfd_create() است.

تغییر یافته در نسخه‌ی 3.9: در macOS، ماژول fcntl ثابت F_GETPATH را در دسترس قرار می‌دهد که مسیر یک پرونده را از یک توصیف‌گر پرونده به دست می‌آورد. در لینوکس (>=3.15)، ماژول fcntl ثابت‌های F_OFD_GETLK، F_OFD_SETLK و F_OFD_SETLKW را در دسترس قرار می‌دهد که هنگام کار با قفل‌های توصیف پرونده باز (open file description locks) استفاده می‌شوند.

تغییر یافته در نسخه‌ی 3.10: در لینوکس >= 2.6.11، ماژول fcntl ثابت‌های F_GETPIPE_SZ و F_SETPIPE_SZ را در معرض قرار می‌دهد که به ترتیب امکان بررسی و تغییر اندازه‌ی پایپ را فراهم می‌کنند.

تغییر یافته در نسخه‌ی 3.11: در FreeBSD، ماژول fcntl ثابت‌های F_DUP2FD و F_DUP2FD_CLOEXEC را در دسترس قرار می‌دهد که امکان تکثیر یک توصیف‌گر پرونده را فراهم می‌کنند؛ دومی علاوه بر این، پرچم FD_CLOEXEC را نیز تنظیم می‌کند.

تغییر یافته در نسخه‌ی 3.12: در لینوکس 4.5 و بالاتر، ماژول fcntl ثابت‌های FICLONE و FICLONERANGE را ارائه می‌دهد، که امکان اشتراک‌گذاری بخشی از داده‌های یک پرونده با پرونده‌ای دیگر را از طریق بازپیوند (reflinking) در برخی سیستم‌های پرونده (مانند btrfs، OCFS2 و XFS) فراهم می‌کنند. این رفتار معمولاً به‌عنوان «کپی هنگام نوشتن (copy-on-write)» شناخته می‌شود.

تغییر یافته در نسخه‌ی 3.13: در لینوکس >= 2.6.32، ماژول fcntl ثابت‌های F_GETOWN_EX، F_SETOWN_EX، F_OWNER_TID، F_OWNER_PID و F_OWNER_PGRP را در معرض قرار می‌دهد، که امکان هدایت سیگنال‌های دسترس‌پذیری I/O به یک نخ، فرایند یا گروه فرایند خاص را فراهم می‌کنند. در لینوکس >= 4.13، ماژول fcntl ثابت‌های F_GET_RW_HINT، F_SET_RW_HINT، F_GET_FILE_RW_HINT، F_SET_FILE_RW_HINT و RWH_WRITE_LIFE_* را در معرض قرار می‌دهد، که امکان اطلاع‌رسانی به هسته درباره طول عمر مورد انتظار نسبی نوشتن‌ها روی یک آی‌نود مشخص یا از طریق یک توصیف پرونده باز (open file description) خاص را فراهم می‌کنند. در لینوکس >= 5.1 و NetBSD، ماژول fcntl ثابت F_SEAL_FUTURE_WRITE را برای استفاده با عملیات‌های F_ADD_SEALS و F_GET_SEALS در معرض قرار می‌دهد. در FreeBSD، ماژول fcntl ثابت‌های F_READAHEAD، F_ISUNIONSTACK و F_KINFO را در معرض قرار می‌دهد. در macOS و FreeBSD، ماژول fcntl ثابت F_RDAHEAD را در معرض قرار می‌دهد. در NetBSD و AIX، ماژول fcntl ثابت F_CLOSEM را در معرض قرار می‌دهد. در NetBSD، ماژول fcntl ثابت F_MAXFD را در معرض قرار می‌دهد. در macOS و NetBSD، ماژول fcntl ثابت‌های F_GETNOSIGPIPE و F_SETNOSIGPIPE را در معرض قرار می‌دهد.

تغییر یافته در نسخه‌ی 3.14: در لینوکس 6.1 یا بالاتر، ماژول fcntl، F_DUPFD_QUERY را برای استعلام توصیف‌گر پرونده‌ای که به همان پرونده اشاره دارد، در دسترس قرار می‌دهد.

این ماژول توابع زیر را تعریف می‌کند:

fcntl.fcntl(fd, cmd, arg=0, /)

عملیات cmd را روی توصیف‌گر پرونده fd انجام می‌دهد (اشیای پرونده‌ای که متد fileno() را فراهم می‌کنند نیز پذیرفته می‌شوند). مقادیر مورد استفاده برای cmd به سیستم‌عامل وابسته‌اند و به‌عنوان ثابت‌هایی در ماژول fcntl در دسترس هستند، با همان نام‌هایی که در پرونده‌های سرآیند C مرتبط استفاده شده‌اند. آرگومان arg می‌تواند یک مقدار عدد صحیح، یک bytes-like object یا یک رشته باشد. نوع و اندازه‌ی arg باید با نوع و اندازه‌ی آرگومان عملیات، همان‌طور که در مستندات C مرتبط مشخص شده است، مطابقت داشته باشد.

هنگامی که arg عدد صحیح باشد، تابع مقدار بازگشتی از نوع عدد صحیحِ فراخوانی C fcntl() را برمی‌گرداند.

هنگامی که آرگومان یک شیء شبه‌بایت (bytes-like) باشد، این آرگومان یک ساختار دودویی را نشان می‌دهد؛ برای مثال، ساختاری که با struct.pack() ایجاد شده است. یک مقدار رشته‌ای با استفاده از کدگذاری UTF-8 به دودویی کدگذاری می‌شود. داده‌های دودویی به یک بافر کپی می‌شوند و نشانی آن به فراخوانی fcntl() در C ارسال می‌شود. مقدار بازگشتی پس از یک فراخوانی موفق، محتوای بافر است که به یک شیء bytes تبدیل می‌شود. طول شیء برگردانده‌شده همان طول آرگومان arg خواهد بود. این مقدار به ۱۰۲۴ بایت محدود است.

اگر فراخوانی fcntl() با شکست مواجه شود، یک OSError پرتاب می‌شود.

توجه

اگر نوع یا اندازه‌ی arg با نوع یا اندازه‌ی آرگومان عملیات مطابقت نداشته باشد (برای مثال، اگر زمانی که اشاره‌گر مورد انتظار است، یک عدد صحیح ارسال شود، یا اطلاعاتی که سیستم‌عامل در بافر برمی‌گرداند بزرگ‌تر از ۱۰۲۴ بایت باشد)، این حالت به احتمال زیاد منجر به نقض قطعه‌بندی یا خرابی داده‌ی پنهان‌تر می‌شود.

یک رویداد حسابرسی fcntl.fcntl را با آرگومان‌های fd، cmd و arg پرتاب می‌کند.

تغییر یافته در نسخه‌ی 3.14: افزودن پشتیبانی از هرگونه شیء شبه‌بایت (bytes-like object)، نه فقط bytes.

fcntl.ioctl(fd, request, arg=0, mutate_flag=True, /)

این تابع با تابع fcntl() یکسان است، با این تفاوت که مدیریت آرگومان‌ها حتی پیچیده‌تر است.

پارامتر request به مقادیری محدود است که می‌توانند در ۳۲ بیت یا ۶۴ بیت جا بگیرند، بسته به سکو. ثابت‌های اضافی مورد توجه برای استفاده به‌عنوان آرگومان request را می‌توان در ماژول termios یافت، با همان نام‌هایی که در پرونده‌های سرآیند C مربوطه استفاده می‌شوند.

پارامتر arg می‌تواند یک عدد صحیح، یک bytes-like object، یا یک رشته باشد. نوع و اندازه‌ی arg باید با نوع و اندازه‌ی آرگومان عملیات، همان‌طور که در مستندات C مربوطه مشخص شده است، مطابقت داشته باشد.

اگر arg از رابط بافر خواندن-نوشتن پشتیبانی نکند یا mutate_flag نادرست باشد، رفتار مانند تابع fcntl() است.

اگر arg از رابط بافر خواندنی-نوشتنی (مانند bytearray) پشتیبانی کند و mutate_flag درست باشد (پیش‌فرض)، آنگاه بافر (در عمل) به فراخوانی سیستمی زیرین ioctl() ارسال می‌شود، کد بازگشتی آن به پایتون فراخواننده بازگردانده می‌شود، و محتوای جدید بافر بازتاب عمل ioctl() است. این یک ساده‌سازی جزئی است، زیرا اگر طول بافر ارائه‌شده کمتر از ۱۰۲۴ بایت باشد، ابتدا در یک بافر ایستا به طول ۱۰۲۴ بایت کپی می‌شود؛ سپس همان بافر به ioctl() ارسال می‌شود و دوباره در بافر ارائه‌شده کپی می‌شود.

اگر فراخوانی ioctl() با شکست مواجه شود، یک استثنای OSError پرتاب می‌شود.

توجه

اگر نوع یا اندازه‌ی arg با نوع یا اندازه‌ی آرگومان عملیات مطابقت نداشته باشد (برای مثال، اگر زمانی که یک اشاره‌گر انتظار می‌رود، یک عدد صحیح ارسال شود، یا اطلاعاتی که سیستم‌عامل در بافر برمی‌گرداند بزرگ‌تر از ۱۰۲۴ بایت باشد، یا اندازه‌ی شیء شبه‌بایت (bytes-like) تغییرپذیر بیش از حد کوچک باشد)، این موضوع به احتمال زیاد منجر به خطای قطعه‌بندی (segmentation violation) یا خرابی داده‌ای ظریف‌تر خواهد شد.

یک مثال:

>>> import array, fcntl, struct, termios, os
>>> os.getpgrp()
13341
>>> struct.unpack('h', fcntl.ioctl(0, termios.TIOCGPGRP, "  "))[0]
13341
>>> buf = array.array('h', [0])
>>> fcntl.ioctl(0, termios.TIOCGPGRP, buf, 1)
0
>>> buf
array('h', [13341])

یک رویداد حسابرسی fcntl.ioctl را با آرگومان‌های fd، request و arg پرتاب می‌کند.

تغییر یافته در نسخه‌ی 3.14: GIL همیشه در حین یک فراخوانی سیستمی آزاد می‌شود. فراخوانی‌های سیستمی که با EINTR ناموفق می‌شوند، به‌طور خودکار دوباره تلاش می‌شوند.

fcntl.flock(fd, operation, /)

عملیات قفل operation را روی توصیف‌گر پرونده fd انجام دهید (اشیاء پرونده‌ای که یک متد fileno() ارائه می‌دهند نیز پذیرفته می‌شوند). برای جزئیات، صفحه‌ی راهنمای Unix flock(2) را ببینید. (در برخی سیستم‌ها، این تابع با استفاده از fcntl() شبیه‌سازی می‌شود.)

اگر فراخوانی flock() ناموفق باشد، استثنای OSError پرتاب می‌شود.

یک رویداد حسابرسی fcntl.flock را با آرگومان‌های fd و operation پرتاب می‌کند.

fcntl.lockf(fd, cmd, len=0, start=0, whence=0, /)

این اساساً پوششی بر روی فراخوانی‌های قفل‌کردن fcntl() است. fd توصیف‌گر پرونده برای پرونده‌ای است که باید قفل یا باز شود (اشیای پرونده‌ای که متد fileno() را ارائه می‌دهند نیز پذیرفته می‌شوند)، و cmd یکی از مقادیر زیر است:

fcntl.LOCK_UN

یک قفل موجود را آزاد کنید.

fcntl.LOCK_SH

یک قفل مشترک را به دست آورید.

fcntl.LOCK_EX

یک قفل انحصاری کسب کنید.

fcntl.LOCK_NB

برای غیرمسدودکننده کردن درخواست، از عملگر OR بیتی با هر یک از سه ثابت دیگر LOCK_* استفاده کنید.

اگر از LOCK_NB استفاده شود و نتوان قفل را کسب کرد، یک OSError پرتاب خواهد شد و ویژگی errno این استثنا روی EACCES یا EAGAIN تنظیم خواهد شد (بسته به سیستم‌عامل؛ برای قابلیت حمل، هر دو مقدار را بررسی کنید). در حداقل برخی سیستم‌ها، تنها در صورتی می‌توان از LOCK_EX استفاده کرد که توصیف‌گر پرونده به پرونده‌ای اشاره کند که برای نوشتن باز شده است.

len تعداد بایت‌هایی است که باید قفل شوند، start آفست بایتی است که قفل از آن شروع می‌شود، نسبت به whence، و whence همانند io.IOBase.seek() است، به‌طور مشخص:

  • 0 -- نسبت به ابتدای پرونده (os.SEEK_SET)

  • 1 -- نسبت به موقعیت فعلی بافر (os.SEEK_CUR)

  • 2 -- نسبت به پایان پرونده (os.SEEK_END)

مقدار پیش‌فرض برای start برابر ۰ است، که به معنای آغاز از ابتدای پرونده است. مقدار پیش‌فرض برای len برابر ۰ است، که به معنای قفل کردن تا پایان پرونده است. مقدار پیش‌فرض برای whence نیز ۰ است.

یک رویداد حسابرسی fcntl.lockf را با آرگومان‌های fd، cmd، len، start و whence پرتاب می‌کند.

مثال‌ها (همه روی یک سیستم منطبق با SVR4):

import struct, fcntl, os

f = open(...)
rv = fcntl.fcntl(f, fcntl.F_SETFL, os.O_NDELAY)

lockdata = struct.pack('hhllhh', fcntl.F_WRLCK, 0, 0, 0, 0, 0)
rv = fcntl.fcntl(f, fcntl.F_SETLKW, lockdata)

توجه داشته باشید که در مثال اول، متغیر مقدار بازگشتی rv حاوی یک مقدار عدد صحیح خواهد بود؛ در مثال دوم، حاوی یک شیء bytes خواهد بود. چیدمان ساختار متغیر lockdata وابسته به سیستم است --- بنابراین استفاده از فراخوانی flock() ممکن است بهتر باشد.

همچنین ملاحظه نمائید

ماژول os

اگر پرچم‌های قفل‌گذاری O_SHLOCK و O_EXLOCK در ماژول os موجود باشند (فقط در BSD)، تابع os.open() جایگزینی برای توابع lockf() و flock() فراهم می‌کند.