Câu hỏi phỏng vấn Linux và Operating System cơ bản đến nâng cao

Tổng hợp câu hỏi phỏng vấn về Linux system programming, process, thread, memory management và các khái niệm OS quan trọng

Câu hỏi phỏng vấn Linux và Operating System cơ bản đến nâng cao

Giới thiệu

Linux và Operating System là nền tảng quan trọng cho nhiều vị trí: Embedded Linux Engineer, Backend Developer, DevOps Engineer, System Administrator. Bài viết này tổng hợp các câu hỏi phỏng vấn phổ biến nhất.

Phần 1: Process và Thread

1. Sự khác biệt giữa Process và Thread?

Đáp án:

FeatureProcessThread
DefinitionIndependent program instanceLightweight process, thuộc 1 process
MemoryRiêng biệt (isolated)Shared memory space
CommunicationIPC (Inter-Process Communication)Shared variables
CreationChậm (fork)Nhanh (pthread_create)
OverheadCaoThấp
Crash impactĐộc lậpẢnh hưởng toàn process

Ví dụ:

// Process creation với fork()
#include <stdio.h>
#include <unistd.h>
 
int main() {
    pid_t pid = fork();
    
    if (pid == 0) {
        // Child process
        printf("Child PID: %d\n", getpid());
    } else {
        // Parent process
        printf("Parent PID: %d, Child PID: %d\n", getpid(), pid);
    }
    return 0;
}
// Thread creation với pthread
#include <stdio.h>
#include <pthread.h>
 
void* thread_function(void* arg) {
    printf("Thread ID: %lu\n", pthread_self());
    return NULL;
}
 
int main() {
    pthread_t thread;
    pthread_create(&thread, NULL, thread_function, NULL);
    pthread_join(thread, NULL);
    return 0;
}

2. Fork() hoạt động như thế nào?

Đáp án:

fork() tạo child process bằng cách copy parent process:

  • Return value: 0 (child), child PID (parent), -1 (error)
  • Copy-on-Write (COW): Memory chỉ copy khi thay đổi (optimize)
  • Shared: Open file descriptors
  • Different: PID, PPID, memory locks, signals
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
 
int main() {
    int value = 10;
    
    pid_t pid = fork();
    
    if (pid < 0) {
        perror("fork failed");
        return 1;
    }
    
    if (pid == 0) {
        // Child process
        value = 20;  // Chỉ thay đổi trong child
        printf("Child: value = %d\n", value);
    } else {
        // Parent process
        wait(NULL);  // Đợi child kết thúc
        printf("Parent: value = %d\n", value);  // Vẫn = 10
    }
    
    return 0;
}

3. Zombie process và Orphan process là gì?

Đáp án:

Zombie Process:

  • Process đã kết thúc nhưng parent chưa gọi wait()
  • Vẫn giữ entry trong process table
  • PID không được release
// Tạo zombie process
int main() {
    if (fork() == 0) {
        // Child exits immediately
        printf("Child exiting\n");
        exit(0);
    }
    
    // Parent không gọi wait()
    sleep(30);  // Child trở thành zombie
    return 0;
}

Orphan Process:

  • Process mà parent đã kết thúc trước
  • Được adopt bởi init process (PID 1)
  • Không phải vấn đề như zombie
// Tạo orphan process
int main() {
    if (fork() == 0) {
        // Child
        sleep(30);  // Parent sẽ kết thúc trước
        printf("Child PPID: %d\n", getppid());  // = 1 (init)
        exit(0);
    }
    
    // Parent exits immediately
    printf("Parent exiting\n");
    return 0;
}

Phần 2: Inter-Process Communication (IPC)

4. Các phương pháp IPC trong Linux?

Đáp án:

  1. Pipe: One-way, parent-child only
  2. Named Pipe (FIFO): One-way, unrelated processes
  3. Message Queue: Message-based, asynchronous
  4. Shared Memory: Fastest, requires synchronization
  5. Semaphore: Synchronization primitive
  6. Socket: Network, bi-directional
  7. Signal: Asynchronous notification

Ví dụ Pipe:

#include <stdio.h>
#include <unistd.h>
#include <string.h>
 
int main() {
    int pipefd[2];
    char buffer[100];
    
    pipe(pipefd);  // [0] = read, [1] = write
    
    if (fork() == 0) {
        // Child: writer
        close(pipefd[0]);
        write(pipefd[1], "Hello from child", 16);
        close(pipefd[1]);
    } else {
        // Parent: reader
        close(pipefd[1]);
        read(pipefd[0], buffer, sizeof(buffer));
        printf("Parent received: %s\n", buffer);
        close(pipefd[0]);
    }
    
    return 0;
}

