Giải thích về lệnh gọi hệ thống Fork, execute, wait và exit trong Linux

Tác giả Network Engineer, T.Một 11, 2022, 05:58:56 CHIỀU

« Chủ đề trước - Chủ đề tiếp »

0 Thành viên và 1 Khách đang xem chủ đề.

Giải thích về lệnh gọi hệ thống Fork, execute, wait và exit trong Linux


Chuỗi các lệnh và dữ liệu có thể được thực hiện một lần, nhiều lần hoặc đồng thời được gọi là chương trình. Và quá trình này là việc thực hiện các chương trình như vậy. Vì vậy các tiến trình đó có thể chạy nhiều chương trình. Trong cùng một quá trình, hệ điều hành có thể tải các chương trình khác nhau. Các trạng thái tiến trình được sử dụng lại như thư mục hiện tại, đặc quyền, xử lý tập tin, v.v. được kế thừa bởi các chương trình mới. Những thứ như vậy được thực hiện ở cùng một cấp với các cuộc gọi tổng hợp như fork(), exec(), wait() và exit().

Trong bài viết này, chúng ta sẽ thảo luận chi tiết về Linux syscalls fork(), exec(), wait() và exit() với các ví dụ và các trường hợp sử dụng.

1. fork()

Fork() là một trong những syscalls rất đặc biệt và hữu ích trong các hệ thống Linux/Unix. Nó được sử dụng bởi các tiến trình để tạo ra các tiến trình là bản sao của chính chúng. Với sự trợ giúp của các lệnh gọi hệ thống như vậy, tiến trình con có thể được tạo bởi tiến trình mẹ. Cho đến khi tiến trình con được thực thi hoàn toàn, tiến trình mẹ sẽ bị tạm dừng.

Một số điểm quan trọng trên fork() như sau.

  • Cha mẹ sẽ nhận được ID tiến trình con có giá trị khác 0.
  • Giá trị 0 được trả lại cho con.
  • Nếu có bất kỳ lỗi hệ thống hoặc phần cứng nào trong khi tạo con, -1 được trả về fork().
  • Với ID tiến trình duy nhất được tiến trình con thu được, nó không khớp với ID của bất kỳ nhóm tiến trình hiện có nào.

Để nói rõ hơn về fork(), hãy lấy một ví dụ làm rõ khái niệm fork().

Mã nguồn [Chọn]
$ sudo vim fork.c
Đây là mã để sao chép và dán vào nó:

Mã nguồn [Chọn]
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>

int main(int argc, char **argv)
{
pid_t pid;
pid = fork();
if(pid==0)
{
printf("It is the child process and pid is %d\n",getpid());
exit(0);
}
else if(pid > 0)
{
printf("It is the parent process and pid is %d\n",getpid());
}
else
{
printf("Error while forking\n");
exit(EXIT_FAILURE);
}
return 0;
}


Đầu ra:

Mã nguồn [Chọn]
$ make fork

Và chạy script, chúng ta sẽ nhận được kết quả như ảnh chụp màn hình bên dưới.

Mã nguồn [Chọn]
$ ./fork

2. exec()

exec() là một lệnh gọi hệ thống chạy bằng cách thay thế hình ảnh tiến trình hiện tại bằng hình ảnh tiến trình mới. Tuy nhiên, tiến trình gốc vẫn là một tiến trình mới nhưng tiến trình mới sẽ thay thế dữ liệu head, dữ liệu ngăn xếp, v.v. Nó chạy chương trình từ điểm vào bằng cách tải chương trình vào vùng tiến trình hiện tại.

Để giải thích kỹ hơn, chúng ta hãy lấy một ví dụ như hình dưới đây.

Mã nguồn [Chọn]
$ sudo vim exec.c
Và đây là mã:

Mã nguồn [Chọn]
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/wait.h>

