আমি systems programming শিখতে চাই — মানে OS কিভাবে কাজ করে, file system কিভাবে কাজ করে, memory কিভাবে manage হয়, process কী — এসব deep level এর জিনিস। আর systems programming শিখতে গেলে C ছাড়া উপায় নেই, কারণ Linux kernel থেকে শুরু করে বেশিরভাগ system tool সব C তে লেখা।

কিন্তু শুধু C এর syntax শিখে কী হবে? আসল শেখা হয় যখন তুমি কিছু বানাও। তাই আমার plan হলো — Linux এ যেসব command প্রতিদিন ব্যবহার করি (ls, cat, grep, wc) সেগুলো একটা একটা করে C তে implement করবো। এতে C ও শেখা হবে, আর system call, file descriptor, memory allocation — এসব systems concept ও হাতে কলমে বোঝা হবে।

শুরু করছি সবচেয়ে সোজা command দিয়ে — ls। এই post টা সেই যাত্রার প্রথম ধাপ।

Hero Image Photo by Clemens van Lay on Unsplash

'ls' কমান্ড সম্পর্কে

ls হলো "list" এর shorthand — Unix এর tradition অনুযায়ী command গুলো যতটা সম্ভব ছোট রাখা হতো (কম typing, fast কাজ)। এটা Linux এর সবচেয়ে বেসিক কমান্ডগুলোর একটা, আর এটা অত্যন্ত কাজের। এটা current directory তে থাকা সব file এর তালিকা দেখায় — মানে তুমি এক নজরে দেখতে পাও ভেতরে কী কী আছে। ls হলো file navigate আর organize করার জন্য সবচেয়ে প্রথম হাতিয়ার। আর এর সাথে অনেক option আছে যেগুলো দিয়ে output টা ঠিক তোমার মনের মতো করে সাজাতে পারো।

কিভাবে বানাবো?

একটু ভাবো — ls কে সোজাভাবে replicate করতে আমাদের কী কী করতে হবে? একটা directory open করতে হবে, তার ভেতরের জিনিস পড়তে হবে, print করতে হবে, আর তারপর directory বন্ধ করতে হবে। ভাগ্য ভালো, Linux এ কিছু function আছে যেগুলো এই কাজে সাহায্য করবে: opendir, readdir, printf, আর closedir।

প্রথমে opendir এর documentation পড়ি। দেখা যাচ্ছে opendir আছে dirent.h header file এ, standard C library তে।

man opendir

man page for opendir opendir এর man page

opendir() বোঝা

opendir function টা directory র নাম string হিসেবে argument নেয় আর DIR এর একটা pointer return করে — এটা হলো directory stream। Directory stream, আমি যেটুকু বুঝি, হলো একটা object যেটা ওই directory কে represent করে যেটা আমরা just open করলাম। আমরা directory stream এর pointer পাই, তারপর এটা ব্যবহার করে stream এর ভেতরের জিনিস পড়তে পারি।

কোড আর Debugging

এবার আমাদের C কোড আর debugger GDB (GNU Debugger) নিয়ে একটু খেলা যাক। GDB হলো GNU Project এর তৈরি একটা command-line debugger — C, C++, Rust সহ অনেক ভাষায় লেখা program runtime এ inspect করা যায় এটা দিয়ে। Variable এর মান দেখা, breakpoint দিয়ে কোড থামানো, memory inspect করা — সব করা যায়। Systems programming শিখতে গেলে GDB হলো তোমার সবচেয়ে কাছের বন্ধু।

দেখি ভেতরে কী হচ্ছে:

//myls.c

#include <dirent.h>
#include <stdio.h>

int main(int argc, char **argv) {
  // DIR এর একটা pointer define করি
  DIR *dir_stream;

  // current directory (".") open করি
  dir_stream = opendir(".");

  // opendir error হলে NULL return করে, তাই error handle করি...
  if (dir_stream == NULL) {
    perror("Error while trying to open directory \n");
    return 1;
  }

  return 0;
}

উপরের কোড এখনো দরকারি কিছু করে না, কিন্তু আমি এটা ব্যবহার করবো DIR এর ভেতরে কী আছে সেটা gdb দিয়ে দেখতে (sorry, কৌতূহল সামলাতে পারছি না!)। প্রথমে gcc তে -g flag দিয়ে compile করবো:

gcc -Wall -Werror -g myls.c -o myls

Executable ready হলে, চলো gdb চালু করি আর দেখি ভেতরে কী হচ্ছে:

gdb ./myls

GDB Commands

