Bài 40.326 phút đọc
exec và mẫu fork cộng exec
Sau bài này bạn sẽ làm được
- Phân biệt sáu biến thể của exec
- Giải thích vì sao exec thành công không bao giờ trả về
- Viết mẫu fork cộng exec cộng wait
- Tự cài system bằng ba lời gọi đó
fork tạo một bản sao của tiến trình hiện tại. Nhưng bạn hiếm khi muốn chạy chính chương trình đó hai lần; bạn muốn con chạy một chương trình khác. Đó là việc của exec, và ghép hai cái lại là nền tảng của mọi shell.
#exec thay chương trình
exec
Thay thế hoàn toàn chương trình đang chạy bằng một chương trình khác. PID giữ nguyên, không tạo tiến trình mới. Mã, dữ liệu, ngăn xếp, heap của chương trình cũ bị vứt hết và thay bằng của chương trình mới.
/* fork = tao mot tien trinh MOI (ban sao)
exec = THAY chuong trinh trong tien trinh HIEN TAI
Tuong tu: fork la sinh mot dua tre giong het ban.
exec la dua tre do dot ngot bien thanh mot nguoi khac,
van cung than xac (cung PID) nhung tam hon moi (chuong
trinh moi).
Ghep lai: fork ra mot con, roi con exec thanh chuong trinh khac.
Do la cach shell chay moi lenh: no khong bien MINH thanh "ls",
ma fork ra mot con roi cho con exec thanh "ls". */terminal
# exec.c goi execlp('ls', ...) -> tien trinh BIEN thanh ls
./exec_demo
Truoc exec: PID=25301 tong 8 -rw-r--r-- 1 an an 312 exec.c -rwxr-xr-x 1 an an 8912 exec_demo (dong nay KHONG in ra: exec da thay chuong trinh)
#Sáu biến thể
Sáu hàm exec, khác nhau ở ba chữ cái
#include <unistd.h>
/* l = danh sach doi so (list), ket thuc bang NULL */
execl ("/bin/ls", "ls", "-l", NULL);
execlp("ls", "ls", "-l", NULL); /* p = tim trong PATH */
execle("/bin/ls", "ls", "-l", NULL, envp); /* e = truyen moi truong */
/* v = mang doi so (vector) */
char *argv[] = { "ls", "-l", NULL };
execv ("/bin/ls", argv);
execvp("ls", argv); /* v + p */
execve("/bin/ls", argv, envp); /* syscall THAT, cac cai kia
deu goi xuong no */
perror("exec"); /* Neu chay toi day thi exec THAT BAI */| Hàm | l/v: đối số | p: tìm PATH | e: môi trường |
|---|---|---|---|
| execl | Danh sách | Không | Không |
| execlp | Danh sách | Có | Không |
| execle | Danh sách | Không | Có |
| execv | Vector | Không | Không |
| execvp | Vector | Có | Không |
| execve | Vector | Không | Có |
#Không bao giờ trả về
#Mẫu fork cộng exec
Mẫu nền tảng của mọi shell
#include <stdio.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
char *argv[] = { "ls", "-l", NULL };
pid_t pid = fork();
if (pid < 0) {
perror("fork");
return 1;
} else if (pid == 0) {
/* CON: bien thanh "ls" */
execvp(argv[0], argv);
perror("execvp"); /* chi toi day neu exec loi */
_exit(127);
} else {
/* CHA: cho con xong */
int status;
waitpid(pid, &status, 0);
if (WIFEXITED(status))
printf("Lenh thoat voi ma %d\n", WEXITSTATUS(status));
}
return 0;
}#Tự cài system
my_system: system() bằng fork + exec + wait
#include <unistd.h>
#include <sys/wait.h>
/* system("lenh") chay "lenh" qua shell /bin/sh -c "lenh".
Tu cai bang fork + exec + wait: */
int my_system(const char *lenh) {
if (lenh == NULL) return 1; /* system(NULL) hoi co shell khong */
pid_t pid = fork();
if (pid < 0) return -1;
if (pid == 0) {
/* con: chay lenh qua shell, de shell lo pipe, chuyen huong... */
execl("/bin/sh", "sh", "-c", lenh, (char *)NULL);
_exit(127);
}
int status;
while (waitpid(pid, &status, 0) < 0)
if (errno != EINTR) return -1; /* thu lai neu bi ngat */
return status;
}terminal
./my_system_demo
chay: echo xin chao xin chao ma thoat: 0
Quên _exit sau exec
if (fork() == 0) {
execvp(argv[0], argv);
perror("execvp");
/* QUEN _exit: neu exec loi, con chay tiep ma cua CHA,
fork them, wait nham... hon loan */
}
Luôn có _exit
if (fork() == 0) {
execvp(argv[0], argv);
perror("execvp");
_exit(127); /* con THOAT ngay khi exec loi */
}
Tự làm thử
- Viết chương trình dùng
execvpđể chạyls -lvà xác nhận dòng sauexeckhông in ra. - Thử cả sáu biến thể
execđể chạy cùng một lệnh. - Bỏ
_exitsauexec, cho exec một lệnh không tồn tại, và quan sát hậu quả. - Cài mẫu
forkcộngexeccộngwaitchạy một lệnh bất kỳ từ đối số dòng lệnh. - Cài
my_systemvà chứng minh lỗ hổng chèn lệnh với một đầu vào độc hại. - Chạy ba lệnh song song bằng ba lần
forkcộngexec, rồi chờ cả ba và báo mã thoát từng cái.
Trình chấm điểm tự động sẽ được bổ sung ở giai đoạn sau. Hiện tại bạn tự chạy thử trên máy.
Tóm tắt
execthay chương trình trong tiến trình hiện tại, giữ nguyên PID, không tạo tiến trình mới.- Sáu biến thể khác ở ba chữ:
l/v(danh sách/mảng),p(tìm PATH),e(truyền môi trường); shell hay dùngexecvp. execthành công không bao giờ trả về; dòng sau nó chỉ chạy khi exec lỗi, nên luôn cóperrorvà_exit.- Mẫu
forkcộngexeccộngwaitlà nền tảng của mọi shell. systemvàmy_systemdùng shell nên có lỗ hổng chèn lệnh; dùngexecvới danh sách đối số cho dữ liệu ngoài.