-muốn học tốt môn hệ điều hành đầu tiên bạn phải có kiến trúc về hệ điều hành, hoc tốt ở đây là sau khi học xong môn học này cái trình của bạn lên đến đâu nếu bạn chỉ học cho qua môn đây là 1 điều nguy hiểm và nó không đóng góp gì cho bạn bởi những người xuất sắc trong ngành ra ngoài mới thành công được, cho nên bạn còn cơ hội học thì hãy đam mê nghiên cứu nó khi ở nhàm, khi đó kiến thức đó mới là của bạn được, còn trên lớp là chỉ đi cho biết học cái gì thôi còn kiến thức đó không phải là của bạn đâu ngành này là tự học tự hiểu và tự tư duy, học ngành này nếu bạn học cả ngày cả đêm thì sau này chỉ việc đếm tiền
-phải có khả năng lập trình các phần mềm hệ thống
-phải cày máy ảo hoặc có máy chạy linux
-giỏi lập trình C
-master về lập trình hướng đối tượng và kĩ thuật lập trình
-biết những câu lệnh shell script
-đầu tiên phải biết trên hệ điều hành được coi như có 2 vùng: 
+user space có user mode
+kernel space có kernel mode mà user space không có quyền truy cập vào bộ nhớ của vùng kernel vậy phải có các API hay còn gọi là system call, hay nói cách khác system call là cơ chế để truy cập vào kernel
+tạo tiến trình con nó tạo hàm gọi hàm fork(), I/O nó gọi read write hay tạo ra cái đường ống IPC
môn này là môn rất nặng nên trước khi học phải xác định tâm thế rõ ràng
Trong hệ điểu hành có rất nhiều quá trình, luồng chạy trong từng mi-li giây, vậy liệu có cơ chế nào cho chúng giao tiếp với nhau? Bài sau đây sẽ giải thích cơ bản cho bạn biết về System Call, cũng như cách chúng gọi API trong hệ điều hành.
Table of Contents
  • SYSTEM CALL LÀ GÌ?
  • SYSTEM CALL SỬ DỤNG NHƯ THẾ NÀO?
  • API LÀ GÌ?
SYSTEM CALL LÀ GÌ?
System call cho người dùng cách tiếp cận những tiện ích/ dịch vụ của hệ điều hành. Mọi phương thức của người dùng qua giao diện (GUI), tập lệnh (Batch) hay câu lệnh (command line, như cmd trong window) đều có cùng mục đích chung là gọi system call, để yêu cầu hệ điều hành thực hiện tác vụ cho mình.
Thông thường, những system call được viết bằng ngôn ngữ lập trình C hoặc C++, hoặc assemble, đây là những ngôn ngữ lập trình cấp thấp (low-level), tức có khả năng tiếp cận trực tiếp đến bộ nhớ hay phần cứng máy tính.
SYSTEM CALL SỬ DỤNG NHƯ THẾ NÀO?
Trước hết, chúng ta cần hiểu system call được sử dụng như thế nào: Giả sử có một chương trình đơn giản có tác vụ đọc dữ liệu trong một file, và sao chép nó qua một file khác.
Để chương trình hoạt động bình thường, chương trình cần phải đọc tên của 2 file (system call 1: đọc file). Nếu có lỗi xảy ra, chương trình phải xuất một dòng báo lỗi ra màn hình cho người dùng (system call 2: xuất ra màn hình) và thoát chương trình ngay (system call 3: thoát). Nếu không có lỗi, sẽ đến một vòng lặp liên tục mà chương trình phải đọc từng dòng bên file này, và sao chép nó qua file kia (system call 4: sao chép). Nếu file đầu ra (file được sao chép dữ liệu đến) trùng tên file với một file có sẵn trong thư mục, phải tự tạo tên đuôi khác cho file (system call 5: tạo tên đuôi) hoặc không cho phép sao chép. Nếu ổ cứng có vấn đề trong việc đọc và ghi, phải được xuất tên lỗi cho người dùng (lại một system call nữa). Khi sao chép thành công, phải xuất ra màn hình báo người dùng “đã thành công”.
Như có thể thấy, một chương trình đơn giản cần tạo ra rất nhiều system call. Thông thường, một hệ thống máy tính thực thi hàng trăm system call mỗi giây.
API LÀ GÌ?
Đa số những lập trình viên không nhìn chi tiết đến vậy. Mà những lập trình viên ứng dụng thiết kế chương trình chạy bằng giao diện lập trình ứng dụng (application programming interface, gọi tắt là API). API là những hàm (function) có sẵn cho các cho các lập trình viên.
Các loại API phổ biến nhất:
  • Window API của hệ điều hành Window
  • POSIX API cho các hệ điều hành chạy nền tảng POSIX
  • Java API cho các chương trình chạy trên máy ảo Java
