Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 1
LỜI NÓI ĐẦU
Nguyên lý hệ điều hành là 1 môn học bổ ích giúp sinh viên chúng em hiểu biết
được cơ cấu tổ chức, cũng như việc quản lý, điều phối các tiến trình của hệ thống máy
tính. Qua đó hiểu biết phần nào về phần mềm cơ bản nhất của máy tính là hệ điều hành.
Việc nghiên cứu, hoàn thành đồ án nguyên lý hệ điều hành cũng giúp chúng em được
hiểu rõ hơn nữa về hệ điều hành Linux, 1 hệ điều hành có nhiều tính năng vượt trội và có
triển vọng trong tương lai.
Chúng em xin chân thành cảm ơn sự hướng dẫn của thầy Mai Văn Hà, đã tận tình
chỉ dẫn giúp chúng em hoàn thành được đề tài đồ án này.
TỔNG QUAN ĐỀ TÀI 1.1. Bối cảnh đề tài:
Nhận thấy việc giao tiếp giữa các tiến trình trên linux có thể giúp ta hiểu rõ hơn cơ chế
xử lý, và trao đổi dữ liệu giữa các tiến trình trong hệ điều hành linux. Trong đó việc trao
đổi dữ liệu, giao tiếp qua đường ống pipe theo cơ chế FIFO tương đối dễ hiểu và cũng
phần nào giúp em hiểu được nguyên lý tổ chức hệ điều hành nên chúng em quyết định
chọn đề tài này.
1.2. Mục tiêu đề tài:
Hiểu hơn về việc giao tiếp giữa các tiến trình trong hệ điều hành linux đặc biệt là bằng
đường ống pipe. Nghiên cứu đề tài này sẽ có thể cho ta hiểu được thế nào là pipe và cơ
GNU emacs Trình soạn thảo văn bản (hỗ trợ cho việc sửa mã nguồn khi lập
trình)
Bash Hệ vỏ Shell hỗ trợ các dòng lệnh của hệ điều hành
Bison Bộ phân tích tương thích với yacc của UNIX
Trong đồ án môn học này, em sử dụng trình biên dịch CodeBlock chạy trên nền hệ điều
hành linux Unbuntu 14.04 để áp dụng.
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 3
CƠ SỞ LÝ THUYẾT
1.5. Giới thiệu về hệ điều hành Linux
1.5.1. Lịch sử ra đời hệ điều hành Linux.
.
1.5.2. Các chức năng của hệ điều hành Linux
Hệ điều hành Linux có rất nhiều chức năng và chúng khai thác khả năng của các hệ Unix hiện
đại theo các cách sau :
Đa xử lí: các bộ đa xử lí có thể thực hiện nhiều chương trình đồng thời bất kể sử dụng một hay
nhiều bộ xử lí.
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 4
Đa nền: Cho phép nhiều người sử dụng : giống như tất cả các hệ Unix, Linux cho phép nhiều
người sử dụng cùng làm việc trên một máy ở cùng thời điểm.
Hỗ trợ truyền thông giao xử lí ( Pipes , IPC , Sockets ).
Quản lí các thông điệp điều khiển khác nhau.
Hệ thống quản lí thiết bị đầu cuối tuân thủ theo tiêu chuẩn POSIX. Linux cũng giả các
thiết bị đầu cuối cũng như điều khiển quá trình.
Hỗ trợ một dải rộng các thiết bị ngoại vi, chẳng hạn như các card âm thanh, giao diện đồ
hoa, mạng, giao diện hệ máy tính nhỏ ….
Buffer cache : vùng bộ nhớ được dành để làm vùng đệm cho các đầu vào và đầu ra từ các
quá trình khác nhau.
Hệ thống quản lí bộ nhớ trang yêu cầu. Một trang sẽ không được nạp chừng nào nó
không thực sự cần thiết ở bộ nhớ.
Các thư viện động và dùng chung : Các thư viện động chỉ được tải khi chúng thật sự cần
thiết và mã của chúng được dùng chung nếu nhiều ứng dụng đang dùng chúng.
Các hệ thống file có thể quản lí tốt và đồng đều các phân hoạch file Linux được sử dụng
bởi filesystem làm các phân hoạch có các định dạng khác ( MS-DOS , ISO9660, vv. ).
Thiết bị của TCP/IP và các giao thức mạng khác.
1.5.3. Hạn chế của hệ điều hành Linux:
Ready ( sẵn sàng ): Tiến trình có thể được thực hiện nhưng một tiến trình khác
lại đang chạy.
Suspended (chờ kích hoạt): Tiến trình chờ diễn ra sự kiện ( Ví dụ như đang chờ
nhập hoặc xuất kết thúc).
Zombie (tồn tại) : Tiến trình đã kết thúc thực hiện, nhưng nó vẫn được tham
chiếu trong hệ thống.
Stop (ngừng) : Tiến trình đã bị treo bởi một tiến trình ngoài
Các thay đổi trạng thái của một tiến trình được trình bày ở sơ đồ trạng thái sau :
Hình 1 : Sơ đồ trạng thái của một tiến trình.
Tại một thời điểm, chỉ có một tiến trình có thể nhận trạng thái Thực hiện trên một
bộ xử lý bất kỳ. Trong khi đó, nhiều tiến trình có thể ở trạng thái khác.
Các cung chuyển tiếp trong sơ đồ trạng thái biễu diễn những sự chuyển trạng thái có thể
xảy ra trong các điều kiện sau :
- Tiến trình mới tạo được đưa vào hệ thống
- Bộ điều phối cấp phát cho tiến trình một khoảng thời gian sử dụng CPU
- Tiến trình kết thúc
Sự
Mã định danh người sử dụng có tên mà tiến trình đang thực hiện.
Thông tin được kernel sử dụng để thiết lập lịch biểu của các tiến trình (thứ tự
ưu tiên, v.v…).
Thông tin về không gian địa chỉ của tiến trình ( các phân đoạn của mã, dữ liệu,
ngăn xếp).
Thông tin về các nhập / xuất được tiến trình thực hiện ( các mô tả về các file
mở, thư mục hiện hành …)
Tính tương thích thông tin tổng kết các tài nguyên do tiến trình sử dụng.
Ví dụ, khi có hai người dùng (user), một mang tên là A, một mang tên là B cùng đăng nhập và
chạy chương trình C ( chương trình có nhiệm vụ tìm một chuỗi ký tự trong file) đồng thời, thực
tế hệ điều hành sẽ quản lý và nạp mã của chương trình C vào hai vùng nhớ khác nhau và gọi mỗi
phân vùng như vậy là tiến trình. Hình cho thấy cách phân chia chương trình C thành hai tiến
trình cho hai người dùng khác nhau sử dụng.
Ở hình minh họa, người dùng A chạy chương trình C tìm chuỗi B trong tệp findgirl.txt
$CB findA.txt
Trong khi người dùng B chạy C và tìm chuỗi A trong tệp findboy.txt
$CA findB.txt
Chúng ta nên nhớ rằng hai người dùng A và B có thể ở hai máy tính khác nhau đăng nhập vào
máy chủ Linux và gọi C chạy đồng thời. Hình 2 là hiện trạng không gian bộ nhớ hệ điều hành
Linux khi chạy chương trình C phục vụ người dùng.
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 7
Nếu dùng lệnh ps, hệ thống sẽ liệt kê cho bạn thông tin về các tiến trình mà hệ điều hành đang
kiểm soát. Ví dụ với tham số -af , chúng ta có thể thấy các thông tin do ps liệt kê như sau
UID PID PPID C STIME TTY TIME CMD
root 2345 1234 0 Apr11 pts/0 00:00:00 [bash]
hàm. Tiến trình cũng được dành cho khoảng không gian dành riêng để lưu các biến môi trường.
Cuối cùng mỗi tiến trình hoạt động trong không gian địa chỉ ảo độc lập do hệ thống cấp phát.
Chúng ta không cần quan tâm đến bộ nhớ vật lý. Linux và UNIX có cách quản lý phân trang
cộng với hoán chuyển giữa bộ nhớ vật lý và bộ nhớ ảo thông qua phân vùng swap.
Bảng thông tin tiến trình
Hệ điều hành lưu dữ một cấu trúc danh sách bên trong hệ thống gọi là bảng tiến trình(process
table).Bảng tiến trình quản lý tất cả các Pid của hệ thống cùng với các thông tin chi tiết về các
tiến trình đang chạy.
Ta có thể xem bảng tiến trình với lệnh ps –af ( với tùy chọn –af để hiển thị chi tiết ) :
UID PID PPID C STIME TTY TIME CMD
root 2345 1234 0 Apr11 pts/0 00:00:00 [bash]
A 111 1235 0 20:44 tty1 00:00:00 C B
find…
B 222 1235 0 20:44 tty1 00:00:00 C A
find
…
Trong đó :
UID : tên người dùng đã gọi tiến trình.
PID : là số định danh mà hệ thống cấp cho tiến trình ( được dùng làm khóa chính và đánh chỉ
mục để truy xuất thông tin).
PPID : parent PID là số định danh của tiến trình cha.
STIME : Thời điểm tiến trình được đưa vào sử dụng.
TTY : Là màn hình terminal ảo nơi gọi thực thi tiến trình
TIME : là thời gian chiếm dụng CPU của tiến trình
CMD : là toàn bộ dòng lệnh khi tiến trình được triệu gọi
1.6.3. Tạo lập tiến trình
Ta có thể gọi một chương trình khác bên trong chương trình đang chạy bằng hàm system().
Hay nói cách khác ta có thể tạo ra một tiến trình mới từ một tiến trình đang chạy. Hàm system()
còn giữ lại. Hàm thay thế ảnh của tiến trình bao gồm tập các hàm sau :
Đa số các hàm này đều yêu cầu bạn chỉ đối số path hoặc file là đường dẫn đến chương trình cần
thực thi trên đĩa. Arg là các đối số cần truyền cho chương trình thực thi, những đối số này tương
tự cách ta gọi chương trình từ dòng lệnh
$sh –c cmdstr
#include <stdlib.h>
#include<stdio.h>
int main()
{
printf(“Running ps with system\n”);
system(“ps –ax”);
printf(“Done.\n”);
exit(0);
}
#include<unistd.h>
extrn 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, const char *argv[]);
int execv(const char *path, const char *argv[]);
int execvp(const char *path, const char *argv[]);
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 10
Ví dụ : exec.c
Khi chạy chương trình lệnh ps được thực thi, chương trình exec của ta không chờ ps
chấm dứt và cũng không bao giờ in ra chuỗi Done. Lý do là toàn bộ mã lệnh và không gian của
tiến trình exec sau khi gọi hàm execlp đã bị thay thế bởi tiến trình mới ps.
int main()
{
printf(“Running ps with execlp\n”);
execlp(“ps”, “ps”, “-ax”, 0);
printf(“Done. – But you never see this line”);
exit();
}
Khởi tạo tiến trình
chính
Gọi fork()
Trả về PID của tiến trình
cha
Trả về trị 0
Mã lệnh kế tiếp
của tiến trình đầu
(cha)
Mã lệnh thực thi tiến
trình mới (con)
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 11
Trường hợp không tách được, fork() sẽ trả về trị -1. Kiểu pid_t được khai báo và định danh trong
unistd.h là kiểu nguyên(int).
Cả hai tiến trình trên hoạt động đồng thời và có thể đang xen nhau.
Ví dụ cho cách sử dụng hàm fork() để nhân đôi tiến trình: 1.6.6. Kiểm soát và đợi tiến trình con
Khi fork() tách tiến trình chính thành hai tiến trình cha và con, trên thực tế cả tiến trình cha
else if (pid ==0) printf(“This is the child”);
else printf(“this is the parent”);
}
#include <sys/types.h>
#include <sys/wait.h>
pid_t wait(int &stat_loc);
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 12
WTERMSIG(stat_loc)
Cho biết số tín hiệu đã hủy tiến trình
con
WIFSTOPPED(stat_loc)
Trả về trị khác 0 nếu tiến trình con đã
dừng
WSTOPSIG(stat_loc)
Trả về số hiệu của signal
1.6.7. Đón xử lý tín hiệu khi tiến trình con kết thúc
Tiến trình cha có thể đón bắt tình huống hay tín hiệu SIGCHILD khi tiến trình con
chấm dứt và gán cho nó một tác vụ thự thi nào đó. Bằng cách này, tiến trình cha có thể
không cần phải gọi wait() để chờ tiến trình con kết thúc mới có thể thực hiện được tác vụ
tiếp theo. Khi tạo tiến trình con xong, tiến trình cha tiếp tục công việc của mình. Khi tiến
trình con chấm dứt và gửi tín hiệu đến tiến trình cha, hàm hay bộ xử lý tín hiệu của tiến
trình cha sẽ được triệu gọi thực thi.
Hàm catch_child() được dùng để xử lý tín hiệu(còn gọi là bộ xử lý tín hiệu). Để
gán một hàm xử lý tín hiệu cho một tín hiệu cụ thể nào đó, ta gọ hàm hệ thống signal().
1.6.8. Bỏ rơi tiến trình con
Như ta đa biết, lệnh fork() tách đôi một tiến trình thành tiến trình cha và tiến trình
- Giao tiếp bằng hàng đợi thông điệp (message queue).
- Giao tiếp bằng sử dụng vùng nhớ chia sẻ (share memory).
- Giao tiếp bằng đồng bộ tín hiệu semephore.
- Giao tiếp trao đổi thông qua socket.
1.7.3. Các vấn đề nảy sinh trong giao tiếp giữa các tiến trình
Do mỗi tiến trình sở hữu một không gian địa chỉ riêng biệt, nên các tiến trình không thể
liên lạc trực tiếp dễ dàng mà phải nhờ vào các cơ chế do hệ điều hành cung cấp. Khi cung
cấp cơ chế liên lạc cho các tiến trình, hệ điều hành thường phải tìm giải pháp cho các vấn
đề chính yếu sau:
- Giao tiếp tường minh hay tiềm ẩn (explicit naming/ implicit naming): tiến trình có cần
phải biết tiến trình nào đang trao đổi hay chia sẻ thông tin với nó? Mỗi liên kết được gọi
là tường minh khi được thiết lập rõ ràng, trực tiếp giữa các tiến trình và là tiềm ẩn khi các
tiến trình liên lạc với nhay thông qua một quy ước ngầm nào đó.
- Giao tiếp theo chế độ đồng bộ hay không đồng bộ (blocking/ nonblocking): Khi tiến
trình trao đổi thông tin với một tiến trình khác, các tiến trình có cần phải đợi cho thao tác
liên lạc hoàn tất rồi mới tiếp tục các xử lý khác? Các tiến trình liên lạc theo cơ chế đồng
bộ sẽ chờ nhau hoàn tất việc liên lạc, còn các tiến trình liên lạc theo cơ chế không đồng
bộ thì không.
- Giao tiếp giữa các tiến trình trong hệ thống tập trung và hệ thống phân tán: Cơ chế liên
lạc giữa các tiến trình trong cùng một máy tính có sự khác biệt so với việc liên lạc giữa
các tiến trình giữa những máy tính khác nhau.
1.8. Cơ chế giao tiếp bằng đƣờng ống (pipe)
1.8.1. Định nghĩa
Các pipe (ống dẫn) tạo thành phương tiện truyền thông giữa những tiến trình. Sự
truyền dữ liệu giữa các tiến trình được thực hiện thông qua một lệnh truyền : dữ liệu đã
ghi tại một đầu của kênh được đọc ở đầu bên kia.
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 14
#include <unistd.h>
int pipe(int filedes[2])
/* Trước hết định nghĩa mảng bao gồm hai phần tử để chứa số mô tả đường
ống */
int pipes[2]
/* Thực hiện tạo đường ống pipe */
int rc = pipe (pipes);
if (rc == -1) { /* tạo đường ống bị lỗi*/
perror(“pipe”);
exit(1);
}
Tiến trình A
Tiến trình B
Ống dẫn truyền thông
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 15
đường ống phía đầu đọc trong khi pipes[1] là số mô tả đường ống phía đầu ghi. Nếu
không tạo được đường ống mã lỗi trả về sẽ là –1.
Trong ví dụ dưới đây, sau khi tạo ra đường ống, ta gọi hàm fork() để tạo ra tiến
trình con. Như ta đã biết, khi nhân đôi tiến trình, bộ mô tả file của tiến trình cha và tiến
trình con có khả năng kế thừa nhau cho nên pipes[] mở bởi tiến trình cha cũng được nhân
bản và sao chép sang tiến trình con. Tiến trình cha sẽ đọc dữ liệu nhập vào từ phía người
dùng và ghi vào đường ống trong khi tiến trình con phía bên kia đường ống tiếp nhận dữ
liệu bằng cách đọc từ đường ống và in ra màn hình.
Ví dụ : oneway_pipe.c
#include <stdio.h>
}
/* Đóng đường ống phía đầu ghi để thông báo phía cuối đường ống dữ
liệu đã ghi hết */
close(data_pipe[1]);
exit(0);
}
/* Chương trình chính*/
int main()
{
int data_pipe[2];
int pid; /* pid của tiến trình con*/
int rc; /* lưu mã lỗi trả về */
/*Tạo đường ống */
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 16
rc = pipe(data_pipe);
if (rc == -1) {
perror(“pipe create error”);
exit(1);
}
/* Tách đôi tiến trình con*/
pid = fork();
switch (pid) {
case –1 :
perror (“fork error”);
exit(1);
case 0:
do_child(data_pipe);
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 17
1.8.4. Pipe đặt tên
Qua ví dụ trên ta thấy đường ống chỉ được tạo ra và truyền dữ liệu giữa hai tiến
trình cha và con. Tiến trình cha gọi fork() để chuyển giao và gắn đường ống vào tiến
trình con, tiến trình con nhận dữ liệu xử lý và chuyển về cho tiến trình cha. Đường ống
tạo ra bằng hàm pipe() được gọi là đường ống vô danh. Nó chỉ được sử dụng giữa tiến
trình cha và con do ta chủ động tạo ra bằng hàm fork(). Một vấn đề đặt ra là, nếu hai tiến
trình không có quan hệ gì với nhau thì có thể sử dụng được cơ chế pipe để giao tiếp với
nhau không? Câu trả lời là có, Linux cho phép ta tạo ra các đường ống đặt tên. Những
đường ống mang tên sẽ nhìn thấy và truy xuất được bởi các tiến trình khác nhau.
* Tạo pipe đặt tên với hàm mkfifo()
Đường ống đặt tên còn được gọi với một tên khác là đối tượng FIFO. Thật sự
đường ống mang tên được biểu diễn như là một file trong hệ thống file của Linux. Ta
dùng lệnh mkfifo để tạo file đường ống với tên chỉ định. Trong chương trình, ta dùng
hàm hệ thống mkfifo() hay mknod(). Khai báo hàm mkfifo() :
#include <sys/types.h>
#include <sys/stat.h>
mkfifo(const char *filename, mode_t mode);
Hàm mkfifo() yêu cầu hai đối số, đối số filename là tên của đường ống cần tạo,
mode là chế độ đọc ghi của đường ống.
Việc tạo đường ống đặt tên không liên quan gì đến các tiến trình. Ta có thể tạo ra
pipe trước khi xây dựng các chương trình sử dụng nó.
Các hàm,biến quan trọng sử dụng trong chương trình
Dùng 2 đường ống pipe : input_pipe và output_pipe.
ParentProcess (int input[2], int output[2]) : Hàm bao gồm các công việc mà tiến
trình cha thực hiện như nhập chuỗi biểu thức từ bàn phím, ghi vào input_pipe ;
đọc kết quả xử lý từ output_pipe nhằm hiện thị cho người dùng biết.
ChildProcess(int input[2], int output[2]): Hàm bao gôm các công việc mà tiến
trình con thực hiện như đọc dữ liệu từ input_pipe, gọi hàm hauto() để xử lý dữ
liệu rồi ghi dữ liệu vào lại output_pipe.
Hauto(char* ch) : Hàm xử lí xâu biểu thức, phần tách thành dãy hậu tố, sau đó
tính kết quả của biểu thức và ghi vào cuối chuỗi đã nhập
AddChar(char* ch, char kt): Nối 1 kí tự vào cuối xâu ch
AddNumber(char *ch, int n): Đổi số thành kí tự và nối vào cuối xâu ch
inKt(char kt) : in ra kí tự
inNumber(int n): in ra số
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 19
Lựa chọn ngôn ngữ và IDE: Chương trình được code bẳng ngôn ngữ C trên Ubuntu, IDE
được sử dụng là CodeBlock
1.11. Chƣơng trình:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <ctype.h>
#include <stdbool.h>
#include <math.h>
if(rc ==-1){
perror("\tmain : output_pipe error");
exit(1);
}
pid = fork();
if (pid == 0)
ChildProcess(input_pipe, output_pipe);
else{
//wait( &child_status );
ParentProcess(input_pipe, output_pipe);
}
P1 xử lý biểu thức
nhập vào
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 20 }
void ChildProcess(int input[2], int output[2])
{
int rc;
char ch[100] = "";
close(input[1]);
close(output[0]);
rc = read(input[0], &ch, 100);
if (rc==-1)
printf("\n\tNhap bieu thuc: "); gets(ch);
rc = write(input[1], ch, strlen(ch));
if (rc==-1)
{
perror("Error");
close(input[0]);
close(input[1]);
exit(1);
} rc = read(output[0], ch, 100);
if (rc==-1)
{
perror("Error");
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 21
close(output[0]);
close(output[1]);
exit(1);
}
close(input[1]);
close(output[0]);
}
void xuly(char *ch){
if(isOperator(ch[i-1])) {
while(j>=0 && (stack_[j]=='a' || stack_[j]=='d'))
kq[++k] = stack_[j ];
stack_[++j] = 'a'; //truong hop nay - la dau am
break;
}
else{
while(j>=0 && (stack_[j]=='+' || stack_[j]=='-'
|| stack_[j]=='*' || stack_[j]=='/' || stack_[j]=='^' || stack_[j]=='a'
|| stack_[j]=='d') ) kq[++k] = stack_[j ];
stack_[++j] = ch[i];
}
break;
case '*':
case '/':
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 22
if( (k<0) || isOperator(ch[i-1]) ) {
errorBt();//Bieu thuc loi
return;
break;
}
else{
while(j>=0 && (stack_[j]=='*' || stack_[j]=='/'
|| stack_[j]=='^' || stack_[j]=='a' || stack_[j]=='d') ) kq[++k] =
stack_[j ];
stack_[++j] = ch[i];
else i = isOther(i);
}
}
while(j>=0){
kq[++k] = stack_[j ];
}
printf("\n\tDang trung to: %s", ch);
printf("\n\tDang hau to: ");
for(i = 0; i <= k; i++){
switch(kq[i]){
case 'd':
inKt('+');
break;
case 'a':
inKt('-');
break;
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 23
case '+':
case '-':
case '*':
case '/':
case '^':
inKt(kq[i]);
break;
case 's':
printf("sin");
case '+':
temp = stack_[j] + stack_[j-1];
stack_[ j] = temp;
break;
case '-':
temp = stack_[j-1] - stack_[j];
stack_[ j] = temp;
break;
case '*':
temp = stack_[j] * stack_[j-1];
stack_[ j] = temp;
break;
case '/':
temp = stack_[j-1] / stack_[j];
stack_[ j] = temp;
break;
default:
stack_[++j] = kq[i];
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 24
}
}
if(j!=0) printf("\nTinh toan sai: j = %d",j);
else printf("\n\tKet qua bieu thuc: %d", stack_[0]);
//Bo sung ket qua sau chuoi da nhap
AddChar(ch, '=');
AddNumber(ch, stack_[0]);
void inNumber(int n){
char a[100];
int i = 0;
if(n < 0){
n = -n;
inKt('-');
}
do{
a[++i] = n % 10 + 48;
n = n /10;
}while(n);
for( ; i >=0 ; i ){
inKt(a[i]);
}
}
Báo cáo Đồ án Nguyên lý hệ điều hành
SVTH: Hoàng Thị Mai Liên Trang 25 bool isOperator(char ch){
switch(ch){
case '+':
case '-':
case '*':
case '/':