Module2: Lập trình Shell và C Quản lý tiến trình A. TÓM TẮT LÝ THUYẾT: 1. SHELL SCRIPT a. Sử dụng các cấu trúc điều khiển trong Module1 b. Tính toán biểu thức trong Shell: sử dụng một trong các cách sau expr command ví dụ: $ expr 5 + 10 Cho kết quả 15 $ a=10 b=5 $ expr $a + $b Cho kết quả 15 $ expr $a / $b Cho kết quả 2 $ expr $a * $b expr: syntax error $ expr $a \* $b Cho kết quả 50 $ x=`expr $a + $b` $ echo $x Cho kết quả 15 $(()) construct. Ví dụ: $ c=$(($a+9)) $ echo $c 19 $ c=$((a+9)) #Đúng, Không cần ký hiệu $. $ c=$((a + 9)) #Đúng, không hạn chế khoảng trắng. $ c= $((a + b)) #Sai, thừa khoảng trắng sau phép gán. let Ví dụ: $ let "a = a + 5";echo $a 20 $ let a++ ; echo $a #tương tự ((i++)) 16 $[] Ví dụ: $ x=85 $ y=15 $ echo $[x+y] cho kết quả 100 $ echo $[x/y] cho kết quả 5 $ c=$[x*y] $ echo $c cho kết quả 1275 2. LẬP TRÌNH C TRONG LINUX
Chọn trình soạn thảo để soạn tập tin.c: vi, nano, gedit, emacs, o Sử dụng vi: $vi <tên tập tin> : Tạo tập tin mới :w - ghi vào tập tin :x - lưu và thoát khỏi chế độ soạn thảo :wq - lưu và thoát khỏi chế độ soạn thảo :w - lưu vào tập tin mới :q - thoát nếu ko có thay đổi :q! - thoát không lưu :r - mở tập tin đọc o Sử dụng nano nano <tên tập tin>: tạo tập tin mới Ctrl-O: Lưu file (giữ phím Ctrl và bấm O) Ctrl-X: Thoát khỏi nano Biên dịch chương trình gcc -o <filename> <filename.c> Thực thi./filename Để tránh việc nhập./ mỗi lần thực thi chương trình, cần thêm đường dẫn đến thư mục hiện hành vào biến PATH. Cú pháp: PATH=$PATH:<Đường dẫn> Lưu ý: Cú pháp và các cấu trúc điều khiển của C (Sinh viên phải tự xem trước) 3. QUẢN LÝ TIẾN TRÌNH a) Định danh của tiến trình: Mỗi tiến trình có một PID duy nhất bắt đầu từ 0. Một số PID đặc biệt: 0: scheduler 1: init 2: page daemon b) Bộ nhớ của tiến trình Text: chứa chương trình code thực thi Data: vùng dữ liệu - chứa các biến Stack: chứa trạng thái và các thông tin liên quan đến việc gọi hàm c) Lấy đối số và biến môi trường int argc: số đối số của chương trình khi chạy char *argv[]: danh sách đối số extern char **environ: danh sách biến môi trường
Ví dụ 1: #include <stdlib.h> extern char **environ; int main(int argc, char *argv[]) { int i; printf("\nnumber of arguments is %d",argc); printf("\narguments:\n"); for(i=0; i<argc; i++) printf("argv[%d]=%s\n",i,argv[i]); getchar(); for (i=0; environ[i]!=(char *)0; i++) printf("%s\n",environ[i]); return 0; d) Lấy PID của tiến trình pid_t getpid(void); //Lấy PID của process hiện hành pid_t getppid(void); //Lấy PID của process cha ví dụ 2: #include <unistd.h> int main() { printf("process id: %d\n", getpid()); printf("parent process id: %d\n", getppid()); return 0; e) Tạo tiến trình Dùng hàm: pid_t fork(void); Nếu thành công, trả về 0 : process con N>0 là Pid của process cha. Nếu thất bại, trả về -1 và lý do: ENOMEM: không đủ bộ nhớ EAGAIN: số process vượt quá giới hạn cho phép
Ví dụ 3: tạo tập tin fork1.c #include <unistd.h> int main() { int childid; if ((childid=fork())==0) { printf("child process output: PID = %d\n",getpid()); else if(childid > 0) { printf("parent process output: PID= %d\t", getpid()); printf("child PID=%d\n", childid); else { printf("fork error!\n"); exit(1); return 0; Ví dụ 4: tạo tập tin fork2.c #include <sys/types.h> #include <unistd.h> #include <stdlib.h> int main() { pid_t pid; char *message; int n; printf( fork program starting\n ); pid = fork(); switch(pid) { case -1: perror( fork failed ); exit(1); case 0: message = This is the child ; n = 5; break; default:
message = This is the parent ; n = 3; break; for(; n > 0; n--) { puts(message); sleep(1); exit(0); f) Kết thúc tiến trình Dùng system call exit() o Orphaned process: process cha kết thúc trước process con sau đó sẽ có cha là init (PID=1) o Zombied process: Process kết thúc nhưng chưa báo trạng thái cho process cha biết. Dùng hàm wait() hay waitpid() ở process cha để lấy trạng thái trả về từ process con Dùng waitpid()system call #include <sys/types.h> #include <sys/wait.h> pid_t waitpid(pid_t pid, int *stat_loc, int options); (Trả về PID của process con kết thúc, hoặc -1 nếu thất bại) o Đối số pid: o <-1: Đợi các process con có process group ID bằng với PID của nó o =-1: Đợi bất kỳ các process con nào o =0:Đợi bất kỳ các process con nào có process group ID bằng với PID của nó. o > 0: Đợi pocess có PID bằng với giá trị này
B. BÀI TẬP I. SHELL 1. Viết một shell script đổi tên tất cả tập tin trong thư mục bằng cách nối thêm một số tăng dần sau tên tập tin, giả sử tên tập tin không có phần mở rộng. Gợi ý: #!/bin/bash # This script finds the list of files in a directory and echoes # each filename with an incremental number appended # instead of echo, we can also use mv to rename filenames i=0 # bash for loop for f in $( ls.); do let i=$i+1 echo "we found the following file: " $f newfile=$f"_"$i echo we found the following file: $newfile done 2. Viết một shell script nhận 3 tham số dòng lệnh là 3 số bất kỳ, in ra số lớn nhất. Shell script in ra thông báo lỗi nếu thiếu một trong 3 tham số. 3. Viết một shell script nhận 3 tham số dòng lệnh Tham số thứ nhất $1: một số bất kỳ Tham số thứ hai $2: một trong các phép toán: + - / * Tham số thứ ba $3: một số bất kỳ Khi thực thi, nếu nhập 3 đối số thì Script in ra kết quả của phép tính tương ứng, ngược lại nếu thiếu một trong các đối số thì in ra thông báo lỗi. 4. Sử dụng lệnh echo và read để viết chương trình in ra menu sau : 1) UNIX 2) Solaris 3) Free BSD 4) Windows 5) Other Xin chọn 1 mục (1..5) : Khi nhập vào 1 trong các mục chọn trên thì hiển thị thông báo chọn mục nào. Nếu nhập sai sẽ in thông báo lỗi. Ví dụ : khi bạn nhập vào số 4 sẽ xuất hiện dòng thông báo sau : You have selected Windows 5. Sử dụng shell script viết chương trình in các tham số dòng lệnh theo thứ tự ngược 6. Viết 1 shell script theo cú pháp MCP1 <file> [<file> ] <TM>
II. Thực hiện copy nhiều <file> vào 1 <thư mục> Kiểm tra có tối thiểu 2 tham số trên dòng lệnh Kiểm tra sự tồn tại của <file>, nếu không có thông báo trên màn hình rồi chạy tiếp cho <file khác> Kiểm tra sự tồn tại của <TM>, nếu không có hỏi user cần tạo? Lập trình C - Quản lý tiến trình 1. Thực hiện các lệnh sau và giải thích ý nghĩa của mỗi lệnh trong lab_report ps ax ps l //xem độ ưu tiên của tiến trình Dùng lệnh renice <độ ưu tiên> <PID> thay đổi độ ưu tiên của tiến trình xem lại kết quả 2. Tạo tiến trình mới: có thể tạo một chương trình bên trong một chương trình khác (tạo tiến trình mới) bằng cách sử dụng hàm system(). Hãy viết chương trình sử dụng hàm system() để thực hiện lệnh ps ax theo hướng dẫn bên dưới, giải thích kết quả #include <stdlib.h> int main() { printf( Running ps with system\n ); system( ps ax ); printf( Done.\n ); exit(0); 3. Nhóm hàm exec: Thay thế tiến trình hiện tại bằng một tiến trình mới với không gian nhớ có sẵn của tiến trình ban đầu- SV tự tìm hiểu về các hàm trong nhóm exec. Hiện thực chương trình sau, so sánh kết quả trong câu 2 và nhận xét. #include <unistd.h> #include <stdlib.h> int main() { printf( Running ps with execlp\n ); execlp( ps, ps, ax, 0); printf( Done.\n ); exit(0); 4. Thực hiện các ví dụ 1 đến ví dụ 5, giải thích ý nghĩa của từng ví dụ trong lab
5. Trong ví dụ 4, tiến trình cha kết thúc trước so với tiến trình con. Hãy sử dụng hàm wait() để hiệu chỉnh chương trình trong ví dụ 4, sao cho tiến trình cha chờ tiến trình con kết thúc trước. In ra ID của tiến trình con và exit code của tiến trình con xác nhận tiến trình con kết thúc bình thường. //wait for the child process to finish. if (pid!= 0) { int stat_val; pid_t child_pid; child_pid = wait(&stat_val); printf( Child has finished: PID = %d\n, child_pid); if(wifexited(stat_val)) printf( Child exited with code %d\n,wexitstatus(stat_val)); else printf( Child terminated abnormally\n ); exit(exit_code); WIFEXITED(stat_val) 0 nếu tiến trình con kết thúc bình thường WEXITSTATUS(stat_val): o Nếu WIFEXITED 0 thì WEXITSTATUS trả về exit code của tiến trình con.