Processes & IPC — fork, exec, pipes

Unix এভাবেই নতুন প্রোগ্রাম তৈরি করে

~50 min Advanced 15 practice problems Live code

1. What Is a Process?

Process = চলমান প্রোগ্রামের একটি instance। নিজস্ব memory space, file descriptor ও state থাকে। Linux/macOS-এ প্রতিটি command-ই একটি process।

Note এই module-এর code POSIX — Linux, macOS, BSD-তে চলবে। Windows-এ WSL, Cygwin বা MSYS2 লাগবে।

2. fork()

fork.c
#include <stdio.h>
#include <unistd.h>

int main(void) {
    pid_t pid = fork();
    if (pid < 0) {
        perror("fork"); return 1;
    } else if (pid == 0) {
        printf("child: my pid = %d, parent = %d\n", getpid(), getppid());
    } else {
        printf("parent: my pid = %d, child = %d\n", getpid(), pid);
    }
    return 0;
}

fork দুবার return করে — parent-এ child-এর PID, child-এ 0। Error হলে -1।

3. exec — Replace the Program

exec.c
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>

int main(void) {
    pid_t pid = fork();
    if (pid == 0) {
        char *args[] = { "ls", "-l", NULL };
        execvp("ls", args);
        perror("exec");       // only if exec fails
        return 1;
    }
    int status;
    waitpid(pid, &status, 0);
    if (WIFEXITED(status)) printf("ls exited with %d\n", WEXITSTATUS(status));
    return 0;
}

exec current process-এর memory image replace করে — new program-এ রূপান্তর। সফল হলে কখনোই return করে না। fork+exec মিলে নতুন সেই program চালানো।

4. wait / waitpid

int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status)) printf("exit=%d\n", WEXITSTATUS(status));
Zombie processes Terminated child-কে parent wait না করলে zombie হয়ে থাকে (kernel-এ entry রয়ে যায়)। Long-running server-এ সতর্ক থাকুন।

5. Pipes — One-Way IPC

pipe.c
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>

int main(void) {
    int fd[2];
    pipe(fd);                   // fd[0] = read, fd[1] = write

    pid_t pid = fork();
    if (pid == 0) {             // child — writer
        close(fd[0]);
        const char *msg = "Hello from child!";
        write(fd[1], msg, strlen(msg));
        close(fd[1]);
    } else {                    // parent — reader
        close(fd[1]);
        char buf[64] = {0};
        read(fd[0], buf, sizeof buf - 1);
        close(fd[0]);
        printf("parent got: %s\n", buf);
        wait(NULL);
    }
    return 0;
}

6. Mini-Shell — ls | wc -l

Pipeline-এর সব কৌশল: fork পাইপের দু-পাশে, dup2 দিয়ে stdin/stdout redirect, তারপর exec। এটাই আসল shell-এর কাজ।

int p[2]; pipe(p);

if (fork() == 0) {           // left: ls
    close(p[0]);
    dup2(p[1], STDOUT_FILENO);
    close(p[1]);
    execlp("ls", "ls", NULL);
}
if (fork() == 0) {           // right: wc -l
    close(p[1]);
    dup2(p[0], STDIN_FILENO);
    close(p[0]);
    execlp("wc", "wc", "-l", NULL);
}
close(p[0]); close(p[1]);
wait(NULL); wait(NULL);

