Tiến trình là gì

Tiến trình – process là một khái niệm cơ bản trong bất kể một hệ quản lý và điều hành nào. Một tiến trình hoàn toàn có thể được định nghĩa là một thực thể chương trình đang được chạy trong mạng lưới hệ thống. Một web server chạy trong thiết bị là một tiến trình, hoặc một chương trình soạn thảo văn bản đang chạy trong thiết bị cũng là một tiến trình .

Một ví dụ khác: Cùng là một chương trình soạn thảo (ví dụ chương trình quen thuộc vim), người dùng mở 16 chương trình vim để thao tác với 16 file khác nhau, chúng ta cho 16 tiến trình chạy trong hệ thống.

Bài này sẽ giới thiệu về tiến trình (process), tiến trình nhẹ (lightweight process) và luồng (thread) trong Linux.

Tiến trình, Tiến trình nhẹ và Luồng

Tiến trình – Process

Như đã đề cập ở trên, tiến trình là một thực thể chương trình đang được thực thi. Nó là một tập hợp những cấu trúc tài liệu miêu tả rất đầy đủ quy trình một chương trình được chạy trong mạng lưới hệ thống .
Một tiến trình cũng trải qua những quy trình như con người : Nó được sinh ra, nó hoàn toàn có thể có một cuộc sống ít ý nghĩa hoặc nhiều ý nghĩa, nó hoàn toàn có thể sinh ra một hoặc nhiều tiến trình con, và thậm chí còn, nó có hoàn toàn có thể chết đi. Điều độc lạ nhỏ duy nhất là : tiến trình không có giới tính. Mỗi tiến trình chỉ có một tiến trình cha ( hoặc hoàn toàn có thể gọi là mẹ, ở trong khóa học sẽ thống nhất gọi là cha J ) duy nhất .
Dưới góc nhìn của kernel, tiến trình là một thực thể chiếm hữu tài nguyên của mạng lưới hệ thống ( Thời gian sử dụng CPU, bộ nhớ, … )
Khi một tiến trình con được tạo ra, nó hầu hết giống hệt như tiến trình cha. Tiến trình con sao chép hàng loạt khoảng trống địa chỉ, thực thi cùng một mã nguồn như tiến trình cha, và khởi đầu chạy tiếp những mã nguồn riêng cho tiến trình con từ thời gian gọi hàm tạo tiến trình mới .
Mặc dù tiến trình cha và tiến trình con cùng san sẻ sử dụng phần mã nguồn của chương trình, nhưng chúng lại có phần tài liệu tách biệt nhau ( stack và heap ). Điều này có nghĩa là, những sự biến hóa tài liệu của tiến trình con không tác động ảnh hưởng đến tài liệu trong tiến trình cha .
Một ví dụ để làm rõ khái niệm này : ​ ​ ​ ​

#include   
#include   
#include   
#include   
#include   
#include   
  
int main()  
{  
    pid_t child_pid;  
    int counter = 2;  
  
    printf("Gia tri khoi tao cua counter = %d\n", counter);  
  
    child_pid = fork();  
    if(child_pid >= 0)  
    {  
        if(0 == child_pid)  
        {  
            counter ++;  
            sleep(2);  
            printf("Day la tien trinh con, gia tri counter sau khi tang = %d\n", counter);  
            while(1);  
        }  
        else  
        {  
            counter ++;  
            sleep(5);  
            printf("Day la tien trinh cha, gia tri counter sau khi tang = %d\n", counter);  
            while(1);  
        }  
    }  
    else  
    {  
        printf("Tao process con khong thanh cong\n");  
    }  
      
    return 0;  
}  

Kết quả chương trình khi chạy :

Trong ví dụ trên, tiến trình con được tạo ra từ tiến trình cha. Bằng câu lệnh ps trong linux, ta sẽ thấy có 2 tiến trình tên là test được chạy trong hệ thống.

Tiến trình con được thừa kế biến counter từ tiến trình cha. Tuy nhiên sự biến hóa biến counter ở cả 2 tiến trình cha và con đều không tác động ảnh hưởng tới nhau .

Luồng – User threads

