Atomic linux là gì
Giới thiệu Như đã nói trong bài học trước, để ngăn chặn hiện tượng race condition, khi lập trình device driver, ta phải tuân theo nguyên tắc mutual exclusion: tại một thời điểm, chỉ có một thread được phép truy cập critical resource. Linux đã xây dựng một số kỹ thuật đồng bộ dựa theo nguyên tắc này. Đôi khi, critical resource chỉ là một biến hay một bit, và thao tác truy cập cũng đơn giản (tăng, giảm,…). Đối với những trường hợp như vậy, Linux cung cấp kỹ thuật atomic để ngăn không cho race condition xảy ra. Bài học hôm nay sẽ trình bày về cơ chế cũng như cách sử dụng kỹ thuật atomic trong lập trình device driver. Cơ chếĐể hiểu về kỹ thuật atomic, ta xét ví dụ sau. Giả sử, device driver có 2 thread. Trong quá trình hoạt động, cả hai đều có nhu cầu sử dụng biến toàn cục a. Giả sử, mỗi khi truy cập biến a, các thread sẽ thực thi lệnh ++a. Thực chất, sau khi biên dịch, lệnh này được tách nhỏ thành 3 lệnh assembly sau:
Nếu cả hai thread truy cập biến a cùng lúc, thì race condition rất có thể sẽ xảy ra. Xét tình huống sau (giả sử ban đầu a = 0):
Lẽ ra, kết quả cuối cùng của a phải bằng 2 thì nó lại chỉ bằng 1. Để giải quyết vấn đề này, ta có thể sử dụng thao tác atomic. Thao tác atomic sẽ gộp 3 thao tác nhỏ thành 1 thao tác duy nhất.
Chú ý rằng, do giới hạn về phần cứng, nên 2 lõi không thể truy cập RAM đồng thời được. Chính vì vậy, 2 lệnh trên xảy ra ở 2 thời điểm khác nhau. Linux kernel hỗ trợ 2 nhóm thao tác atomic. Một nhóm dùng để truy cập critical resource là số nguyên (atomic integer operation). Một nhóm dùng để truy cập critical resource là bit (atomic bitwise operation). Dưới đây, chúng ta sẽ cùng tìm hiểu cách sử dụng 2 nhóm này. Các thao tác atomic trên số nguyênKhi critical resource là một số nguyên, và ta muốn áp dụng kỹ thuật atomic để bảo vệ critical resource đó, ta cần khai báo số nguyên này theo kiểu atomic_t, chứ không phải theo kiểu int. Dưới đây là các thao tác atomic làm việc với số nguyên có kiểu atomic_t. Hầu hết các kiến trúc vi xử lý đều chứa những thao tác này trong tập lệnh.
Các thao tác atomic trên bitKhi critical resource là một bit, và ta muốn áp dụng kỹ thuật atomic để bảo vệ critical resource đó, ta có thể sử dụng các thao tác sau:
Dưới đây là một ví dụ về sử dụng các thao tác này:
Case studyTrong ví dụ này, chúng ta sẽ áp dụng các thao tác atomic trên số nguyên để cải thiện vchar driver trong bài hôm trước. Đầu tiên, ta tạo thư mục cho bài học ngày hôm nay như sau:
Sau đó, trong vchar_driver.c, ta thay đổi lại khai báo của biến critical_resource. Cuối cùng, ta thay thế các thao tác cộng/trừ thông thường bằng các thao tác atomic để tránh xảy ra race condition trên biến này. Bây giờ, ta gõ lệnh make để biên dịch lại char driver. Sau khi biên dịch thành công, ta thực hiện kiểm tra như hình 1 dưới đây và thấy rằng, kết quả cuối cùng của biến critical_resource đúng bằng 3,145,728: Hình 1. Sử dụng các thao tác atomic giúp ngăn ngừa race condition trên biến critical_resource Kết luậnKhi critical resource chỉ là một số nguyên hoặc một bit, và thao tác truy cập cũng đơn giản (cộng, trừ, tăng, giảm,…), lập trình viên nên áp dụng kỹ thuật atomic để tránh xảy ra race condition. Trong các bài học tiếp theo, ta sẽ cùng tìm hiểu các kỹ thuật đồng bộ khác cho phép bảo vệ các crtical resource phức tạp hơn. |