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
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:
| Feature | Process | Thread |
|---|---|---|
| Definition | Independent program instance | Lightweight process, thuộc 1 process |
| Memory | Riêng biệt (isolated) | Shared memory space |
| Communication | IPC (Inter-Process Communication) | Shared variables |
| Creation | Chậm (fork) | Nhanh (pthread_create) |
| Overhead | Cao | Thấ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:
- Pipe: One-way, parent-child only
- Named Pipe (FIFO): One-way, unrelated processes
- Message Queue: Message-based, asynchronous
- Shared Memory: Fastest, requires synchronization
- Semaphore: Synchronization primitive
- Socket: Network, bi-directional
- 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:
| Feature | Mutex | Semaphore |
|---|---|---|
| Purpose | Mutual exclusion | Signaling, counting |
| Ownership | Thread lock phải unlock | Bất kỳ thread nào unlock được |
| Count | Binary (0 hoặc 1) | Counting (0 đến N) |
| Use case | Protect critical section | Resource 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
8. Hard link và Symbolic link khác nhau thế nào?
Đáp án:
| Feature | Hard Link | Symbolic Link |
|---|---|---|
| Points to | Inode (physical) | File path (logical) |
| Across filesystems | ❌ No | ✅ Yes |
| Directories | ❌ No | ✅ Yes |
| Original deleted | Still works | Broken link |
| Inode | Same | Different |
# 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 number9. select() vs poll() vs epoll()?
Đáp án:
| Feature | select() | poll() | epoll() |
|---|---|---|---|
| Max FDs | 1024 (FD_SETSIZE) | Unlimited | Unlimited |
| Performance | O(n) | O(n) | O(1) |
| Modify set | Rebuild | Rebuild | Add/remove |
| Best for | Few FDs | Medium | Many 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ả argumentsPhần 6: Performance và Debugging
11. Làm sao để debug memory leak trong Linux?
Đáp án:
Tools:
- Valgrind (powerful, slow):
valgrind --leak-check=full --show-leak-kinds=all ./program- AddressSanitizer (fast, compile-time):
gcc -fsanitize=address -g program.c -o program
./program- 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.txt12. 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 logsKế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
Bài viết liên quan
Top 20 câu hỏi phỏng vấn C/C++ cho Embedded Engineer
Chuẩn bị cho buổi phỏng vấn vị trí Embedded Engineer với 20 câu hỏi C/C++ phổ biến nhất, kèm theo đáp án chi tiết và ví dụ minh họa.
Linux Process Management: fork, exec, wait
Process management là core concept của Linux. Hiểu rõ fork(), exec(), và wait() giúp bạn master được system programming trên Linux.
systemd: Quản lý Services trên Linux
systemd là init system mặc định trên hầu hết các distros Linux hiện đại. Học cách tạo và quản lý services với systemd một cách chuyên nghiệp.