Luồng – User thread ( phân biệt với kernel thread ) là một luồng thực thi mã nguồn trong một tiến trình. Một tiến trình gồm có nhiều luồng thực thi như thế được gọi là một tiến trình đa luồng – multithreaded process .
Các threads trong một tiến trình chỉ có ý nghĩa đối trong tiến trình đó ( không hề nhìn thấy từ bên ngoài ), và chúng san sẻ tài liệu sử dụng trong tiến trình. Chính do đó, khác với tiến trình cha – con, sự đổi khác tài liệu ở luồng này sẽ tác động ảnh hưởng tới những luồng còn lại .
Một ví dụ về luồng : Một ứng dụng chơi cờ đơn thuần khi chạy cũng là một tiến trình đa luồng. Khi ứng dụng chạy, ứng dụng sẽ phải vừa chờ đón những nước đi của người chơi để bộc lộ trên hình ảnh ( đây sẽ là một luồng ), vừa phải thống kê giám sát những nước đi tiếp theo của máy để thắng lợi được người chơi ( đây là luồng thứ 2 ). Hai luồng này san sẻ tài liệu chung là nước đi của người chơi, nhưng vẫn hoạt động giải trí song song và thực thi 2 việc làm khác nhau .

Tuy nhiên, dưới góc nhìn của kernel, một tiến trình đa luồng cũng giống hệt một tiến trình bình thường khác. Ví dụ chúng ta đang chạy 3 tiến trình, 2 tiến trình 1 luồng, 1 tiến trình 3 luồng. Khi kernel lập lịch chạy cho 3 tiến trình đó, tiến trình có 3 luồng vẫn chỉ có thể chiếm 1/3 thời gian hoạt động của CPU, giống như 2 tiến trình còn lại. Điều này có nghĩa là, việc tạo ra, quản lý và lập lịch cho các luồng trong một tiến trình sẽ được thực hiện hoàn toàn ở user space.

Ngày nay, hầu hết những tiến trình đa luồng được viết bởi thư viện chuẩn pthread ( POSIX thread ) .

Một ví dụ về chương trình đa luồng:

#include   
#include   
#include   
#include   
  
pthread_mutex_t lock= PTHREAD_MUTEX_INITIALIZER;  
int counter = 2;  
  
void* thread1()  
{  
    sleep(1);  
    pthread_mutex_lock(&lock);  
    counter ++;  
    printf("Day la thread 1: gia tri counter = %d\n", counter);  
    pthread_mutex_unlock(&lock);  
    while(1);  
}  
  
void* thread2()  
{  
    sleep(3);  
    pthread_mutex_lock(&lock);  
    counter ++;  
    printf("Day la thread 2: gia tri counter = %d\n", counter);  
    pthread_mutex_unlock(&lock);  
    while(1);  
}  
  
int main()  
{  
        pthread_t th1, th2;  
  
        pthread_create(&th1,NULL,thread1,NULL);  
        pthread_create(&th2,NULL,thread2,NULL);  
  
        pthread_join(th1,NULL);  
        pthread_join(th2,NULL);  
        return 0;  
}  

Trong ví dụ này, tiến trình test tạo ra 2 luồng .
Hai luồng cùng sử dụng chung biến counter. Khi có sự biến hóa giá trị của biến counter tại một luồng, thì luồng còn lại cũng sẽ “ nhận ra ” được sự biến hóa đó .
Kết quả khi chạy chương trình :

Tiến trình nhẹ – Lightweight process