Ví dụ Shared Memory:

#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdio.h>
#include <unistd.h>
#include <string.h>
 
int main() {
    const char *name = "/myshm";
    int shm_fd = shm_open(name, O_CREAT | O_RDWR, 0666);
    ftruncate(shm_fd, 4096);
    
    void *ptr = mmap(0, 4096, PROT_WRITE, MAP_SHARED, shm_fd, 0);
    
    if (fork() == 0) {
        // Child: write
        sprintf(ptr, "Hello from child");
        exit(0);
    } else {
        // Parent: read
        wait(NULL);
        printf("Parent read: %s\n", (char *)ptr);
        shm_unlink(name);
    }
    
    return 0;
}

5. Mutex và Semaphore khác nhau như thế nào?

Đáp án:

FeatureMutexSemaphore
PurposeMutual exclusionSignaling, counting
OwnershipThread lock phải unlockBất kỳ thread nào unlock được
CountBinary (0 hoặc 1)Counting (0 đến N)
Use caseProtect critical sectionResource pool, producer-consumer

Mutex example:

#include <pthread.h>
#include <stdio.h>
 
pthread_mutex_t lock = PTHREAD_MUTEX_INITIALIZER;
int counter = 0;
 
void* increment(void* arg) {
    for (int i = 0; i < 1000000; i++) {
        pthread_mutex_lock(&lock);
        counter++;  // Critical section
        pthread_mutex_unlock(&lock);
    }
    return NULL;
}
 
int main() {
    pthread_t t1, t2;
    pthread_create(&t1, NULL, increment, NULL);
    pthread_create(&t2, NULL, increment, NULL);
    pthread_join(t1, NULL);
    pthread_join(t2, NULL);
    printf("Counter: %d\n", counter);  // = 2000000
    return 0;
}

Semaphore example (Producer-Consumer):

#include <semaphore.h>
#include <pthread.h>
#include <stdio.h>
 
#define BUFFER_SIZE 5
int buffer[BUFFER_SIZE];
int in = 0, out = 0;
 
sem_t empty, full;
pthread_mutex_t mutex;
 
void* producer(void* arg) {
    for (int i = 0; i < 10; i++) {
        sem_wait(&empty);
        pthread_mutex_lock(&mutex);
        
        buffer[in] = i;
        in = (in + 1) % BUFFER_SIZE;
        printf("Produced: %d\n", i);
        
        pthread_mutex_unlock(&mutex);
        sem_post(&full);
    }
    return NULL;
}
 
void* consumer(void* arg) {
    for (int i = 0; i < 10; i++) {
        sem_wait(&full);
        pthread_mutex_lock(&mutex);
        
        int item = buffer[out];
        out = (out + 1) % BUFFER_SIZE;
        printf("Consumed: %d\n", item);
        
        pthread_mutex_unlock(&mutex);
        sem_post(&empty);
    }
    return NULL;
}
 
int main() {
    sem_init(&empty, 0, BUFFER_SIZE);
    sem_init(&full, 0, 0);
    pthread_mutex_init(&mutex, NULL);
    
    pthread_t prod, cons;
    pthread_create(&prod, NULL, producer, NULL);
    pthread_create(&cons, NULL, consumer, NULL);
    
    pthread_join(prod, NULL);
    pthread_join(cons, NULL);
    
    return 0;
}

Phần 3: Memory Management

6. Virtual memory là gì? Tại sao quan trọng?

Đáp án:

Virtual Memory:

  • Mỗi process có address space riêng (isolated)
  • MMU (Memory Management Unit) map virtual → physical address
  • Cho phép processes dùng memory > RAM (swap to disk)

Lợi ích:

  • Isolation: Process không truy cập memory của nhau
  • Large address space: 64-bit = 16 exabytes
  • Memory protection: Kernel vs User space
  • Efficient sharing: Shared libraries

Page Fault:

  • Truy cập page chưa load vào RAM
  • OS load từ disk vào RAM
  • Expensive operation (milliseconds)

7. Stack overflow và heap overflow là gì?

Đáp án:

Stack Overflow:

  • Stack vượt quá giới hạn (thường ~8MB trên Linux)
  • Nguyên nhân: Deep recursion, large local arrays
  • Kết quả: Segmentation fault
// ❌ Stack overflow
void recursive() {
    int array[10000];  // Large local variable
    recursive();       // Infinite recursion
}

Heap Overflow:

  • Ghi vượt quá buffer được allocate trên heap
  • Nguyên nhân: Buffer overflow bug
  • Kết quả: Corruption, security vulnerability
