توی این درس یاد میگیری هر برنامهی در حال اجرا یک پروسه (process) است با شمارهی یکتای PID؛ با ps aux، top و htop میبینی روی سیستم چه میگذرد؛ با سیگنالها (kill یعنی SIGTERM و kill -9 یعنی SIGKILL) پروسه را مؤدبانه یا اجباری متوقف میکنی؛ کار طولانی را با & به پسزمینه میبری و با jobs، fg و bg کنترل میکنی؛ و با nohup کاری را بعد از بستن ترمینال زنده نگه میداری. تمرین: یک پروسهی طولانی را به پسزمینه میبری، دوباره میآوری و متوقفش میکنی.
مسئله: کامپیوتر همزمان صدها کار میکند
Section titled “مسئله: کامپیوتر همزمان صدها کار میکند”روی هر سیستم لینوکسی همزمان دهها تا صدها برنامه در حال اجراست: وبسرور، دیتابیس، ssh، و shell خودت. وقتی سرور کند میشود، یک اسکریپت گیر میکند یا باید یک کار چندساعته را رها کنی و بروی، باید بتوانی ببینی چه چیزی اجراست، کدامش مشکل دارد و چطور کنترلش کنی.
تشبیه: سفارشهای یک آشپزخانه
Section titled “تشبیه: سفارشهای یک آشپزخانه”هر سفارش در آشپزخانه یک شماره دارد (PID)؛ سرآشپز (هسته) تصمیم میگیرد کدام سفارش چقدر از اجاق (CPU) بگیرد. kill مثل این است که به آشپز بگویی «لطفاً این را تمام کن و تمیز کن» (او میتواند ظرفهایش را جمع کند)؛ kill -9 مثل این است که گاز را قطع کنی: همان لحظه میایستد، بدون هیچ تمیزکاری.
مثالهای عملی
Section titled “مثالهای عملی”مثال ۱: ps aux، همهی پروسهها
Section titled “مثال ۱: ps aux، همهی پروسهها”اول چند پروسهی پسزمینه میسازم تا چیزی برای دیدن باشد (علامت & آخر دستور یعنی «در پسزمینه اجرا کن»؛ مثال ۵):
sleep 600 &sleep 601 &python3 -c 'import time; time.sleep(602)' &sleep 1ps auxUSER PID %CPU %MEM VSZ RSS TTY STAT START TIME COMMANDroot 1 0.0 0.0 872 504 ? Ss 21:00 0:00 /sbin/docker-init -- sleep infinityroot 6 0.0 0.0 2280 1220 ? S 21:00 0:00 sleep infinityali 350 0.0 0.0 4472 3264 ? Ss 21:07 0:00 bashali 359 0.0 0.0 2708 1664 ? S 21:07 0:00 sleep 600ali 360 0.0 0.0 2708 1640 ? S 21:07 0:00 sleep 601ali 361 0.0 0.2 14396 8336 ? S 21:07 0:00 python3 -c import time; time.sleep(602)ali 363 0.0 0.0 8032 3840 ? R 21:07 0:00 ps auxروی یک سرور واقعی دهها تا صدها خط میبینی؛ این آزمایشگاه یک کانتینر کوچک است. ستونها:
| ستون | معنی |
|---|---|
USER |
صاحب پروسه |
PID |
شمارهی یکتای پروسه |
%CPU / %MEM |
سهم CPU (از شروع پروسه) و حافظهی فیزیکی |
VSZ / RSS |
حافظهی مجازی و حافظهی واقعی مصرفی (KB) |
TTY |
ترمینال متصل؛ ? یعنی بدون ترمینال (مثل این آزمایش؛ در ترمینال واقعی pts/0 است) |
STAT |
وضعیت: S خواب (منتظر رویداد)، R در حال اجرا، T متوقف، Z زامبی، s سرپرست session، + پیشزمینه |
START / TIME |
زمان شروع / مجموع زمان CPU |
COMMAND |
دستور با آرگومانها |
دو پروسهی اول از خود کانتینرند: PID 1 اولین پروسه است و والد همهی پروسهها (در سرور واقعی systemd). حالا ببین هر پروسه والدش (PPID) کیست، و چطور ستونهای دلخواه بخواهی:
echo "--- ps -ef (سبک UNIX؛ ستون PPID = پروسهی والد؛ فقط سه خط اول):"ps -ef | head -3echo "--- ستونهای دلخواه با -o، مرتب بر اساس حافظهی واقعی (RSS):"ps -eo pid,user,%mem,rss,etime,args --sort=-rss | head -6--- ps -ef (سبک UNIX؛ ستون PPID = پروسهی والد؛ فقط سه خط اول):UID PID PPID C STIME TTY TIME CMDroot 1 0 0 21:00 ? 00:00:00 /sbin/docker-init -- sleep infinityroot 6 1 0 21:00 ? 00:00:00 sleep infinity--- ستونهای دلخواه با -o، مرتب بر اساس حافظهی واقعی (RSS): PID USER %MEM RSS ELAPSED COMMAND 361 ali 0.2 8336 00:01 python3 -c import time; time.sleep(602) 366 ali 0.0 3812 00:00 ps -eo pid,user,%mem,rss,etime,args --sort=-rss 350 ali 0.0 3264 00:01 bash 359 ali 0.0 1664 00:01 sleep 600 360 ali 0.0 1640 00:01 sleep 601ps aux (سبک BSD، بدون خطتیره) و ps -ef (سبک UNIX) تقریباً همارزند؛ با -o هر ستونی را انتخاب میکنی و با --sort=-ستون مرتب میکنی (- یعنی نزولی). برای پیدا کردن PID از روی اسم، pgrep سادهتر از ps | grep است:
echo "--- PID پروسههای sleep خودم:"pgrep -u ali sleepecho "--- با دستور کامل (-a):"pgrep -u ali -a sleepecho "--- جستجو در کل خط فرمان (-f):"pgrep -a -f time.sleepecho "--- تعداد (-c):"pgrep -c -u ali sleep--- PID پروسههای sleep خودم:359360--- با دستور کامل (-a):359 sleep 600360 sleep 601--- جستجو در کل خط فرمان (-f):361 python3 -c import time; time.sleep(602)--- تعداد (-c):2مثال ۲: من، والد و فرزندانم
Section titled “مثال ۲: من، والد و فرزندانم”برای دیدن ساختار درختی، یک shell واقعی در ترمینال مجازی (tmux، مثل درسهای قبل) باز میکنم. $$ شمارهی PID خود shell و $PPID والد اوست:
run_in_tmux() { tmux -u kill-server 2>/dev/null; tmux -u new-session -d -s t -x "${TW:-76}" -y "${TH:-24}" "$@"; sleep 1; }screen() { tmux capture-pane -t t -p | sed -e :a -e '/^[[:space:]]*$/{$d;N;ba' -e '}'; }run_in_tmux "bash --norc"tmux send-keys -t t 'echo "PID من: $$ | PID والد: $PPID"' Enter; sleep 1tmux send-keys -t t 'ps -o pid,ppid,stat,tty,comm -p $$,$PPID' Enter; sleep 1tmux send-keys -t t 'sleep 300 &' Enter; sleep 1tmux send-keys -t t 'ps -o pid,ppid,stat,tty,cmd --ppid $$' Enter; sleep 1tmux send-keys -t t 'pstree -p $PPID' Enter; sleep 1screenbash-5.2$ echo "PID من: $$ | PID والد: $PPID"PID من: 376 | PID والد: 375bash-5.2$ ps -o pid,ppid,stat,tty,comm -p $$,$PPID PID PPID STAT TT COMMAND 375 1 Ss ? tmux: server 376 375 Ss pts/0 bashbash-5.2$ sleep 300 &[1] 385bash-5.2$ ps -o pid,ppid,stat,tty,cmd --ppid $$ PID PPID STAT TT CMD 385 376 S pts/0 sleep 300 388 376 R+ pts/0 ps -o pid,ppid,stat,tty,cmd --ppid 376bash-5.2$ pstree -p $PPIDtmux: server(375)───bash(376)─┬─pstree(391) └─sleep(385)bash-5.2$shell من فرزند tmux: server است، و sleep 300 که الان ساختم فرزند shell است (PPID آن برابر PID shell). pstree -p همین رابطهی درختی را نشان میدهد (از بستهی psmisc). هر پروسهای که اجرا میکنی فرزند پروسهای است که او را راه انداخته؛ این زنجیره به PID 1 میرسد.
مثال ۳: top و htop، نمای زنده
Section titled “مثال ۳: top و htop، نمای زنده”ps یک عکس لحظهای است؛ top همان را هر چند ثانیه بهروز میکند. برای اینکه چیزی پرمصرف ببینیم، یک پروسهی عمداً شلوغ راه میاندازم (yes تا ابد «y» چاپ میکند؛ خروجی را دور میریزم) و top را در ترمینال مجازی باز میکنم:
yes > /dev/null &TW=100run_in_tmux "top"sleep 2screentmux send-keys -t t q; sleep 1top - 21:07:12 up 1 day, 2:05, 0 user, load average: 0.56, 3.94, 3.39Tasks: 10 total, 2 running, 8 sleeping, 0 stopped, 0 zombie%Cpu(s): 3.7 us, 9.8 sy, 0.0 ni, 86.6 id, 0.0 wa, 0.0 hi, 0.0 si, 0.0 stMiB Mem : 3916.4 total, 409.1 free, 2301.0 used, 1394.7 buff/cacheMiB Swap: 1024.0 total, 774.4 free, 249.6 used. 1615.4 avail Mem
PID USER PR NI VIRT RES SHR S %CPU %MEM TIME+ COMMAND 395 ali 20 0 2708 1572 1468 R 90.9 0.0 0:00.22 yes 1 root 20 0 872 504 460 S 0.0 0.0 0:00.08 docker-init 6 root 20 0 2280 1220 1140 S 0.0 0.0 0:00.00 sleep 350 ali 20 0 4472 3280 2984 S 0.0 0.1 0:00.02 bash 359 ali 20 0 2708 1664 1568 S 0.0 0.0 0:00.00 sleep 360 ali 20 0 2708 1640 1548 S 0.0 0.0 0:00.00 sleep 361 ali 20 0 14396 8336 5620 S 0.0 0.2 0:00.00 python3 400 ali 20 0 9496 4044 2980 S 0.0 0.1 0:00.01 tmux: server 401 ali 20 0 9072 4960 2888 R 0.0 0.1 0:00.00 top 403 ali 20 0 2708 1684 1592 S 0.0 0.0 0:00.00 sleepخطهای بالای top:
load average: میانگین تعداد پروسههای منتظر CPU در ۱، ۵ و ۱۵ دقیقهی اخیر (درس منابع سیستم).Tasks: تعداد پروسهها به تفکیک وضعیت.%Cpu(s):us(برنامههای کاربر)،sy(هسته)،id(بیکار)،wa(منتظر دیسک).MiB MemوMiB Swap: حافظهی کل، آزاد، مصرفی و کش.
yes با ۱۰۰٪ CPU بالای فهرست است. کلیدهای مفید top: q خروج، P مرتبسازی با CPU، M با حافظه، k فرستادن سیگنال به یک PID، 1 نمایش هر هسته جدا، h راهنما. htop همین کار را رنگی و با منوی کلیدهای F انجام میدهد (اگر نصب نبود: sudo apt install htop):
run_in_tmux "htop"sleep 2screentmux send-keys -t t q; sleep 1tmux -u kill-server 2>/dev/null 0[|| 1.3%] 4[ 0.0%] 1[ 0.0%] 5[ 0.0%] 2[||||||||||||||||||||||||||||||||||||100.0%] 6[ 0.0%] 3[| 0.7%] 7[ 0.0%] Mem[|||||||||||||||||||||||||||||||2.09G/3.82G] Tasks: 10, 0 thr, 0 kthr; 2 running Swp[|||||||||||| 151M/1024M] Load average: 0.60 3.89 3.37 Uptime: 1 day, 02:06:03
[Main] [I/O] PID USER PRI NI VIRT RES SHR S CPU%▽MEM% TIME+ Command 395 ali 20 0 2708 1572 1468 R 100.3 0.0 0:05.64 yes 1 root 20 0 872 504 460 S 0.0 0.0 0:00.07 /sbin/docker-init -- sleep infini 6 root 20 0 2280 1220 1140 S 0.0 0.0 0:00.00 sleep infinity 350 ali 20 0 4472 3284 2984 S 0.0 0.1 0:00.02 bash 359 ali 20 0 2708 1664 1568 S 0.0 0.0 0:00.00 sleep 600 360 ali 20 0 2708 1640 1548 S 0.0 0.0 0:00.00 sleep 601 361 ali 20 0 14396 8336 5620 S 0.0 0.2 0:00.00 python3 -c import time; time.slee 413 ali 20 0 9496 4060 2980 S 0.0 0.1 0:00.00 tmux -u new-session -d -s t -x 10 414 ali 20 0 4992 3524 2960 R 0.0 0.1 0:00.00 htop 417 ali 20 0 2708 1692 1596 S 0.0 0.0 0:00.00 sleep 2
F1Help F2Setup F3SearchF4FilterF5Tree F6SortByF7Nice -F8Nice +F9Kill F10Quitپایین htop منوی کلیدها هست: F3 جستجو، F4 فیلتر، F5 نمای درختی، F6 ترتیب مرتبسازی، F9 فرستادن سیگنال (kill) و F10 یا q خروج. با کلیدهای جهتنما روی یک پروسه میروی و F9 میزنی.
مثال ۴: سیگنالها، kill و kill -9
Section titled “مثال ۴: سیگنالها، kill و kill -9”سیگنال یک پیام کوچک است که هسته (یا یک پروسهی دیگر) به یک پروسه میفرستد. فهرست مهمترینها با شمارهشان:
for s in HUP INT QUIT KILL TERM STOP CONT TSTP; do printf '%s=%s ' "$s" "$(kill -l $s)"; done; echoecho "--- ابتدای فهرست کامل (kill -l):"kill -l | head -3HUP=1 INT=2 QUIT=3 KILL=9 TERM=15 STOP=19 CONT=18 TSTP=20--- ابتدای فهرست کامل (kill -l): 1) SIGHUP 2) SIGINT 3) SIGQUIT 4) SIGILL 5) SIGTRAP 6) SIGABRT 7) SIGBUS 8) SIGFPE 9) SIGKILL 10) SIGUSR111) SIGSEGV 12) SIGUSR2 13) SIGPIPE 14) SIGALRM 15) SIGTERM| سیگنال | شماره | چطور میرسد | معنی |
|---|---|---|---|
SIGHUP |
1 | بستهشدن ترمینال | «ارتباط قطع شد»؛ پیشفرض: توقف |
SIGINT |
2 | Ctrl+C |
قطع کردن |
SIGTERM |
15 | kill PID |
درخواست مؤدبانهی توقف |
SIGKILL |
9 | kill -9 PID |
توقف اجباری؛ قابلگرفتن نیست |
SIGSTOP |
19 | kill -STOP PID |
مکث (قابلگرفتن نیست) |
SIGTSTP |
20 | Ctrl+Z |
مکث از طرف ترمینال |
SIGCONT |
18 | kill -CONT PID، fg، bg |
ادامه |
فرق عملی kill و kill -9 را با یک «سرویس» نمونه ببین. این اسکریپت یک فایل lock میسازد (نشان «من در حال اجرا هستم»، که برنامههای واقعی مثل دیتابیسها هم میسازند) و اگر SIGTERM بگیرد، تمیزکاری میکند:
#!/bin/bash# یک «سرویس» نمونه: lock میسازد و با SIGTERM تمیزکاری میکندif [ -e /tmp/worker.lock ]; then echo "[worker] خطا: فایل lock هست؛ نمونهی دیگری در حال اجراست؟" exit 1ficleanup() { echo "[worker] SIGTERM گرفتم: lock را پاک میکنم و خارج میشوم"; rm -f /tmp/worker.lock; exit 0; }trap cleanup TERMtouch /tmp/worker.lockecho "[worker] شروع شد (PID $$)"while true; do sleep 1; doneاول با kill (همان SIGTERM پیشفرض):
chmod +x worker.sh./worker.sh > worker.log 2>&1 &pid=$!sleep 2echo "قبل از kill: $(ls /tmp/worker.lock)"kill $pidwait $pidecho "کد خروج: $?"cat worker.logls /tmp/worker.lock 2>&1قبل از kill: /tmp/worker.lockکد خروج: 0[worker] شروع شد (PID 438)[worker] SIGTERM گرفتم: lock را پاک میکنم و خارج میشومls: cannot access '/tmp/worker.lock': No such file or directoryسرویس سیگنال را گرفت، پیام تمیزکاری را چاپ کرد، lock را پاک کرد و با کد ۰ (موفق) خارج شد. حالا همان را با kill -9:
./worker.sh > worker.log 2>&1 &pid=$!sleep 2kill -9 $pidwait $pid 2>/dev/nullecho "کد خروج: $? (یعنی 128+9)"cat worker.logecho "--- lock هنوز هست:"ls -l /tmp/worker.lockecho "--- و اجرای بعدی سرویس:"./worker.shrm -f /tmp/worker.lockکد خروج: 137 (یعنی 128+9)[worker] شروع شد (PID 448)--- lock هنوز هست:-rw-r--r-- 1 ali ali 0 Oct 3 21:07 /tmp/worker.lock--- و اجرای بعدی سرویس:[worker] خطا: فایل lock هست؛ نمونهی دیگری در حال اجراست؟بدون فرصت تمیزکاری، lock مانده و اجرای بعدی سرویس شکست میخورد (در برنامههای واقعی مثل PostgreSQL همین شکل «فایل pid/lock مانده» دردسر میسازد). قانون: اول kill، چند ثانیه صبر، و فقط اگر نمُرد kill -9. دو سیگنال دیگر برای مکث و ادامهی یک پروسه:
sleep 300 &pid=$!sleep 1echo "اجرا: $(ps -o stat= -p $pid)"kill -STOP $pid; sleep 1echo "مکث: $(ps -o stat= -p $pid) (T = Stopped)"kill -CONT $pid; sleep 1echo "ادامه: $(ps -o stat= -p $pid)"kill $pid; wait $pid 2>/dev/nullاجرا: Sمکث: T (T = Stopped)ادامه: Sکدام سیگنال را پروسه نمیتواند بگیرد و نادیده بگیرد؟
SIGKILL (و SIGSTOP) را هسته مستقیم اعمال میکند؛ برای همین پروسه فرصت تمیزکاری ندارد و باید آخرین راه باشد.
مثال ۵: پسزمینه و پیشزمینه، &، jobs، fg و bg
Section titled “مثال ۵: پسزمینه و پیشزمینه، &، jobs، fg و bg”پیشفرض: دستوری که میزنی در پیشزمینه اجرا میشود و ترمینال تا پایانش قفل است. با & آخر دستور آن را به پسزمینه میفرستی و دستور بعدی را میزنی. کارهای shell را jobs فهرست میکند، و fg (foreground) و bg (background) آنها را جابهجا میکنند. اگر کاری در پیشزمینه است و میخواهی ترمینال را پس بگیری، Ctrl+Z متوقفش میکند و بعد bg ادامهاش را در پسزمینه میدهد. این کنترل فقط در shell تعاملی هست، پس دوباره از ترمینال مجازی استفاده میکنم:
TW=76TH=28run_in_tmux "bash --norc"tmux send-keys -t t "sleep 300" Enter; sleep 1tmux send-keys -t t C-z; sleep 1tmux send-keys -t t "bg" Enter; sleep 1tmux send-keys -t t "sleep 400 &" Enter; sleep 1tmux send-keys -t t "jobs -l" Enter; sleep 1tmux send-keys -t t "fg %1" Enter; sleep 1tmux send-keys -t t C-c; sleep 1tmux send-keys -t t "jobs" Enter; sleep 1tmux send-keys -t t "kill %2" Enter; sleep 1tmux send-keys -t t "jobs" Enter; sleep 1screenbash-5.2$ sleep 300^Z[1]+ Stopped sleep 300bash-5.2$ bg[1]+ sleep 300 &bash-5.2$ sleep 400 &[2] 479bash-5.2$ jobs -l[1]- 472 Running sleep 300 &[2]+ 479 Running sleep 400 &bash-5.2$ fg %1sleep 300^Cbash-5.2$ jobs[2]+ Running sleep 400 &bash-5.2$ kill %2bash-5.2$ jobs[2]+ Terminated sleep 400bash-5.2$قدمبهقدم:
sleep 300پیشزمینه را قفل کرد؛Ctrl+Zآن را متوقف کرد (Stopped) و shell برگشت.bgهمان کار را در پسزمینه ادامه داد ([1]+ sleep 300 &).sleep 400 &مستقیم در پسزمینه شروع شد و شمارهی کار ([2]) و PID را داد.jobs -lهر دو کار را با PID و وضعیت نشان داد (+کار «جاری» و-کار قبلی).fg %1کار شمارهی ۱ را به پیشزمینه آورد (%1یعنی «کار شمارهی ۱»)، وCtrl+C(SIGINT) آن را کشت.kill %2کار دوم را با شمارهی کار (نه PID) کشت؛jobsبعدش «Terminated» را گزارش کرد.
مثال ۶: nohup، بعد از بستن ترمینال زنده بمان
Section titled “مثال ۶: nohup، بعد از بستن ترمینال زنده بمان”وقتی ترمینال (یا اتصال ssh) بسته میشود، به shell سیگنال SIGHUP («ارتباط قطع شد») میرسد و shell آن را به همهی کارهایش میرساند؛ پس کارهای پسزمینه هم میمیرند. nohup (no hangup) SIGHUP را برای آن پروسه نادیده میگیرد. سه کار میسازم: یکی ساده، یکی با nohup، و یکی با disown (شل او را از فهرست کارهایش حذف میکند و دیگر SIGHUP نمیفرستد). بعد ترمینال را میبندم:
TW=76TH=14run_in_tmux "bash --norc"tmux send-keys -t t "sleep 701 &" Enter; sleep 1tmux send-keys -t t "nohup sleep 702 &" Enter; sleep 1tmux send-keys -t t "sleep 703 &" Enter; sleep 1tmux send-keys -t t "disown" Enter; sleep 1tmux send-keys -t t "ls -l nohup.out" Enter; sleep 1screenecho "--- ترمینال را میبندم (shell SIGHUP میگیرد)..."tmux -u kill-serversleep 1echo "--- کدام زنده ماند؟"pgrep -a -f 'sleep 70[123]'bash-5.2$ sleep 701 &[1] 504bash-5.2$ nohup sleep 702 &[2] 507bash-5.2$ nohup: ignoring input and appending output to 'nohup.out'sleep 703 &[3] 510bash-5.2$ disownbash-5.2$ ls -l nohup.out-rw------- 1 ali ali 0 Oct 3 21:07 nohup.outbash-5.2$--- ترمینال را میبندم (shell SIGHUP میگیرد)...--- کدام زنده ماند؟507 sleep 702510 sleep 703فقط 702 (با nohup) و 703 (با disown) زنده ماندند؛ 701 با بستهشدن ترمینال مُرد. nohup وقتی خروجی ترمینالی باشد پیام «ignoring input and appending output to nohup.out» را میدهد و خروجی را در فایل nohup.out میریزد (برای کنترل بهتر: nohup دستور > log.txt 2>&1 &). روی سرور برای کارهای تعاملی طولانی، ابزار tmux (که در این درسها دیدی) بهتر است، چون بعداً دوباره به همان جلسه وصل میشوی.
مثال ۷: پیدا کردن و کشتن پروسهی پرمصرف
Section titled “مثال ۷: پیدا کردن و کشتن پروسهی پرمصرف”همان سناریوی «سرور کند شده». yes از مثال ۳ هنوز CPU میخورد. پرمصرفترینها را مرتبشده بگیر، با pgrep PID را پیدا کن و بکش:
echo "--- پرمصرفترین پروسهها بر اساس CPU:"ps -eo pid,user,%cpu,%mem,etime,comm --sort=-%cpu | head -4echo "--- PID با نام:"pgrep -a yesecho "--- pkill: کشتن با نام (SIGTERM):"pkill yessleep 1pgrep -a yes || echo "yes دیگر نیست"--- پرمصرفترین پروسهها بر اساس CPU: PID USER %CPU %MEM ELAPSED COMMAND 395 ali 99.9 0.0 00:34 yes 350 ali 0.0 0.0 00:41 bash 1 root 0.0 0.0 07:34 docker-init--- PID با نام:395 yes--- pkill: کشتن با نام (SIGTERM):yes دیگر نیستpkill نام (و killall نام) به همهی پروسههایی که اسمشان منطبق است سیگنال میفرستد؛ قبل از زدن، با pgrep -a ببین چه کسانی میخورند.
پشت پرده: fork، exec و /proc
Section titled “پشت پرده: fork، exec و /proc”هر پروسه با دو قدم ساخته میشود: fork() پروسهی فعلی را کپی میکند (فرزند، با PID تازه)، و فرزند با exec() برنامهی دیگری را بهجای خودش لود میکند. بعد والد با wait() منتظر پایان فرزند میماند و کد خروجیاش را میگیرد. وقتی در shell ls میزنی همین اتفاق میافتد:
و ps اطلاعاتش را از کجا میآورد؟ هسته برای هر پروسه یک پوشهی مجازی در /proc/PID میسازد (درس ساختار پوشهها):
sleep 603 &pid=$!echo "--- بعضی از چیزهای داخل /proc/$pid:"ls /proc/$pid | head -14 | tr '\n' ' '; echoecho "--- وضعیت:"grep -E '^(Name|State|Pid|PPid|Uid)' /proc/$pid/statusecho "--- خط فرمان:"tr '\0' ' ' < /proc/$pid/cmdline; echoecho "--- برنامهی اجراشده و فایلهای باز (fd 0 و 1 و 2 = stdin، stdout، stderr):"ls -l /proc/$pid/exe /proc/$pid/fdkill $pid; wait $pid 2>/dev/null--- بعضی از چیزهای داخل /proc/529:attr autogroup auxv cgroup clear_refs cmdline comm coredump_filter cpuset cwd environ exe fd fdinfo--- وضعیت:Name: sleepState: S (sleeping)Pid: 529PPid: 350Uid: 1001 1001 1001 1001--- خط فرمان:sleep 603--- برنامهی اجراشده و فایلهای باز (fd 0 و 1 و 2 = stdin، stdout، stderr):lrwxrwxrwx 1 ali ali 0 Oct 3 21:07 /proc/529/exe -> /usr/bin/sleep
/proc/529/fd:total 0lr-x------ 1 ali ali 64 Oct 3 21:07 0 -> /dev/nulll-wx------ 1 ali ali 64 Oct 3 21:07 1 -> pipe:[3088651]l-wx------ 1 ali ali 64 Oct 3 21:07 2 -> pipe:[3088651]ps و top کاری جز خواندن همین فایلهای /proc و مرتبکردنشان ندارند. وضعیتهای پروسه (ستون STAT):
| حرف | معنی | توضیح |
|---|---|---|
R |
Running | در حال اجرا یا آمادهی اجرا روی CPU |
S |
Sleeping | منتظر یک رویداد (ورودی، شبکه، زمان) |
D |
Disk sleep | منتظر دیسک یا I/O (قابلقطع نیست؛ زیاد بودنش نشانهی مشکل دیسک) |
T |
Stopped | با Ctrl+Z یا SIGSTOP متوقف شده |
Z |
Zombie | تمام شده ولی والدش هنوز wait() نکرده |
جدولهای مرجع
Section titled “جدولهای مرجع”| دستور | کار |
|---|---|
ps aux / ps -ef |
همهی پروسهها |
ps -eo pid,ppid,user,%cpu,%mem,args --sort=-%cpu |
ستونهای دلخواه، مرتبشده |
pgrep -a نام |
PID و دستور پروسههای منطبق |
pstree -p |
درخت پروسهها |
top / htop |
نمای زنده (خروج: q) |
kill PID |
SIGTERM، درخواست مؤدبانه |
kill -9 PID |
SIGKILL، اجباری (آخرین راه) |
kill -l |
فهرست سیگنالها |
pkill نام / killall نام |
سیگنال به همهی پروسههای همنام |
دستور & |
اجرا در پسزمینه |
jobs / jobs -l |
کارهای این shell |
fg %N / bg %N |
آوردن کار به پیشزمینه / ادامه در پسزمینه |
Ctrl+Z / Ctrl+C |
متوقف کردن (SIGTSTP) / قطع کردن (SIGINT) |
nohup دستور & |
زنده ماندن بعد از بستهشدن ترمینال |
disown |
جدا کردن کار از shell |
اشتباهات رایج
Section titled “اشتباهات رایج”۱) kill -9 بهعنوان اولین راه
Section titled “۱) kill -9 بهعنوان اولین راه”مثال ۴ را دیدی: بدون فرصت تمیزکاری، lock یا فایل موقت میماند و اجرای بعدی خراب میشود؛ برای دیتابیسها میتواند به خرابی داده هم برسد. راهحل: اول kill، ۵ تا ۱۰ ثانیه صبر، بعد اگر لازم بود kill -9.
۲) سیگنال به PID اشتباه یا مال دیگران
Section titled “۲) سیگنال به PID اشتباه یا مال دیگران”echo "--- پروسهی root را میخواهم بکشم (PID 1):"kill 1echo "--- PID که وجود ندارد:"kill 999999--- پروسهی root را میخواهم بکشم (PID 1):bash: kill: (1) - Operation not permitted--- PID که وجود ندارد:bash: kill: (999999) - No such processOperation not permitted: فقط صاحب پروسه (یا root) میتواند به آن سیگنال بفرستد. No such process: PID مرده یا اشتباه است (PID ها بعد از مدتی دوباره استفاده میشوند؛ قبل از kill با ps -p PID مطمئن شو همان است که فکر میکنی). راهحل: ps یا pgrep -a را بزن و فقط PID درست را بکش؛ برای پروسهی دیگران، sudo kill.
۳) pkill با نام خیلی عمومی
Section titled “۳) pkill با نام خیلی عمومی”sleep 9998 &sleep 9999 &echo "--- آنچه pkill sleep میگرفت (پیشنمایش با pgrep):"pgrep -a -u ali sleep | grep -E 'sleep (600|601|9998|9999)$'echo "--- دقیق: فقط یکی را با -f و الگوی کامل:"pkill -f 'sleep 9999'sleep 1pgrep -a -f 'sleep 999[89]'pkill -f 'sleep 9998'--- آنچه pkill sleep میگرفت (پیشنمایش با pgrep):359 sleep 600360 sleep 601536 sleep 9998537 sleep 9999--- دقیق: فقط یکی را با -f و الگوی کامل:536 sleep 9998pkill sleep همهی sleepها را میکشت، از جمله کارهای مهم دیگر. راهحل: اول pgrep -a برای پیشنمایش، و برای دقت pkill -f 'الگوی کامل' یا خود PID.
۴) تلاش برای کشتن zombie
Section titled “۴) تلاش برای کشتن zombie”bash -c 'sleep 1 & exec sleep 30' &parent=$!sleep 2echo "--- فرزند تمامشدهای که والدش wait نکرده (Z):"ps -o pid,ppid,stat,cmd --ppid $parentzpid=$(ps -o pid= --ppid $parent | tr -d ' ')kill -9 $zpidsleep 1echo "--- بعد از kill -9 روی خود zombie:"ps -o pid,ppid,stat,cmd --ppid $parentecho "--- راهحل: والدش را متوقف کن (zombie به PID 1 میرسد و جمع میشود):"kill $parent; wait $parent 2>/dev/nullsleep 1ps -o pid,ppid,stat,cmd --ppid $parentecho "(خالی: zombie رفت)"--- فرزند تمامشدهای که والدش wait نکرده (Z): PID PPID STAT CMD 546 544 Z [sleep] <defunct>--- بعد از kill -9 روی خود zombie: PID PPID STAT CMD 546 544 Z [sleep] <defunct>--- راهحل: والدش را متوقف کن (zombie به PID 1 میرسد و جمع میشود): PID PPID STAT CMD(خالی: zombie رفت)zombie پروسهی «مرده» است که فقط یک ردیف در جدول پروسهها اشغال کرده تا والد کد خروجیاش را بخواند؛ نمیشود با سیگنال کشتش (قبلاً مرده). تعداد کم بیخطر است، ولی اگر هزاران تا جمع شوند جدول پروسهها پر میشود. راهحل: والد را درست کن (باگ در wait()) یا متوقفش کن تا PID 1 آنها را جمع کند.
۵) fg وقتی کاری نیست
Section titled “۵) fg وقتی کاری نیست”TW=70TH=6run_in_tmux "bash --norc"tmux send-keys -t t "fg" Enter; sleep 1tmux send-keys -t t "jobs" Enter; sleep 1screentmux -u kill-serverbash-5.2$ fgbash: fg: current: no such jobbash-5.2$ jobsbash-5.2$fg: current: no such job، یعنی این shell کار متوقف یا پسزمینهای ندارد. دقت کن کارها مال همین shell هستند: در یک ترمینال دیگر، یا بعد از بستهشدن shell، jobs خالی است و برای پیدا کردن پروسه باید از ps یا pgrep استفاده کنی.
سه پروسهی sleep در پسزمینه بساز، با pgrep -c تعدادشان را بشمار، با pkill همه را بکش و دوباره بشمار.
دیدن جواب
pkill -u ali sleepsleep 801 & sleep 802 & sleep 803 &sleep 1echo "تعداد: $(pgrep -c -u ali sleep)"pkill -u ali sleepsleep 1echo "بعد از pkill: $(pgrep -c -u ali sleep)"تعداد: 3بعد از pkill: 0تمرین اصلی: یک پروسهی طولانی را به پسزمینه ببر، دوباره بیاور و متوقفش کن. sleep 500 را در پیشزمینه اجرا کن، با Ctrl+Z متوقفش کن، با bg به پسزمینه بفرست، با jobs ببین، با fg برگردان و با Ctrl+C تمامش کن.
دیدن جواب
TW=70TH=16run_in_tmux "bash --norc"tmux send-keys -t t "sleep 500" Enter; sleep 1tmux send-keys -t t C-z; sleep 1tmux send-keys -t t "bg" Enter; sleep 1tmux send-keys -t t "jobs" Enter; sleep 1tmux send-keys -t t "fg" Enter; sleep 1tmux send-keys -t t C-c; sleep 1tmux send-keys -t t "jobs" Enter; sleep 1screentmux -u kill-serverbash-5.2$ sleep 500^Z[1]+ Stopped sleep 500bash-5.2$ bg[1]+ sleep 500 &bash-5.2$ jobs[1]+ Running sleep 500 &bash-5.2$ fgsleep 500^Cbash-5.2$ jobsbash-5.2$پروسهای که SIGTERM را عمداً نادیده میگیرد (trap "" TERM) را اجرا کن. نشان بده kill معمولی کارش نمیکند و kill -9 میکند. کد خروجی را هم ببین.
دیدن جواب
bash -c 'trap "" TERM; while true; do sleep 1; done' &pid=$!sleep 1kill $pidsleep 2echo "بعد از kill (SIGTERM): $(ps -o stat= -p $pid) (زنده است)"kill -9 $pidwait $pid 2>/dev/nullecho "کد خروج بعد از kill -9: $?"ps -p $pid > /dev/null || echo "پروسه تمام شد"بعد از kill (SIGTERM): S (زنده است)کد خروج بعد از kill -9: 137پروسه تمام شدآزمونک
Section titled “آزمونک”PID 1 چیست؟
همهی پروسهها در نهایت از PID 1 مشتق میشوند؛ یتیمها هم به او میرسند.
تفاوت kill PID و kill -9 PID؟
اول SIGTERM، و فقط اگر جواب نداد SIGKILL.
چطور دستوری را از ابتدا در پسزمینه اجرا میکنی؟
sleep 300 &؛ بعد با jobs فهرست و با fg برمیگردانی.
کاری در پیشزمینه است. چطور ترمینال را پس بگیری و کار ادامه یابد؟
Ctrl+C کار را میکشد؛ Ctrl+Z فقط متوقف میکند.
چرا کار پسزمینه با بستهشدن ssh میمیرد و nohup چه میکند؟
راههای دیگر: disown، یا اجرا در tmux.
پروسهای با وضعیت Z (zombie) را چطور از بین میبری؟
zombie قبلاً مرده؛ فقط منتظر است والد کد خروجی را بخواند.
جمعبندی
Section titled “جمعبندی”- هر برنامهی در حال اجرا یک پروسه است با PID، والد (PPID)، کاربر و وضعیت؛ هسته اطلاعاتشان را در
/proc/PIDمیگذارد وps/topهمان را میخوانند. ps auxعکس لحظهای،top/htopنمای زنده؛pgrep -a نامبرای پیدا کردن PID؛ps -eo ... --sort=-%cpuبرای پرمصرفها.kill PID= SIGTERM (مؤدبانه، تمیزکاری)،kill -9 PID= SIGKILL (اجباری، آخرین راه).Ctrl+C= SIGINT،Ctrl+Z= SIGTSTP.&اجرا در پسزمینه؛jobs،fg %N،bg %Nکارهای همان shell را کنترل میکنند.- بستهشدن ترمینال = SIGHUP برای کارها؛ با
nohup(یاdisown، یا tmux) زنده میمانند. - zombie (
Z) را نمیشود کشت؛ والدش را درست کن.
| دستور | کاری که میکند |
|---|---|
ps aux / ps -ef | همهی پروسهها |
ps -eo pid,ppid,user,%cpu,%mem,args --sort=-%cpu | ستون دلخواه، مرتبشده |
pgrep -a نام | PID و دستور پروسههای منطبق |
top / htop | نمای زنده (q = خروج) |
kill PID | SIGTERM (مؤدبانه) |
kill -9 PID | SIGKILL (اجباری، آخرین راه) |
pkill -f "الگو" | سیگنال به پروسههای منطبق |
دستور & | اجرا در پسزمینه |
jobs / fg %1 / bg %1 | کنترل کارهای shell |
Ctrl+Z / Ctrl+C | متوقف کردن / قطع کردن |
nohup دستور > log 2>&1 & | زنده ماندن بعد از بستهشدن ترمینال |
kill -l | فهرست سیگنالها |