Nội dung của tài liệu trình bày về lý thuyết về xử lý tiến trình trong Linux, khái quát về Linux, định nghĩa của tiến trình, cách hoạt động của tiến trình, cấu trúc tiến trình, bảng thông tin tiến trình, xem thông tin của tiến trình, tạo lập tiến trình và một số bài thực hành về tạo lập tiến trình.
Bài 3: XỬ LÝ TIẾN TRÌNH TRONG LINUX I Lý Thuyết Khái quát - Một đặc điểm bật Linux khả chạy đồng thời nhiều chương trình Hệ Điều Hành xem đơn thể mã lệnh mà điều khiển tiến trình (process) Một chương trình bao gồm nhiều tiến trình kết hợp với - Đối với Hệ Điều Hành, tiến trình hoạt động chia sẻ tốc độ xử lý CPU, dùng chung vùng nhớ tài nguyên hệ thống khác Các tiến trình điều phối xoay vịng Hệ Điều Hành Một chương trình mở rộng dần ra, có lúc cần phải tách thành nhiều tiến trình để xử lý công việc độc lập với Các lệnh Linux thực tế lệnh riêng lẻ có khả kết hợp truyền liệu cho thông qua chế : đường ống pipe, chuyển hướng xuất nhập (redirect), phát sinh tín hiệu (signal), … Chúng gọi chế giao tiếp liên tiến trình (IPC – Inter Process Comunication) Đối với tiến trình, tìm hiểu cách tạo, hủy, tạm dừng tiến trình, đồng hóa tiến trình giao tiếp tiến trình với - Xây dựng ứng dụng mơi trường đa tiến trình Linux cơng việc khó khăn Khơng mơi trường đơn nhiệm, mơi trường đa nhiệm tiến trình có tài nguyên hạn hẹp Tiến trình hoạt động phải trạng thái tôn trọng sẵn sàng nhường quyền xử lý CPU cho tiến trình khác thời điểm nào, hệ thống có u cầu Nếu tiến trình xây dựng không tốt, đổ vỡ gây lỗi, làm treo tiến trình khác hệ thống hay chí phá vỡ (crash) Hệ Điều Hành - Định nghĩa tiến trình: thực thể điều khiển đoạn mã lệnh có riêng khơng gian địa chỉ, có ngăn xếp stack riêng rẽ, có bảng chứa thơng số mơ tả file mở tiến trình đặc biệt có định danh PID (Process Identify) toàn hệ thống vào thời điểm tiến trình chạy Như thấy, tiến trình khơng phải chương trình (tuy đơi lúc chương trình đơn giản cấn tiến trình để hồn thành tác vụ, trường hợp xem tiến trình chương trình một) Rất nhiều tiến trình thực thi máy với Hệ Điều Hành, người dùng nhiều người dùng đăng nhập khác Ví dụ shell bash tiến trình thực thi lệnh ls hay cp Bản thân ls, cp lại tiến trình hoạt động tách biệt khác - Trong Linux, tiến trình cấp khơng gian địa nhớ phẳng 4GB Dữ liệu tiến trình khơng thể đọc truy xuất tiến trình khác Hai tiến trình khác xâm phạm biến Tuy nhiên, muốn chia sẻ liệu hai tiến trình, Linux cung cấp cho vùng không gian địa chung để làm điều Cách hoạt động tiến trình - Khi chương trình chạy từ dịng lệnh, nhấn phím Ctrl+z để tạm dùng chương trình đưa vào hoạt động phía hậu trường (background) Tiến trình Linux có trạng thái: + Đang chạy (running) : lúc tiến trình chiếm quyền xử lý CPU dùng tính tốn hay thực cơng việc + Chờ (waiting) : tiến trình bị Hệ Điều Hành tước quyền xử lý CPU, chờ đến lược cấp phát khác + Tạm dừng (suspend) : Hệ Điều Hành tạm dừng tiến trình Tiến trình đưa vào trạng thái ngủ (sleep) Khi cần thiết có nhu cầu, Hệ Điều Hành đánh thức (wake up) hay nạp lại mã lệnh tiến trình vào nhớ Cấp phát tài nguyên CPU để tiến trình tiếp tục hoạt động - Trên dịng lệnh, thay dùng lệnh Ctrl+z, sử dụng lệnh bg để đưa tiến trình vào hoạt động phía hậu trường Chúng ta yêu cầu tiến trình chạy cú pháp & Ví dụ: $ls –R & Lệnh fg đem tiến trình trở hoạt động ưu tiên phía trước Thực tế đăng nhập vào hệ thống tương tác dòng lệnh, lúc tiến trình shell bash Khi gọi lệnh có nghĩa yêu cầu bash tạo thêm tiến trình thực thi khác Về mặt lập trình, dùng lệnh fork() để nhân tiến trình từ tiến trình cũ Hoặc dùng lệnh system() để triệu gọi tiến trình Hệ Điều Hành Hàm exec() có khả tạo tiến trình khác Cấu trúc tiến trình - Chúng ta xem Hệ Điều Hành quản lý tiến trình nào? Nếu có hai người dùng: user1 user2 đăng nhập vào chạy chương trình grep đồng thời, thực tế, Hệ Điều Hành quản lý nạp mã chương trình grep vào hai vùng nhớ khác gọi phân vùng tiến trình Hình sau cho thấy cách phân chia chương trình grep thành hai tiến trình cho hai người khác sử dụng Trong hình này, user1 chạy chương trình grep tìm chuỗi abc tập tin file1 $grep abc file1 user2 chạy chương trình grep tìm chuỗi cde tập tin user1 $grep abc file1 user2 $grep cde file2 PID 101 PID 102 Code Data s=abc Code Data s=cde Library Thư viện C Library filede Tiến trình quản lý Hệ Điều Hành filede file1 mã lệnh grep file2 file2 $grep cde file2 Chúng ta cần ta cần nhớ hai người dùng user1 user2 hai máy tính khác đăng nhập vào máy chủ Linux gọi grep chạy đồng thời Hình trạng khơng gian nhớ Hệ Điều Hành Linux chương trình grep phục vụ người dùng - Nếu dùng lệnh ps, hệ thống liệt kê cho thông tin tiến trình mà Hệ Điều Hành kiểm sốt, Ví dụ: $ps –af Mỗi tiến trình gán cho định danh để nhận dạng gọi PID (process identify) PID thường số nguyên dương có giá trị từ 2-32768 Khi tiến trình yêu cầu khởi động, Hệ Điều Hành chọn lấy số (chưa bị tiến trình chạy chiếm giữ) khoảng số nguyên cấp phát cho tiến trình Khi tiến trình chấm dứt, hệ thống thu hồi số PID để cấp phát cho tiến trình khác lần sau PID giá trị giá trị dành cho tiến trình gọi init Tiến trình init chạy khởi động Hệ Điều Hành init tiến trình quản lý tạo tiến trình khác Ở ví dụ trên, thấy lệnh ps –af hiển thị tiến trình grep chạy user1 user2 với số PID 101 102 - Mã lệnh thực thi lệnh grep chứa tập tin chương trình nằm đĩa cứng Hệ Điều Hành nạp vào vùng nhớ Như thấy lược đồ trên, tiến trình Hệ Điều hành phân chia rõ ràng: vùng chứa mã lệnh (code) vùng chứa liệu (data) Mã lệnh thường giống sử dụng chung Linux quản lý cho phép tiến trình chương trình sử dụng chung mã lệnh Thư viện Trừ thư viện đặc thù cịn thư viện chuẩn Hệ Điều Hành cho phép chia sẻ dùng chung tiến trình hệ thống Bằng cách chia sẻ thư viện, kích thước chương trình giảm đáng kể Mã lệnh chương trình chạy hệ thống dạng tiến trình đỡ tốn nhớ - Trừ mã lệnh thư viện chia sẻ, cịn liệu khơng thể chia sẻ tiến trình Mỗi tiến trình sở hữu phân đoạn liệu riêng Ví dụ tiến trình grep user1 nắm giữ lưu giữ biến s có giá trị 'abc', grep user2 nắm giữ lại có biến s với giá trị 'cde' Mỗi tiến trình hệ thống dành riêng cho bảng mô tả file (file description table) Bảng chứa số mô tả áp đặt cho file mở Khi tiến trình khởi động, thường Hệ Điều Hành mở sẳn cho file : stdin (số mô tả 0), stdout (số mô tả 1), stderr (số mô tả 2) Các file tượng trưng cho thiết bị nhập, xuất, thông báo lỗi Chúng ta mở thêm file khác Ví dụ user1 mở file file1, user2 mở file file2 Hệ Điều Hành cấp phát số mô tả file cho tiến trình lưu riêng chúng bảng mơ tả file tiến trình - Ngồi ra, tiến trình có riêng ngăn xếp stack để lưu biến cục giá trị trả sau lời gọi hàm Tiến trình dành cho khoảng không gian riêng để lưu biến môi trường Chúng ta dùng lệnh putenv getenv để đặt riêng biến mơi trường cho tiến trình a) Bảng thơng tin tiến trình - Hệ Điều Hành lưu giữ cấu trúc danh sách bên hệ thống gọi bảng tiến trình (process table) Bảng tiến trình quản lý tất PID hệ thống với thông tin chi tiết tiến trình chạy Ví dụng gọi lệnh ps, Linux thường đọc thông tin bảng tiến trình hiển thị lệnh hay tên tiến trình gọi: thời gian chiếm giữ CPU tiến trình, tên người sử dụng tiến trình, … b) Xem thơng tin tiến trình - Lệnh ps Hệ Điều Hành dùng để hiển thị thơng tin chi tiết tiến trình Tùy theo tham số, ps cho biết thơng tin tiến trình người dùng, tiến trình hệ thống tất tiến trình chạy Ví dụ ps đưa chi tiết tham số -af - Trong thông tin ps trả về, UID tên người dùng gọi tiến trình, PID số định danh mà hệ thống cấp cho tiến trình, PPID số định danh tiến trình cha (parent PID) Ở gặp số tiến trình có định danh PPID 1, định danh tiến trình init, gọi chạy hệ thống khởi động Nếu hủy tiến trình init Hệ Điều Hành chấm dứt phiên làm việc STIME thời điểm tiến trình đưa vào sử dụng TIME thời gian chiếm dụng CPU tiến trình CMD tồn dịng lệnh tiến trình triệu gọi TTY hình terminal ảo nơi gọi thực thi tiến trình Như biết, người dùng đăng nhập vào hệ thống Linux từ nhiều terminal khác để gọi tiến trình Để liệt kê tiến trình hệ thống, sử dụng lệnh: $ps –ax Tạo lập tiến trình a) Gọi tiến trình hàm system() - Chúng ta gọi tiến trình khác bên chương trình thực thi hàm system() Có nghĩa tạo tiến trình từ tiến trình chạy Hàm system() khai báo sau: #include int system( const char (cmdstr) ) Hàm gọi chuỗi lệnh cmdstr thực thi chờ lệnh chấm dứt quay nơi gọi hàm Nó tương đương với việc bạn gọi shell thực thi lệnh hệ thống: $sh –c cmdstr system() trả mã lỗi 127 không khởi động shell để gọi lệnh cmdstr Mã lỗi -1 gặp lỗi khác Còn lại, mã trả system() mã lỗi cmdstr sau lệnh gọi trả Ví dụ sử dụng hàm system(), system.c #include #include int main() { printf( "Thuc thi lenh ps voi system\n" ); system( "ps –ax" ); system(“mkdir daihoc”); system(“mkdir caodang”); printf( "Thuc hien xong \n" ); exit( ); } Hàm system() sử dụng để gọi lệnh “ps –ax” Hệ Điều Hành b) Thay tiến trình hành với hàm exec - Mỗi tiến trình Hệ Điều Hành cấp cho khơng gian nhớ tách biệt để tiến trình tự hoạt động Nếu tiến trình A triệu gọi chương trình ngồi B (bằng hàm system()chẳng hạn), Hệ Điều Hành thường thực thao tác như: cấp phát khơng gian nhớ cho tiến trình mới, điều chỉnh lại danh sách tiến trình, nạp mã lệnh chương trình B đĩa cứng khơng gian nhớ vừa cấp phát cho tiến trình Đưa tiến trình vào danh sách cần điều phối Hệ Điều Hành Những công việc thường thời gian đáng kể chiếm giữ thêm tài nguyên hệ thống Nếu tiến trình A chạy muốn tiến trình B khởi động chạy khơng gian nhớ có sẵn tiến trình A sử dụng hàm exec cung cấp bới Linux Các hàm exec thay tồn ảnh tiến trình A (bao gồm mã lệnh, liệu, bảng mô tả file) thành ảnh tiến trình B hồn tồn khác Chỉ có số định danh PID tiến trình A cịn giữ lại Tập hàm exec bao gồm hàm sau: #include extern char **environ; int execl( const char *path, const char *arg, ); int execlp( const char *file, const char *arg, ); int execle( const char *path, const char *arg, , char *const envp[] ); int exect( const char *path, char *const argv[] ); int execv( const char *path, char *const argv[] ); int execvp( const char *file, char *const argv[] ); - Đa số hàm yêu cầu đối số path file đường dẫn đến tên chương trình cần thực thi đĩa arg đối số cần truyền cho chương trình thực thi, đối số tương tự cách gọi chương trình từ dịng lệnh c) Nhân tiến trình với hàm fork() - Thay tiến trình đơi bất lợi với Đó Khởi tạo tiến trình chiếm giữ tồn khơng gian tiến tiến trình trình cũ khơng có khả kiểm sốt điều khiển tiếp tiến trình hành sau gọi hàm exec Cách đơn giản mà chương trình Linux thường dùng sử dụng hàm Gọi fork() fork() để nhân hay tạo tiến trình fork() hàm đặc biệt, thực thi, trả giá trị khác lần thực thi, so với hàm bình thường trả giá trị lần thực thi Khai báo Trả PID tiến trình Trả trị hàm fork() sau: #include #include pid_t fork() - Nếu thành cơng, fork() tách tiến trình hành tiến trình (dĩ nhiên Hệ Điều Hành phải cấp phát thêm Mã lệnh tiếp Mã lệnh thực thi tiến trình ban đầu tiến trình (tiến khơng gian nhớ để tiến trình hoạt động) Tiến trình con) (tiến trình cha) trình ban đầu gọi tiến trình cha (parent process) tiến trình gọi tiến trình (child process) Tiến trình có số định danh PID riêng biệt ra, tiến trình cịn mang thêm định danh PPID số định danh PID tiến trình cha Cơ chế phân chia tiến trình fork() - Sau tách tiến trình, mã lệnh thực thi hai tiến trình chép hồn tồn giống Chỉ có dấu hiệu để nhận dạng tiến trình cha tiến trình con, trị trả hàm fork() Bên tiến trình con, hàm fork() trả trị Trong bên tiến trình cha, hàm fork() trả trị số nguyên PID tiến trình vừa tạo Trường hợp khơng tách tiến trình, fork() trả trị -1 Kiểu pid_t khai báo định nghĩa uinstd.h kiểu số nguyên (int) - Đoạn mã điều khiển sử dụng hàm fork() thường có dạng chuẩn sau: pid_t new_pid; new_pid = fork(); // tách tiế n trình switch (new_pid) { case -1: printf( "Khong the tao tien trinh moi" ); break; case 0: printf( "Day la tien trinh con" ); // mã lệnh dành cho tiến trình đặt break; default: printf( "Day la tien trinh cha" ); // mã lệnh dành cho tiến trình cha đặt break; } d) Kiểm sốt đợi tiến trình - Khi fork() tách tiến trình thành hai tiến trình cha con, thực tế hai tiến trình cha lẫn tiến trình hoạt động độc lập Đơi lúc tiến trình cha cần phải đợi tiến trình thực xong tác vụ tiếp tục thực thi Ở ví dụ trên, thực thi, thấy tiến trình cha kết thúc mà tiến trình in thơng báo tiến trình cha tiến trình tranh gởi kết hình Chúng ta khơng muốn điều này, muốn tiến trình cha kết thúc tiến trình hồn tất thao tác Hơn nữa, chương trình cần thực xong tác vụ đến chương trình cha Để làm việc này, sử dụng hàm wait() #include #include pid_t wait(int &stat_loc); Hàm wait gọi yêu cầu tiến trình cha dừng lại chờ tiến trình kết thúc trước thực tiếp lệnh điều khiển tiến trình cha wait() làm cho liên hệ tiến trình cha tiến trình trở nên Khi tiến trình kết thúc, hàm trả số PID tương ứng tiến trình Nếu truyền thêm đối số stat_loc khác NULL cho hàm wait() trả trạng thái mà tiến trình kết thúc biến stat_loc Chúng ta sử dụng macro khai báo sẵn sys/wait.h sau: WIFEXITED (stat_loc) Trả trị khác tiến trình kết thúc bình thường WEXITSTATUS (stat_loc) Nếu WIFEXITED trả trị khác 0, macro trả mã lỗi tiến trình WIFSIGNALED (stat_loc) Trả trị khác tiến trình kết thúc tín hiệu gửi đến WTERMSIG(stat_loc) Nếu WIFSIGNALED khác 0, macro cho biết số tín hiệu hủy tiến trình WIFSTOPPED(stat_loc) Trả trị khác tiến trình dừng WSTOPSIG(stat_loc) Nếu WIFSTOPPED trả trị khác 0, macro trả số hiệu signal II Thực Hành Bài Sử dụng hàm system(), system_demo.c tạo tiến trình sau: Tạo thư mục ThucHanh1 ThucHanh2 Tạo tập tin Tho.c thư mục ThucHanh1 ghi chuỗi “troi hom that dep !” vào tập tin vừa tạo (sử dụng lệnh echo để ghi chuỗi vào tập tin: echo noi_dung_chuoi >ten_tap_tin) Sao chép tập tin vừa tạo sang thư mục ThucHanh2 hiển thị lên hình Bài Sử dụng hàm execlp để thay tiến trình tiến trình ps –af Hệ Điều Hành #include #include int main() { printf( "Thuc thi lenh ps voi execlp\n" ); execlp( "ps", "ps", "–ax", ); printf( "Thuc hien xong! Nhung chung ta se khong thay duoc dong nay.\n" ); exit( ); } Bài Tạo tập tin fork_demo.c sử dụng hàm fork() đó: In câu thơng báo: “Khong the tao tien trinh !” hàm fork() trả giá trị -1 Ngược lại: - In lần câu thông báo: “Day la tien trinh !” mã trả - In lần câu thông báo: “Day la tien trinh cha !” mã trả PID tiến trình #include #include #include int main() { pid_t pid; char * message; int n; pid = fork(); switch ( pid ) { case -1: printf( "Khong the tao tien trinh !" ); exit(1); case 0: message = "Day la tien trinh !"; n = 0; for ( ; n < 5; n++ ) { printf( "%s", message ); sleep( ); } break; default: message = "Day la tien trinh cha !"; n = 0; for ( ; n < 3; n++ ) { printf( "%s", message ); sleep( ); } break; } exit( ); } Biên dịch thực thi chương trình này, thấy tiến trình hoạt động đồng thời in kết đan xen Nếu muốn xem liên quan PID PPID tiến trình cha lệnh fork() phát sinh, thực chương trình sau: $./fork_demo & ps – af Bài Sử dụng hàm wait() để chờ tiến trình kết thúc sau gọi fork(), wait_child.c #include #include #include #include int main() { pid_t pid; int child_status; int n; // nhân tiến trình, tạo pid = fork(); switch ( pid ) { case -1: // fork không tạo tiến trình printf("Khong the tao tien trinh moi"); exit( ); case 0: // fork thành công, tiến trình n = 0; for ( ; n < 5; n++ ) { printf( "Tien trinh con" ); sleep( ); } exit( ); // Mã lỗi trả tiến trình default: // fork thành công, tiến trình cha printf("Tien trinh cha, cho tien trinh hoan thanh.\n”); // Chờ tiến trình kết thúc wait( &child_status ); printf("Tien trinh cha – tien trinh hoan thanh.\n"); } return ( ); } Bài Sử dụng hàm wait() để chờ tiến trình kết thúc sau gọi fork(), wait_child2.c, kiểm tra mã lỗi trả từ tiến trình #include #include #include #include int main() { pid_t pid; int child_status; int n; // nhân tiến trình, tạo pid = fork(); switch ( pid ) { case -1: // fork khơng tạo tiến trình printf("Khong the tao tien trinh moi"); exit( ); case 0: // fork thành cơng, tiến trình n = 0; for ( ; n < 5; n++ ) { printf( "Tien trinh con" ); sleep( ); } exit( 37 ); // Mã lỗi trả tiến trình default: // fork thành cơng, tiến trình cha n = 3; for ( ; n > 0; n ) { printf( "Tien trinh cha" ); sleep( ); } // Chờ tiến trình kế t thúc wait( &child_status ); // Kiểm tra in mã lỗi trả tiến trình printf( "Tien trinh hoan thanh: PID = %d\n", pid ); if ( WIFEXITED( child_status )) printf( "Tien trinh thoat voi ma %d\n", WEXITSTATUS( child_status ) ); else printf( "Tien trinh ket thuc binh thuong\n" ); break; } exit( ); } ... thực thi tiến trình ban đầu tiến trình (tiến khơng gian nhớ để tiến trình hoạt động) Tiến trình con) (tiến trình cha) trình ban đầu gọi tiến trình cha (parent process) tiến trình gọi tiến trình. .. “ps –ax” Hệ Điều Hành b) Thay tiến trình hành với hàm exec - Mỗi tiến trình Hệ Điều Hành cấp cho không gian nhớ tách biệt để tiến trình tự hoạt động Nếu tiến trình A triệu gọi chương trình ngồi... cho tiến trình cha đặt break; } d) Kiểm sốt đợi tiến trình - Khi fork() tách tiến trình thành hai tiến trình cha con, thực tế hai tiến trình cha lẫn tiến trình hoạt động độc lập Đơi lúc tiến trình