پوشهی /dev توی لینوکس چیه و فایلهایی مثل /dev/null و /dev/urandom چیکار میکنن؟
حدود ۸ دقیقه مطالعه
اگه تا حالا توی ترمینال یه دستور رو به یه جای عجیب ریدایرکت کردی که آخرش اسم null داشت، یعنی بدون اینکه شاید دقیق بدونی، داشتی از یه device file استفاده میکردی. پوشهی dev یکی از اون بخشهاییه که همه ازش استفاده میکنن ولی کمتر کسی کامل میفهمه چی توشه و چرا اونجوری کار میکنه. نکتهی جالبش اینه که چند تا از فایلهای این پوشه، فقط با دونستن چندتا ترفند، کارهای روزمرهی ترمینال رو خیلی سادهتر میکنن.
چرا اصلاً فایل برای دستگاه؟
فلسفهی یونیکس اینه که «همهچیز یه فایله». دیسک، ترمینال، صفحهکلید، حتی یه منبع تولید عدد تصادفی، همه از طریق یه فایل توی پوشهی dev در دسترس هستن. یعنی همون ابزارهایی که برای خوندن و نوشتن فایل معمولی استفاده میکنی (مثل cat و echo و dd و ریدایرکت) روی این دستگاهها هم کار میکنن. این فایلها محتوای واقعی روی دیسک ندارن؛ یه دروازه به کرنل هستن، و موقع خوندن یا نوشتن، کرنل یا درایور مربوطه جواب میده.
اگه فایلها رو با ls بهصورت long listing ببینی، فرقشون با فایلهای عادی همون حرف اول خروجیه:
ls -l /dev/null /dev/sda
# crw-rw-rw- 1 root root 1, 3 ... /dev/null
# brw-rw---- 1 root disk 8, 0 ... /dev/sda
معنی حرف اول خروجی اینهاست:
c = character device (داده رو بایتبهبایت و جریانی رد و بدل میکنه؛ مثل ترمینال یا null)
b = block device (داده رو در بلوکهای ثابت میخونه و مینویسه؛ مثل دیسک و SSD)
اون دو عدد جدا شده با کاما هم major و minor number هستن: major میگه کدوم درایور مسئوله و minor میگه کدوم دستگاه از اون درایور.
/dev/null، سیاهچالهی سیستم
هرچی توش بنویسی دور ریخته میشه، و اگه ازش بخونی بلافاصله EOF (پایان فایل) میگیری، یعنی هیچی. پرکاربردترین استفادهش ساکت کردن خروجی یه دستوره:
# خروجی عادی رو دور بنداز، خطاها رو نگه دار
command > /dev/null
# فقط خطاها رو دور بنداز
command 2> /dev/null
# هر دو رو دور بنداز
command > /dev/null 2>&1
# معادل کوتاهتر توی bash
command &> /dev/null
یه نکتهی ظریف: ترتیب ریدایرکت مهمه. توی حالت اول، ابتدا خروجی عادی به null میره و بعد خطاها هم به همون مقصدِ خروجی عادی وصل میشن. اگه ترتیب رو برعکس بنویسی، خطاها هنوز به ترمینال میرن، چون وقتی ریدایرکت خطاها ارزیابی میشه، خروجی عادی هنوز به ترمینال اشاره میکرده:
# درست: هر دو خروجی دور ریخته میشن
command > /dev/null 2>&1
# اشتباه: خطاها هنوز روی ترمینال میان
command 2>&1 > /dev/null
چند کاربرد دیگهش:
# خالی کردن محتوای یه فایل بدون پاک کردنش (مثلاً لاگ)
cat /dev/null > app.log
# فقط بررسی موفقیت یه دستور، بدون چاپ خروجیش
if grep -q "error" app.log 2>/dev/null; then
echo "خطا پیدا شد"
fi
# ساکت کردن یه cron job
0 * * * * /path/to/script.sh > /dev/null 2>&1
/dev/zero، منبع بیپایان صفر
هر بار که ازش بخونی، بایت صفر میده، بینهایت. به درد ساختن فایلهایی با اندازهی مشخص میخوره:
# ساخت یه فایل ۱۰۰ مگابایتی پر از صفر
dd if=/dev/zero of=test.img bs=1M count=100
# ساخت یه swap file ۲ گیگابایتی
sudo dd if=/dev/zero of=/swapfile bs=1M count=2048
sudo chmod 600 /swapfile
sudo mkswap /swapfile
sudo swapon /swapfile
اگه فقط فایلی با اندازهی مشخص بخوای و لازم نباشه واقعاً صفر روی دیسک نوشته بشه، دستور truncate خیلی سریعتره، چون یه sparse file میسازه که فضاش تا وقتی استفاده نشه اشغال نمیشه:
truncate -s 100M test.img
/dev/urandom و /dev/random، عدد تصادفی
این دو تا بایت تصادفی از مولد اعداد تصادفی کرنل میدن. سالها یه توصیهی رایج بود که random امنتره چون وقتی «آنتروپی» کم باشه بلاک میکنه، و urandom هیچوقت بلاک نمیکنه. ولی توی کرنلهای جدید این تفاوت عملاً از بین رفته: از کرنل ۴.۸ هر دو از یه CSPRNG یکسان تغذیه میشن، و از کرنل ۵.۶ به بعد random فقط تا زمانی که مولد موقع بوت اولیهسازی بشه بلاک میکنه. برای تقریباً هر کاربرد عملی، urandom انتخاب درسته.
# ۳۲ بایت تصادفی، بهصورت base64
head -c 32 /dev/urandom | base64
# رشتهی hex برای secret یا token
head -c 16 /dev/urandom | xxd -p
# یه پسورد ۲۴ کاراکتری فقط با حروف و عدد
tr -dc 'A-Za-z0-9' < /dev/urandom | head -c 24; echo
# معادل با openssl، اگه نصب باشه
openssl rand -base64 32
یه استفادهی دیگهش پر کردن دیسک با دادهی تصادفی قبل از رمزنگاری یا دور انداختنشه، ولی اینجا باید حواست باشه: روی SSD و فلش، پاکسازی با overwrite بهخاطر wear leveling تضمینشده نیست، و این روش بیشتر برای هارد مکانیکی معنی داره.
/dev/full، دیسک همیشه پر
این یکی کمکاربردتره ولی برای تست جالبه: هر نوشتنی توش با خطای «No space left on device» شکست میخوره. برای اینکه ببینی برنامهت موقع پر شدن دیسک درست رفتار میکنه یا نه:
echo "test" > /dev/full
# bash: echo: write error: No space left on device
/dev/stdin، /dev/stdout و /dev/stderr
این سه تا در واقع symlink به file descriptor های استاندارد پروسس فعلی هستن:
/dev/stdin -> /proc/self/fd/0
/dev/stdout -> /proc/self/fd/1
/dev/stderr -> /proc/self/fd/2
کاربردش وقتیه که یه برنامه فقط اسم فایل میگیره و تو میخوای ورودی یا خروجی استاندارد رو بهش بدی:
# پیام خطا رو به stderr بفرست
echo "یه مشکلی پیش اومد" > /dev/stderr
# فایل رو از stdin بخون، وقتی برنامه فقط مسیر فایل قبول میکنه
cat file.txt | some-tool --input /dev/stdin
/dev/tty، ترمینال واقعی
وقتی ورودی یه اسکریپت از pipe میاد، دیگه نمیتونی از کاربر ورودی بگیری. فایل tty همیشه به ترمینال کنترلکنندهی پروسس اشاره میکنه، حتی وقتی ورودی و خروجی ریدایرکت شدن. برای همین ابزارهایی مثل ssh و sudo پسورد رو از همینجا میخونن:
echo "ادامه بدم؟ (y/n)" > /dev/tty
read answer < /dev/tty
دیسکها و فایلهای بلوکی
دیسکها و پارتیشنها هم توی همین پوشهان. اسمگذاریشون این شکلیه:
/dev/sda, /dev/sda1 دیسک و پارتیشن SATA
/dev/nvme0n1, /dev/nvme0n1p1 دیسک و پارتیشن NVMe
قبل از هر کاری روشون، با lsblk و blkid مطمئن شو داری با دستگاه درست کار میکنی:
lsblk
blkid
اسم این دستگاهها ممکنه بعد از ریبوت یا وصل کردن یه دیسک جدید عوض بشه، برای همین توی fstab و اسکریپتها بهتره از مسیرهای پایدار استفاده کنی:
/dev/disk/by-uuid/
/dev/disk/by-id/
یه هشدار جدی: dd یکی از خطرناکترین ابزارهای لینوکسه چون هیچ تأییدی نمیگیره. اگه توی مقصد اسم دیسک اشتباه رو بنویسی، دادهت بدون برگشت پاک میشه. قبل از اجرا، مقصد رو دوباره چک کن:
# نوشتن یه ISO روی فلش (مطمئن شو sdX درسته!)
sudo dd if=ubuntu.iso of=/dev/sdX bs=4M status=progress conv=fsync
چند فایل دیگه که ارزش شناختن دارن
اینها فایلهای دیگهای هستن که گاهی سر راهت قرار میگیرن:
/dev/shm یه tmpfs توی RAM؛ برای فایلهای موقت خیلی سریع و shared memory
/dev/loop* وصل کردن یه فایل معمولی بهعنوان بلاک دیوایس (برای mount کردن image)
/dev/pts/* ترمینالهای مجازی (SSH، tmux و ...)
/dev/kmsg پیامهای لاگ کرنل؛ دستور dmesg از همین میخونه
/dev/fuse لازم برای فایلسیستمهای userspace مثل sshfs
/dev/kvm دسترسی به مجازیسازی سختافزاری برای QEMU/KVM
یه چیز جالب هم هست که واقعاً فایل نیست: مسیرهای tcp و udp زیر dev ویژگی خود bash هستن، نه یه فایل واقعی. با اینها میتونی بدون netcat یا curl یه پورت رو چک کنی:
# بررسی باز بودن پورت ۴۴۳ بدون هیچ ابزار اضافه
timeout 3 bash -c '</dev/tcp/example.com/443' && echo "باز است" || echo "بسته است"
اگه اتفاقی /dev/null رو پاک کردی
این اتفاق از چیزی که فکر میکنی رایجتره (مثلاً با یه ریدایرکت اشتباه یا یه اسکریپت بد). اگه null یه فایل معمولی شد یا پاک شد، خیلی از برنامهها عجیب رفتار میکنن. برای بازسازیش:
sudo rm -f /dev/null
sudo mknod -m 666 /dev/null c 1 3
اون آخرین بخش دستور، همون character device با major برابر ۱ و minor برابر ۳ هست که قبلاً دیدیم. برای بقیه هم همین الگو برقراره:
/dev/null c 1 3
/dev/zero c 1 5
/dev/full c 1 7
/dev/random c 1 8
/dev/urandom c 1 9
جمعبندی
اگه بخوای از این مقاله فقط چند چیز رو به یاد داشته باشی: null برای دور ریختن خروجی (با دقت به ترتیب ریدایرکت)، zero برای ساختن فایلهای پر از صفر، urandom برای تولید secret و رشتهی تصادفی، tty برای گرفتن ورودی از کاربر وسط pipe، و همیشه قبل از هر dd روی دیسک، یه بار دیگه با lsblk مطمئن شو داری روی دیسک درست مینویسی.