Như đề cập ở phần trên, tiến trình đa luồng được kernel nhìn giống hệt như những tiến trình đơn luồng khác, nên kernel sẽ cung ứng những tài nguyên của mạng lưới hệ thống ( thời hạn sử dụng CPU ) giống nhau giữa những tiến trình đa luồng và tiến trình đơn luồng. Điều nay khiến cho những tiến trình đa luồng trở nên không thực sự tốt .
Trở lại ví dụ ứng dụng chơi cờ đề cập ở phần trên. Trong khi luồng thứ nhất ( chờ đón những nước đi của người chơi để biểu lộ trên hình ảnh ) được triển khai, thì luồng thứ hai ( đo lường và thống kê những nước đi tiếp theo của máy ) nên được chạy một cách liên tục, tận dụng khoảng chừng thời hạn tâm lý của người chơi để đưa ra những nước đi tốt nhất .
Tuy nhiên, giả sử luồng thứ nhất thực thi một blocking system call nào đó, thì luồng thứ hai cũng sẽ bị block lại theo. Lý do do tại kernel coi một tiến trình đa luồng giống như một tiến trình đơn luồng thông thường khác, nên khi luồng thứ nhất thực thi một blocking system call, kernel sẽ coi đó là hành vi của cả tiến trình và block cả tiến trình đó lại. Do đó, tiến trình đa luồng trở nên không tối ưu cho ứng dụng .
Linux sử dụng tiến trình nhẹ – lightweight process để cung ứng phương pháp thực thi tối ưu hơn cho một ứng dụng đa luồng. Về cơ bản, hai tiến trình nhẹ hoàn toàn có thể san sẻ và sử dụng cùng một tài nguyên của hệ thông như khoảng trống địa chỉ, files … Khi một tiến trình nhẹ biến hóa tài nguyên dùng chung, tiến trình nhẹ còn lại hoàn toàn có thể nhìn thấy sự biến hóa đó .

Tiến trình nhẹ là một tiến trình, nhưng thay vì có không gian địa chỉ, tài nguyên riêng biệt như khái niệm tiến trình bình thường, chúng có thể chia sẻ sử dụng chung tài nguyên với nhau như khái niệm của luồng trên user space.

Điểm độc lạ giữa tiến trình nhẹ và luồng là việc kernel nhìn tiến trình nhẹ như một tiến trình thông thường, nên kernel sẽ phân phối tài nguyên mạng lưới hệ thống, lập lịch thao tác cho tiến trình nhẹ. Nên nếu mạng lưới hệ thống đang chạy hai tiến trình, một tiền trình đơn luồng, một tiến trình đa luồng gồm hai tiến trình nhẹ, thì tiến trình đa luồng đó sẽ chiếm 2/3 thời hạn sử dụng CPU .
Một cách cơ bản để tạo ra một ứng dụng đa luồng là gán mỗi một luồng với một tiến trình nhẹ. Có nhiều thư viện tạo luồng sử dụng phương pháp này ví dụ như : LinuxThreads, Native POSIX Thread Library ( NPTL ) …
Quay trở lại lần nữa với ví dụ về ứng dụng chơi cờ : Nếu gán hai luồng của ứng dụng vào hai tiến trình nhẹ, thì dùng luồng một ( chờ đón những nước đi của người chơi để bộc lộ trên hình ảnh ) có thực thi một blocking system call, thì luồng thứ hai ( giám sát những nước đi tiếp theo của máy ) sẽ vẫn được triển khai liên tục do kernel sẽ lập lịch thao tác cho luồng thứ hai .

Hiển thị tiến trình nhẹ: Chạy lại chương trình ví dụ với luồng bên trên và dùng câu lệnh ps với tùy chọn “-fL -C”. Cột LWP hiển thị số tiến trình nhẹ mà tiến trình test đang có. Tiến trình test sẽ có 3 tiến trình nhẹ, một là tiến trình chính, và hai tiến trình nhẹ con tương ứng với hai luồng con được tạo ra (thread1 và thread2)

Mối quan hệ giữa tiến trình – luồng – tiến trình nhẹ:

Hình vẽ bên trên miêu tả mối quan hệ giữa tiến trình – luồng – tiến trình nhẹ. Một tiến trình chạy trong mạng lưới hệ thống hoàn toàn có thể gồm nhiều luồng ( user threads ), một luồng này được link trực tiếp 1-1 với một tiến trình nhẹ, hoặc nhiều luồng hoàn toàn có thể link nhiều – 1 với một tiến trình nhẹ. Việc thực thi, lập lịch cho những tiến trình nhẹ được triển khai bởi kernel .

Kết luận

Sau bài này, những bạn đã có những khái niệm về tiến trình, luồng và tiến trình nhẹ trong Linux. Bài tiếp theo sẽ khám phá sâu hơn những trạng thái của tiến trình, và làm cách nào Kernel quản trị những tiến trình trong mạng lưới hệ thống .

Trả lời

Email của bạn sẽ không được hiển thị công khai. Các trường bắt buộc được đánh dấu *