ভেতরটা দেখার জন্য আমি যেসব basic gdb command ব্যবহার করেছি:

  • tui layout src — একটা সুন্দর split-screen দেয়, উপরে source code আর নিচে gdb commands।

  • set print pretty on — printed output কে সুন্দর readable format এ দেখায়।

  • b 9 বা break 9 — line 9 (DIR *dir_stream) এ breakpoint set করে।

  • run — gdb দিয়ে program চালু করে।

  • p *dir_stream বা print *dir_stream — dir_stream pointer এর ভেতরের content দেখায়।

  • n বা next — কোডের পরবর্তী line execute করে।

DIR Structure এর ভেতরটা

dir_stream এর ভেতরে উঁকি দিলে, এরকম কিছু দেখা যায়:

$1 = {
  fd = 3,
  lock = 0,
  allocation = 32768,
  size = 0,
  offset = 0,
  filepos = 0,
  errcode = 0,
  data = 0x5555555592d0 ""
}

চলো বুঝি এখানে কী কী আছে:

  • fd = 3: এটা আমাদের file descriptor number। File descriptor হলো integer যেটা একটা program এ open file কে identify করে। 0, 1, আর 2 নম্বরগুলো সাধারণত standard input, output, আর error এর জন্য reserved থাকে, তাই 3 হলো পরবর্তী available নম্বর।

  • allocation = 32768: এটা বলছে 32KB memory allocate হয়েছে।

  • size = 0: File এর current size 0 bytes।

  • offset = 0: এটা দেখাচ্ছে পরবর্তী read/write এর position file এর শুরুতে আছে।

  • data = 0x5555555592d0: এটা হলো memory address যেখানে directory stream এর data store আছে।

কোডটা ধাপে ধাপে কী করছে

আগের কোডটা আবার দেখি, এবার একটু ভেঙে বুঝি:

1. Header include:

#include <dirent.h>
#include <stdio.h>

dirent.h — directory related functions (opendir, readdir, closedir)। stdio.h — input/output functions (printf, perror)।

2. DIR pointer declare:

DIR *dir_stream;

DIR হলো একটা opaque type — মানে ভেতরে কী আছে সেটা আমাদের জানার কথা না (কিন্তু GDB দিয়ে দেখে ফেললাম!)। pointer ব্যবহার করি কারণ opendir() একটা pointer return করে।

3. Directory open:

dir_stream = opendir(".");

"." মানে current directory। এটা Unix convention — . = যেখানে আছি, .. = এক ধাপ উপরে।

4. Error handling:

if (dir_stream == NULL) {
    perror("Error while trying to open directory");
    return 1;
}

opendir() fail করলে NULL return করে। perror() শুধু message print করে না — errno থেকে actual কারণটাও বলে দেয় (যেমন "Permission denied" বা "No such file or directory")।

File Descriptor বিস্তারিত

DIR structure এ fd = 3 দেখেছি। এটা একটু বিস্তারিত বুঝি কারণ systems programming এ file descriptor অনেক গুরুত্বপূর্ণ concept:

Unix এ সবকিছুই file — regular file, directory, socket, pipe, device — সবার একটা file descriptor (fd) আছে। এটা OS এর দেওয়া একটা integer:

fd 0 → stdin  (keyboard input)
fd 1 → stdout (screen output)
fd 2 → stderr (error output)
fd 3 → আমাদের directory (opendir দিয়ে পেলাম)
fd 4 → পরবর্তী কিছু open করলে...

প্রতিটা process শুরু হলে 0, 1, 2 already reserved থাকে। তাই আমাদের directory র fd = 3 — পরবর্তী available number।

আজকে কী শিখলাম

বিষয়কী শিখলাম
opendir()Directory open করে DIR* pointer return করে
DIR structureভেতরে fd, allocation, offset etc. থাকে — GDB দিয়ে দেখা যায়
File DescriptorOS এর দেওয়া integer — প্রতিটা open resource এর জন্য
perror()Error message + errno র কারণ print করে
GDBRuntime এ যেকোনো variable/struct inspect করা যায়
Unix convention. = current dir, .. = parent dir, সবকিছুই file

উপসংহার

তোমাদের জানি না, কিন্তু আমার কাছে — এখন যখন দেখতে পাচ্ছি ভেতরে কী হচ্ছে, তখন আর শুরুতে যতটা ভয়ংকর লাগতো ততটা লাগছে না — well, এখনো একটু ভয়ংকর, কিন্তু আগের চেয়ে কম।

Part 2 তে আমরা এই directory থেকে পড়বো আর তার content print করবো।

Hasta la vista!


সম্পূর্ণ সিরিজ

Partবিষয়
Part 1 (এই post)opendir(), DIR structure, GDB দিয়ে debug, file descriptor
Part 2readdir(), dirent struct, closedir(), সম্পূর্ণ ls