7. Practice Problems

  1. Fork a child and print PIDs from both sides.
    Fork করে দুই side থেকে PID।
    ✨ Show Answer

    Section 2 reference।

  2. Fork + exec to run ls -l.
    Fork+exec দিয়ে ls চালান।
    ✨ Show Answer

    Section 3 reference।

  3. Parent waits for child and prints its exit code.
    Parent wait করে exit code।
    ✨ Show Answer

    waitpid(pid, &status, 0); WEXITSTATUS(status) — Section 3 reference।

  4. Create a zombie process deliberately; then fix.
    Zombie তৈরি ও ফিক্স।
    ✨ Show Answer

    Child exit করলে parent-এ wait না দেওয়া → zombie। Fix: wait() call, বা SIGCHLD handler, বা signal(SIGCHLD, SIG_IGN)।

  5. Pipe between parent and child; child capitalizes text.
    Pipe দিয়ে child uppercase করুক।
    ✨ Show Answer

    Two pipes (parent→child text, child→parent result)। Child loop-এ read-toupper-write। Parent text পাঠিয়ে উত্তর পড়ে।

  6. ls | wc -l via fork + pipe + exec.
    ls|wc -l।
    ✨ Show Answer

    Section 6 reference।

  7. Three-stage pipeline: cat | grep | sort.
    Three-stage pipeline।
    ✨ Show Answer

    Two pipes, three forks। Middle child: stdin=pipe1-read, stdout=pipe2-write। Loop-এ n-stage generalize করুন।

  8. Write a mini-shell supporting cd, exit and simple commands.
    Mini-shell।
    ✨ Show Answer

    Read line → parse tokens → built-in (cd, exit) হলে parent-এ handle, নতুবা fork+execvp। 50 লাইনেই একটা কার্যকর shell।

  9. Handle SIGINT so Ctrl-C doesn't kill your shell.
    SIGINT handle।
    ✨ Show Answer

    Parent-এ signal(SIGINT, SIG_IGN);। Child-এ default রাখুন — Ctrl-C শুধু foreground child-কে মারবে।

  10. Spawn N children; measure creation time.
    N-টি child spawn time।
    ✨ Show Answer

    Loop-এ fork; child immediately exit, parent wait। clock_gettime দিয়ে time। Linux-এ সাধারণত 50-200 µs/fork — copy-on-write-এর কারণে।

  11. Use popen/pclose to read output of another command.
    popen/pclose।
    ✨ Show Answer
    FILE *f = popen("ls -l", "r");
    char line[256];
    while (fgets(line, sizeof line, f)) fputs(line, stdout);
    pclose(f);
  12. Compare process creation cost (fork) with thread creation (pthread).
    Fork vs pthread creation cost।
    ✨ Show Answer

    pthread সাধারণত 10-50× দ্রুত — shared address space, নতুন memory space copy লাগে না। fork copy-on-write হলেও new page table, file table-এর জন্য ~100 µs।

  13. Share memory with parent and child via mmap MAP_SHARED.
    mmap shared memory।
    ✨ Show Answer

    void *p = mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_SHARED|MAP_ANONYMOUS, -1, 0); — fork-এর পর parent এবং child উভয়ই একই pages share করবে।

  14. Signal a child from parent and handle it.
    Parent child-কে signal।
    ✨ Show Answer

    Child-এ signal(SIGUSR1, handler);। Parent-এ kill(child_pid, SIGUSR1);। Handler-এ async-safe কাজ করুন (printf safe নয়, volatile flag set OK)।

  15. Why is copy-on-write crucial for fork's efficiency?
    COW fork-এ কেন জরুরি?
    ✨ Show Answer

    fork-এ parent-এর সব page আসলে কপি হয় না — দুই process একই physical pages share করে। যতক্ষণ না কেউ write করছে, কপি হবে না। Exec করলে পুরোটাই discard — কোনো copy-ই অপচয় হতো।

Glossary (শব্দকোষ)

TermMeaningবাংলায়
ProcessAn independent running program with its own memory.নিজস্ব মেমরি-যুক্ত একটি চলমান প্রোগ্রাম।
PIDProcess ID — kernel's identifier for a process.Kernel-এর দেওয়া process-শনাক্তকারী।
forkCreates a near-identical child process.প্রায় সমান একটি child process তৈরি।
exec familyReplaces the current process image with a new program.চলমান process-কে নতুন program দিয়ে replace।
wait / waitpidParent waits for a child to finish.Parent child শেষ হওয়ার অপেক্ষা করে।
Zombie ProcessChild has exited but parent has not yet reaped it.Exit হওয়া child — যাকে parent এখনও reap করেনি।
Orphan ProcessChild whose parent already exited; adopted by init.Parent শেষ — init-এর কাছে দত্তক।
IPCInter-Process Communication.Process-এর মধ্যে যোগাযোগ।
PipeOne-way byte stream between related processes.একমুখী byte-stream সংযোগ।
Named Pipe (FIFO)Pipe accessible via a filesystem name.Filename-যুক্ত pipe।
SignalAsynchronous notification (SIGINT, SIGKILL...).Asynchronous সংকেত।
Shared MemoryRegion mapped into multiple processes for fast IPC.একাধিক process-এ যুক্ত মেমরি।
Message QueueKernel-managed queue for IPC messages.Kernel-managed message queue।
Copy-on-WritePages shared after fork until one writes.Fork-এর পর page শেয়ার্ড — কেউ লিখলে কপি হয়।
dup2Duplicates a file descriptor — used for redirection.FD নকল করা — redirection-এ ব্যবহার্য।

Summary — Module 37

fork নতুন process; exec program replace; wait reap করে; pipe+dup2+fork+exec = Unix shell। Backend system এই primitive-এর উপরেই দাঁড়িয়ে।

Next Module → Multithreading with pthreads।