Linux Process Management: fork, exec, wait
Tìm hiểu chi tiết về quản lý tiến trình trong Linux: fork() để tạo process mới, exec() để thay thế process, và wait() để đồng bộ hóa
Linux Process Management: fork, exec, wait
Process management là một trong những concepts quan trọng nhất khi làm việc với Linux system programming. Ba system calls cốt lõi là fork(), exec(), và wait().
Process là gì?
Process là một instance đang chạy của một program. Mỗi process có:
- Process ID (PID): Identifier duy nhất
- Parent Process ID (PPID): PID của process cha
- Memory space: Code, data, heap, stack
- File descriptors: Open files, sockets, pipes
- Environment variables
fork() - Tạo Process Mới
fork() tạo ra một child process là bản sao hoàn toàn của parent process.
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
int main() {
pid_t pid;
printf("Before fork\n");
pid = fork(); // Tạo child process
if (pid < 0) {
// Fork failed
perror("fork failed");
return 1;
}
else if (pid == 0) {
// Child process
printf("Child process: PID = %d, Parent PID = %d\n",
getpid(), getppid());
}
else {
// Parent process
printf("Parent process: PID = %d, Child PID = %d\n",
getpid(), pid);
}
printf("After fork\n");
return 0;
}Output:
Before fork
Parent process: PID = 1234, Child PID = 1235
After fork
Child process: PID = 1235, Parent PID = 1234
After fork
Điều gì xảy ra khi fork()?
- OS tạo child process với PID mới
- Child copy toàn bộ memory của parent (COW - Copy on Write)
- Child và parent chạy độc lập, không chia sẻ memory
fork()returns:0trong child processchild PIDtrong parent process-1nếu lỗi
exec() Family - Thay Thế Process
exec() family thay thế process hiện tại bằng một program khác.
#include <stdio.h>
#include <unistd.h>
int main() {
printf("Before exec\n");
// Thay thế process bằng /bin/ls
execl("/bin/ls", "ls", "-l", NULL);
// Code này chỉ chạy nếu exec failed
perror("exec failed");
return 1;
}Các variant của exec():
// execl - list arguments
execl("/bin/ls", "ls", "-l", "-a", NULL);
// execv - array of arguments
char *args[] = {"ls", "-l", "-a", NULL};
execv("/bin/ls", args);
// execlp - search in PATH
execlp("ls", "ls", "-l", NULL);
// execvp - search in PATH, array args
char *args[] = {"ls", "-l", NULL};
execvp("ls", args);wait() - Đồng Bộ Parent và Child
wait() làm parent process đợi child process kết thúc.
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// Child process
printf("Child: Starting work...\n");
sleep(2);
printf("Child: Work done!\n");
return 42; // Exit code
}
else {
// Parent process
int status;
printf("Parent: Waiting for child...\n");
pid_t child_pid = wait(&status);
if (WIFEXITED(status)) {
printf("Parent: Child %d exited with code %d\n",
child_pid, WEXITSTATUS(status));
}
}
return 0;
}Output:
Parent: Waiting for child...
Child: Starting work...
Child: Work done!
Parent: Child 1235 exited with code 42
Kết Hợp fork + exec + wait
Pattern phổ biến nhất: fork child để chạy program khác
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// Child: Execute ls command
execl("/bin/ls", "ls", "-l", NULL);
perror("exec failed");
return 1;
}
else {
// Parent: Wait for child
int status;
wait(&status);
printf("Child completed\n");
}
return 0;
}Zombie Processes
Zombie process xảy ra khi child kết thúc nhưng parent không gọi wait().
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
printf("Child exiting\n");
return 0; // Child exits
}
else {
printf("Parent sleeping (not calling wait)\n");
sleep(30); // Child becomes zombie!
}
return 0;
}Kiểm tra zombie processes:
ps aux | grep Z # Z = zombie stateOrphan Processes
Orphan process xảy ra khi parent kết thúc trước child.
#include <stdio.h>
#include <unistd.h>
int main() {
pid_t pid = fork();
if (pid == 0) {
// Child
sleep(5);
printf("Child PID: %d, Parent PID: %d\n",
getpid(), getppid()); // Parent PID will be 1 (init)
}
else {
// Parent exits immediately
printf("Parent exiting\n");
}
return 0;
}Child sẽ được adopt bởi init process (PID = 1).
waitpid() - Advanced Wait
waitpid() cho phép wait specific child hoặc non-blocking wait.
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main() {
pid_t pid1 = fork();
pid_t pid2 = fork();
if (pid1 == 0) {
sleep(1);
return 1;
}
else if (pid2 == 0) {
sleep(2);
return 2;
}
else {
// Wait for specific child
int status;
waitpid(pid2, &status, 0); // Wait only for pid2
printf("pid2 finished\n");
// Non-blocking wait
while (waitpid(-1, &status, WNOHANG) == 0) {
printf("Still waiting...\n");
sleep(1);
}
}
return 0;
}Practical Example: Simple Shell
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
int main() {
char command[256];
while (1) {
printf("myshell> ");
if (fgets(command, sizeof(command), stdin) == NULL) break;
// Remove newline
command[strcspn(command, "\n")] = 0;
if (strcmp(command, "exit") == 0) break;
pid_t pid = fork();
if (pid == 0) {
// Child: execute command
char *args[] = {"/bin/sh", "-c", command, NULL};
execv("/bin/sh", args);
perror("exec failed");
exit(1);
}
else {
// Parent: wait for child
wait(NULL);
}
}
return 0;
}Best Practices
- Always check fork() return value
- Call wait() to prevent zombies
- Check exec() errors (though they replace the process)
- Handle signals for robust process management
- Use waitpid() for more control
Kết luận
Fork, exec, và wait là building blocks của process management trong Linux. Master ba concepts này giúp bạn:
- Hiểu cách shell hoạt động
- Viết được servers và daemons
- Debug process-related issues
- Làm việc hiệu quả với Linux system programming
Practice là cách tốt nhất để hiểu sâu về process management!
Bài viết liên quan
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.
Câu hỏi phỏng vấn Linux và Operating System cơ bản đến nâng cao
Chuẩn bị cho phỏng vấn vị trí Linux Developer/DevOps/System Engineer với các câu hỏi từ cơ bản đến nâng cao về Linux và Operating System.
Tối ưu hiệu suất với Compiler Optimization
Compiler optimization có thể cải thiện hiệu suất code lên đến 300%. Tìm hiểu các optimization levels và khi nào nên sử dụng chúng.