Một lập trình viên có thể truy cập API thông qua các hàm có sẵn trong các thư viện của hệ điều hành. Nếu hệ điều hành được viết bằng ngôn ngữ lập trình C (Unix hay Linux), tên của thư viện đó là libc. Và các hàm đó có chức năng gọi system call thay các lập trình viên.
Ví dụ của một API chuẩn:
api chuan


Một chương trình có sử dụng hàm read() (đọc) phải “include” thư viện unistd.h. Khi đọc thành công, Số lượng byte đã đọc sẽ được trả về.
Tại sao các lập trình viên thích sử dụng API hơn sử dụng thẳng system call?
Đó là vì:
  • Đa hỗ trợ: lập trình sử dụng API có thể chạy trên các hệ điều hành khác nhau nếu chúng có hỗ trợ cùng API.
  • Dễ hơn: hệ thống thật sẽ vô cùng chi tiết và khó lập trình.
system call interface


Với đa số các ngôn ngữ lập trình, sẽ có một lớp ngăn cách giữa các ứng dụng và hệ điều hành gọi là system call interface. Các ứng dụng trên máy tính muốn xài chức năng nào, dịch vụ nào của hệ điều hành phải gọi thông qua lớp này. Mỗi dịch vụ của hệ điều hành đều được đánh số, hệ điều hành sẽ tìm trong hệ thống của mình và sẽ trả về dịch vụ có số đánh tương ứng cho các ứng dụng.
Bài viết liên quan: quy trình nạp hệ điều hành .
System Call
Trong kiến trúc Linux, không gian bộ nhớ được chia thành hai phần là user space và kernel space. Theo đó, cũng tồn tại hai chế độ (mode) là user mode và kernel mode. Các chỉ lệnh được gọi từ chương trình như đóng mở file (fopen, fclose), hoặc in một thông tin (printf) chỉ có thể thực thi và truy cập vùng nhớ ở tầng user mà không được truy cập vùng nhớ của kernel.
Cơ chế phân tách user space với kernel space và không cho phép người dùng tự ý truy cập tài nguyên của kernel giúp quản lý và bảo vệ kernel cũng như toàn bộ hệ thống. Thật vậy, khi bạn truy cập vùng nhớ trái phép trên user space thì chỉ ứng dụng của bạn crash, còn khi bạn truy cập trái phép vùng nhớ của kernel thì toàn bộ thiết bị của bạn sẽ bị crash.
Vấn đề đặt ra là làm cách nào để user gọi xuống kernel hay thao tác điều khiển các device driver? Để đáp ứng yêu cầu này, kernel cung cấp cho user space các API (còn gọi là các dịch vụ) là system call.
System call là một cửa ngõ vào kernel, cho phép tiến trình trên tầng user yêu cầu kernel thực thi một vài tác vụ cho mình. Những dịch vụ này có thể là tạo một tiến trình mới (fork), thực thi I/O (read, write), hoặc tạo ra một pipe cho giao tiếp liên tiến trình (IPC).
Có một số điều cần chú ý về system call như sau:
  • Khi một tiến trình gọi một system call, CPU sẽ chuyển từ chế độ user mode sang kernel mode, điều này cho phép CPU truy cập các vùng nhớ và thực hiện các chỉ lệnh của kernel.
  • Mỗi system call được kernel định danh bằng một số duy nhất. Tiến trình trên tầng user không biết đến các số này, thay vào đó, nó gọi một system call bằng tên hàm (ví dụ như open(), read()...).
  • Mỗi system call có thể có một số tham số truyền để cung cấp thông tin từ user truyền xuống kernel và ngược lại.