// ❌ Heap overflow
char *buffer = malloc(10);
strcpy(buffer, "This is a very long string");  // Buffer overflow!

Cách tránh:

// ✅ Stack: Dùng heap cho data lớn
int *array = malloc(10000 * sizeof(int));
 
// ✅ Heap: Check size
char *buffer = malloc(10);
strncpy(buffer, source, 9);  // Leave room for null terminator
buffer[9] = '\0';

Phần 4: File System và I/O

Đáp án:

FeatureHard LinkSymbolic Link
Points toInode (physical)File path (logical)
Across filesystems❌ No✅ Yes
Directories❌ No✅ Yes
Original deletedStill worksBroken link
InodeSameDifferent
# Hard link
ln original.txt hardlink.txt
ls -li  # Cùng inode number
 
# Symbolic link
ln -s original.txt symlink.txt
ls -li  # Khác inode number

9. select() vs poll() vs epoll()?

Đáp án:

Featureselect()poll()epoll()
Max FDs1024 (FD_SETSIZE)UnlimitedUnlimited
PerformanceO(n)O(n)O(1)
Modify setRebuildRebuildAdd/remove
Best forFew FDsMediumMany FDs (servers)

epoll example (hiệu quả nhất):

#include <sys/epoll.h>
#include <stdio.h>
#include <unistd.h>
 
int main() {
    int epfd = epoll_create1(0);
    
    struct epoll_event ev, events[10];
    ev.events = EPOLLIN;
    ev.data.fd = STDIN_FILENO;
    
    epoll_ctl(epfd, EPOLL_CTL_ADD, STDIN_FILENO, &ev);
    
    while (1) {
        int nfds = epoll_wait(epfd, events, 10, -1);
        
        for (int i = 0; i < nfds; i++) {
            if (events[i].data.fd == STDIN_FILENO) {
                char buf[100];
                read(STDIN_FILENO, buf, sizeof(buf));
                printf("Read: %s", buf);
            }
        }
    }
    
    return 0;
}

Phần 5: Shell và Scripting

10. Bash script: $? và $$ là gì?

Đáp án:

#!/bin/bash
 
# $$ - PID của shell hiện tại
echo "Current shell PID: $$"
 
# $? - Exit status của lệnh trước đó
ls /nonexistent
echo "Exit status: $?"  # 2 (error)
 
ls /tmp
echo "Exit status: $?"  # 0 (success)
 
# $! - PID của background process cuối cùng
sleep 100 &
echo "Background PID: $!"
 
# $0 - Tên script
# $1, $2, ... - Arguments
# $# - Số lượng arguments
# $@ - Tất cả arguments

Phần 6: Performance và Debugging

11. Làm sao để debug memory leak trong Linux?

Đáp án:

Tools:

  1. Valgrind (powerful, slow):
valgrind --leak-check=full --show-leak-kinds=all ./program
  1. AddressSanitizer (fast, compile-time):
gcc -fsanitize=address -g program.c -o program
./program
  1. mtrace (glibc built-in):
#include <mcheck.h>
 
int main() {
    mtrace();  // Start tracing
    
    char *leak = malloc(100);
    // Forgot free(leak)
    
    muntrace();  // Stop tracing
    return 0;
}
gcc -g program.c -o program
MALLOC_TRACE=leak.txt ./program
mtrace ./program leak.txt

12. Các Linux commands để monitor system?

Đáp án:

# CPU usage
top         # Interactive
htop        # Better UI
mpstat      # Multi-processor stats
 
# Memory
free -h     # RAM usage
vmstat      # Virtual memory stats
 
# Disk I/O
iostat      # I/O statistics
iotop       # I/O by process
 
# Network
netstat -tulpn   # Network connections
ss -tulpn        # Socket statistics (faster)
iftop            # Network bandwidth
 
# Process
ps aux           # All processes
pstree           # Process tree
lsof             # Open files
 
# System logs
dmesg            # Kernel messages
journalctl       # systemd logs
tail -f /var/log/syslog  # Real-time logs

Kết luận

Để thành công trong phỏng vấn Linux/OS:

  • Hands-on experience: Không chỉ lý thuyết, phải code thực tế
  • System programming: Viết programs dùng system calls
  • Debug skills: Biết dùng gdb, strace, valgrind
  • Shell scripting: Automation và system administration
  • Read man pages: man 2 fork, man 3 pthread_create

Tài liệu tham khảo

9 min read
InterviewLinuxOperating SystemSystem ProgrammingDevOps

Bài viết liên quan