main(void) {
  pid_t pid = 0;
  int status;
  pid = fork();

if (pid == 0) {
  printf("I am the child.");
  execl("/bin/ls", "ls", "-l", "/home/ubuntu/", (char *) 0);
  perror("In exec(): ");
}

if (pid > 0) {
  printf("I am the parent, and the child is %d.\n", pid);
  pid = wait(&status);
  printf("End of process %d: ", pid);

  if (WIFEXITED(status)) {
    printf("The process ended with exit(%d).\n", WEXITSTATUS(status));
  }

  if (WIFSIGNALED(status)) {
    printf("The process ended with kill -%d.\n", WTERMSIG(status));
  }
}

if (pid < 0) {
  perror("In fork():");
}

exit(0);
}


Đầu ra:

Mã nguồn [Chọn]
$ make exec

Và chạy script, chúng ta sẽ nhận được kết quả như ảnh chụp màn hình bên dưới.

Mã nguồn [Chọn]
$ ./exec

3. wait()

Như trong trường hợp fork, các tiến trình con được tạo và được thực thi nhưng tiến trình mẹ bị tạm dừng cho đến khi tiến trình con thực thi. Trong trường hợp này, lệnh gọi hệ thống wait() được kích hoạt tự động do tiến trình mẹ bị tạm dừng. Sau khi tiến trình con kết thúc quá trình thực thi, tiến trình mẹ lại giành được quyền kiểm soát.

Để giải thích rõ hơn về hàm wait(), hãy lấy một ví dụ làm rõ lệnh gọi hệ thống wait().

Mã nguồn [Chọn]
$ sudo vim wait.c
Đây là ví dụ mã:

Mã nguồn [Chọn]
#include<stdio.h> // printf()
#include<stdlib.h> // exit()
#include<sys/types.h> // pid_t
#include<sys/wait.h> // wait()
#include<unistd.h> // fork

int main(int argc, char **argv)
{

pid_t pid;
pid = fork();

if(pid==0)

{
printf("It is the child process and pid is %d\n",getpid());

int i=0;
for(i=0;i<8;i++)
{
printf("%d\n",i);
}

exit(0);

}
else if(pid > 0)
{

printf("It is the parent process and pid is %d\n",getpid());

int status;
wait(&status);
printf("Child is reaped\n");
}

else
{

printf("Error in forking..\n");
exit(EXIT_FAILURE);

}

return 0;

}


Đầu ra:

Mã nguồn [Chọn]
$ make wait

Và chạy script, chúng ta sẽ nhận được kết quả như ảnh chụp màn hình bên dưới.

Mã nguồn [Chọn]
$ ./wait

4. exit()

exit() là một hàm như vậy hoặc một trong các lệnh gọi hệ thống được sử dụng để kết thúc quá trình. Lệnh gọi hệ thống này xác định rằng việc thực thi luồng đã hoàn tất, đặc biệt là trong trường hợp môi trường đa luồng. Để tham khảo trong tương lai, trạng thái của quá trình được ghi lại.

Sau khi sử dụng lệnh gọi hệ thống exit(), tất cả các tài nguyên được sử dụng trong quá trình sẽ được hệ điều hành truy xuất và sau đó kết thúc tiến trình. Lệnh gọi hệ thống Exit() tương đương với exit().

Mã nguồn [Chọn]
#include <unistd.h>
void _exit(int status);
#include <stdlib.h>
void _Exit(int status);

Bạn có thể thấy việc sử dụng hàm exit() trong các ví dụ trên về fork(), wait(). Việc sử dụng lệnh gọi hệ thống exit() được thực hiện để kết thúc tiến trình.

Trong bài này, chúng ta đã tìm hiểu chi tiết các lệnh gọi hệ thống fork(), execute(), wait() và exit() với một số ví dụ. Để biết thêm chi tiết, hãy thử chạy các chương trình bằng cách sử dụng các lệnh gọi hệ thống đó và xem kết quả.