Quá trình thực thi system call
Đứng trên góc nhìn của lập trình viên, việc gọi một system call trông có vẻ như là chỉ gọi một hàm C bình thường. Tuy nhiên, đằng sau việc đó là rất nhiều bước được thực hiện từ user space xuống kernel space.
Cụ thể, chúng ta thử xét việc gọi một hàm thư viện được dùng rất thường xuyên sau đây:
Hàm gọi:
#include
 FILE *fopen(const char *filename, const char *mode)
Hàm fopen() là một hàm thư viện (wrapper function) được dùng để thực thi việc chuyển xuống kernel mode và yêu cầu kernel mở một file dưới kernel có đường dẫn là “filename” với chế độ "mode", chi tiết về hàm này các bạn xem tại đây. Hàm fopen() được triển khai bằng cách gọi system call open(), cụ thể các bước như sau:
  1. Hàm wrapper copy các đối số ( trong trường hợp này là "filename" và "mode") vào các thanh ghi, nơi mà các lệnh của luồng thực thi system call sẽ đọc và sử dụng được.
  2. Hàm wrapper sao chép số system call vào một thanh ghi của CPU (%eax). Ví dụ system call number của open() là 5, hàm này sẽ sao chép giá trị 5 vào thanh ghi %eax.
  3. Hàm wrapper thực hiện một chỉ lệnh máy gọi là trap machine instruction để chuyển chế độ CPU từ user mode sang kernel mode. Chỉ lệnh này có thể là một ngắt mềm (software interrupt) với số ngắt (interrupt number) là 0x80 (int 0x80) hoặc chỉ lệnh SYSENTER (trong các kiến trúc Intel gần đây) hoặc chỉ lệnh SYSCALL (trong AMD)
  4. Kernel gọi đến luồng system_call (nằm trong file arch/x86/entry_32.S), tại đây nó sẽ làm các công việc: copy giá trị các đối số trong các thanh ghi mà đã copy vào trong bước 1 vào kernel stack; kiểm tra tính hợp lệ của các đối số; gọi đến system call service routine thích hợp bằng cách tra cứu số system call được sao chép ở bước 2 trong bảng system call routine (sys_call_table); gửi kết quả trả về lên cho hàm wrapper và cuối cùng là chuyển chế độ của CPU từ kernel mode sang user mode.
  5. Hàm wrapper trả về giá trị là một số nguyên cho hàm gọi nó để thông báo lời gọi system call có thành công không. Nếu system call trả về giá trị lỗi, hàm wrapper sẽ set giá trị cho một biến toàn cục “errno” từ giá trị lỗi này.
Lưu ý rằng các bước trên chỉ giới thiệu tinh thần chung của việc tiếp nhận xử lý một system call, các kiến trúc khác nhau sẽ có các cách triển khai tinh thần chung đó khác nhau ít nhiều.
Để hiểu rõ hơn về bức tranh tổng thể của system call, chúng ta sẽ đi vào chi tiết cách system call open(). Trong Linux x86_32, open() có số system call là 5, vì vậy trong system call vector (arch/x86/entry/syscall/syscall_32.tbl), open system call sẽ là entry thứ 5, tương ứng với system call routine là sys_open.


Từ đây, kernel sẽ gọi đến hàm sys_open() trong /fs/open.c để thực thi việc mở một file trong hệ thông file system và trả về một mô tả file fd cho user. Cụ thể, chúng ta xem hình vẽ chi tiết như hình dưới đây:


Kết luận
System call là một cơ chế quan trọng mà bất kỳ lập trình viên Linux nào cũng phải nắm được. Sử dụng system call để yêu cầu dịch vụ hoặc tài nguyên của kernel không những giúp bảo vệ được hệ thống mà còn giúp cho công việc lập trình viên trở nên dễ dàng hơn. Theo đó, bạn chỉ cần sử dụng các API thân thiện với người dùng hơn là phải quan tâm đến các thanh ghi và luồng của hệ thống.
Bạn cũng cần lưu ý rằng các kiến trúc CPU khác nhau thì cách triển khai xử lý system call cũng có thể khác nhau. Vì vậy, bạn cũng không cần cố gắng chọc ngoáy sâu xuống code của kernel về việc xử lý system call (trừ khi bạn cần tạo một system call của riêng mình), mà chỉ cần dùng các hàm wrapper của thư viện C là đủ.
Bài học sau sẽ giới thiệu về các thư viện hàm thư viện C trong lập trình Linux.