এই পর্বে যা শিখবেন
- OS-এর প্রশ্নে কাঠামো মেনে উত্তর দেওয়া — সংক্ষেপে শুরু, চাইলে গভীরে, আর না জানলে কীভাবে যুক্তি দিয়ে এগোবেন
- process, thread, async, lock, memory, file আর I/O-র প্রশ্নে বাস্তব Linux-এর আচরণ দিয়ে উত্তর
- “./a.out চালালে কী হয়” আর “google.com লিখে Enter চাপলে OS কী করে” — শুরু থেকে শেষ পর্যন্ত বলতে পারা
- container, exit code 137, CPU throttling, high load, ধীর ডিস্ক — DevOps/SRE ইন্টারভিউর দৃশ্যভিত্তিক প্রশ্ন সামলানো
- নিজের দুর্বল জায়গা ধরে দুই সপ্তাহে কোর্সের দরকারি পর্বগুলো রিভিশন করা
১ · উত্তর দেওয়ার পদ্ধতি — জোরে ভাবা, প্রশ্ন করা, ছবি আঁকা
পর্ব ৬০–৬২-এ আপনি numerical, MCQ আর লিখিত-মৌখিক প্রশ্নের অনুশীলন করেছেন। চাকরির ইন্টারভিউ এর থেকে আলাদা। সেখানে “সংজ্ঞা লিখুন” প্রায় আসে না; আসে “আমাদের container বারবার 137 দিয়ে মরছে, কোথা থেকে শুরু করবেন?” বা “একটি thread আর একটি process-এর মধ্যে কোনটি বেছে নেবেন, কেন?”। ইন্টারভিউয়ার দেখতে চান আপনি ধারণাটি বাস্তব সিস্টেমের সাথে জুড়তে পারেন কি না, আর চাপের মধ্যে গুছিয়ে ভাবতে পারেন কি না। এ ধরনের প্রশ্ন backend, DevOps/SRE আর systems ইন্টারভিউতে প্রায়ই আসে; কোনো নির্দিষ্ট প্রতিষ্ঠানের প্রশ্ন নয়।
| পদ | সাধারণত যেদিকে জোর | এই পাতার যে অংশ বেশি কাজে লাগবে |
|---|---|---|
| Software engineer / backend | thread বনাম async, lock আর deadlock, memory leak, file descriptor, fsync, epoll, “google.com লিখলে কী হয়” | অংশ ২, ৩, ৪, ৫, ৬ |
| DevOps / SRE / platform | container, cgroup, exit code, OOM, CPU throttling, load average, ডিস্ক আর “সার্ভার ধীর” তদন্ত | অংশ ৪, ৫, ৭, ৮ |
| Systems / embedded / kernel-ঘেঁষা | context switch, page fault, memory ordering, lock-free, system call, interrupt, ASLR-এর মতো সুরক্ষা | অংশ ২, ৩, ৪, ৯ আর পর্ব ৬৪ |
চারটি অভ্যাস
- জোরে ভাবা (think aloud): ইন্টারভিউয়ার আপনার মাথার ভেতর দেখতে পান না। “প্রথমে দেখব CPU-সংখ্যার তুলনায় load কত, কারণ…” — এভাবে বললে ভুল পথে গেলেও তিনি আপনাকে ফেরাতে পারেন, আর আপনার যুক্তির মান বুঝতে পারেন।
- প্রশ্ন করে পরিস্থিতি পরিষ্কার করা (clarifying question): OS-এর উত্তর প্রায়ই “কোন OS, কোন সংস্করণ, কোন পরিবেশে” তার ওপর নির্ভর করে। “Linux ধরে নিচ্ছি, ঠিক আছে?”, “এটি কি container-এর ভেতরে?”, “ডিস্ক HDD না NVMe?”, “সমস্যাটি হঠাৎ, নাকি ধীরে ধীরে?” — এক-একটি উত্তর তদন্তের অর্ধেক পথ বাদ দেয়।
- অনিশ্চয়তা সৎভাবে স্বীকার: “ঠিক সংখ্যাটি মনে নেই, তবে ধারণাটি হলো… আর আমি
man 2 fsyncবা kernel-এর ডকুমেন্টেশন দেখে নিশ্চিত হতাম” — বানিয়ে বলার চেয়ে এটি অনেক ভালো শোনায়। ভুল তথ্য আত্মবিশ্বাসের সাথে বলা ইন্টারভিউতে সবচেয়ে বড় লাল সংকেতগুলোর একটি। - ছবি আঁকা: process state diagram, একটি process-এর মেমরি-বিন্যাস (text, data, heap, mmap, stack), একটি অনুরোধের পথ (app → syscall → kernel → driver → ডিভাইস), বা দুই thread আর দুই lock-এর চক্র — হোয়াইটবোর্ডে বা কাগজে ৩০ সেকেন্ডের একটি ছবি এক মিনিটের কথার সমান।
হাসপাতালের জরুরি বিভাগে অভিজ্ঞ ডাক্তার আগে দুই লাইনে বলেন কী মনে হচ্ছে (“সম্ভবত ডিহাইড্রেশন”), তারপর প্রমাণ চান (প্রেশার, রক্ত পরীক্ষা), আর নিশ্চিত না হলে বলেন “এটি বাদ দিতে আরেকটি পরীক্ষা লাগবে।” ইন্টারভিউর উত্তরও এমন: আগে সংক্ষিপ্ত সিদ্ধান্ত, তারপর কারণ আর প্রমাণ, শেষে কী যাচাই করবেন।
দুই স্তরের উত্তরের কাঠামো
প্রথম ৩০ সেকেন্ডে মূল কথাটি বলুন, তারপর থামুন বা জিজ্ঞেস করুন কোন দিকে গভীরে যাবেন — দুই মিনিট পটভূমির পরে আসল উত্তর নয়। নিচের প্রতিটি উত্তর ঠিক এই কাঠামোয় লেখা।
যে ভুলগুলো সবচেয়ে বেশি চোখে পড়ে: Linux-এর আচরণকে সব OS-এর নিয়ম বলা (যেমন “load average-এ সবসময় I/O-অপেক্ষা গোনা হয়” — এটি Linux-এর বৈশিষ্ট্য); পাঠ্যবইয়ের সংজ্ঞা মুখস্থ বলে বাস্তব উদাহরণ না দেওয়া; “restart করে দেব” দিয়ে তদন্ত শুরু; আর chmod 777 বা sudo-কে সমাধান হিসেবে প্রস্তাব করা।
২ · প্রসেস, থ্রেড আর async
রিভিশন: পর্ব ৮ · প্রসেস, পর্ব ৯ · Context switch, পর্ব ১০ · fork, exec আর wait, পর্ব ১১ · Shell আর /proc, পর্ব ১৩ · Signal, পর্ব ১৪ · থ্রেড, পর্ব ৫ · System call, পর্ব ৩৯ · I/O system
এই অংশের সবচেয়ে পরিচিত বড় প্রশ্ন: “টার্মিনালে ./a.out লিখে Enter চাপলে শুরু থেকে শেষ পর্যন্ত কী হয়?” এক প্রশ্নে process, system call, ELF, virtual memory, scheduling আর signal — সব যাচাই হয়ে যায়। আগে পুরো পথটি দেখে নিন:
- ১ · shellপড়া, ভাঙা, fork
- ২ · execveELF, নতুন address space
- ৩ · loaderld.so, libc, main
- ৪ · প্রথম ছোঁয়াpage fault
- ৫ · চলাsyscall, scheduling
- ৬ · exitflush, _exit, zombie
- ৭ · wait$? আর reap
| কে চলছে | |
| মূল system call বা ঘটনা | |
| মেমরির অবস্থা |
- Shell পড়ে, ভাঙে, fork করে। bash লাইনটি পড়ে শব্দে ভাঙে।
./a.out-এ/আছে, তাই PATH-এ খোঁজা হয় না। এটি builtin নয়, তাই bash fork করে (Linux-এ ভেতরেclone). Child পায় parent-এর মেমরির copy-on-write ভাগ, খোলা fd-এর কপি আর নতুন PID — এখনো সে আসলে bash-ই। - execve — একই PID, নতুন প্রোগ্রাম। Child
execveডাকে। Kernel যাচাই করে ফাইলটিতে x অনুমতি আছে কি না (আর file systemnoexecদিয়ে mount কি না), প্রথম বাইটগুলো পড়ে চেনে এটি ELF, পুরোনো address space ফেলে দেয়, ELF-এর segment (code, data) map করে, আর নতুন stack-এ argv, envp আর auxiliary vector সাজায়। ASLR চালু থাকলে stack, mmap-এলাকা আর PIE হলে প্রোগ্রামের ভিত্তি-ঠিকানা এলোমেলো হয়। ফাইলটি script হলে (#!) kernel interpreter চালায়। - Dynamic loader থেকে main. ELF-এ
PT_INTERPথাকলে kernel আগে dynamic linker (ld-linux-x86-64.so.2) চালায়। সেlibc.so.6খুলে mmap করে, relocation করে, GOT ভরে (lazy বা bind-now), তারপর লাফ দেয়_start-এ →__libc_start_main→ আপনারmain. Static binary-তে এই ধাপ নেই। - প্রথম ছোঁয়ায় page fault. Map করা মানে RAM-এ আনা নয়। প্রতিটি page প্রথম ছোঁয়ায় page fault হয়: ফাইলটি আগে থেকে page cache-এ থাকলে (সদ্য compile করা বা আগে চালানো) সেটি minor fault — শুধু page table-এ জোড়া; না থাকলে ডিস্ক থেকে পড়া, major fault. তাই একই প্রোগ্রাম দ্বিতীয়বার দ্রুত শুরু হয়।
- চলার সময়।
printfলেখাটি user space-এর buffer-এ রাখে; terminal-এ লাইন-শেষে, pipe-এ buffer ভরলে বা শেষেwrite(1, …)system call হয়। Timer interrupt-এ scheduler অন্য task-কে CPU দিতে পারে (context switch);read-এর মতো blocking call-এ process ঘুমায়, ডেটা এলে আবার ready হয়। - exit.
mainফিরলে libcexitচালায়: atexit handler, stdio buffer flush, তারপরexit_groupsystem call. Kernel মেমরি, fd, lock ফেরত নেয়; process-টি zombie হয় — শুধু PID আর exit status-এর ছোট রেকর্ড থাকে — আর parent পায় SIGCHLD. - wait আর $?. bash waitpid (ভেতরে
wait4) থেকে status পায়, zombie মুছে যায় (reap).$?হয় 0. signal-এ মরলে$?= ১২৮ + signal নম্বর — যেমন SIGSEGV-তে 139, SIGKILL-এ 137. Windows-এ পথটি আলাদা:CreateProcessএক ধাপে নতুন process আর প্রোগ্রাম দুটিই তৈরি করে, fork-এর মতো ভাগ করা ধাপ নেই।
প্র ০১Process আর thread-এর পার্থক্য কী? একটি নতুন সার্ভিসে কোনটি কখন বেছে নেবেন? (সহজ)
সংক্ষিপ্ত উত্তর: Process হলো চলমান প্রোগ্রাম, যার নিজের address space, fd টেবিল আর অনুমতি আছে। Thread একটি process-এর ভেতরের কাজের ধারা — নিজের stack আর register, কিন্তু মেমরি ও fd ভাগ করা। Thread সস্তা আর দ্রুত ডেটা ভাগ করে; process বিচ্ছিন্নতা দেয় — একটি ভাঙলে অন্যটি টেকে।
গভীর উত্তর: Linux-এ দুটোই kernel-এর কাছে task (task_struct); পার্থক্য clone-এর flag-এ — thread-এ CLONE_VM, CLONE_FILES ইত্যাদি দিয়ে মেমরি আর fd ভাগ হয়। তাই ps -L বা /proc/<pid>/task-এ প্রতিটি thread আলাদা দেখা যায়। বেছে নেওয়ার যুক্তি: crash বা মেমরি-দুর্নীতি একে অন্যকে না ছোঁয়াতে, আলাদা অনুমতি (privilege separation) বা আলাদা ভাষার অংশে process (Chrome-এর প্রতিটি সাইটের renderer, PostgreSQL-এর প্রতিটি connection); প্রচুর ভাগ-করা ডেটা আর কম latency-তে thread (একটি Java বা Go সার্ভার)। Thread-এর দাম — data race আর lock-এর জটিলতা।
সম্ভাব্য ফলো-আপ: “একটি thread segfault করলে কী হয়?” → পুরো process মরে, কারণ signal-এর ডিফল্ট কাজ process-স্তরে। “Thread switch কেন সস্তা?” → প্রশ্ন ০২.
প্র ০২Context switch-এ কী কী সংরক্ষণ হয়, আর এর খরচ আসলে কোথায়? (মাঝারি)
সংক্ষিপ্ত উত্তর: চলতি task-এর register, program counter, stack pointer আর FPU/SIMD অবস্থা kernel-এ সংরক্ষণ করে পরের task-এরটি ফিরিয়ে আনা হয়; process বদলালে page table-ও (x86-এ CR3). প্রত্যক্ষ খরচ সাধারণত কয়েক মাইক্রোসেকেন্ড; বড় খরচটি পরোক্ষ — cache আর TLB আবার গরম হতে সময় লাগে।
গভীর উত্তর: পর্ব ৯-এর মোটা দাগের হিসাব: একটি ছোট syscall-এর mode switch প্রায় ১০০ ns থেকে কয়েকশো ns, context switch-এর প্রত্যক্ষ খরচ প্রায় ১ µs থেকে কয়েক µs, আর পরোক্ষ ক্ষতি বড় working set-এ শত µs পর্যন্ত। Thread switch-এ address space একই থাকে, তাই TLB টেকে (PCID/ASID থাকলে process switch-এও অংশত টেকে)। মাপতে: perf bench sched pipe, আর একটি process-এর জন্য /proc/<pid>/status-এর voluntary_ctxt_switches (নিজে ঘুমিয়েছে — I/O বা lock) বনাম nonvoluntary_ctxt_switches (CPU-র জন্য কাড়াকাড়ি)। সার্ভারে সেকেন্ডে লাখো switch হলে thread কমানো, batch করা বা async মডেল ভাবতে হয়।
সম্ভাব্য ফলো-আপ: “Mode switch কি context switch?” → না, একই task user↔kernel যায়-আসে। “VM-এ খরচ বেশি কেন?” → virtualization আর নিরাপত্তা-প্রতিরোধের (যেমন KPTI) বাড়তি কাজ।
প্র ০৩fork() ডাকলে ঠিক কী হয়? ১ GB মেমরির process fork করলে কি ১ GB কপি হয়? (মাঝারি)
সংক্ষিপ্ত উত্তর: Kernel নতুন PID-সহ একটি child বানায়, যে parent-এর মেমরি, fd আর signal-সেটিংয়ের কপি পায়। মেমরি আসলে কপি হয় না — copy-on-write-এ দুজনের page table একই frame দেখায়, শুধু-পড়া হিসেবে; কেউ লিখলে তবেই সেই একটি page কপি হয়। fork ফেরে দুবার: child-এ 0, parent-এ child-এর PID.
গভীর উত্তর: কপি হয় page table আর kernel-এর হিসাবপত্র, তাই বিশাল process-এও fork মিলিসেকেন্ডের ঘরে হতে পারে, শূন্য নয়। fd টেবিল কপি হয়, কিন্তু দুজনের fd একই open file description দেখায় — offset ভাগ। Multithreaded process fork করলে child-এ শুধু যে thread fork ডেকেছে সেটি থাকে; অন্য thread কোনো mutex ধরে থাকলে child-এ সেটি চিরকাল ধরা থাকে — তাই fork-এর পর exec-এর আগে শুধু async-signal-safe ফাংশন ডাকা নিরাপদ। বাস্তব উদাহরণ: Redis snapshot (BGSAVE) নিতে fork করে; ভারী লেখার সময় COW-এ মেমরি প্রায় দ্বিগুণ হতে পারে।
সম্ভাব্য ফলো-আপ: “লুপে ৩ বার fork করলে কতটি process?” → ২৩ = ৮টি, যার ৭টি নতুন। “stdout pipe-এ থাকলে printf-এর লেখা দুবার কেন আসে?” → fork-এর আগে না-ছাপা buffer দুজনেই কপি পায়; fork-এর আগে fflush.
প্র ০৪Unix-এ fork আর exec আলাদা কেন? posix_spawn কখন ভালো? (মাঝারি)
সংক্ষিপ্ত উত্তর: exec নতুন process বানায় না, একই PID-এ প্রোগ্রাম বদলায়। দুই ধাপ আলাদা বলে মাঝখানে child নিজের পরিবেশ সাজাতে পারে — fd redirect (dup2), working directory বদল, অনুমতি কমানো, signal ঠিক করা — shell-এর > আর | ঠিক এভাবেই কাজ করে।
গভীর উত্তর: খরচের দিক: বড় process থেকে শুধু একটি ছোট কমান্ড চালাতে fork-এর page-table কপি অপচয়। তাই vfork (child exec বা _exit না করা পর্যন্ত parent থামে, মেমরি ভাগ) আর posix_spawn (glibc ভেতরে clone(CLONE_VM|CLONE_VFORK) ব্যবহার করে) — অনেক মেমরির সার্ভার থেকে subprocess চালাতে এগুলো ভালো। Windows-এর CreateProcess ঠিক এক ধাপের নকশা; সেখানে redirect আর পরিবেশ প্যারামিটার হিসেবে দিতে হয়।
সম্ভাব্য ফলো-আপ: “exec সফল হলে পরের লাইনটি চলে?” → না, কখনো ফেরে না; ফিরলে মানে ব্যর্থ। “exec-এর পর খোলা fd কী হয়?” → খোলাই থাকে, যদি না O_CLOEXEC দেওয়া থাকে — fd leak ঠেকাতে এটি ডিফল্ট অভ্যাস করুন।
প্র ০৫Zombie আর orphan process কী? সার্ভারে শত শত zombie দেখলে কী করবেন? (সহজ)
সংক্ষিপ্ত উত্তর: Zombie: শেষ হয়ে গেছে, কিন্তু parent এখনো wait করে exit status নেয়নি — ps-এ STAT Z, <defunct>. Orphan: এখনো চলছে, কিন্তু parent আগে মরেছে — তাকে init (PID 1) বা কোনো subreaper দত্তক নেয়, যে পরে reap করে।
গভীর উত্তর: Zombie-কে kill -9 দিয়ে মারা যায় না — সে তো আগেই মৃত; মেমরিও নেয় না, কিন্তু PID আর process-টেবিলের জায়গা নেয়। হাজার হাজার জমলে PID ফুরিয়ে (বা cgroup-এর pids.max ছুঁয়ে) নতুন fork ব্যর্থ হয়। সমাধান parent-এ: SIGCHLD handler-এ waitpid(-1, &st, WNOHANG) লুপ, বা SIGCHLD-কে SIG_IGN করা (Linux-এ তখন child নিজে reap হয়)। parent ঠিক করা না গেলে parent-কে বন্ধ করলে zombie-রা init-এর কাছে গিয়ে reap হয়। Container-এ PID 1 যদি আপনার অ্যাপ হয় আর সে reap না করে, zombie জমে — তাই tini বা docker run --init (প্রশ্ন ৪৫)।
সম্ভাব্য ফলো-আপ: “নিজের হাতে একটি zombie বানিয়ে দেখাবেন?” → নিচের উদাহরণ। “Orphan কি খারাপ?” → না; daemon বানানোর পুরোনো কৌশলই ইচ্ছা করে orphan করা।
একটি zombie নিজের চোখে দেখুন, সাথে signal-এ মরা process-এর exit status. কৌশলটি: exec করা sleep 1 কখনো wait ডাকে না, তাই তার আগের child শেষ হয়ে zombie হয়ে পড়ে থাকে।
# Process states, a deliberate zombie, and exit codes after a signal
echo "load average: $(cut -d' ' -f1-3 /proc/loadavg) on $(nproc) CPUs"
bash -c 'sleep 0.2 & exec sleep 1' & # the exec'd "sleep 1" never calls wait()
parent=$!
sleep 0.5 # the child has finished by now
child=$(ps -o pid= --ppid "$parent" | tr -d ' ')
echo "parent: state $(cut -d' ' -f3 /proc/$parent/stat) (S = sleeping)"
echo "child : state $(cut -d' ' -f3 /proc/$child/stat) (Z = zombie, waiting to be reaped)"
ps -o stat=,comm= -p "$child"
wait "$parent"
[ -e /proc/$child ] && echo "child still exists" || echo "after the parent exited, the zombie is gone (reaped)"
{
sleep 5 & k=$!; kill -TERM "$k"; wait "$k"; echo "SIGTERM -> exit status $?"
sleep 5 & k=$!; kill -KILL "$k"; wait "$k"; echo "SIGKILL -> exit status $?"
bash -c 'exit 3'; echo "normal exit(3) -> exit status $?"
} 2>/dev/null
echo "states of the processes we can see:"
ps -eo stat= | cut -c1 | sort | uniq -c | tr -s ' ' | paste -sd ','একটি আসল রানের আউটপুট (load average আর শেষ লাইনের সংখ্যা প্রতিবার আলাদা হবে):
load average: 1.47 1.91 2.72 on 2 CPUs
parent: state S (S = sleeping)
child : state Z (Z = zombie, waiting to be reaped)
ZN sleep
after the parent exited, the zombie is gone (reaped)
SIGTERM -> exit status 143
SIGKILL -> exit status 137
normal exit(3) -> exit status 3
states of the processes we can see:
3 R, 5 S
Zombie: sleep 0.2 শেষ হয়েছে, কিন্তু তার parent (exec-এর পর এখন sleep 1) কখনো wait ডাকে না, তাই child-এর অবস্থা Z. ps-এর ZN-এর N মানে process-টির nice মান বেশি (কম অগ্রাধিকার) — এই অনলাইন runner প্রোগ্রামগুলো কম অগ্রাধিকারে চালায়। Parent শেষ হতেই zombie অনাথ হয়, init বা subreaper তাকে reap করে — /proc-এ আর নেই। Exit status: 143 = ১২৮ + ১৫ (SIGTERM), 137 = ১২৮ + ৯ (SIGKILL), আর স্বাভাবিক exit(3) দেয় 3 — container-এর 137 এই নিয়মেই আসে। শেষ লাইন: এই container-এর PID namespace-এ মাত্র ৮টি process দেখা যায়, অথচ প্রথম লাইনের load average পুরো host-এর — /proc/loadavg namespace মানে না (প্রশ্ন ৪২-এর সতর্কতা)।
প্র ০৬SIGTERM আর SIGKILL-এর পার্থক্য কী? একটি সার্ভিসকে নিরাপদে (graceful) বন্ধ করবেন কীভাবে? (সহজ)
সংক্ষিপ্ত উত্তর: SIGTERM (১৫) ধরা যায় — প্রোগ্রাম নতুন অনুরোধ নেওয়া বন্ধ করে, চলতি কাজ শেষ করে, ফাইল flush করে বের হতে পারে। SIGKILL (৯) ধরা বা উপেক্ষা করা যায় না; kernel সঙ্গে সঙ্গে শেষ করে, কোনো cleanup হয় না। তাই আগে SIGTERM, অপেক্ষা, তারপর শেষ উপায় হিসেবে SIGKILL.
গভীর উত্তর: systemd, Docker আর Kubernetes ঠিক এই ক্রম মানে: docker stop SIGTERM পাঠিয়ে ডিফল্টে ১০ সেকেন্ড অপেক্ষা করে, তারপর SIGKILL; Kubernetes-এ terminationGracePeriodSeconds (ডিফল্ট ৩০)। Handler-এ ভারী কাজ নয় — একটি volatile sig_atomic_t flag বসিয়ে মূল লুপে বন্ধ করা, কারণ handler-এ শুধু async-signal-safe ফাংশন নিরাপদ (printf নয়)। SIGKILL-এও D-অবস্থায় আটকে থাকা process সঙ্গে সঙ্গে মরে না — I/O শেষ না হওয়া পর্যন্ত signal পৌঁছায় না।
সম্ভাব্য ফলো-আপ: “Ctrl+C কী পাঠায়?” → terminal foreground process group-কে SIGINT.
প্র ০৭১০,০০০ connection সামলাতে প্রতি connection-এ একটি thread, নাকি async I/O? (মাঝারি)
সংক্ষিপ্ত উত্তর: Thread-per-connection লেখা সহজ, কিন্তু প্রতিটি thread-এর stack মেমরি, scheduling আর context switch-এর খরচ আছে; বেশিরভাগ connection বেশিরভাগ সময় অলস থাকলে সেই খরচ অপচয়। Async মডেলে অল্প কয়েকটি thread epoll-এর মতো I/O multiplexing দিয়ে হাজার fd একসাথে দেখে — শুধু প্রস্তুতগুলো নিয়ে কাজ করে।
গভীর উত্তর: প্রতিটি Linux thread-এর stack ডিফল্টে প্রায়ই ৮ MiB virtual (ulimit -s), যদিও RAM-এ আসে শুধু ছোঁয়া অংশ; সমস্যা বেশি হয় scheduler-এর চাপ, cache দূষণ আর lock-প্রতিযোগিতায়। nginx, Redis আর Node.js event loop + epoll মডেলে; Go-র goroutine আর Java 21-এর virtual thread মাঝামাঝি পথ — কোড দেখতে blocking, কিন্তু runtime ভেতরে অল্প OS thread আর epoll চালায় (M:N). সিদ্ধান্তের মাপকাঠি: কাজ CPU-নির্ভর হলে core-সংখ্যার কাছাকাছি thread; I/O-নির্ভর আর অনেক অলস connection হলে async বা সস্তা thread.
সম্ভাব্য ফলো-আপ: “Async-এর অসুবিধা?” → callback/await-এর জটিলতা, একটি blocking call বা লম্বা হিসাব পুরো loop আটকায় (প্রশ্ন ০৮)। “C10K কী?” → প্রশ্ন ৩৯.
প্র ০৮Event loop কী? Node.js সার্ভারে একটি অনুরোধ ধীর হলে সব অনুরোধ কেন ধীর হয়? (মাঝারি)
সংক্ষিপ্ত উত্তর: Event loop একটি লুপ: OS-কে জিজ্ঞেস করে কোন fd বা timer প্রস্তুত (Linux-এ epoll), তারপর সেগুলোর callback একে একে একই thread-এ চালায়। কোনো callback যদি CPU ধরে রাখে (বড় JSON parse, sync crypto, readFileSync), সেই সময় অন্য কোনো callback চলতে পারে না — সবার latency বাড়ে।
গভীর উত্তর: Node.js-এর libuv নেটওয়ার্কের জন্য epoll (macOS-এ kqueue, Windows-এ IOCP) ব্যবহার করে; কিন্তু সাধারণ ফাইলের I/O, DNS-এর getaddrinfo আর কিছু crypto চালায় একটি ছোট thread pool-এ (ডিফল্টে ৪টি thread), কারণ Linux-এ সাধারণ ফাইলকে epoll-এ যোগই করা যায় না (EPERM), আর select/poll তাকে সবসময় “প্রস্তুত” বলে। সমাধান: ভারী হিসাব worker thread বা আলাদা process-এ, sync API এড়ানো, আর event-loop lag মাপা (মেট্রিক হিসেবে)। একই নীতি Python-এর asyncio আর Rust-এর tokio-তেও।
সম্ভাব্য ফলো-আপ: “Readiness আর completion মডেলের পার্থক্য?” → epoll বলে “এখন পড়া যাবে”, io_uring/IOCP বলে “পড়া শেষ” (প্রশ্ন ৩৫)।
প্র ০৯Python-এ ৪ core-এ ৪টি thread দিলে CPU-নির্ভর কাজ ৪ গুণ দ্রুত হয় না কেন? Go-র goroutine কীভাবে আলাদা? (মাঝারি)
সংক্ষিপ্ত উত্তর: প্রচলিত CPython-এ GIL এক মুহূর্তে একটিই thread-কে Python bytecode চালাতে দেয়, তাই CPU-নির্ভর কাজে thread বাড়িয়ে গতি সাধারণত বাড়ে না; I/O-অপেক্ষায় thread তবু কাজে লাগে। Go-র goroutine runtime-এর M:N scheduler-এ চলে — হাজার goroutine কয়েকটি OS thread-এ, আর সত্যিই সব core ব্যবহার করে।
গভীর উত্তর: Python-এ CPU-কাজের জন্য multiprocessing বা ProcessPoolExecutor (প্রতিটি process-এর নিজের GIL), অথবা NumPy-র মতো C-লাইব্রেরি, যা হিসাবের সময় GIL ছেড়ে দেয়। Python 3.13 থেকে GIL-ছাড়া একটি পরীক্ষামূলক build পাওয়া যায়। Go-তে GOMAXPROCS ঠিক করে একসাথে কতটি OS thread Go কোড চালাবে; blocking syscall-এ runtime নতুন thread জোগায়, আর নেটওয়ার্কে নিজের netpoller (Linux-এ epoll). Container-এ CPU limit থাকলে GOMAXPROCS host-এর সব CPU ধরে নিলে throttling বাড়ে — প্রশ্ন ৪৪।
সম্ভাব্য ফলো-আপ: “তাহলে Python-এ thread একেবারে অকেজো?” → না; নেটওয়ার্ক বা ডিস্কের অপেক্ষায় GIL ছাড়া থাকে, তাই I/O-ভারী কাজে কাজে লাগে।
প্র ১০System call কী, আর সাধারণ function call-এর চেয়ে দামি কেন? vDSO কী সমস্যা মেটায়? (সহজ)
সংক্ষিপ্ত উত্তর: System call হলো user program-এর kernel-এর কাছে সেবা চাওয়ার নির্দিষ্ট দরজা: বিশেষ instruction (x86-64-এ syscall) CPU-কে kernel mode-এ নেয়, kernel argument যাচাই করে কাজ করে, তারপর ফেরে। Mode বদল, যাচাই আর নিরাপত্তা-প্রতিরোধের জন্য এটি function call-এর চেয়ে বহু গুণ দামি।
গভীর উত্তর: Syscall নম্বর register-এ (x86-64 Linux-এ rax), argument rdi, rsi, rdx, r10, r8, r9-এ; ব্যর্থ হলে libc wrapper errno বসিয়ে −1 ফেরত দেয়। vDSO হলো kernel-এর user space-এ map করা একটি ছোট লাইব্রেরি, যাতে clock_gettime-এর মতো কেবল-পড়ার কাজ mode বদল ছাড়াই হয় — সেকেন্ডে লাখো বার সময় পড়া সার্ভারে বড় সাশ্রয়। কোন syscall কতবার হচ্ছে দেখতে strace -c (production-এ সাবধানে, এটি প্রোগ্রামকে অনেক ধীর করে) বা perf trace.
সম্ভাব্য ফলো-আপ: “printf কি system call?” → না, library ফাংশন; শেষে write ডাকে। “Syscall কমাবেন কীভাবে?” → buffering, বড় ব্লকে পড়া, readv/writev, epoll-এ এক ঘুমে অনেক ঘটনা, io_uring.
৩ · কনকারেন্সি — race, lock, deadlock আর lock-free
রিভিশন: পর্ব ২২ · Race condition, পর্ব ২৩ · Atomic আর spinlock, পর্ব ২৪ · Mutex আর semaphore, পর্ব ২৫ · Condition variable, পর্ব ২৭ · Deadlock, পর্ব ২৯ · কনকারেন্সির বাগ, পর্ব ১৫ · POSIX thread; কোড লেখার সমস্যা পর্ব ৬৪-এ
প্র ১১Race condition আর data race কি একই জিনিস? (মাঝারি)
সংক্ষিপ্ত উত্তর: না। Data race একটি নির্দিষ্ট সংজ্ঞা: দুই thread একই মেমরি-ঘর একসাথে ছোঁয়, অন্তত একজন লেখে, আর মাঝে কোনো synchronization নেই — C/C++-এ এটি undefined behavior. Race condition আরও বড় ধারণা: ফল ঘটনার সময়-ক্রমের ওপর নির্ভর করে — data race ছাড়াও হতে পারে।
গভীর উত্তর: উদাহরণ: প্রতিটি অংশ আলাদা lock-এ সুরক্ষিত, তবু “ব্যালান্স পড়া → যাচাই → টাকা কাটা” তিন ধাপের মাঝে অন্য thread ঢুকে পড়তে পারে — data race নেই, race condition আছে (check-then-act). ফাইলেও হয়: if (!exists(f)) create(f) — দুই process একসাথে; সমাধান open(O_CREAT|O_EXCL)-এর মতো অবিভাজ্য অপারেশন। Data race ধরতে ThreadSanitizer (-fsanitize=thread); race condition ধরতে নকশা পর্যালোচনা আর invariant ভাবা।
সম্ভাব্য ফলো-আপ: “দুই thread ১০ লাখ করে lock ছাড়া counter++ করলে ফল?” → ২ থেকে ২০ লাখের যেকোনো মান হতে পারে (load-add-store মিশে lost update); বাস্তবে সাধারণত ২০ লাখের অনেক কম।
প্র ১২Mutex, semaphore আর spinlock — কোনটি কখন? (সহজ)
সংক্ষিপ্ত উত্তর: Mutex একজনকে ঢুকতে দেয়, আর তার মালিক আছে — যে নিয়েছে সে-ই ছাড়ে; অপেক্ষাকারী ঘুমায়। Semaphore একটি গণনা — N জন একসাথে (যেমন ১০টি DB connection), মালিক নেই, তাই এক thread অন্য thread-কে সংকেত দিতে পারে। Spinlock-এ অপেক্ষাকারী ঘুমায় না, ঘুরতে থাকে — শুধু multicore-এ খুব ছোট critical section-এ, বা যেখানে ঘুমানো যায় না (interrupt handler).
গভীর উত্তর: মালিকানা থাকায় mutex-এ priority inheritance, recursive lock-এর নিয়ম আর “অন্যের lock ছাড়লে” ভুল ধরা সম্ভব। Linux-এর pthread mutex futex-এর ওপর: প্রতিযোগিতা না থাকলে একটি atomic instruction-এ user space-এই শেষ, system call শুধু ঘুমানো-জাগানোর সময়; অনেক বাস্তবায়ন ঘুমানোর আগে অল্প ঘোরে (adaptive). Uniprocessor-এ spinlock অর্থহীন — ঘোরার সময় holder চলতেই পারে না। User space-এ সাধারণত mutex-ই ঠিক; নিজের spinlock লেখা প্রায়ই ভুল সিদ্ধান্ত।
সম্ভাব্য ফলো-আপ: “Binary semaphore কি mutex?” → প্রায়, কিন্তু মালিক নেই। “Semaphore-এর মান ১০; ৬টি P, ৪টি V — এখন?” → ১০ − ৬ + ৪ = ৮.
প্র ১৩নিজের কোডে deadlock কীভাবে এড়াবেন? Production-এ ঘটলে কীভাবে ধরবেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: সবচেয়ে কার্যকর নিয়ম lock ordering: সব জায়গায় lock একই global ক্রমে নেওয়া (যেমন account id বা ঠিকানার ছোট থেকে বড়) — তাহলে circular wait অসম্ভব। এর সাথে: lock ধরে বাইরের কোড বা I/O না ডাকা, একবারে কম lock, আর দরকারে timeout-সহ trylock.
গভীর উত্তর: ক্লাসিক ফাঁদ: transfer(a, b) আগে a-র lock, তারপর b-র; একই সময়ে transfer(b, a) উল্টো ক্রমে — deadlock. সমাধান: min(a,b) আগে। Linux kernel lockdep দিয়ে চলার সময় lock-ক্রমের নিয়মভঙ্গ ধরে; user space-এ ThreadSanitizer-ও lock-order-inversion জানায়। Production-এ আটকে গেলে: CPU প্রায় শূন্য, অনুরোধ জমছে; gdb -p PID বা pstack-এ thread apply all bt — দেখবেন দুই thread পরস্পরের ধরা lock-এ futex_wait-এ বসে আছে; Java-য় jstack সরাসরি “Found one Java-level deadlock” লেখে।
সম্ভাব্য ফলো-আপ: “Livelock কী?” → সবাই চলছে, বারবার পিছিয়ে যাচ্ছে, কেউ এগোচ্ছে না; সমাধান এলোমেলো backoff.
প্র ১৪Lock-free কী? CAS দিয়ে একটি stack বানালে কোন সূক্ষ্ম বাগ আসতে পারে? (কঠিন)
সংক্ষিপ্ত উত্তর: Lock-free মানে কোনো thread থেমে গেলেও (preempt বা ধীর) অন্তত কেউ না কেউ সবসময় এগোয়। মূল হাতিয়ার CAS: “মান এখনো X থাকলে তবেই Y লেখা হবে” — একটি অবিভাজ্য instruction; ব্যর্থ হলে আবার পড়ে চেষ্টা। CAS-stack-এর বিখ্যাত বাগ ABA.
গভীর উত্তর: ABA: thread 1 head = A পড়ে, next = B মনে রাখে, তারপর থেমে যায়। এর মধ্যে অন্যরা A আর B pop করে, A আবার push করে (হয়তো একই ঠিকানায় নতুন node). Thread 1-এর CAS দেখে head এখনো A — সফল, আর head বানায় B, যা আর তালিকায় নেই। সমাধান: version tag-সহ pointer (double-width CAS), hazard pointer, epoch-ভিত্তিক মুক্তি, বা GC-যুক্ত ভাষা। Lock-free সবসময় দ্রুত নয় — প্রতিযোগিতা বেশি হলে CAS বারবার ব্যর্থ হয় আর cache line টানাটানি করে; সঠিকতা প্রমাণও কঠিন। Linux kernel-এ পাঠক-প্রধান ডেটায় RCU জনপ্রিয়।
সম্ভাব্য ফলো-আপ: “Lock-free আর wait-free?” → wait-free-এ প্রত্যেকেই সসীম ধাপে শেষ করে — আরও কড়া নিশ্চয়তা। “x86 আর ARM-এ CAS?” → x86-এ lock cmpxchg, ARM-এ LL/SC জোড়া (ldxr/stxr) বা নতুন ARM-এ cas.
প্র ১৫False sharing কী? Lock নেই, ডেটাও আলাদা — তবু থ্রেড বাড়ালে প্রোগ্রাম ধীর কেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: CPU cache-এ ডেটা আসে cache line ধরে (সাধারণত ৬৪ বাইট)। দুই thread দুই আলাদা ভ্যারিয়েবলে লিখছে, কিন্তু দুটো একই line-এ পড়েছে — তখন coherence protocol line-টি দুই core-এর মধ্যে বারবার টানাটানি করে। এটি false sharing: ফল ঠিক, কিন্তু গতি কয়েক গুণ কমতে পারে।
গভীর উত্তর: ক্লাসিক উদাহরণ: long counters[8], প্রতি thread একটি ঘরে লুপে লেখে — আটটি ঘর মিলে ৬৪ বাইট, একটিই line. সমাধান: প্রতিটি ঘর আলাদা line-এ (padding বা alignas(64); Java-য় @Contended), বা local ভ্যারিয়েবলে জমিয়ে শেষে একবার লেখা। ধরা পড়ে মাপলে: perf c2c cache-to-cache টানাটানির ঠিকানা দেখায়; টেস্টে ধরা পড়ে না, কারণ এটি সঠিকতার নয়, গতির সমস্যা।
সম্ভাব্য ফলো-আপ: “True sharing থেকে আলাদা কীভাবে?” → true sharing-এ সত্যিই একই ভ্যারিয়েবল ভাগ — তখন নকশা বদলাতে হয় (প্রতি-CPU counter, শেষে যোগ)।
প্র ১৬Condition variable-এ wait কেন সবসময় while লুপে? (মাঝারি)
সংক্ষিপ্ত উত্তর: দুই কারণে: spurious wakeup — কেউ signal না দিলেও wait ফিরতে পারে; আর জাগার পর mutex ফেরত পাওয়ার আগেই অন্য thread শর্তটি আবার বদলে দিতে পারে (Mesa semantics). তাই জেগে শর্তটি আবার দেখতে হয়: while (!ready) pthread_cond_wait(&cv, &m);
গভীর উত্তর: Condition variable-এর কোনো স্মৃতি নেই — কেউ অপেক্ষায় না থাকলে signal হারিয়ে যায়; শর্তটি থাকে mutex-রক্ষিত একটি ভ্যারিয়েবলে। wait অবিভাজ্যভাবে mutex ছেড়ে ঘুমায়, তাই “শর্ত দেখলাম মিথ্যা” আর “ঘুমালাম”-এর মাঝে signal হারানোর (lost wakeup) ফাঁক থাকে না। একাধিক ধরনের অপেক্ষাকারী একই CV-তে থাকলে broadcast নিরাপদ, তবে thundering herd-এর দাম আছে; ভালো নকশা প্রতিটি শর্তের আলাদা CV (যেমন bounded buffer-এ notFull আর notEmpty).
সম্ভাব্য ফলো-আপ: “Semaphore দিয়ে একই কাজ?” → semaphore গণনা মনে রাখে, তাই আগে দেওয়া V হারায় না।
প্র ১৭Priority inversion কী, আর এর প্রতিকার কী? (মাঝারি)
সংক্ষিপ্ত উত্তর: উচ্চ-অগ্রাধিকারের task একটি lock-এর অপেক্ষায়, যা ধরে আছে নিম্ন-অগ্রাধিকারের task; মাঝারি-অগ্রাধিকারের task এসে নিম্নটিকে CPU থেকে সরিয়ে রাখে — ফলে উচ্চ task কার্যত মাঝারির চেয়েও পিছিয়ে যায়। প্রতিকার priority inheritance: lock-ধারী সাময়িকভাবে অপেক্ষমাণের অগ্রাধিকার পায়।
গভীর উত্তর: ১৯৯৭ সালের Mars Pathfinder-এর ঘটনা এর পরিচিত উদাহরণ: watchdog বারবার সিস্টেম রিসেট করত; VxWorks-এ mutex-এর priority inheritance চালু করে সমস্যা মেটানো হয়েছিল বলে জানা যায়। বিকল্প priority ceiling protocol. Linux-এ PTHREAD_PRIO_INHERIT দিয়ে mutex বানানো যায়, আর PREEMPT_RT kernel-এর rt-mutex এটি দেয়।
সম্ভাব্য ফলো-আপ: “Semaphore-এ inheritance কেন হয় না?” → মালিক নেই, তাই কাকে অগ্রাধিকার দেবে জানা যায় না।
প্র ১৮C-তে একটি flag-কে volatile করলেই কি দুই thread-এর মধ্যে নিরাপদ? (কঠিন)
সংক্ষিপ্ত উত্তর: না। volatile শুধু compiler-কে বলে প্রতিবার মেমরি থেকে পড়তে-লিখতে; এটি অবিভাজ্যতা দেয় না, আর অন্য ভ্যারিয়েবলের সাথে ক্রম (ordering) নিশ্চিত করে না — CPU আর compiler দুজনেই লেখা-পড়া আগে-পিছে করতে পারে। দরকার _Atomic/std::atomic (সঠিক memory ordering-সহ) বা lock.
গভীর উত্তর: ক্লাসিক ভুল: thread A ডেটা লিখে ready = 1; thread B ready দেখে ডেটা পড়ে। ARM-এর মতো দুর্বল-ক্রমের CPU-তে B নতুন ready কিন্তু পুরোনো ডেটা দেখতে পারে। সমাধান: লেখায় release, পড়ায় acquire (atomic_store_explicit(&ready, 1, memory_order_release)). x86-এর TSO তুলনামূলক কড়া, তবু store→load পুনর্বিন্যাস হয় (store buffer), তাই Peterson-এর মতো algorithm বাধা (fence) ছাড়া ভাঙে। “আমার x86 ল্যাপটপে চলে” প্রমাণ নয়। volatile-এর আসল জায়গা memory-mapped device register আর signal handler-এর sig_atomic_t.
সম্ভাব্য ফলো-আপ: “Java-র volatile?” → আলাদা: Java-য় volatile visibility আর ক্রমের নিশ্চয়তা দেয় (তবু count++ অবিভাজ্য নয়)।
প্র ১৯একটি thread pool-এ কতটি thread রাখবেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: কাজের ধরনের ওপর নির্ভর করে। CPU-নির্ভর কাজে প্রায় core-সংখ্যা (বা core + ১); বেশি দিলে শুধু context switch বাড়ে। I/O-নির্ভর কাজে বেশি: মোটামুটি Ncore × (১ + অপেক্ষার সময় ÷ হিসাবের সময়)। তারপর মেপে ঠিক করা।
গভীর উত্তর: উদাহরণ: ৮ core, প্রতিটি অনুরোধে ১০ ms হিসাব আর ৯০ ms DB-অপেক্ষা — ৮ × (১ + ৯) = ৮০টি thread প্রায় সব core ব্যস্ত রাখে। কিন্তু DB-র connection pool যদি ২০টি হয়, ৮০ thread-এর ৬০টি শুধু pool-এর লাইনে দাঁড়াবে — নিচের সম্পদের সীমাই আসল সীমা। Container-এ “core-সংখ্যা” মানে CPU limit, host-এর nproc নয় (JVM ১০ সংস্করণ থেকে container-এর সীমা নিজে পড়ে)। Bounded queue আর backpressure রাখুন, নইলে অতিরিক্ত লোডে queue অসীম বাড়ে আর latency ফেটে যায় (Little's law: L = λW).
সম্ভাব্য ফলো-আপ: “Pool কেন, প্রতি অনুরোধে নতুন thread কেন নয়?” → তৈরির খরচ আর একসাথে চলা thread-এর সীমা — হঠাৎ লোডে সিস্টেম ডোবে না।
৪ · মেমরি — virtual memory, malloc, RSS আর OOM
রিভিশন: পর্ব ৩০ · মেমরির ভিত্তি, পর্ব ৩২ · Paging, পর্ব ৩৩ · TLB, পর্ব ৩৫ · Virtual memory, পর্ব ৩৭ · Thrashing, পর্ব ৩৮ · Linux-এ মেমরি, C কোর্সের pointer আর মেমরির পর্বগুলো
প্র ২০Virtual memory কী, আর কেন দরকার? (সহজ)
সংক্ষিপ্ত উত্তর: প্রতিটি process নিজের একটি বড়, একটানা ঠিকানা-জগৎ দেখে; MMU প্রতিটি ঠিকানা page table দিয়ে আসল RAM-এর frame-এ অনুবাদ করে। এতে তিনটি লাভ: বিচ্ছিন্নতা (একজন অন্যের মেমরি ছুঁতে পারে না), সুবিধা (প্রতিটি প্রোগ্রাম একই বিন্যাস পায়), আর RAM-এর চেয়ে বেশি মেমরির ভান — যা লাগে শুধু তা-ই RAM-এ।
গভীর উত্তর: Virtual memory-এর ওপর দাঁড়িয়ে আছে demand paging (প্রথম ছোঁয়ায় page আনা), copy-on-write fork, shared library (একই libc-এর page সব process-এ ভাগ), mmap দিয়ে ফাইল পড়া আর swap. খরচ: প্রতিটি ঠিকানা-অনুবাদ — x86-64-এ ৪ স্তরের page table, যা TLB cache করে; TLB miss হলে page walk. তাই TLB-বান্ধব (ঘনিষ্ঠ ঠিকানায়) কোড দ্রুত, আর huge page TLB-র নাগাল বাড়ায় (প্রশ্ন ২৭)।
সম্ভাব্য ফলো-আপ: “Virtual memory মানে কি swap?” → না, swap এর একটি ব্যবহার মাত্র; swap ছাড়াও virtual memory পুরোদমে কাজ করে।
প্র ২১Page fault কত ধরনের? Page fault মানেই কি সমস্যা? (মাঝারি)
সংক্ষিপ্ত উত্তর: তিন রকম। Minor: ডিস্ক ছাড়াই মেটে — প্রথম ছোঁয়ায় শূন্য page দেওয়া, COW কপি, বা page cache-এ আগে থেকে থাকা ফাইল-page জোড়া। Major: ডিস্ক বা swap থেকে পড়তে হয় — মিলিসেকেন্ডের খরচ। অবৈধ: map না করা বা অনুমতি-ভাঙা ঠিকানা — kernel SIGSEGV পাঠায় (segmentation fault).
গভীর উত্তর: সুস্থ সার্ভারে সেকেন্ডে হাজার হাজার minor fault স্বাভাবিক — প্রতিটি fork, exec, malloc-এর নতুন page-এ এগুলো হয়। সমস্যা হলো major fault-এর হার বাড়া, বিশেষ করে swap-in: তখন latency লাফ দেয়। দেখার জায়গা: /proc/<pid>/stat-এর minflt/majflt, ps -o min_flt,maj_flt, vmstat-এর si/so, আর perf stat -e page-faults,major-faults. লেখা-অনুমতিহীন page-এ লেখাও fault — COW-তে সেটি বৈধ (কপি হয়), শুধু-পড়ার code-এ সেটি SIGSEGV.
সম্ভাব্য ফলো-আপ: “Page fault-এ OS ধাপে ধাপে কী করে?” → trap, ঠিকানা যাচাই, frame জোগাড় (দরকারে replacement), দরকার হলে ডিস্ক থেকে পড়া (ততক্ষণ অন্য process চলে), page table হালনাগাদ, একই instruction আবার।
প্র ২২malloc(100 MB) ডাকলে ভেতরে কী হয়? সাথে সাথে কি ১০০ MB RAM খরচ হয়? (মাঝারি)
সংক্ষিপ্ত উত্তর: না। glibc-র malloc বড় অনুরোধ (mmap threshold, শুরুতে ১২৮ KiB, পরে নিজে বদলায়, তার সমান বা বড়) mmap দিয়ে নতুন anonymous এলাকা হিসেবে নেয়; ছোটগুলো brk-এ বাড়ানো heap থেকে। Kernel শুধু ঠিকানার এলাকা (VMA) লিখে রাখে — RAM আসে প্রতিটি page প্রথম ছোঁয়ার সময়, minor fault-এ। তাই VSZ সঙ্গে সঙ্গে বাড়ে, RSS বাড়ে ছোঁয়ার সাথে সাথে।
গভীর উত্তর: Linux ডিফল্টে overcommit করে (vm.overcommit_memory = 0, heuristic): ফাঁকা RAM-এর চেয়ে বেশি চাইলেও malloc সাধারণত সফল হয়, আর আসল ঘাটতি ধরা পড়ে পরে — ছোঁয়ার সময়, তখন OOM killer. তাই “malloc NULL দেয়নি, তবু প্রোগ্রাম মরল” Linux-এ সম্ভব। mmap-এ পাওয়া চাংক free করলে সঙ্গে সঙ্গে munmap হয়ে RAM ফেরত যায়; brk-heap-এর মাঝের ফাঁকা জায়গা সাধারণত process-এর ভেতরেই থাকে, পরের malloc-এ কাজে লাগে। নিচের উদাহরণে দেখুন।
সম্ভাব্য ফলো-আপ: “calloc কেন প্রায়ই malloc + memset-এর চেয়ে দ্রুত?” → mmap-এর নতুন page kernel এমনিতেই শূন্য দেয়, তাই calloc আবার শূন্য লেখা এড়াতে পারে।
একটি ৬৪ MiB malloc-এর পর VSZ আর RSS কীভাবে বদলায় — প্রথমে শুধু ঠিকানা, তারপর ছোঁয়ার সাথে RAM:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
/* Read a "VmXXX: 1234 kB" line from /proc/self/status */
static long vm(const char *key) {
char line[256]; long kb = -1;
FILE *f = fopen("/proc/self/status", "r");
while (f && fgets(line, sizeof line, f))
if (strncmp(line, key, strlen(key)) == 0) sscanf(line + strlen(key), "%ld", &kb);
if (f) fclose(f);
return kb;
}
int main(void) {
long size0 = vm("VmSize:"), rss0 = vm("VmRSS:");
size_t n = 64u << 20; /* 64 MiB, above the mmap threshold */
#define SHOW(what) printf("%-22s VSZ %+5ld MiB RSS %+5ld MiB\n", what, \
(vm("VmSize:") - size0) / 1024, (vm("VmRSS:") - rss0) / 1024)
SHOW("start");
char *p = malloc(n);
if (!p) return 1;
SHOW("after malloc(64 MiB)");
memset(p, 1, n / 2);
SHOW("after touching half");
memset(p, 1, n);
SHOW("after touching all");
free(p);
SHOW("after free");
return 0;
}একটি আসল রানের আউটপুট:
start VSZ +0 MiB RSS +0 MiB
after malloc(64 MiB) VSZ +64 MiB RSS +0 MiB
after touching half VSZ +64 MiB RSS +32 MiB
after touching all VSZ +64 MiB RSS +64 MiB
after free VSZ +0 MiB RSS +0 MiB
malloc-এর পর VSZ ৬৪ MiB বেড়েছে, RSS এক MiB-ও নয় — ঠিকানার এলাকা তৈরি, কিন্তু RAM আসেনি। অর্ধেক ছোঁয়ায় RSS +৩২, পুরোটা ছোঁয়ায় +৬৪: প্রতিটি নতুন page প্রথম ছোঁয়ায় একটি minor page fault-এ RAM পেয়েছে। free-এর পর দুটোই শূন্যে ফিরেছে, কারণ ৬৪ MiB mmap threshold-এর অনেক ওপরে, তাই glibc এটি mmap দিয়ে নিয়েছিল আর free-তে munmap করে সরাসরি OS-কে ফেরত দিয়েছে। অনেকগুলো ছোট (ধরুন ১০০ বাইটের) ব্লক হলে সেগুলো brk-heap থেকে আসত, আর free-র পরেও RSS প্রায়ই নামত না — প্রশ্ন ২৩-এর “জোয়ারের দাগ”।
প্র ২৩Free করার পরও একটি সার্ভারের RSS কমছে না, ধীরে ধীরে বাড়ছে — leak, নাকি অন্য কিছু? (মাঝারি)
সংক্ষিপ্ত উত্তর: সবসময় leak নয়। Allocator ছোট free-করা ব্লক OS-কে ফেরত না দিয়ে নিজের কাছে রাখে (পরে কাজে লাগবে), heap-এর মাঝে fragmentation হয়, আর multithreaded প্রোগ্রামে glibc প্রতি thread-দলের আলাদা arena রাখে। ফলে RSS একটি উঁচু “জোয়ারের দাগে” থেমে থাকে। Leak হলে কিন্তু একই লোডে RSS সময়ের সাথে থামে না।
গভীর উত্তর: পার্থক্য করার উপায়: একই লোড দিয়ে কয়েক ঘণ্টা RSS দেখা — সমতলে থামে, নাকি সরলরেখায় বাড়ে। glibc-তে malloc_trim(0) ডাকলে RSS কমলে সমস্যা allocator-এর ধারণক্ষমতা; MALLOC_ARENA_MAX কমানো বা jemalloc/tcmalloc ব্যবহারে অনেক সময় RSS কমে। মাপ ঠিকমতো নেওয়াও জরুরি: RSS-এ shared library-র ভাগ-করা page-ও গোনা; process-এর নিজের অংশ বুঝতে PSS/USS (/proc/<pid>/smaps_rollup).
সম্ভাব্য ফলো-আপ: “Java/Go-তে?” → GC-ভাষায় heap-এর আকার runtime ঠিক করে, তাই “ব্যবহৃত heap” আর RSS আলাদা মেট্রিক।
প্র ২৪VSZ, RSS আর PSS-এর পার্থক্য কী? কোনটি দিয়ে বলবেন “এই process কত মেমরি খাচ্ছে”? (সহজ)
সংক্ষিপ্ত উত্তর: VSZ: process-এর সব map করা এলাকার মোট আকার — ছোঁয়া হোক বা না হোক। RSS: এই মুহূর্তে RAM-এ থাকা page, ভাগ-করা page-সহ। PSS: ভাগ-করা page তার অংশীদারের সংখ্যা দিয়ে ভাগ করে গোনা — সব process-এর PSS যোগ করলে মোট ব্যবহৃত RAM-এর কাছাকাছি আসে।
গভীর উত্তর: ২০টি PHP-FPM worker-এর প্রতিটির RSS ১০০ MB দেখালে মোট ২ GB ভাবা ভুল, যদি তার ৬০ MB সবার ভাগ-করা code আর library হয়। ঠিক প্রশ্নের ঠিক মেট্রিক: “এই process বন্ধ করলে কত RAM ফেরত পাব?” → USS (একান্ত নিজের); “মোট খরচে এর ভাগ?” → PSS; “ঠিকানা-জগৎ কত বড়?” → VSZ (Java বা Go-তে প্রায়ই বিশাল, চিন্তার কিছু নয়)। Kubernetes আর cgroup-এর OOM সিদ্ধান্ত হয় cgroup-এর memory.current দিয়ে, যাতে anonymous মেমরি, page cache আর kernel-এর কিছু অংশ থাকে।
সম্ভাব্য ফলো-আপ: “top-এর VIRT, RES, SHR?” → যথাক্রমে VSZ, RSS আর তার ভাগযোগ্য অংশ।
প্র ২৫OOM killer কখন চলে, কাকে মারে, আর একটি জরুরি process-কে কীভাবে রক্ষা করবেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: Reclaim (page cache ফেলা, swap) করেও মেমরি জোগানো না গেলে OOM killer চলে — পুরো সিস্টেমে, বা একটি cgroup তার memory.max ছুঁলে শুধু সেই দলের ভেতরে। সে সবচেয়ে বেশি “badness” পয়েন্টের process বাছে — মূলত RSS + swap + page table-এর আকার, যার সাথে oom_score_adj (−১০০০ থেকে +১০০০) যোগ হয় — আর তাকে SIGKILL পাঠায়।
গভীর উত্তর: প্রমাণ মেলে dmesg বা journalctl -k-এ “Out of memory: Killed process …”, আর cgroup-এ memory.events-এর oom_kill গণকে। রক্ষার উপায়: oom_score_adj = -1000 (কখনো বাছা হবে না — শুধু সত্যিই জরুরি daemon-এ, কারণ তখন অন্য কেউ মরবে), systemd-এ OOMScoreAdjust=; আর ভালো সমাধান প্রতিটি সার্ভিসের সীমা (memory.max) আর আগাম সতর্ক সংকেত (memory.high, PSI). systemd-oomd বা Android-এর lmkd-এর মতো user-space daemon kernel-এর আগেই চাপ দেখে ব্যবস্থা নেয়।
সম্ভাব্য ফলো-আপ: “OOM-এ মরা container-এর exit code?” → 137 (প্রশ্ন ৪৩)। “overcommit বন্ধ করলে?” → vm.overcommit_memory = 2-এ malloc আগেই ব্যর্থ হয়, কিন্তু fork-ভারী সিস্টেম কষ্ট পায়।
প্র ২৬Production-এ একটি memory leak কীভাবে খুঁজবেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: আগে প্রমাণ — একই লোডে RSS বা heap সময়ের সাথে সরলরেখায় বাড়ছে, GC বা restart ছাড়া কমছে না। তারপর কোন ধরনের মেমরি বাড়ছে (smaps: heap, anonymous mmap, নাকি থ্রেড-stack), তারপর কোন কোড-পথ বরাদ্দ করছে — heap profiler দিয়ে দুই সময়ের snapshot তুলনা।
গভীর উত্তর: C/C++: test পরিবেশে AddressSanitizer-এর LeakSanitizer বা Valgrind (অনেক ধীর); production-এ কম-খরচের heaptrack, jemalloc/tcmalloc-এর heap profiling, বা eBPF-ভিত্তিক memleak (bcc) — শেষেরটি root আর সতর্কতা চায়। Java-য় heap dump (jcmd GC.heap_dump) আর dominator tree; Go-তে pprof-এর heap profile; Node.js-এ heap snapshot. প্রচলিত কারণ: সীমাহীন cache বা map, বন্ধ না হওয়া listener বা callback, থ্রেড বা goroutine leak (প্রতিটি নিজের stack নিয়ে), fd leak (প্রশ্ন ৩৮)। সমাধানের পর একই লোডে আবার মেপে নিশ্চিত হন।
সম্ভাব্য ফলো-আপ: “রোজ restart করলেই তো হয়?” → উপসর্গ ঢাকা; leak-এর হার বাড়লে বিরতিও ছোট হবে, আর কারণ অজানা থাকবে।
প্র ২৭Huge page কী সুবিধা দেয়? কিছু ডেটাবেস Transparent Huge Pages বন্ধ রাখতে বলে কেন? (কঠিন)
সংক্ষিপ্ত উত্তর: x86-64-এ সাধারণ page ৪ KiB, huge page ২ MiB (বা ১ GiB). একটি TLB entry তখন ৫১২ গুণ বেশি মেমরি ঢাকে, আর page walk-এ একটি স্তর কম — বড় working set-এর অ্যাপে (ডেটাবেস, JVM, VM) TLB miss অনেক কমে। দাম: internal fragmentation আর একটানা ২ MiB ফাঁকা RAM জোগাড়ের খরচ।
গভীর উত্তর: দুই পথ: hugetlbfs (আগে থেকে সংরক্ষিত, অনুমানযোগ্য — Oracle আর PostgreSQL-এর huge_pages), আর THP (kernel নিজে, khugepaged দিয়ে পেছনে জোড়া লাগায়)। THP-র ঝুঁকি: page fault-এর সময় বা পেছনে compaction-এ হঠাৎ দেরি (tail latency), আর ছড়ানো ছোট লেখায় fork-এর পর COW-তে পুরো ২ MiB কপি হতে পারা। তাই Redis-এর মতো কিছু ডেটাবেস THP চালু থাকলে সতর্ক বার্তা দেয়। বহু distro এখন /sys/kernel/mm/transparent_hugepage/enabled-এ madvise রাখে — শুধু যে অ্যাপ চায় (madvise(MADV_HUGEPAGE)) সে পায়।
সম্ভাব্য ফলো-আপ: “লাভ হচ্ছে কি না কীভাবে মাপবেন?” → perf stat -e dTLB-load-misses আগে-পরে, আর p99 latency.
প্র ২৮free -m-এ “free” মাত্র ২০০ MB, অথচ সার্ভার ঠিকঠাক চলছে। সমস্যা? (সহজ)
সংক্ষিপ্ত উত্তর: সাধারণত না। Linux ফাঁকা RAM ফেলে না রেখে page cache-এ ফাইলের ডেটা রাখে; দরকার হলে পরিষ্কার cache-page সঙ্গে সঙ্গে ফেলে দেওয়া যায়। আসল প্রশ্নের উত্তর “available” কলাম (MemAvailable) — swap ছাড়া নতুন কাজের জন্য আনুমানিক কত মেমরি পাওয়া যাবে।
গভীর উত্তর: উদ্বেগের সংকেত অন্যখানে: available ক্রমাগত কমা, vmstat-এ si/so (swap in/out) নিয়মিত শূন্যের বেশি, major fault বাড়া, PSI-র /proc/pressure/memory-এ “some/full” বাড়া, আর dmesg-এ OOM. echo 3 > /proc/sys/vm/drop_caches-কে “মেমরি খালি করার” সমাধান বলা একটি প্রচলিত ভুল পরামর্শ — cache হারিয়ে পরের পড়াগুলো ডিস্কে যায়, সিস্টেম আরও ধীর হয়।
সম্ভাব্য ফলো-আপ: “buff/cache-এর সবটাই কি ফেলা যায়?” → না; dirty page আগে লিখতে হয়, আর tmpfs/shmem-এর page cache-এ দেখালেও ফেলা যায় না।
৫ · I/O আর ফাইল — cat, page cache, fsync, inode আর epoll
রিভিশন: পর্ব ৩৯ · I/O system, পর্ব ৪০ · ডিস্ক আর SSD, পর্ব ৪১ · File descriptor, পর্ব ৪২ · Directory আর link, পর্ব ৪৩ · Inode, পর্ব ৪৪ · Journaling আর fsync
প্র ২৯টার্মিনালে cat notes.txt লিখে Enter চাপলে OS-এর ভেতরে কী কী হয়? (মাঝারি)
সংক্ষিপ্ত উত্তর: Shell fork + exec করে cat চালায় (PATH-এ খুঁজে)। cat ডাকে openat — kernel path ধরে directory-র পর directory খুঁজে (dentry cache-এ, না থাকলে ডিস্কে) inode পায়, অনুমতি যাচাই করে, একটি fd ফেরত দেয়। তারপর read: ডেটা page cache-এ থাকলে সরাসরি কপি, না থাকলে file system ব্লক খুঁজে block layer আর driver-এর মাধ্যমে ডিস্ক থেকে আনে। শেষে write(1, …) দিয়ে terminal-এ, close, exit.
গভীর উত্তর: ডিস্কের পথ: VFS → ext4-এর মতো file system (logical block → ডিস্কের ব্লক, extent দিয়ে) → block layer-এর I/O scheduler → NVMe বা SATA driver → ডিভাইস DMA দিয়ে RAM-এ লেখে → interrupt → ঘুমন্ত cat জাগে। Kernel একসাথে read-ahead করে পরের ব্লকগুলোও আনে। Terminal-এর দিকে: fd 1 একটি pty — লেখা যায় tty line discipline হয়ে terminal emulator-এর কাছে, যে অক্ষর আঁকে। strace cat notes.txt দিয়ে ঠিক এই syscall-ক্রম দেখা যায়।
সম্ভাব্য ফলো-আপ: “দ্বিতীয়বার কেন দ্রুত?” → page cache (প্রশ্ন ৩০)। “ফাইলটি NFS-এ হলে?” → VFS-এর নিচে NFS client, ডিস্কের বদলে নেটওয়ার্ক।
প্র ৩০Page cache কী? একই ফাইল দ্বিতীয়বার পড়া কেন অনেক দ্রুত? (সহজ)
সংক্ষিপ্ত উত্তর: Page cache হলো kernel-এর RAM-এ রাখা ফাইলের page — সব process-এর ভাগ। প্রথম পড়ায় ডিস্ক থেকে এনে রাখা হয়; পরের পড়া শুধু RAM থেকে কপি। লেখাও প্রথমে এখানেই যায় (dirty page), পরে পেছনে ডিস্কে (writeback).
গভীর উত্তর: mmap করা ফাইল আর read() দুজনেই একই page cache দেখে (unified cache). Kernel active/inactive তালিকা দিয়ে কোন page ফেলবে বাছে; তবু ভারী backup-এর মতো বড় একবারের স্ক্যান গরম ডেটা তাড়িয়ে অ্যাপ ধীর করতে পারে। ডেটাবেস প্রায়ই নিজের buffer pool রাখে, তাই কেউ কেউ O_DIRECT দিয়ে page cache এড়ায় (double caching বাঁচাতে)। নিচের উদাহরণে root ছাড়াই page cache-এর অবস্থা মাপা হয়েছে mincore দিয়ে।
সম্ভাব্য ফলো-আপ: “Benchmark-এ cache-এর প্রভাব কীভাবে বাদ দেবেন?” → test মেশিনে drop_caches (root), posix_fadvise(DONTNEED), বা O_DIRECT; আর cold ও warm দুটো আলাদা করে রিপোর্ট করা।
#define _GNU_SOURCE
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <time.h>
#include <unistd.h>
#define SIZE (8L << 20) /* an 8 MiB file */
static long resident(int fd) { /* how many of its pages are in the page cache? */
long pg = sysconf(_SC_PAGESIZE), n = SIZE / pg, in = 0;
unsigned char *vec = malloc(n);
void *m = mmap(NULL, SIZE, PROT_READ, MAP_SHARED, fd, 0);
mincore(m, SIZE, vec);
for (long i = 0; i < n; i++) in += vec[i] & 1;
munmap(m, SIZE); free(vec);
return 100 * in / n;
}
static double read_all_ms(int fd) {
static char buf[1 << 16]; struct timespec a, b;
clock_gettime(CLOCK_MONOTONIC, &a);
for (lseek(fd, 0, SEEK_SET); read(fd, buf, sizeof buf) > 0;) {}
clock_gettime(CLOCK_MONOTONIC, &b);
return (b.tv_sec - a.tv_sec) * 1e3 + (b.tv_nsec - a.tv_nsec) / 1e6;
}
int main(void) {
int fd = open("pc_demo.bin", O_RDWR | O_CREAT | O_TRUNC, 0600);
char *blk = calloc(1, 1 << 20);
for (int i = 0; i < 8; i++)
if (write(fd, blk, 1 << 20) != 1 << 20) { printf("write failed: %m\n"); return 0; }
printf("after write() : %3ld%% of pages in page cache\n", resident(fd));
fsync(fd); /* dirty pages -> disk, now clean */
posix_fadvise(fd, 0, 0, POSIX_FADV_DONTNEED); /* ask the kernel to drop them */
printf("after fsync + DONTNEED : %3ld%% of pages in page cache\n", resident(fd));
double cold = read_all_ms(fd);
printf("after first read() : %3ld%% (took %.1f ms)\n", resident(fd), cold);
printf("second read() : took %.1f ms\n", read_all_ms(fd));
unlink("pc_demo.bin");
return 0;
}একটি আসল রানের আউটপুট (সময়গুলো প্রতিবার আলাদা হবে):
after write() : 100% of pages in page cache
after fsync + DONTNEED : 0% of pages in page cache
after first read() : 100% (took 24.7 ms)
second read() : took 3.2 ms
mincore বলে একটি map করা ফাইলের কোন page এখন RAM-এ, তাই root ছাড়াই page cache দেখা যায়। লেখার ঠিক পরে ফাইলের ১০০% page cache-এ — dirty অবস্থায়, ডিস্কে তখনো না-ও যেতে পারে। fsync সেগুলো ডিস্কে লিখে পরিষ্কার করে; তারপর POSIX_FADV_DONTNEED-এ kernel পরিষ্কার page ফেলে দেয় — ০%. (fsync না করলে dirty page ফেলা যেত না।) প্রথম read-এ ডেটা storage থেকে আবার আসে — ২৪.৭ ms, শেষে আবার ১০০% cache-এ; দ্বিতীয় পড়া শুধু RAM থেকে কপি — ৩.২ ms, প্রায় ৮ গুণ দ্রুত। এই runner-এর storage নিজেই host-এর cache-এর পেছনে থাকতে পারে, তাই আসল HDD বা network ডিস্কে পার্থক্য আরও বড় হয়।
প্র ৩১write() সফলভাবে ফিরেছে। এখন বিদ্যুৎ চলে গেলে কি ডেটা নিরাপদ? (মাঝারি)
সংক্ষিপ্ত উত্তর: না, নিশ্চিত নয়। write সাধারণত শুধু page cache-এ কপি করে ফেরে; ডিস্কে যায় পরে, writeback-এ। টেকসই (durable) করতে fsync(fd) — যা ফেরে ডেটা আর metadata ডিস্কে পৌঁছানোর পর (ডিস্কের নিজের write cache-ও flush করিয়ে)। fdatasync শুধু দরকারি metadata লেখে, তাই একটু সস্তা।
গভীর উত্তর: নতুন ফাইল তৈরি বা rename-এর পর ফাইলের fsync যথেষ্ট নয় — directory entry-টিও টেকসই করতে directory-র fd-তে fsync লাগে। একটি কনফিগ ফাইল নিরাপদে বদলানোর প্রচলিত ধরন (atomic rename): temp ফাইলে লেখা → fsync(temp) → rename(temp, real) → fsync(directory). crash হলে পুরোনো বা নতুন পুরো ফাইল থাকে, আধা-লেখা নয়। ডেটাবেস ঠিক এভাবে WAL-এর commit-এ fsync করে; তাই commit-এর latency ডিস্কের flush-গতির ওপর নির্ভর করে, আর power-loss protection-ওয়ালা SSD সেখানে বড় পার্থক্য আনে। ২০১৮ সালে PostgreSQL দলের আলোচনায় (“fsyncgate”) সামনে আসে, fsync ব্যর্থ হলে Linux dirty page-কে পরিষ্কার চিহ্ন দিতে পারে — তাই ব্যর্থ fsync-এর পর আবার চেষ্টাকে সফলতা ধরা নিরাপদ নয়।
সম্ভাব্য ফলো-আপ: “ext4-এ data=ordered কী দেয়?” → নতুন বরাদ্দ ব্লকে আগে ডেটা, তারপর metadata commit — জঞ্জাল দেখা যায় না, কিন্তু fsync-এর বিকল্প নয়।
প্র ৩২Inode কী? Hard link আর symbolic link-এর পার্থক্য কী? (মাঝারি)
সংক্ষিপ্ত উত্তর: Inode হলো ফাইলের আসল রেকর্ড — আকার, মালিক, অনুমতি, সময়, link count আর ডেটা-ব্লকের ঠিকানা; ফাইলের নাম থাকে না। নাম থাকে directory entry-তে, যা নাম → inode নম্বর জোড়ে। Hard link একই inode-এর আরেকটি নাম (link count বাড়ে, একই file system-এ, directory-র জন্য নয়); symlink একটি আলাদা ছোট ফাইল, যার ভেতরে একটি path লেখা — অন্য file system বা directory-কেও দেখাতে পারে, লক্ষ্য মুছলে ঝুলে যায়।
গভীর উত্তর: rm আসলে unlink: একটি নাম মোছে আর link count কমায়। ডেটা মুক্ত হয় তখনই, যখন link count শূন্য এবং কোনো process ফাইলটি খোলা রাখেনি। তাই চলমান সার্ভিসের log মুছলে ডিস্কের জায়গা ফেরত আসে না (প্রশ্ন ৩৩)। Directory-র hard link নিষেধ, কারণ তাতে cycle তৈরি হতো — গাছ-ঘোরা অসীম হতো আর reference count দিয়ে জায়গা ফেরত আনা যেত না।
সম্ভাব্য ফলো-আপ: “mv একই file system-এ কেন তাৎক্ষণিক?” → শুধু directory entry বদলায়, ডেটা সরে না; অন্য file system-এ হলে কপি + মোছা।
প্র ৩৩“No space left on device”, অথচ df বলছে জায়গা আছে — বা df ১০০% কিন্তু du-র যোগফল অনেক কম। কেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: প্রথম ক্ষেত্রে সম্ভবত inode ফুরিয়েছে — লাখো ছোট ফাইল (session, cache) — df -i দেখুন। দ্বিতীয় ক্ষেত্রে সাধারণত মুছে ফেলা কিন্তু এখনো খোলা ফাইল: du নাম ধরে গোনে, তাই নামহীন ফাইল দেখে না; df file system-এর আসল ব্যবহার দেখায়।
গভীর উত্তর: খোলা-মোছা ফাইল খুঁজতে: lsof +L1, বা ls -l /proc/*/fd 2>/dev/null | grep deleted. সমাধান: সেই process-কে log আবার খুলতে বলা (অনেক daemon SIGHUP-এ করে, logrotate-এর postrotate), বা restart; জরুরি অবস্থায় : > /proc/PID/fd/N দিয়ে ফাইলটি ছোট করা যায়। অন্য কারণ: ext4 root-এর জন্য ডিফল্টে প্রায় ৫% ব্লক সংরক্ষিত রাখে, কোটা, বা একটি directory-র ওপর অন্য file system mount হয়ে নিচের ফাইল ঢেকে গেছে।
সম্ভাব্য ফলো-আপ: “ভবিষ্যতে ঠেকাবেন কীভাবে?” → logrotate-এ copytruncate বা সংকেত দিয়ে আবার খোলা, ডিস্ক আর inode দুটোরই সতর্ক-সংকেত।
প্র ৩৪select/poll থেকে epoll কেন ভালো? Level-triggered আর edge-triggered কী? (কঠিন)
সংক্ষিপ্ত উত্তর: select আর poll প্রতিবার পুরো fd-তালিকা kernel-এ পাঠায়, kernel প্রতিটি দেখে — খরচ O(n), হাজার connection-এ ভারী (select-এ সাধারণত ১০২৪ fd-এর সীমাও আছে)। epoll-এ তালিকা kernel-এ একবার নিবন্ধন হয়, আর epoll_wait শুধু প্রস্তুত fd ফেরত দেয় — খরচ প্রস্তুত fd-এর সংখ্যার অনুপাতে।
গভীর উত্তর: Level-triggered (ডিফল্ট): যতক্ষণ পড়ার ডেটা আছে, প্রতিবার জানায় — ভুল কম হয়। Edge-triggered (EPOLLET): শুধু অবস্থা বদলের মুহূর্তে জানায়; অর্ধেক পড়ে থামলে নতুন ডেটা না আসা পর্যন্ত আর খবর নেই — তাই fd non-blocking রেখে EAGAIN না পাওয়া পর্যন্ত পড়তে হয়। অনেক thread একই fd-তে অপেক্ষা করলে thundering herd এড়াতে EPOLLEXCLUSIVE বা প্রতি-thread আলাদা listen socket (SO_REUSEPORT). BSD/macOS-এ সমতুল্য kqueue, Windows-এ completion-মডেলের IOCP. সাধারণ ফাইলে epoll কাজে আসে না — epoll_ctl EPERM দেয়, কারণ ডিস্কের ফাইল কখনো “অপ্রস্তুত” হয় না।
সম্ভাব্য ফলো-আপ: “epoll কবে এসেছে?” → Linux ২.৫/২.৬-এর সময়, ২০০২-এর আশেপাশে; nginx, Redis, libuv এর ওপর দাঁড়িয়ে।
প্র ৩৫io_uring কী নতুন এনেছে? (কঠিন)
সংক্ষিপ্ত উত্তর: io_uring (Linux ৫.১, ২০১৯) একটি completion-ভিত্তিক async I/O: অ্যাপ আর kernel-এর মধ্যে ভাগ-করা দুটি ring buffer — submission queue-তে অনুরোধ রাখা হয়, completion queue-তে ফল আসে। এক system call-এ অনেক অনুরোধ জমা দেওয়া যায়, আর সাধারণ ফাইলেও সত্যিকারের async I/O হয়, যা epoll পারে না।
গভীর উত্তর: IORING_SETUP_SQPOLL-এ একটি kernel thread submission ring poll করে, তাই কোনো system call-ই লাগে না; buffer আর fd আগে থেকে register করলে প্রতিবার page pin বা fd খোঁজার খরচ বাঁচে। দাম: kernel-এর বড় নতুন attack surface — এর বাগ নিয়ে বেশ কিছু নিরাপত্তা-সমস্যা প্রকাশ পেয়েছে, তাই কিছু পরিবেশ (যেমন কিছু container runtime-এর ডিফল্ট seccomp নীতি বা কিছু distro) এটি সীমিত বা বন্ধ রাখে। POSIX aio_* glibc-তে ভেতরে thread দিয়ে চলে, তাই আসল async নয়।
সম্ভাব্য ফলো-আপ: “Readiness বনাম completion?” → epoll: “এখন পড়া যাবে”, তারপর অ্যাপ নিজে read করে; io_uring/IOCP: “পড়া হয়ে গেছে, ফল তৈরি”।
প্র ৩৬File descriptor আসলে কী? fork-এর পর দুই process একই fd-তে লিখলে কী হয়? (সহজ)
সংক্ষিপ্ত উত্তর: File descriptor একটি ছোট পূর্ণসংখ্যা — process-এর fd টেবিলের একটি সূচক, যা kernel-এর একটি open file description দেখায় (offset, mode, flag), সেটি আবার inode দেখায়। fork আর dup-এর পর দুই fd একই description ভাগ করে, তাই offset-ও ভাগ — দুজনের লেখা পাশাপাশি জোড়ে, একে অন্যকে মুছে না।
গভীর উত্তর: একই ফাইল দুবার open করলে দুটি আলাদা description, আলাদা offset — দুজন একসাথে লিখলে একে অন্যের লেখা ঢেকে দিতে পারে; log-এর জন্য O_APPEND নিশ্চিত করে প্রতিটি write শেষে বসবে। নতুন fd সবসময় সবচেয়ে ছোট খালি নম্বর পায় — shell-এর redirect এই নিয়মের ওপর দাঁড়িয়ে। fd শুধু ফাইল নয়: socket, pipe, eventfd, timerfd, epoll — “সবকিছুই ফাইল” Unix-দর্শন। Windows-এ সমতুল্য handle, যা যেকোনো kernel object-এর জন্য।
সম্ভাব্য ফলো-আপ: “fd 0, 1, 2?” → stdin, stdout, stderr. “একটি process-এর খোলা fd কীভাবে দেখবেন?” → ls -l /proc/PID/fd বা lsof -p PID.
৬ · নেটওয়ার্কের OS-দিক — socket buffer, fd-সীমা, C10K আর google.com
রিভিশন: পর্ব ১২ · IPC আর socket, পর্ব ৩৯ · I/O multiplexing আর NAPI, পর্ব ৪১ · File descriptor, পর্ব ৫৩ · RPC; প্রোটোকলের দিক (TCP, DNS, HTTP) কম্পিউটার নেটওয়ার্ক কোর্সে
প্র ৩৭send() সফল হলে কি অপর পক্ষ ডেটা পেয়ে গেছে? Socket buffer কী? (মাঝারি)
সংক্ষিপ্ত উত্তর: না। send/write সফল মানে শুধু ডেটা kernel-এর socket buffer-এ (send buffer) কপি হয়েছে; kernel-এর TCP stack পরে পাঠাবে, হারালে আবার পাঠাবে। অপর পক্ষের অ্যাপ পড়েছে কি না জানার একমাত্র উপায় application-স্তরের উত্তর (ack).
গভীর উত্তর: প্রতিটি TCP socket-এর একটি send আর একটি receive buffer; Linux এগুলোর আকার নিজে বদলায় (autotuning, net.ipv4.tcp_rmem/tcp_wmem-এর সীমার ভেতরে)। Send buffer ভরলে blocking socket-এ send থামে, non-blocking-এ EAGAIN — এটাই প্রাকৃতিক backpressure. Receive buffer ভরলে TCP window ছোট হয়ে প্রেরককে ধীর করে।ss -tm প্রতিটি socket-এর buffer আর queue দেখায়; Recv-Q বাড়তে থাকা মানে অ্যাপ যথেষ্ট দ্রুত পড়ছে না।
সম্ভাব্য ফলো-আপ: “close() করলে না-পাঠানো ডেটা?” → kernel পেছনে পাঠাতে থাকে (SO_LINGER না দিলে); receive buffer-এ না-পড়া ডেটা থাকলে কিন্তু RST যেতে পারে।
প্র ৩৮সার্ভারের log-এ “Too many open files” (EMFILE). কী মানে, আর কী করবেন? (সহজ)
সংক্ষিপ্ত উত্তর: Process তার fd-সীমা (RLIMIT_NOFILE, ulimit -n) ছুঁয়েছে — প্রতিটি socket, ফাইল আর pipe একটি করে fd নেয়। আগে দেখুন সত্যিই এত connection লাগে, নাকি fd leak (বন্ধ না করা socket বা ফাইল): ls /proc/PID/fd | wc -l সময়ের সাথে বাড়ছে কি না।
গভীর উত্তর: সীমা দুটি: soft (চলতি, process নিজে hard পর্যন্ত বাড়াতে পারে) আর hard (শুধু বিশেষ অধিকারে বাড়ে)। অনেক distro-তে login shell-এ soft ডিফল্ট ১০২৪। systemd সার্ভিসে সীমা আসে unit-এর LimitNOFILE= থেকে, shell-এর ulimit বা /etc/security/limits.conf থেকে নয় — এটি একটি পরিচিত ফাঁদ। চলমান process-এর সীমা দেখতে cat /proc/PID/limits. পুরো সিস্টেমের সীমা আলাদা: fs.file-max (তখন ত্রুটি ENFILE). Leak হলে সীমা বাড়ানো শুধু দেরি করায়। নিচের উদাহরণে সীমা ১৬ করে ঠিক কোথায় EMFILE আসে, দেখুন।
সম্ভাব্য ফলো-আপ: “fd-সংখ্যা বাড়লে কোন kernel সম্পদ খরচ হয়?” → প্রতিটি open file বা socket-এর kernel struct আর socket buffer — তাই সীমা বাড়ানোর সাথে মেমরিও ভাবুন।
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <sys/resource.h>
#include <unistd.h>
int main(void) {
struct rlimit rl;
getrlimit(RLIMIT_NOFILE, &rl);
printf("limit at start: soft %llu, hard %llu\n",
(unsigned long long)rl.rlim_cur, (unsigned long long)rl.rlim_max);
rl.rlim_cur = 16; /* lower only the soft limit */
if (setrlimit(RLIMIT_NOFILE, &rl) != 0) { perror("setrlimit"); return 1; }
int first = -1, last = -1, count = 0, fd;
while ((fd = open("/dev/null", O_RDONLY)) >= 0) {
if (first < 0) first = fd;
last = fd; count++;
}
printf("opened %d fds (%d..%d), then open() failed: %s\n",
count, first, last, errno == EMFILE ? "EMFILE (Too many open files)" : strerror(errno));
close(7); /* free one slot in the middle */
printf("next open() returns fd %d (the lowest free number)\n", open("/dev/null", O_RDONLY));
return 0;
}একটি আসল রানের আউটপুট:
limit at start: soft 1024, hard 1024
opened 13 fds (3..15), then open() failed: EMFILE (Too many open files)
next open() returns fd 7 (the lowest free number)
শুরুতে soft আর hard দুটোই ১০২৪ — এই পরিবেশের সীমা; আপনার যন্ত্রে hard সীমা অনেক বড় হতে পারে। Soft সীমা ১৬ করার মানে fd নম্বর ০ থেকে ১৫ পর্যন্ত বৈধ। ০, ১, ২ (stdin, stdout, stderr) আগে থেকেই খোলা, তাই নতুন খোলা গেল ৩ থেকে ১৫ — ১৩টি; ১৪তম চেষ্টায় EMFILE, ঠিক সার্ভারের log-এর “Too many open files”. সীমা কমাতে বিশেষ অধিকার লাগে না, বাড়াতে hard সীমা পর্যন্ত লাগে না। শেষ লাইনে fd ৭ বন্ধ করার পর নতুন open ৭-ই পেয়েছে: kernel সবসময় সবচেয়ে ছোট খালি নম্বরটি দেয় — dup2 আর shell-এর redirect এর ওপর নির্ভর করে।
প্র ৩৯C10K সমস্যা কী ছিল, আর OS কীভাবে এর সমাধান দিল? (মাঝারি)
সংক্ষিপ্ত উত্তর: C10K — এক সার্ভারে একসাথে ১০,০০০ client সামলানো। Dan Kegel ১৯৯৯ সালে এই নামে একটি লেখায় প্রশ্নটি তোলেন। তখনকার প্রচলিত নকশা — প্রতি connection-এ একটি process বা thread আর select/poll — এত connection-এ মেমরি আর CPU-তে ডুবে যেত। সমাধান এসেছে OS আর নকশা মিলে: epoll/kqueue-এর মতো O(প্রস্তুত) multiplexing, non-blocking socket আর event-driven সার্ভার।
গভীর উত্তর: সাথে আরও কিছু kernel-উন্নতি: সস্তা thread (NPTL), sendfile-এর মতো zero-copy, accept-এর thundering herd কমানো, SO_REUSEPORT দিয়ে প্রতি core-এ আলাদা listen socket, আর NIC-এর একাধিক queue ও RSS. আজ লাখো connection সাধারণ, যেখানে সীমা হয় fd-সীমা, প্রতি socket-এর buffer-মেমরি, ephemeral port (একটি client থেকে একটি গন্তব্যে), আর conntrack টেবিল। চরম ক্ষেত্রে kernel-কে পাশ কাটানো (DPDK) বা XDP.
সম্ভাব্য ফলো-আপ: “অনেক TIME_WAIT socket দেখলে?” → যে পক্ষ আগে বন্ধ করে, সে-ই TIME_WAIT-এ থাকে (Linux-এ ৬০ সেকেন্ড); সাধারণত ক্ষতিকর নয়, তবে outgoing connection-ভারী client-এ port ফুরাতে পারে — connection reuse (keep-alive, pool) আসল সমাধান।
প্র ৪০ব্রাউজারে google.com লিখে Enter চাপলে OS-এর দিক থেকে কী কী হয়? (কঠিন)
সংক্ষিপ্ত উত্তর: কীবোর্ডের interrupt থেকে ব্রাউজারের event loop; তারপর নাম থেকে IP (ব্রাউজারের cache, তারপর OS-এর resolver — /etc/hosts, তারপর DNS server-এ UDP); socket + connect — kernel-এর TCP stack handshake করে; TLS চলে user space-এর লাইব্রেরিতে; send/recv-এ ডেটা socket buffer, driver আর NIC-এর DMA পেরোয়; শেষে GPU আর compositor দিয়ে পাতা পর্দায়।
গভীর উত্তর: নিচের stepper ধাপে ধাপে দেখায়। শুরুতেই বলে নিন আপনি OS-এর দিকটি বলছেন (process, syscall, resolver, socket, buffer, interrupt, cache), আর জিজ্ঞেস করুন DNS বা TCP-র প্রোটোকল-খুঁটিনাটিতে যাবেন কি না — সেটি নেটওয়ার্ক কোর্সের বিষয়। OS-ভেদে resolver আলাদা: Linux-এ glibc-র getaddrinfo (/etc/nsswitch.conf মেনে), Windows-এ DNS Client সার্ভিস, macOS-এ mDNSResponder; কিছু ব্রাউজার নিজস্ব resolver বা DNS-over-HTTPS-ও ব্যবহার করে।
সম্ভাব্য ফলো-আপ: “getaddrinfo blocking — ব্রাউজার বা Node.js কী করে?” → আলাদা thread-এ চালায় (libuv-এর thread pool) বা নিজের async resolver.
- ১ · কী চাপাinterrupt → event
- ২ · নাম → IPcache, hosts, DNS
- ৩ · socketconnect, handshake
- ৪ · TLS আর HTTPuser space
- ৫ · উত্তর আসাNIC, NAPI, buffer
- ৬ · পর্দায়renderer, GPU, cache
| কোথায় কাজ | |
| মূল syscall বা ঘটনা | |
| যা মনে রাখার |
- কী চাপা থেকে event. কীবোর্ড (USB-তে host controller-এর মাধ্যমে) একটি interrupt তোলে; driver কী-কোড kernel-এর input subsystem-এ দেয়; Linux-এ display server (Wayland compositor বা X) সেটি পড়ে ফোকাস-করা জানালা — ব্রাউজারকে পাঠায়। ব্রাউজারের UI thread নিজের event loop-এ (epoll বা সমতুল্যে) ঘুমিয়ে ছিল; ঘটনা পেয়ে জাগে, Enter দেখে ঠিকানা-অনুরোধ শুরু করে।
- নাম থেকে IP. আগে ব্রাউজারের নিজের DNS cache. না পেলে OS-এর resolver: Linux-এ glibc-র
getaddrinfo/etc/nsswitch.confমেনে/etc/hostsফাইল পড়ে (openat,read— page cache থেকে), তারপর/etc/resolv.conf-এর server-কে UDP port 53-এ প্রশ্ন পাঠায় (প্রায়ই স্থানীয় systemd-resolved, যার নিজের cache আছে)। এটি blocking call, তাই ব্রাউজার একে আলাদা thread-এ চালায় বা নিজের async resolver ব্যবহার করে। - Socket আর connect. ব্রাউজার
socket(AF_INET, SOCK_STREAM)ডাকে — নতুন fd; non-blockingconnect-এ kernel একটি ephemeral port বেছে SYN পাঠায়, তিন-ধাপের handshake নিজেই সারে, আর connection তৈরি হলে fd “লেখার জন্য প্রস্তুত” হয় — epoll জানায়। Handshake আর retransmission পুরোটাই kernel-এর কাজ; অ্যাপ শুধু ফল দেখে। - TLS আর HTTP — user space-এ। TLS handshake আর এনক্রিপশন সাধারণত ব্রাউজারের নিজের লাইব্রেরিতে (Chrome-এ BoringSSL, Firefox-এ NSS) user space-এ হয়; kernel শুধু এনক্রিপ্ট-করা বাইট বয়ে নেয় (Linux-এ kTLS দিয়ে এনক্রিপশন kernel-এ নেওয়ার পথও আছে, মূলত সার্ভারে)। HTTP অনুরোধ
write-এ socket buffer-এ যায়, kernel ভাগ করে প্যাকেট বানিয়ে driver-এর ring-এ দেয়, NIC DMA দিয়ে তুলে নেয়। - উত্তর আসা। NIC প্যাকেট DMA দিয়ে RAM-এ লিখে interrupt তোলে; Linux-এ NAPI প্রথম interrupt-এর পর polling-এ চলে যায়, যাতে প্যাকেটের ঢেউয়ে interrupt-ঝড় না হয়। TCP stack ক্রম মেলায়, ack পাঠায়, ডেটা receive buffer-এ রাখে আর অপেক্ষমাণ thread-কে জাগায়; ব্রাউজার
readদিয়ে user space-এ কপি করে, TLS খোলে। - পর্দায় আর cache-এ। HTML আলাদা renderer process-এ পাঠানো হয় — sandbox-এ, কম অনুমতিতে (seccomp, namespace) — যে layout করে; GPU process ছবি আঁকে, display server/compositor পর্দায় বসায়। Image, CSS, JS ডিস্ক-cache-এ লেখা হয় (page cache হয়ে), তাই পরের বার অনেক কিছু নেটওয়ার্ক ছাড়াই আসে।
strace -f curl …দিয়ে এই পথের একটি ছোট সংস্করণ নিজে দেখা যায়।
৭ · কনটেইনার আর ভার্চুয়ালাইজেশন — namespace, cgroup, 137 আর throttling
রিভিশন: পর্ব ৫০ · Virtual machine, পর্ব ৫১ · Container-এর ভেতর, পর্ব ৫২ · VM, container, microVM, পর্ব ২১ · CFS আর cgroup, পর্ব ৪৯ · seccomp আর SELinux; চালানোর দিক Cloud ও DevOps কোর্সে
প্র ৪১Container আর VM-এর মূল পার্থক্য কী? কখন কোনটি? (সহজ)
সংক্ষিপ্ত উত্তর: VM-এ hypervisor নকল হার্ডওয়্যার দেয়, আর প্রতিটি VM নিজের পুরো kernel চালায়। Container হলো host-এর একই kernel-এ চলা সাধারণ process — namespace দিয়ে আলাদা দেখানো আর cgroup দিয়ে সীমিত। তাই container হালকা আর মুহূর্তে চালু হয়; VM-এর সীমানা শক্ত, আর ভিন্ন OS চালানো যায়।
গভীর উত্তর: Container-এর ভেতর থেকে uname -r host-এর kernel দেখায় — Linux container-এ Windows চলে না (Docker Desktop macOS আর Windows-এ ভেতরে একটি Linux VM চালায়)। একটি kernel বাগ সব container-কে ঝুঁকিতে ফেলে, তাই অবিশ্বস্ত বহু-গ্রাহকের কাজে microVM (Firecracker) বা gVisor-এর মতো মাঝামাঝি পথ। সিদ্ধান্ত: একই দলের অনেক সার্ভিস ঘন করে চালাতে container; কঠোর বিচ্ছিন্নতা, ভিন্ন kernel বা পুরো OS লাগলে VM; প্রায়ই দুটো একসাথে — cloud VM-এর ভেতরে container.
সম্ভাব্য ফলো-আপ: “Hypervisor type 1 আর type 2?” → type 1 সরাসরি হার্ডওয়্যারে (ESXi, Xen, Hyper-V), type 2 একটি host OS-এর অ্যাপ হিসেবে (VirtualBox); KVM দুই দলেই ফেলার যুক্তি আছে।
প্র ৪২Namespace আর cgroup — কোনটি কী করে? কয়টি namespace আছে? (মাঝারি)
সংক্ষিপ্ত উত্তর: Namespace ঠিক করে process কী দেখে; cgroup ঠিক করে কতটা ব্যবহার করতে পারে আর তার হিসাব রাখে। Linux-এ namespace আটটি: PID, mount, network, UTS (hostname), IPC, user, cgroup আর time.
গভীর উত্তর: PID namespace-এ container-এর প্রথম process নিজেকে PID 1 দেখে; network namespace-এ নিজের interface, route আর port (host-এর সাথে veth জোড়া দিয়ে যুক্ত); user namespace-এ ভেতরের UID 0 বাইরের একজন সাধারণ user-এ map হতে পারে (rootless). cgroup v2-এ এক গাছ, প্রতিটি দলে controller-এর ফাইল: cpu.max, cpu.weight, memory.max, memory.high, pids.max, io.max. একটি সতর্কতা: /proc/meminfo, /proc/stat আর nproc-এর অনেক সংখ্যা namespace মানে না, পুরো host-এর ছবি দেখায় — container-এর আসল সীমা cgroup ফাইলে (নিচের উদাহরণ)। tool হিসেবে unshare, nsenter, lsns.
সম্ভাব্য ফলো-আপ: “chroot কি container?” → না; শুধু path-এর মূল বদলায়, PID, নেটওয়ার্ক, UID বা সম্পদ আলাদা করে না।
একটি container-এর ভেতর থেকে নিজের cgroup-সীমা, throttling-গণক আর namespace দেখুন — এই উদাহরণটি নিজেই একটি container-এ চলে:
# What does a container look like from the inside? (cgroup v2 + namespaces)
echo "my cgroup: $(cat /proc/self/cgroup)"
cg=/sys/fs/cgroup$(cut -d: -f3 /proc/self/cgroup)
for f in cpu.max memory.max memory.current pids.max pids.current; do
if [ -r "$cg/$f" ]; then printf '%-15s %s\n' "$f" "$(cat "$cg/$f")"; fi
done
[ -r "$cg/cpu.stat" ] && grep -E '^(nr_periods|nr_throttled|throttled_usec)' "$cg/cpu.stat"
[ -r "$cg/memory.events" ] && grep -E '^(max|oom|oom_kill) ' "$cg/memory.events"
echo "PID of this shell: $$ (inside its PID namespace)"
echo "namespaces:"
for n in pid mnt net uts ipc user cgroup; do
printf ' %-7s %s\n' "$n" "$(readlink /proc/self/ns/$n)"
done
echo "hostname: $(cat /proc/sys/kernel/hostname) CPUs visible: $(nproc)"একটি আসল রানের আউটপুট (namespace নম্বর আর hostname প্রতিবার আলাদা):
my cgroup: 0::/
cpu.max max 100000
memory.max max
memory.current 925696
pids.max 2048
pids.current 3
nr_periods 0
nr_throttled 0
throttled_usec 0
max 0
oom 0
oom_kill 0
PID of this shell: 2 (inside its PID namespace)
namespaces:
pid pid:[4026532821]
mnt mnt:[4026532817]
net net:[4026532747]
uts uts:[4026532819]
ipc ipc:[4026532820]
user user:[4026532740]
cgroup cgroup:[4026532822]
hostname: 21626f510c87 CPUs visible: 2
0::/ মানে cgroup v2-এর একক গাছ, আর cgroup namespace-এর কারণে container নিজের দলকেই গাছের মূল (/) দেখে। cpu.max = max 100000: period ১০০ ms, কিন্তু কোনো কোটা নেই — তাই nr_throttled 0. Kubernetes-এ limits.cpu: 500m দিলে এখানে 50000 100000 দেখতেন (প্রশ্ন ৪৪)। মেমরি-সীমাও নেই (max); pids.max 2048 একটি fork-বোমা আটকাবে; memory.events-এর oom_kill 0 — 137-এর তদন্তে প্রথম দেখার জায়গা। Shell-এর PID 2, কারণ নিজের PID namespace-এ গোনা শুরু হয় নতুন করে। বন্ধনীর সংখ্যা প্রতিটি namespace-এর পরিচয় — দুটি process-এ একই সংখ্যা মানে একই namespace. Hostname একটি এলোমেলো container-id (UTS namespace).
প্র ৪৩একটি container বা pod বারবার exit code 137 দিয়ে মরছে। এর মানে কী, আর কীভাবে তদন্ত করবেন? (সহজ)
সংক্ষিপ্ত উত্তর: 137 = ১২৮ + ৯ — process-টি SIGKILL-এ মরেছে। সবচেয়ে সাধারণ কারণ মেমরি-সীমা: cgroup-এর memory.max ছুঁয়ে OOM kill (Kubernetes-এ OOMKilled). দ্বিতীয় সাধারণ কারণ: graceful সময় শেষে runtime-এর SIGKILL (docker stop-এর ১০ সেকেন্ড, বা liveness probe ব্যর্থ হয়ে restart).
গভীর উত্তর: প্রমাণ খুঁজুন: kubectl describe pod-এ “Last State: Terminated, Reason: OOMKilled”; docker inspect-এ State.OOMKilled; node-এ dmesg-এ “Memory cgroup out of memory”; cgroup-এর memory.events-এ oom_kill বাড়া। তারপর কেন: আসল চাহিদা কি limit-এর বেশি (লোডে মাপুন), leak (প্রশ্ন ২৬), নাকি runtime limit জানে না — যেমন পুরোনো JVM-এর heap limit-এর কাছাকাছি বা বেশি, যার ওপর thread stack, metaspace আর native মেমরি যোগ হয়। এককের ভুলও হয়: 512M (১০৬) আর 512Mi (২২০) আলাদা।
সম্ভাব্য ফলো-আপ: “OOMKilled না দেখালেও 137?” → node-এর মেমরি-চাপে kubelet-এর eviction, বা কেউ হাতে kill -9 — event আর node-এর log দেখুন।
প্র ৪৪Kubernetes-এ একটি সার্ভিসের গড় CPU ব্যবহার limit-এর অনেক নিচে, তবু p99 latency খারাপ। CPU throttling কীভাবে হয়? (কঠিন)
সংক্ষিপ্ত উত্তর: CPU limit হয় CFS bandwidth control-এর কোটা: প্রতি period-এ (ডিফল্ট ১০০ ms) মোট এত CPU-সময়। limits.cpu: 500m মানে cpu.max = "50000 100000" — প্রতি ১০০ ms-এ ৫০ ms. কোটা period-এর শুরুতেই শেষ হলে বাকি সময় পুরো দল থেমে থাকে — throttling. গড় কম হলেও ছোট ছোট বিস্ফোরণে latency লাফ দেয়।
গভীর উত্তর: হিসাব: ৫০ ms কোটা, ৪টি thread একসাথে পুরো CPU-নির্ভর কাজ শুরু করল ৪টি ফাঁকা core-এ — ৫০ ÷ ৪ = ১২.৫ ms-এ কোটা শেষ, তারপর period-এর বাকি ১০০ − ১২.৫ = ৮৭.৫ ms চারটিই throttled. সেই ফাঁকে আসা অনুরোধ ৮৭.৫ ms পর্যন্ত অপেক্ষা করে। অথচ এমন বিস্ফোরণ প্রতি চারটি period-এ একবার হলে গড় ব্যবহার ৫০ ÷ ৪০০ = ০.১২৫ CPU — limit-এর মাত্র ২৫%. প্রমাণ: cgroup-এর cpu.stat-এ nr_throttled আর throttled_usec বাড়ছে। প্রতিকার: thread-সংখ্যা limit-এর সাথে মেলানো (GOMAXPROCS, JVM-এর CPU-গণনা, worker-সংখ্যা — নতুন Go আর JVM container-এর সীমা নিজে পড়ে), limit বাড়ানো বা latency-সংবেদনশীল সার্ভিসে CPU limit না দিয়ে শুধু request (cpu.weight) রাখা — এ নিয়ে দলভেদে মত আলাদা।
সম্ভাব্য ফলো-আপ: “Request আর limit-এর পার্থক্য?” → request দিয়ে scheduler node বাছে আর CPU-র ভাগ (weight) ঠিক হয়; limit cgroup-এ কড়া সীমা — CPU-তে throttling, মেমরিতে OOM kill.
প্র ৪৫docker stop দিলে container ১০ সেকেন্ড আটকে থেকে তারপর মরে। কেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: Container-এর ভেতরে আপনার অ্যাপ PID 1. Linux-এ PID namespace-এর init-কে kernel আলাদা চোখে দেখে: যে signal-এর handler সে বসায়নি, তার ডিফল্ট কাজ (মরে যাওয়া) প্রয়োগ হয় না — SIGTERM চুপচাপ উপেক্ষিত। Docker ১০ সেকেন্ড অপেক্ষা করে SIGKILL পাঠায় — exit 137.
গভীর উত্তর: আরেকটি সাধারণ কারণ shell-রূপের CMD node app.js — তখন PID 1 হয় /bin/sh -c, যে signal child-কে পাঠায় না। সমাধান: exec-রূপ CMD ["node", "app.js"], অ্যাপে SIGTERM handler, অথবা একটি ছোট init — tini বা docker run --init — যে signal সামনে পাঠায় আর orphan-দের reap করে (zombie জমা ঠেকায়, প্রশ্ন ০৫)। PID 1 মরলে namespace-এর বাকি সবাই SIGKILL পায়।
সম্ভাব্য ফলো-আপ: “Kubernetes-এ graceful shutdown?” → preStop hook, readiness ব্যর্থ করে ট্রাফিক সরানো, তারপর SIGTERM; সময় terminationGracePeriodSeconds.
প্র ৪৬Container কি একটি নিরাপত্তা-সীমানা? কীভাবে শক্ত করবেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: আংশিক। সব container একই kernel ভাগ করে, তাই kernel-এর একটি বাগ বা ভুল কনফিগ (privileged container, host-এর Docker socket mount) পুরো host-কে ঝুঁকিতে ফেলে। VM-এর সীমানা তুলনায় শক্ত। Container-কে শক্ত করা হয় স্তরে স্তরে: root নয় এমন user, কম capability, seccomp, MAC আর read-only file system.
গভীর উত্তর: ব্যবহারিক তালিকা: ভেতরে non-root user (বা rootless runtime, যেখানে ভেতরের root বাইরে সাধারণ user); --cap-drop=ALL আর শুধু দরকারিগুলো ফেরত; ডিফল্ট seccomp profile চালু রাখা; AppArmor বা SELinux; --read-only root আর no-new-privileges; --privileged আর /var/run/docker.sock mount এড়ানো; সম্পদ-সীমা (pids.max fork-বোমা ঠেকায়)। অবিশ্বস্ত কোডে gVisor (user-space kernel) বা Kata/Firecracker (microVM).
সম্ভাব্য ফলো-আপ: “Docker socket mount কেন বিপজ্জনক?” → সেটি দিয়ে নতুন privileged container চালানো যায় — কার্যত host-এর root.
৮ · পারফরম্যান্স আর ডিবাগিং — “সার্ভার ধীর, ধাপে ধাপে বলুন”
রিভিশন: পর্ব ১১ · ps, top আর load average, পর্ব ৫৭ · USE method, vmstat, perf, পর্ব ৩৭ · PSI, পর্ব ৪০ · ডিস্ক, পর্ব ৫৫ · eBPF
এই অংশের প্রশ্নগুলো দৃশ্যভিত্তিক: ইন্টারভিউয়ার একটি লক্ষণ বলেন, আপনি পদ্ধতি দেখান। আগে বড় প্রশ্নটির একটি পূর্ণ উদাহরণ — সংখ্যাগুলো একটি কাল্পনিক কিন্তু বাস্তবসম্মত ঘটনার:
| ধাপ | |
| কমান্ড | |
| যা দেখা গেল | |
| সিদ্ধান্ত |
- আগে প্রশ্ন। “ধীর মানে কী — কোন endpoint, কতটা (p99 ২০০ ms থেকে ৩ s), কবে থেকে (২০ মিনিট), কী বদলেছে (deploy নেই)?” লক্ষণ মাপার এই ধাপ (RED: rate, error, duration) বাদ দিলে পরে বোঝা যায় না সমাধান কাজ করল কি না। তারপর পর্ব ৫৭-এর ৬০ সেকেন্ডের চেকলিস্ট ধরে এগোবেন।
- uptime. load average ২৪.১, ১৮.৫, ৯.২ — ১ মিনিটেরটি সবচেয়ে বড়, তাই চাপ বাড়ছে। ৮ CPU-তে ২৪ মানে প্রায় ৩ গুণ বেশি task চলতে বা অপেক্ষায়। কিন্তু Linux-এর load average শুধু CPU-র লাইন নয় — D-অবস্থায় (uninterruptible sleep, সাধারণত ডিস্ক বা NFS-এর অপেক্ষা) আটকে থাকা task-ও গোনে। তাই পরের প্রশ্ন: CPU ব্যস্ত কি না?
- vmstat 1. r = ১ (চলতে-চাওয়া task মাত্র একটি), b = ২২ (I/O-তে আটকে), us + sy = ৭%, id ১৮%, wa ৭৫%. iowait হলো CPU-র অলস সময়, যখন কোনো I/O অপেক্ষায় ছিল — CPU বাড়ালে কিছুই হবে না। High load + অলস CPU + বড় b = I/O-র দিকে তাকান।
- কারা D-তে।
ps-এর STATDধরে ছাঁকলে ২১টি অ্যাপ worker আর একটি backup process;wchan(কোথায় ঘুমাচ্ছে) দেখায়io_schedule-এর মতো ডিস্ক-অপেক্ষার kernel ফাংশন। PSI-র/proc/pressure/io-এর “full” সংখ্যাও এখানে উঁচু থাকত। D-অবস্থার task SIGKILL-এও সঙ্গে সঙ্গে মরে না। - iostat -xz 1. sdb-তে r_await ১৯০ ms (সাধারণ দিনে কয়েক ms), aqu-sz ৩১ — লম্বা সারি, %util ১০০. এই ডিস্কে (HDD বা একটি IOPS-সীমিত cloud volume) এটি সত্যিই saturation; NVMe-তে শুধু %util দেখে সিদ্ধান্ত নেওয়া যেত না, await আর সারির প্রবণতা দেখতে হতো (পর্ব ৫৭)।
- কারণ, সমাধান, যাচাই।
pidstat -d 1দেখায় একটি backup process সবচেয়ে বেশি পড়ছে — cron-এ রাত ১০টার job, যা আগে রাত ২টায় ছিল। সমাধান: সময় বদলানো,ionice -c3বা cgroup-এরio.maxদিয়ে সীমা, বা আলাদা ডিস্ক। তারপর একই সংখ্যা আবার মাপা: load, b, await আর p99 স্বাভাবিকে ফিরল কি না। একবারে একটি পরিবর্তন।
প্র ৪৭Load average অনেক বেশি, অথচ top-এ CPU বেশিরভাগ অলস। কী হতে পারে? (মাঝারি)
সংক্ষিপ্ত উত্তর: Linux-এ load average চলমান আর চলতে-চাওয়া task-এর সাথে D-অবস্থার (uninterruptible sleep) task-ও গোনে। তাই অনেক task ডিস্ক, NFS বা অন্য ধীর I/O-তে আটকে থাকলে CPU অলস থেকেও load বিশাল হয়। পরের ধাপ: vmstat-এর b কলাম, ps-এ D-অবস্থার task আর iostat.
গভীর উত্তর: এটি Linux-এর নিজস্ব সিদ্ধান্ত — অন্য অনেক Unix শুধু CPU-র চাহিদা গোনে। Load একটি exponentially-damped চলমান গড় (১, ৫, ১৫ মিনিট), CPU-সংখ্যার সাথে তুলনা করে পড়তে হয়। কারণের তালিকা: saturated ডিস্ক, সাড়া না দেওয়া NFS server (hard mount-এ task অনির্দিষ্টকাল D-তে), ব্যর্থ-হতে-থাকা ডিস্ক (dmesg-এ I/O error), বা kernel-এর lock-এ আটকে থাকা। PSI (/proc/pressure/{cpu,memory,io}) সরাসরি বলে কোন সম্পদের জন্য task-রা সময় হারাচ্ছে — load-এর চেয়ে স্পষ্ট সংকেত।
সম্ভাব্য ফলো-আপ: “D-অবস্থার task মারা যায় না কেন?” → kernel I/O-র মাঝখানে তাকে signal-এ জাগাতে পারে না; নতুন kernel-এ কিছু অপেক্ষা TASK_KILLABLE — শুধু মারাত্মক signal-এ জাগে।
প্র ৪৮একটি process ১০০% CPU খাচ্ছে। কোন ফাংশনে সময় যাচ্ছে, কীভাবে খুঁজবেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: আগে কোন process আর কোন thread (top -H, pidstat -t 1), আর user না kernel সময় (us বনাম sy). তারপর sampling profiler: perf top -p PID সরাসরি, বা perf record -F 99 -g -p PID -- sleep 30 নিয়ে flame graph — সবচেয়ে চওড়া চ্যাপ্টা মাথাটি আসল খরচ।
গভীর উত্তর: perf নির্দিষ্ট হারে stack-এর নমুনা নেয়, তাই খরচ কম আর production-এ সাবধানে চলে। stack ঠিকমতো পেতে frame pointer বা debug symbol লাগে; JIT-ভাষায় (Java, Node.js) symbol map লাগে (যেমন Node-এ --perf-basic-prof). sy বেশি হলে syscall-ঝড় বা lock-প্রতিযোগিতা — perf trace -s বা strace -c -p (strace ভারী, অল্প সময়ের জন্য)। “CPU ১০০%” মানেই হিসাব নয় — মেমরির অপেক্ষাও CPU-সময়; perf stat-এ IPC (instructions per cycle) কম হলে cache miss দায়ী। Spinlock বা ব্যস্ত লুপে থাকা thread-ও ১০০% দেখায়।
সম্ভাব্য ফলো-আপ: “Flame graph-এর x-অক্ষ সময়?” → না; নমুনার অনুপাত, ফ্রেমগুলো সাধারণত বর্ণানুক্রমে। “অপেক্ষার সময় কোথায় যায়?” → off-CPU analysis.
প্র ৪৯ডিস্ক ধীর মনে হচ্ছে। iostat-এর কোন সংখ্যাগুলো দেখবেন? (মাঝারি)
সংক্ষিপ্ত উত্তর: iostat -xz 1-এ প্রতি ডিভাইসে: r/s আর w/s (IOPS), rkB/s আর wkB/s (throughput), r_await আর w_await (গড় অপেক্ষা + সেবার সময়, ms), aqu-sz (গড় সারির দৈর্ঘ্য) আর %util. await-এর স্বাভাবিক মানের সাথে তুলনা আর সারি বাড়ছে কি না — এ দুটিই মূল সংকেত।
গভীর উত্তর: %util শুধু বলে সময়ের কত অংশে অন্তত একটি অনুরোধ চলছিল; একসাথে বহু অনুরোধ সামলানো NVMe বা RAID-এ ১০০% মানেই ভরপুর নয়। Cloud volume-এ প্রায়ই IOPS বা throughput-এর কোটা থাকে — কোটায় পৌঁছালে await লাফ দেয়, যদিও ডিভাইস নিজে দ্রুত। কে I/O করছে: pidstat -d 1 বা iotop; latency-র বণ্টন দেখতে eBPF-এর biolatency. ডিস্কের নিজের সমস্যা: dmesg-এ I/O error, smartctl. অ্যাপের দিকে: ছোট ছোট sync লেখা (প্রতিটিতে fsync) ডিস্ককে IOPS-এ আটকে দেয় — batch বা group commit সাহায্য করে।
সম্ভাব্য ফলো-আপ: “HDD-তে random বনাম sequential?” → random I/O-তে seek আর rotation প্রায় পুরো সময় নেয় (7200 RPM-এ গড় rotation প্রায় ৪.১৭ ms), তাই IOPS শ’খানেকের ঘরে।
প্র ৫০“সার্ভার ধীর, আমাকে ধাপে ধাপে বলুন কীভাবে খুঁজবেন।” (কঠিন)
সংক্ষিপ্ত উত্তর: প্রথমে লক্ষণ পরিষ্কার করা (কী ধীর, কার জন্য, কতটা, কবে থেকে, কী বদলেছে), তারপর baseline-এর সাথে তুলনা, তারপর USE method — CPU, মেমরি, ডিস্ক আর নেটওয়ার্কের প্রতিটিতে utilization, saturation, error — ৬০ সেকেন্ডের চেকলিস্টে, তারপর সন্দেহের দিকে গভীর টুল, একবারে একটি পরিবর্তন আর আবার মাপা।
গভীর উত্তর: কমান্ডের ক্রম, প্রতিটির সাথে কী খুঁজছেন বলে বলে: uptime (load আর প্রবণতা) → dmesg -T | tail (OOM, I/O error, throttling) → vmstat 1 (r, b, si/so, us/sy/wa/st) → mpstat -P ALL 1 (একটি CPU কি একাই ভরা — একটি thread বা interrupt) → pidstat 1 → iostat -xz 1 → free -m → sar -n DEV,TCP,ETCP 1 (নেটওয়ার্ক, retransmit) → top. VM-এ steal বেশি হলে প্রতিবেশীর ভিড়; container-এ cgroup-এর throttling আর memory events. উপরের stepper এর একটি পূর্ণ উদাহরণ। শেষে বলুন কীভাবে আবার না ঘটে — alert, capacity, postmortem.
সম্ভাব্য ফলো-আপ: “সব সংখ্যা স্বাভাবিক, তবু ধীর?” → সমস্যা নিচের নির্ভরতায় (DB, DNS, বাইরের API) বা অ্যাপের ভেতরে lock — distributed trace আর off-CPU analysis.
প্র ৫১একটি process ঝুলে আছে — CPU খাচ্ছে না, সাড়াও দিচ্ছে না। কীভাবে দেখবেন সে কোথায় আটকে? (মাঝারি)
সংক্ষিপ্ত উত্তর: ps -o stat,wchan:32 -p PID — S (কিছুর অপেক্ষায়), D (I/O-তে), T (থামানো)? তারপর user-space-এর stack: gdb -p PID → thread apply all bt (বা eu-stack, Java-য় jstack), আর কোন syscall-এ আটকে: strace -p PID (অল্প সময়ের জন্য)। root থাকলে kernel-এর দিকের stack /proc/PID/stack-এ।
গভীর উত্তর: সাধারণ ছবি: সব thread futex_wait-এ — lock-এর deadlock বা হারানো signal (প্রশ্ন ১৩, ১৬); read-এ একটি socket-এ — অপর পক্ষ উত্তর দিচ্ছে না, আর timeout নেই; epoll_wait-এ — event loop স্বাভাবিক অলস, সমস্যা অন্যখানে; pipe-এর write-এ — পাঠক পড়ছে না, buffer ভরা; D-তে nfs_* — NFS server. ls -l /proc/PID/fd দিয়ে দেখুন ওই fd কোন ফাইল বা socket. তদন্তের সময় core dump (gcore) রেখে দিন, তারপর restart — প্রমাণ হারায় না।
সম্ভাব্য ফলো-আপ: “strace বা gdb attach করতে অনুমতি না পেলে?” → kernel.yama.ptrace_scope আর CAP_SYS_PTRACE — নিরাপত্তার জন্যই সীমিত।
৯ · নিরাপত্তার মৌলিক প্রশ্ন — setuid, least privilege আর ASLR
রিভিশন: পর্ব ৪৬ · Protection, পর্ব ৪৭ · Permission, setuid আর sudo, পর্ব ৪৮ · OS নিরাপত্তা, পর্ব ৪৯ · Sandbox আর MAC; গভীর নিরাপত্তা সাইবার নিরাপত্তা কোর্সে
এখানে সব প্রশ্ন প্রতিরক্ষার দিক থেকে: কোন সুরক্ষা কী থামায়, কীভাবে চালু আর যাচাই করবেন।
প্র ৫২Setuid কী? এটি কেন দরকার, আর ঝুঁকি কোথায়? (মাঝারি)
সংক্ষিপ্ত উত্তর: Setuid bit-ওয়ালা প্রোগ্রাম exec হলে process-এর effective UID হয় ফাইলের মালিক — সাধারণ user-ও সাময়িকভাবে মালিকের অধিকার পায়। উদাহরণ passwd: root-এর /etc/shadow বদলাতে হয়, আর real UID দিয়ে বোঝে কার পাসওয়ার্ড বদলাচ্ছে। ঝুঁকি: এমন প্রোগ্রামের একটি বাগ মানে সরাসরি root-এর অধিকার।
গভীর উত্তর: তাই setuid প্রোগ্রাম যত কম তত ভালো, আর যেগুলো আছে সেগুলো কাজ শেষে দ্রুত অধিকার ছাড়ে, environment বিশ্বাস করে না। Linux script-এর (#!) setuid bit মানে না — race-এর ঝুঁকি বেশি বলে। যাচাই: find / -perm -4000 -type f 2>/dev/null দিয়ে তালিকা নিয়ে অপ্রত্যাশিত কিছু আছে কি না দেখা; ব্যবহারকারীর লেখা যায় এমন file system nosuid দিয়ে mount. আধুনিক বিকল্প: পুরো root-এর বদলে নির্দিষ্ট capability (যেমন ping-এ শুধু CAP_NET_RAW, বা unprivileged ICMP socket), বা একটি ছোট privileged সার্ভিস।
সম্ভাব্য ফলো-আপ: “Directory-তে setgid আর sticky bit?” → setgid: নতুন ফাইল directory-র group পায় (দলের ভাগ-করা ফোল্ডার); sticky (/tmp-এর 1777): শুধু মালিক নিজের ফাইল মুছতে পারেন।
প্র ৫৩একটি নতুন web সার্ভিস deploy করছেন। Least privilege বাস্তবে কীভাবে প্রয়োগ করবেন? (সহজ)
সংক্ষিপ্ত উত্তর: Least privilege: প্রতিটি অংশ শুধু নিজের কাজের ন্যূনতম অধিকার পাবে। সার্ভিস নিজের আলাদা অ-root user-এ চলবে, শুধু নিজের ফাইলে লিখতে পারবে, দরকারি port আর নেটওয়ার্ক-পথ ছাড়া কিছু পাবে না, আর secret পাবে শুধু যেগুলো তার দরকার।
গভীর উত্তর: systemd unit-এ কয়েক লাইনেই অনেকটা: User=, NoNewPrivileges=yes, ProtectSystem=strict, ProtectHome=yes, PrivateTmp=yes, CapabilityBoundingSet=, SystemCallFilter=@system-service, ReadWritePaths=; systemd-analyze security একটি unit কতটা খোলা তার মূল্যায়ন দেখায়। ৮০ বা ৪৪৩ port-এ বসতে root নয় — শুধু CAP_NET_BIND_SERVICE, বা সামনে একটি reverse proxy. ডেটাবেস-user-কেও শুধু দরকারি টেবিলের অধিকার। ফাইলের অনুমতি: সাধারণ ফাইল 644 বা 640, secret 600, directory 755/750 — কখনো 777 নয়।
সম্ভাব্য ফলো-আপ: “chmod 777 দিলে ‘Permission denied’ চলে যায় — সমস্যা কী?” → যে কেউ ফাইল বদলাতে বা বদলে দিয়ে কোড চালাতে পারে; ঠিক সমাধান হলো সঠিক মালিক বা group আর ন্যূনতম bit.
প্র ৫৪ASLR, NX bit আর stack canary — প্রতিটি কোন ধরনের আক্রমণ কঠিন করে? (মাঝারি)
সংক্ষিপ্ত উত্তর: NX (W^X): ডেটার page (stack, heap) থেকে কোড চালানো আটকায়। ASLR: stack, heap, library আর (PIE হলে) প্রোগ্রামের ঠিকানা প্রতিবার এলোমেলো — আক্রমণকারী কোন ঠিকানায় লাফাবে আগে জানে না। Stack canary: ফেরার ঠিকানার আগে একটি গোপন মান; একটানা উপচে পড়া লেখা সেটি বদলালে ফেরার আগেই প্রোগ্রাম থামে।
গভীর উত্তর: এগুলো স্তরে স্তরে কাজ করে, কোনোটিই একা যথেষ্ট নয় — যেমন একটি ঠিকানা ফাঁস হলে ASLR দুর্বল হয়, তাই CFI আর shadow stack-এর মতো নতুন প্রতিরক্ষা। যাচাই: cat /proc/sys/kernel/randomize_va_space (2 = পূর্ণ), একই প্রোগ্রাম দুবার চালিয়ে /proc/self/maps-এর ঠিকানা তুলনা; binary-র সুরক্ষা দেখতে checksec বা readelf; compile-এ -fstack-protector-strong, -D_FORTIFY_SOURCE, -fPIE -pie, linker-এ RELRO. Kernel নিজের জন্য KASLR, SMEP আর SMAP ব্যবহার করে। মূল সমাধান অবশ্য memory-safe কোড — Rust, bounds-checking, sanitizer দিয়ে টেস্ট।
সম্ভাব্য ফলো-আপ: “Debug করার সময় ASLR বিরক্ত করে?” → gdb ডিফল্টে নিজের চালানো প্রোগ্রামে এটি বন্ধ রাখে; production-এ বন্ধ করা উচিত নয়।
প্র ৫৫Root আর Linux capabilities-এর সম্পর্ক কী? seccomp কোথায় আসে? (মাঝারি)
সংক্ষিপ্ত উত্তর: ঐতিহ্যগত Unix-এ UID 0 সব পারে। Linux সেই সর্বশক্তিকে কয়েক ডজন capability-তে ভাগ করেছে — CAP_NET_BIND_SERVICE, CAP_SYS_ADMIN, CAP_NET_ADMIN ইত্যাদি — তাই একটি process-কে পুরো root-এর বদলে শুধু দরকারি টুকরো দেওয়া যায়। seccomp আরেক স্তর: process কোন system call ডাকতে পারবে, তার তালিকা — বাকিগুলো ব্যর্থ বা process মরে।
গভীর উত্তর: Capability দেওয়া যায় ফাইলে (setcap cap_net_bind_service=+ep ./server, দেখতে getcap) বা systemd/Docker-এর কনফিগে; চলমান process-এর অবস্থা /proc/PID/status-এর CapEff লাইনে (capsh --decode). CAP_SYS_ADMIN এত কিছু খোলে যে একে প্রায় “নতুন root” বলা হয় — এড়িয়ে চলুন। seccomp-bpf-এ প্রতিটি syscall-এ একটি BPF ফিল্টার রায় দেয় (ALLOW, ERRNO, KILL…); Docker-এর ডিফল্ট profile ঝুঁকিপূর্ণ কিছু syscall আটকায়, Chrome-এর renderer আর systemd-এর SystemCallFilter এটি ব্যবহার করে। no_new_privs নিশ্চিত করে exec করে setuid দিয়ে অধিকার বাড়ানো যাবে না।
সম্ভাব্য ফলো-আপ: “DAC আর MAC?” → DAC-এ মালিক অনুমতি ঠিক করেন (rwx); MAC-এ কেন্দ্রীয় নীতি (SELinux, AppArmor), root-ও এড়াতে পারে না।
১০ · দুই সপ্তাহের রিভিশন পরিকল্পনা আর AI দিয়ে প্রস্তুতি
প্রতিদিন দেড়-দুই ঘণ্টা: পর্বগুলোর TL;DR, stepper আর “পরীক্ষা ও ইন্টারভিউতে” বাক্স; তারপর মিলিয়ে-থাকা প্রশ্নের জোরে উত্তর; শেষে একটি কমান্ড নিজের Linux-এ (বা WSL/VM-এ) চালানো।
| দিন | যে পর্বগুলো রিভিশন | এই পাতার প্রশ্ন | হাতে-কলমে |
|---|---|---|---|
| ১ | ১, ৪, ৫, ৬ — OS-এর কাজ, হার্ডওয়্যার, system call, kernel-এর গঠন | ১০ | strace -c ls |
| ২ | ৭, ৮, ৯, ১০ — বুট, প্রসেস, context switch, fork/exec | ০১–০৪ আর ./a.out stepper | fork-এর কোড, /proc/PID/status |
| ৩ | ১১, ১২, ১৩ — shell, IPC, signal | ০৫, ০৬ আর zombie-উদাহরণ | kill -TERM বনাম -KILL, $? |
| ৪ | ১৪, ১৫, ১৬, ২১ — থ্রেড, pthread, scheduling-এর মাপ, CFS | ০৭, ০৮, ০৯, ১৯ | ps -L, nice |
| ৫ | ২২, ২৩, ২৪, ২৫ — race, atomic, mutex, condition variable | ১১, ১২, ১৪, ১৫, ১৬, ১৮ | lock ছাড়া counter, TSan |
| ৬ | ২৬, ২৭, ২৮, ২৯ — ক্লাসিক সমস্যা, deadlock, Banker's, কনকারেন্সির বাগ | ১৩, ১৭ | দুই lock-এর deadlock আর lock ordering |
| ৭ | বিরতি-দিন: অংশ ১–৩ আবার; একজন বন্ধুর সাথে মৌখিক mock | ০১–১৯ এলোমেলো | উত্তর রেকর্ড করে শোনা |
| ৮ | ৩০, ৩২, ৩৩, ৩৫ — মেমরির ভিত্তি, paging, TLB, virtual memory | ২০, ২১ | /proc/self/maps |
| ৯ | ৩৬, ৩৭, ৩৮ — replacement, thrashing, Linux-এর মেমরি | ২২–২৮ আর VSZ/RSS-উদাহরণ | free -m, vmstat 1, smaps_rollup |
| ১০ | ৩৯, ৪১, ৪২, ৪৩, ৪৪ — I/O, fd, link, inode, fsync | ২৯–৩৮ আর দুটি উদাহরণ | ls -li, ln, ulimit -n |
| ১১ | ৪৬, ৪৭, ৪৮, ৪৯ — protection, permission, নিরাপত্তা, sandbox | ৫২–৫৫ | find -perm -4000, systemd-analyze security |
| ১২ | ৫০, ৫১, ৫২ — VM, container, cloud | ৪১–৪৬ আর cgroup-উদাহরণ | docker run --memory, cpu.stat |
| ১৩ | ৫৫, ৫৭, ৪০ — Linux kernel, পারফরম্যান্স, ডিস্ক | ৪৭–৫১, google.com stepper (৩৯, ৪০) | ৬০ সেকেন্ডের চেকলিস্ট |
| ১৪ | পর্ব ৬৪ (systems coding) আর পূর্ণ mock | দুর্বল তালিকার সব প্রশ্ন | ৪৫ মিনিটের mock: ৫টি ধারণা + ১টি দৃশ্য + ১টি কোড |
প্রতিদিন একটি “দুর্বল তালিকা” রাখুন — যে প্রশ্নে আটকেছেন; দিন ৭ আর ১৪ সেই তালিকার জন্য। তালিকায় না-থাকা পর্বগুলো (যেমন ১৭–২০, ৩১, ৪৫, ৫৪, ৫৮) numerical আর লিখিত পরীক্ষায় বেশি লাগে (পর্ব ৬০–৬২)।
AI দিয়ে প্রস্তুতি — সৎ ব্যবহার আর যাচাই
AI ভালো mock-ইন্টারভিউয়ার হতে পারে, কিন্তু OS-এ তার পরিচিত ভুলগুলো জানা দরকার — ইন্টারভিউতে সেই ভুল আপনার মুখে বেরোলে দায় আপনার।
| AI-এর সাধারণ ভুল | কেন সমস্যা | কীভাবে যাচাই করবেন |
|---|---|---|
ভুল OS বা distro-র কমান্ড (macOS-এ free, পুরোনো ifconfig, ভুল flag) | ইন্টারভিউতে বললে অনভিজ্ঞ শোনায় | man পাতা, নিজের মেশিনে চালানো |
| পুরোনো kernel-তথ্য (cgroup v1-এর ফাইল-নাম, পুরোনো scheduler) | আজকের সিস্টেমে মেলে না | kernel.org-এর ডকুমেন্টেশন, আপনার kernel-এর /sys |
“Permission denied? chmod 777 বা sudo দিন” | নিরাপত্তা ভাঙে; ইন্টারভিউতে লাল সংকেত | কে মালিক, কার দরকার — ন্যূনতম অধিকার |
| race-ওয়ালা concurrent কোড, যা “একবার চালিয়ে ঠিক ছিল” | বাগ লোডে বা অন্য CPU-তে বেরোয় | ThreadSanitizer, বহুবার আর বেশি thread-এ চালানো |
| সংখ্যা বানিয়ে বলা (“context switch ঠিক ৫ µs”) বা ভুল হিসাব (throttling, fork-গণনা) | ফলো-আপে ধরা পড়ে | হাতে আবার হিসাব; নিজে মাপা (perf bench) |
| Linux-এর আচরণকে সব OS-এর নিয়ম বলা (load average-এ D, overcommit) | “Windows-এ?” ফলো-আপে আটকে যাবেন | প্রতিটি দাবিতে জিজ্ঞেস করুন: কোন OS-এ? |
AI-কে mock-ইন্টারভিউয়ার বানাতে এমন একটি prompt দিন — উত্তর আপনি দেবেন, AI শুধু প্রশ্ন আর মূল্যায়ন করবে:
AI-এর উত্তর মুখস্থ করার আগে — চেকলিস্ট
নিজেকে যাচাই করুন
ভুল হলে কারণসহ ব্যাখ্যা পাবেন, আবার চেষ্টাও করতে পারবেন।
ক্রমএকটি দৃশ্যভিত্তিক OS-প্রশ্নের (“container বারবার মরছে”) উত্তরের ধাপগুলো সঠিক ক্রমে সাজান (▲▼ চেপে সরান)
- ❓ প্রশ্ন করে পরিস্থিতি পরিষ্কার করা — কোন OS, কোন পরিবেশ, কবে থেকে
- 🗣️ ২–৩ লাইনের সংক্ষিপ্ত সিদ্ধান্ত বা অনুমান বলা
- 🧠 গভীর ব্যাখ্যা — ভেতরের mechanism আর trade-off
- 🔍 প্রমাণ খোঁজার কমান্ড আর প্রত্যাশিত ফল বলা
- ✅ সমাধান, আবার মাপা আর ভবিষ্যতে ঠেকানোর উপায়
(সহজ) Kubernetes-এ একটি container বারবার exit code 137 দিয়ে বন্ধ হচ্ছে। এর সবচেয়ে সরাসরি অর্থ কী?
- অ্যাপ নিজে main থেকে 137 ফেরত দিয়েছে
- process SIGTERM পেয়ে বন্ধ হয়েছে
- process SIGKILL-এ মরেছে — প্রায়ই মেমরি-সীমায় OOM kill
- process segmentation fault করেছে
(মাঝারি) ৮ CPU-র একটি Linux সার্ভারে load average ৩০, আর top দেখাচ্ছে us ৩%, sy ২%, id ২০%, wa ৭৫%. সবচেয়ে সম্ভাব্য ব্যাখ্যা কোনটি?
- অনেক task I/O-র অপেক্ষায় D-অবস্থায় আটকে আছে
- CPU-নির্ভর কাজ সব core দখল করে আছে
- hypervisor vCPU কেড়ে নিচ্ছে (steal)
- ৮ CPU-তে load ৩০ স্বাভাবিক, কোনো সমস্যা নেই
(কঠিন) একটি container-এর CPU limit 500m, অর্থাৎ cgroup v2-এ cpu.max = "50000 100000" (প্রতি ১০০ ms period-এ ৫০ ms কোটা)। period-এর শুরুতে ৪টি thread একসাথে পুরোপুরি CPU-নির্ভর কাজ শুরু করল, আর ৪টি core ফাঁকা। কতক্ষণ পর throttling শুরু হয়, আর সেই period-এ কতক্ষণ চলে?
- ৫০ ms চলে, তারপর ৫০ ms থামে
- ১২.৫ ms চলে, তারপর ৩৭.৫ ms থামে
- পুরো ১০০ ms চলে, throttling হয় না
- ১২.৫ ms চলে, তারপর ৮৭.৫ ms throttled
(মাঝারি) Linux-এ একটি C প্রোগ্রাম malloc দিয়ে ৬৪ MiB নিল, কিন্তু এখনো একটি বাইটও লেখেনি বা পড়েনি। /proc/self/status-এ কী দেখা যাওয়ার কথা?
- VmSize আর VmRSS দুটোই প্রায় ৬৪ MiB বাড়ে
- VmSize প্রায় ৬৪ MiB বাড়ে, VmRSS প্রায় একই থাকে
- VmRSS ৬৪ MiB বাড়ে, VmSize একই থাকে
- malloc NULL দেয়, কারণ RAM আগে থেকে সংরক্ষণ করা যায়নি
(মাঝারি) একটি Linux প্রোগ্রাম ext4-এর একটি ফাইলে write() করল, আর write() পুরো বাইট-সংখ্যা ফেরত দিল। এক সেকেন্ড পরে বিদ্যুৎ চলে গেল। কোনটি ঠিক?
- ডেটা নিশ্চিতভাবে ডিস্কে আছে, কারণ write() সফল
- ডেটা হারাতে পারে; টেকসই করতে fsync দরকার ছিল
- ext4-এর journaling থাকায় ডেটা কখনো হারায় না
- ডেটা শুধু O_DIRECT দিয়ে খুললে হারাত
(সহজ) Mutex আর counting semaphore সম্পর্কে কোন বাক্যটি ঠিক?
- দুটিই একসাথে শুধু একজন thread-কে ঢুকতে দেয়
- semaphore-এর মালিক থাকে, mutex-এর থাকে না
- mutex-এ অপেক্ষাকারী সবসময় ঘুরতে থাকে (busy wait)
- mutex যে thread নেয় সে-ই ছাড়ে; semaphore-এ অন্য thread-ও V (signal) দিতে পারে
(মাঝারি) একটি struct-এ পাশাপাশি দুটি long counter; দুটি thread দুই আলাদা core-এ প্রত্যেকে নিজের counter-এ একটি ঘন লুপে লেখে। কোনো lock নেই, ফলও ঠিক আসে, তবু এক thread-এর চেয়ে মোট গতি কম। সবচেয়ে সম্ভাব্য কারণ?
- false sharing — দুটি counter একই cache line-এ
- data race-এ lost update হচ্ছে
- দুই thread lock-এর জন্য deadlock করছে
- ঘন ঘন context switch হচ্ছে
(মাঝারি) একটি Linux সার্ভারে df বলছে / ১০০% ভরা, কিন্তু du -sh / -এর যোগফল মাত্র ৪০%. সবচেয়ে সম্ভাব্য কারণ কোনটি?
- inode ফুরিয়ে গেছে
- page cache ডিস্কের জায়গা দখল করেছে
- মুছে ফেলা কিন্তু এখনো খোলা একটি বড় ফাইল (যেমন log)
- অনেক symbolic link দুবার গোনা হয়েছে
(মাঝারি) একটি Linux সার্ভিস (systemd দিয়ে চালানো) মাঝে মাঝে “Too many open files” (EMFILE) দেয়; তার soft সীমা ১০২৪. সবচেয়ে ভালো প্রথম পদক্ষেপ কোনটি?
- নিজের shell-এ ulimit -n 65535 চালানো
- প্রতি রাতে সার্ভিস restart-এর cron বসানো
- sysctl দিয়ে fs.file-max বাড়ানো
- /proc/PID/fd গুনে leak কি না দেখা, তারপর দরকার হলে unit-এ LimitNOFILE বাড়ানো
(সহজ) প্রচলিত CPython-এ (GIL-সহ) একটি বিশুদ্ধ Python-এর CPU-নির্ভর লুপ ৪ core-এর যন্ত্রে ৪টি thread-এ ভাগ করা হলো। প্রত্যাশিত ফল কী?
- গতি প্রায় বাড়ে না; CPU-কাজে multiprocessing লাগবে
- প্রায় ৪ গুণ দ্রুত হয়
- Amdahl-এর সূত্রে ঠিক ২.৫ গুণ
- GIL-এর কারণে প্রোগ্রাম deadlock করে
শব্দকোষ
লেখার মধ্যে ডটেড দাগ দেওয়া শব্দে মাউস রাখুন বা চাপুন — অর্থ দেখাবে। এই পর্বের সব শব্দ এখানে একসাথে:
মনে রাখুন
- কাঠামো: প্রশ্ন করে পরিস্থিতি পরিষ্কার → ২–৩ লাইনের সংক্ষিপ্ত উত্তর → চাইলে গভীরে → প্রমাণ বা যাচাইয়ের পথ; জোরে ভাবুন, ছবি আঁকুন, না জানলে সৎভাবে বলুন আর যুক্তি দিয়ে এগোন।
- Process ও thread: fork মানে copy-on-write, exec মানে একই PID-এ নতুন প্রোগ্রাম, zombie মানে না-নেওয়া exit status; signal-এ মরলে status ১২৮ + n — 137 SIGKILL, 143 SIGTERM, 139 SIGSEGV.
- মেমরি: malloc-এ VSZ বাড়ে, ছোঁয়ায় RSS; free-র পর RSS না কমা মানেই leak নয়; “free” কম হলে available দেখুন; OOM killer badness আর oom_score_adj দিয়ে বাছে।
- I/O: write() মানে page cache, টেকসই মানে fsync (নতুন ফাইলে directory-ও); মুছে ফেলা খোলা ফাইল জায়গা ছাড়ে না; epoll জানায় “প্রস্তুত”, io_uring জানায় “শেষ”; EMFILE হলে আগে leak খুঁজুন।
- Container ও তদন্ত: namespace দেখায়, cgroup সীমিত করে; CPU limit-এ কোটা ফুরালে throttling; Linux-এর load average-এ D-অবস্থা গোনা — লক্ষণ, baseline, USE, গভীর টুল, একবারে একটি পরিবর্তন।
আরও পড়ুন · ABCL TECH-এ আপনার পরবর্তী পদক্ষেপ
- পর্ব ৬৪ · Systems প্রোগ্রামিং ইন্টারভিউ — concurrency আর C কোডিং সমস্যাপরবর্তী পর্বthread, lock আর system call দিয়ে কোড লেখার সমস্যা।
- অপারেটিং সিস্টেম · কোর্স হোম৬৪টি পর্বপুরো কোর্সের রোডম্যাপ — ১০টি মডিউল।
- পর্ব ৫৭ · পারফরম্যান্স আর observabilityপেছনে ফিরেUSE method, ৬০ সেকেন্ডের চেকলিস্ট, perf আর flame graph.
- পর্ব ৫১ · Container-এর ভেতরপেছনে ফিরেnamespace, cgroup, overlayfs আর PID 1 হাতে-কলমে।
- কম্পিউটার নেটওয়ার্ক কোর্সপাশের ট্র্যাকDNS, TCP আর HTTP — google.com প্রশ্নের প্রোটোকলের দিক।
- DBMS · SQL ইন্টারভিউপাশের ট্র্যাকbackend ইন্টারভিউর আরেক বড় অংশ — প্যাটার্ন ধরে SQL.