lOMoARcPSD| 58833082
Chương 12 : I/O SYSTEM
Hai công việc chính của máy tính là I/O và tính toán. Trong nhiều trường hợp, công việc
chính là I/O và việc tính toán hoặc xử lý chỉ là ngẫu nhiên. Chẳng hạn, khi chúng ta duyệt
một trang web hoặc chỉnh sửa một tệp, mối quan tâm ngay lập tức của chúng ta là đọc
hoặc nhập một số thông tin, chứ không phải tính toán câu trả lời.
Vai trò của hệ điều hành trong I/O máy tính là quản lý và kiểm soát các hoạt động I/O và
thiết bị I/O. Mặc dù các chủ đề liên quan xuất hiện trong các chương khác, nhưng ở đây
chúng tôi tập hợp các phần lại với nhau để vẽ nên một bức tranh hoàn chỉnh về I/O. Đầu
tiên, chúng tôi mô tả nhng điều cơ bản về phần cứng I/O, bởi vì bản chất của giao diện
phần cứng đặt ra những hạn chế đối với các cơ sở bên trong của hệ điều hành. Tiếp theo,
chúng ta thảo luận về các dịch vụ I/O được cung cấp bởi hệ điều hành và hiện thân của các
dịch vụ này trong giao diện I/O của ứng dụng. Sau đó, chúng tôi giải thích cách hệ điu
hành thu hẹp khoảng cách giữa giao diện phần cứng và giao diện ứng dụng. Chúng tôi
cũng thảo luận về cơ chế UNIX System V STREAM, cho phép một ứng dụng lắp ráp các
đường dẫn mã trình điều khiển một cách linh hoạt. Cuối cùng, chúng ta thảo luận về các
khía cạnh hiệu suất của I/O và các nguyên tắc thiết kế hệ điều hành giúp cải thiện hiệu suất
I/O
CHƯƠNG MỤC TIÊU
Khám phá cấu trúc của hệ thống con I/O của một hđiều hành.
Thảo luận về các nguyên tắc và sự phức tạp của phần cứng I/O.
Giải thích các khía cạnh hiệu suất của phần cứng và phần mềm I/O
12.1 Tổng quan
Việc kiểm soát các thiết bị kết nối với máy tính là mối quan tâm chính của các nhà thiết
kế hệ điều hành. Bởi vì các thiết bị I/O rất khác nhau về chức năng và tốc độ của chúng (ví
dụ như chuột, đĩa cứng, ổ đĩa flash và rô-bốt băng từ), nên cần có nhiều phương pháp khác
nhau để điều khiển chúng. Các phương thức này tạo thành hệ thống con I/O của kernel,
phân tách phần còn lại của kernel khỏi sự phức tạp của việc quản lý các thiết bI/O
Công nghệ thiết bị I/O thể hiện hai xu hướng trái ngược nhau. Một mặt, chúng ta thấy
tiêu chuẩn hóa ngày càng tăng của giao diện phần mềm và phần cứng. Xu hướng này giúp
chúng tôi kết hợp các thế hệ thiết bị cải tiến vào các máy tính và hệ điều hành hiện có. Mặt
khác, chúng ta thấy ngày càng có nhiều loại thiết bI/O. Mt số thiết bị mới không giống các
thiết bị trước đây đến mức khó kết hợp chúng vào máy tính và hệ điều hành của chúng ta.
Thách thức này được giải quyết bằng sự kết hợp giữa kỹ thuật phần cứng và phần mềm.
Các phần tử phần cứng I/O cơ bản, chẳng hạn như cổng, bus và bộ điều khiển thiết bị, phù
hợp với nhiều loại thiết bị I/O. Để đóng gói các chi tiết và điểm kỳ lạ của các thiết bị khác
nhau, nhân của một hệ điều hành được cấu trúc để sử dụng các mô-đun trình điều khiển
thiết bị. Trình điều khiển thiết bị trình bày giao diện truy cập thiết bthống nhất cho hệ thống
con I/O, giống như các lệnh gọi hệ thống cung cấp giao diện tiêu chuẩn giữa ứng dụng và
hệ điều hành
lOMoARcPSD| 58833082
12.2 Phần cứng I/O
Máy tính vận hành rất nhiều loại thiết bị. Hầu hết phù hợp với các loại thiết bị lưu trữ
chung (đĩa, băng), thiết bị truyền dẫn (kết nối mạng, Bluetooth) và thiết bị giao diện người
dùng (màn hình, bàn phím, chuột, đầu vào và đầu ra âm thanh). Các thiết bị khác chuyên
dụng hơn, chẳng hạn như những thiết bị liên quan đến việc điều khiển máy bay phản lực.
Trong những chiếc máy bay này, con người cung cấp đầu vào cho máy tính chuyến bay
thông qua cần điều khiển và bàn đạp chân, đồng thời máy tính gửi các lệnh đầu ra khiến
động cơ di chuyển bánh lái và cánh tà cũng như nạp nhiên liệu cho động cơ. Tuy nhiên, bất
chấp sự đa dạng đáng kinh ngạc của các thiết bị I/O, chúng ta chỉ cần một vài khái niệm để
hiểu cách các thiết bđược gắn vào và cách phần mềm có thể điều khiển phần cứng.
Một thiết bị giao tiếp với hệ thống máy tính bằng cách gửi tín hiệu qua cáp hoặc thậm
chí qua không khí. Thiết bị giao tiếp với máy thông qua điểm kết nối hoặc cổng— ví dụ:
cổng nối tiếp. (Thuật ngữ PHY, viết tt của lớp vật lý mô hình OSI, cũng được sử dụng để
chỉ các cổng nhưng phổ biến hơn trong danh pháp trung tâm dữ liệu.) Nếu các thiết bị chia
sẻ một bộ dây chung, kết nối được gọi là b. Một bus, giống như bus PCI được sử dụng
trong hầu hết các máy tính ngày nay, là một tập hợp các dây và một giao thức được xác
định rõ ràng để chỉ định một tập hợp các thông báo có thể được gửi trên các dây. Về mặt
điện tử, các thông điệp được truyền tải bằng các mẫu điện áp đặt vào dây dẫn với thời gian
xác định. Khi thiết bA có cáp cắm vào thiết bị B và thiết bị B có cáp cắm vào thiết bị C và
thiết bị C cắm vào một cổng trên máy tính, sự sắp xếp này được gọi là chuỗi xích. Một
chuỗi daisy thường hoạt động như một chiếc xe buýt
Bus được sử dụng rộng rãi trong kiến trúc máy tính và khác nhau về phương thức
truyền tín hiệu, tốc độ, thông lượng và phương thức kết nối. Một cấu trúc bus PC điển hình
xuất hiện trong Hình 12.1. Trong hình, một bus PCIe (bus hệ thống PC phổ biến) kết nối hệ
thống con bộ xử -bộ nhớ với các thiết bị nhanh và một bus mở rộng kết nối các thiết bị
tương đối chậm, chẳng hạn như bàn phím, cổng nối tiếp và cổng USB. Ở phần phía dưới
bên trái của hình, bốn đĩa được kết nối với nhau trên một bus SCSI (SAS) gắn nối tiếp
được cắm vào bộ điều khiển SAS. PCIe là một bus linh hoạt gửi dữ liệu qua một hoặc nhiều
“làn”. Một làn bao gồm hai cặp tín hiệu, một cặp để nhận dữ liệu và cặp kia để truyền. Do
đó, mỗi làn bao gồm bốn dây và mỗi làn được sử dụng làm luồng byte song công hoàn
toàn, vận chuyển các gói dữ liu ở định dạng byte tám bit đồng thời theo cả hai hướng. Về
mặt vật lý, các liên kết PCIe có thể chứa 1, 2, 4, 8, 12, 16 hoặc 32 làn, được biểu thị bằng
tiền tố “x”. Ví dụ: thẻ hoc đầu nối PCIe sử dụng 8 làn được chỉ định là x8. Ngoài ra, PCIe
đã trải qua nhiều “thế hệ”, và sẽ có nhiều thế hệ hơn trong tương lai. Vì vậy, ví dụ, một thẻ
có thể là “PCIe gen3 x8”, có nghĩa là nó hoạt động với thế hệ 3 của PCIe và sử dụng 8 làn.
Một thiết bị như vậy có thông lượng tối đa là 8 gigabyte mỗi giây. Thông tin chi tiết về PCIe
có thể được tìm thấy tại https://pcisig.com
lOMoARcPSD| 58833082
Bộ điều khiển là một tập hợp các thiết bđiện tử có thể vận hành một cổng, một xe buýt
hoặc một thiết bị. Bộ điều khiển cổng nối tiếp là bộ điều khiển thiết bị đơn giản. Đó là một
con chip (hoặc một phần của con chip) trong máy tính điều khiển các tín hiệu trên dây của
cổng nối tiếp. Ngược lại, bộ điều khiển bus kênh sợi quang (FC) không đơn giản. Do giao
thức FC phức tạp và được sử dụng trong các trung tâm dliệu hơn là trên PC nên bộ điu
khiển bus FC thường được triển khai dưới dạng một bảng mạch riêng—hoặc bộ điều hợp
bus chủ (HBA)— kết nối với bus trong máy tính. Nó thường chứa một bộ xử lý, vi mã và
một số bộ nhriêng để cho phép nó xử lý các thông báo giao thức FC. Một số thiết bị có bộ
điều khiển tích hợp riêng. Nếu bạn nhìn vào ổ đĩa, bạn sẽ thấy một bảng mạch được gắn
một bên. Bảng này là bộ điều khiển đĩa. Nó thực hiện phía đĩa của giao thức cho một s
loại kết nối chẳng hạn như SAS và SATA. Nó có vi mã và bộ xử lý để thực hiện nhiều tác
vụ, chẳng hạn như lập bản đồ khu vực xấu, tìm nạp trước, tạo bộ đệm và lưu vào bộ nh
đệm.
12.2.1 I/O ánh xạ bộ nh
Làm thế nào để bộ xử lý đưa ra các lệnh và dữ liệu cho bộ điều khiển để thực hiện
chuyển giao I/O? Câu trả lời ngắn gọn là bộ điều khiển có một hoặc nhiều thanh ghi dữ liu
và tín hiệu điều khiển. Bộ xử lý giao tiếp với bộ điều khiển bằng cách đọc và ghi các mẫu bit
trong các thanh ghi này. Một cách mà giao tiếp này có thể xảy ra là thông qua việc sử dụng
các lệnh I/O đặc biệt chỉ định việc truyền một byte hoặc một từ tới một địa chỉ cổng I/O.
Lệnh I/O kích hoạt các đường bus để chọn thiết bị thích hợp và di chuyển các bit vào hoặc
ra khỏi thanh ghi thiết bị. Ngoài ra, thiết bị có thể hỗ trợ I/O được ánh xạ bộ nhớ. Trong
trường hợp này, các thanh ghi điều khiển thiết bđược ánh xạ vào không gian địa chỉ của
lOMoARcPSD| 58833082
bộ xử lý. CPU thực thi các yêu cầu I/O bằng cách sử dụng các lệnh truyền dữ liệu tiêu
chuẩn để đọc và ghi các thanh ghi điều khiển thiết bị tại các vị trí được ánh xạ của chúng
trong bộ nhớ vật lý
Trước đây, PC thường sử dụng các lệnh I/O để điều khiển một số thiết bị và I/O được
ánh xạ bộ nhớ để điều khiển các thiết bị khác. Hình 12.2 hiển thị các địa chỉ cổng I/O thông
thường cho PC. Bộ điều khiển đồ họa có các cổng I/O cho các hoạt động điều khiển cơ
bản, nhưng bộ điều khiển có vùng ánh xạ bộ nhớ lớn để chứa nội dung màn hình. Một
luồng gửi đầu ra tới màn hình bằng cách ghi dữ liu vào vùng ánh xạ bộ nh. Bđiều khiển
tạo ra hình ảnh màn hình dựa trên nội dung của bộ nhớ này. Kỹ thuật này là đơn giản để sử
dụng. Hơn nữa, việc ghi hàng triệu byte vào bộ nhớ đồ họa nhanh hơn việc đưa ra hàng
triệu lệnh I/O. Do đó, theo thời gian, các hệ thống đã chuyển sang I/O được ánh xạ bộ nh.
Ngày nay, hầu hết I/O được thực hiện bởi bộ điều khiển thiết bị sử dụng I/O ánh xạ bộ nh
Điều khiển thiết bị I/O thường bao gồm bốn thanh ghi, được gọi là trạng thái, các
thanh ghi điều khiển, nhập dữ liệu và xuất dữ liu.
Thanh ghi dữ liệu vào được máy chủ đọc để nhận đầu vào.
Thanh ghi dữ liệu ra được ghi bởi máy chủ để gửi đầu ra.
Thanh ghi trạng thái chứa các bit mà máy chủ có thể đọc được. Các bit này cho biết
các trạng thái, chẳng hạn như liệu lệnh hiện tại đã hoàn thành hay chưa, liệu một byte có
sẵn để đọc từ thanh ghi dữ liệu vào hay không và liệu có xảy ra lỗi thiết bị hay không.
lOMoARcPSD| 58833082
Thanh ghi điều khiển có thể được ghi bởi máy chủ để bắt đầu một lệnh hoặc để thay
đổi chế độ của thiết bị. Chẳng hạn, một bit nhất định trong thanh ghi điều khiển của cổng
nối tiếp chọn giữa giao tiếp song công hoàn toàn và bán song công, bit khác cho phép
kiểm tra chẵn lẻ, bit thứ ba đặt độ dài từ thành 7 hoặc 8 bit và các bit khác chọn một trong
các tốc độ được hỗ trợ bởi cổng nối tiếp.
Các thanh ghi dữ liệu thường có kích thước từ 1 đến 4 byte. Một số bộ điều khiển có
chip FIFO có thể chứa vài byte dữ liệu đầu vào hoặc đầu ra để mở rộng dung lượng của bộ
điều khiển ngoài kích thước của thanh ghi dữ liệu. Chip FIFO có thể chứa một loạt dữ liu
nhỏ cho đến khi thiết bị hoặc máy chủ có thể nhận những dữ liệu đó.
12.2.2 Polling
Giao thức hoàn chỉnh cho sự tương tác giữa máy chủ và bộ điều khiển có thể phức tạp,
nhưng khái niệm bắt tay cơ bản thì đơn giản. Chúng tôi giải thích việc bắt tay bằng một ví
dụ. Giả sử rằng 2 bit được sử dụng để điều phối mối quan hệ người sản xuất-người tiêu
dùng giữa bộ điều khiển và máy chủ. Bộ điều khiển cho biết trạng thái của nó thông qua bit
bận trong thanh ghi trạng thái. (Nhớ lại rằng để thiết lập một bit có nghĩa là ghi 1 vào bit và
xóa một bit có nghĩa là ghi 0 vào nó.) Bộ điều khiển thiết lập bit bận khi nó đang bận làm
việc và xóa bit bận khi nó sẵn sàng hoạt động. chấp nhận lệnh tiếp theo. Máy chủ báo hiệu
mong muốn của nó thông qua bit sẵn sàng cho lệnh trong thanh ghi lệnh. Máy chủ đặt bit
sẵn sàng cho lệnh khi có lệnh để bộ điều khiển thực thi. Đối với ví dụ này, máy chủ ghi đầu
ra thông qua một cổng, phối hợp với bộ điều khiển bằng cách bắt tay như sau.
1. Máy chủ liên tục đọc bit bận cho đến khi bit đó trở nên rõ ràng.
2. Máy chủ đặt bit ghi trong thanh ghi lệnh và ghi một byte vào thanh ghi dliu
ra.
3. Máy chủ đặt bit sẵn sàng cho lệnh.
4. Khi bộ điều khiển thông báo rằng bit sẵn sàng cho lệnh được đặt, nó sẽ đặt bit
bận.
5. Bộ điều khiển đọc thanh ghi lệnh và xem lệnh ghi. Nó đọc thanh ghi xuất dữ
liệu để lấy byte và thực hiện I/O cho thiết bị.
6. Bộ điều khiển xóa bit sẵn sàng cho lệnh, xóa bit lỗi trong thanh ghi trạng thái
để cho biết rằng I/O của thiết bị đã thành công và xóa bit bận để cho biết rằng nó đã kết
thúc
Vòng lặp này được lặp lại cho mỗi byte
ớc 1, máy chủ đang bận chờ đợi hoặc bỏ phiếu: nó ở trong một vòng lặp, đọc đi
đọc lại thanh ghi trạng thái cho đến khi bit bận trở nên rõ ràng. Nếu bộ điều khiển và thiết bị
nhanh, phương pháp này là một phương pháp hợp lý. Nhưng nếu thời gian chờ đợi có thể
lâu, chủ nhà có lẽ nên chuyển sang nhiệm vụ khác. Sau đó, làm thế nào để máy chủ biết
khi nào bộ điều khiển không hoạt động? Đối với một số thiết bị, máy chủ phải nhanh chóng
phục vụ thiết bị, nếu không dữ liệu sẽ bị mất. Chẳng hạn, khi dữ liệu đang truyền vào cổng
lOMoARcPSD| 58833082
nối tiếp hoặc từ bàn phím, bộ đệm nhỏ trên bộ điều khiển sẽ tràn và dữ liệu sẽ bị mất nếu
máy chủ đợi quá lâu trước khi quay lại đọc byte
Trong nhiều kiến trúc máy tính, ba chu kỳ ớng dẫn CPU là đủ để thăm dò một thiết
bị: đọc thanh ghi thiết bị, logic-và để trích xuất một bit trạng thái và phân nhánh nếu không
phải là 0. Rõ ràng, hoạt động bỏ phiếu cơ bản là hiệu quả. Tuy nhiên, việc bỏ phiếu trở nên
không hiệu quả khi nó được thử lặp đi lặp lại nhưng hiếm khi tìm thấy một thiết bị sẵn sàng
hoạt động, trong khi quá trình xử lý CPU hữu ích khác vẫn chưa hoàn thành. Trong những
trường hợp như vậy, có thể hiệu quả hơn nếu sắp xếp để bộ điều khiển phần cứng thông
báo cho CPU khi thiết bị sẵn sàng hoạt động, thay vì yêu cầu CPU thăm dò liên tục để hoàn
thành I/O. Cơ chế phần cứng cho phép thiết bị thông báo cho CPU được gọi là ngắt.
12.2.3 Ngắt
Cơ chế ngắt cơ bản hoạt động như sau. Phần cứng CPU có một dây được gọi là dây
yêu cầu ngắt mà CPU cảm nhận được sau khi thực hiện mọi lệnh. Khi CPU phát hiện ra
rằng bộ điều khiển đã xác nhận tín hiệu trên dòng yêu cầu ngắt, CPU sẽ thực hiện lưu trạng
thái và chuyển sang quy trình xử lý ngắt tại một địa chỉ cố định trong bộ nhớ. Trình xử lý
ngắt xác định nguyên nhân gây ra ngắt, thực hiện quá trình xử lý cần thiết, thực hiện khôi
phục trạng thái và thực hiện quay lại lệnh ngắt để đưa CPU về trạng thái thực thi trước khi
ngắt. Chúng tôi nói rằng bộ điều khiển thiết bị tạo ra một ngắt bằng cách xác nhận tín hiệu
trên dòng yêu cầu ngắt, CPU bắt ngắt và gửi nó đến bộ xử lý ngắt, và bộ xử lý sẽ xóa ngắt
bằng cách bảo dưỡng thiết bị. Hình 12.3 tóm tắt chu trình I/O điều khiển ngắt.
Chúng tôi nhấn mạnh đến việc quản lý ngắt trong chương này vì ngay cả các hệ thống
hiện đại chỉ có một người dùng cũng quản lý hàng trăm ngắt mỗi giây và các máy chủ lên
tới hàng trăm nghìn ngắt mỗi giây. Ví dụ: Hình 12.4 hiển thị đầu ra lệnh có độ trễ trên
macOS, cho thấy rằng trong hơn 10 giây, một máy tính để bàn yên tĩnh đã thực hiện gần
23.000 lần ngắt.
Cơ chế ngắt cơ bản vừa được mô tả cho phép CPU phản hồi sự kiện không đồng bộ,
chẳng hạn như khi bộ điều khiển thiết bị sẵn sàng hoạt động. Tuy nhiên, trong một hệ điu
hành hiện đại, chúng ta cần các tính năng xử lý ngắt phức tạp hơn.
1. Chúng tôi cần khả năng trì hoãn xử lý ngắt trong quá trình xử lý quan trọng.
2. Chúng ta cần một cách hiệu quả để gửi đến trình xử lý ngắt phù hợp cho một thiết bị
mà không cần bỏ phiếu trước cho tất cả các thiết bị để xem thiết bị nào gây ra ngắt.
3. Chúng ta cần các ngắt đa cấp để hệ điều hành có thể phân biệt giữa các ngắt có
mức độ ưu tiên cao và thấp và có thể phản hồi với mức độ khẩn cấp thích hợp khi có nhiều
ngắt đồng thời.
lOMoARcPSD| 58833082
4. Chúng tôi cần một cách để ớng dẫn thu hút sự chú ý của hệ điều hành một cách
trực tiếp (tách biệt với các yêu cầu I/O), đối với các hoạt động như lỗi trang và lỗi chẳng
hạn như chia cho 0. Như chúng ta sẽ thấy, nhiệm vụ này được thực hiện bằng “bẫy”.
Trong phần cứng máy tính hiện đại, các tính năng này được cung cấp bởi CPU và phần
cứng của bộ điều khiển ngắt.
Hầu hết các CPU đều có hai dòng yêu cầu ngắt. Một là ngắt không che được, được
dành riêng cho các sự kiện như lỗi bộ nhớ không thể phục hồi. Dòng ngắt thứ hai có thể
che được: CPU có thể tắt nó trước khi thực hiện các chuỗi lệnh quan trọng không được
ngắt. Ngắt có thể che dấu được bộ điều khiển thiết bị sử dụng để yêu cầu dịch vụ.
lOMoARcPSD| 58833082
Cơ chế ngắt chấp nhận một địa chỉ—một số chọn một thói quen xử lý ngắt cụ thể từ
một tập hợp nhỏ. Trong hầu hết các kiến trúc, địa chỉ này là một phần bù trong bảng được
gọi là vectơ ngắt. Vectơ này chứa các địa chỉ bộ nhớ của các trình xử lý ngắt chuyên biệt.
Mục đích của cơ chế ngắt theo vectơ là giảm nhu cầu về một trình xử lý ngắt duy nhất để
tìm kiếm tất cả các nguồn ngắt có thể có để xác định cái nào cần dịch vụ. Tuy nhiên, trong
thực tế, máy tính có nhiều thiết bị hơn (và do đó, các trình xử lý ngắt) hơn là chúng có các
thành phần địa chỉ trong vectơ ngắt. Một cách phổ biến để giải quyết vấn đề này là sử dụng
chuỗi ngắt, trong đó mỗi phần tử trong vectơ ngắt trỏ đến phần đầu của danh sách các trình
xử lý ngắt. Khi một ngắt được đưa ra, các trình xử lý trong danh sách tương ứng được gọi
từng cái một, cho đến khi tìm thấy một trình xử lý có thể phục vụ yêu cầu. Cấu trúc này là
sự thỏa hiệp giữa chi phí hoạt động của một bảng ngắt khổng lồ và sự kém hiệu quả của
việc gửi tới một trình xử lý ngắt duy nhất
Hình 12.5 minh họa thiết kế của vectơ ngắt cho bộ xử lý Intel Pen tium. Các sự kiện từ
0 đến 31, không thể ẩn được, được sử dụng để báo hiệu các tình trạng lỗi khác nhau (gây
ra sự cố hệ thống), lỗi trang (cần hành động ngay lập tức) và yêu cầu gỡ lỗi (dừng hoạt
động bình thường và chuyển sang ứng dụng trình gỡ lỗi). Các sự kiện từ 32 đến 255, có
thể che được, được sử dụng cho các mục đích như ngắt do thiết bị tạo.
Cơ chế ngắt cũng thực hiện một hthống các mức ưu tiên ngắt. Các mức này cho
phép CPU trì hoãn việc xử lý các ngắt có mức độ ưu tiên thấp mà không che dấu tất cả các
ngắt và giúp cho ngắt có mức độ ưu tiên cao có thể chiếm trước việc thực hiện ngắt có
mức độ ưu tiên thấp.
lOMoARcPSD| 58833082
Một hđiều hành hiện đại tương tác với cơ chế ngt theo một số cách. Tại thời điểm
khởi động, hệ điều hành thăm dò các bus phần cứng để xác định thiết bị nào có mặt và cài
đặt các trình xử lý ngắt tương ứng vào vectơ ngắt. Trong quá trình I/O, các bộ điều khiển
thiết bị khác nhau sẽ tăng các ngắt khi chúng sẵn sàng hoạt động. Các ngắt này biểu thị
rằng đầu ra đã hoàn thành hoặc dữ liệu đầu vào có sẵn hoặc một lỗi đã được phát hiện. Cơ
chế ngắt cũng được sử dụng để xử lý nhiều loại ngoại lệ, chẳng hạn như chia cho 0, truy
cập địa chỉ bộ nhớ lều được bảo vệ hoc không tồn tại hoặc cố gắng thực hiện lệnh đặc
quyền từ chế độ người dùng. Các sự kiện kích hoạt các ngắt có một thuộc tính chung:
chúng là các sự kiện xảy ra khiến hệ điều hành thực thi một thói quen khẩn cấp, khép kín.
Do việc xử lý ngắt trong nhiều trường hợp bị hạn chế về thời gian và tài nguyên, do đó
phức tạp để thực hiện, các hệ thống thường phân chia việc quản lý ngắt giữa trình xử lý
ngắt cấp một (FLIH) và trình xử lý ngắt cấp hai (SLIH). FLIH thực hiện chuyển đổi ngữ
cảnh, lưu trữ trạng thái và xếp hàng của thao tác xử lý, trong khi SLIH được lập lịch trình
riêng biệt thực hiện xử lý thao tác được yêu cầu
lOMoARcPSD| 58833082
Các hệ điều hành cũng có những cách sử dụng tốt khác cho các ngắt. Ví dụ, nhiều hệ
điều hành sử dụng cơ chế ngắt để phân trang bộ nhớ ảo. Lỗi trang là một ngoại lệ làm tăng
ngắt. Ngắt sẽ tạm dừng tiến trình hiện tại và nhảy tới trình xử lý lỗi trang trong hạt nhân.
Trình xử lý này lưu trạng thái của quy trình, di chuyển quy trình vào hàng đợi, thực hiện
quản lý bộ đệm trang, lên lịch cho thao tác I/O để tìm nạp trang, lên lịch cho một quy trình
khác để tiếp tục thực thi và sau đó quay lại từ ngt.
Một ví dụ khác được tìm thấy trong việc thực hiện các cuộc gọi hệ thống. Thông
thường, một chương trình sử dụng lời gọi thư viện để thực hiện lời gọi hệ thống. Các
thường trình thư viện kiểm tra các đối số do ứng dụng đưa ra, xây dựng cấu trúc dữ liệu để
truyền các đối số tới nhân, sau đó thực hiện một lệnh đặc biệt gọi là ngắt phần mềm hoặc
bẫy. Hướng dẫn này có một toán hạng xác định dịch vụ hạt nhân mong muốn. Khi một quy
trình thực thi lệnh bẫy, phần cứng ngắt sẽ lưu trạng thái của mã người dùng, chuyển sang
chế độ nhân và gửi đến thủ tục nhân hoặc luồng thực hiện dịch vụ được yêu cầu. Cái bẫy
được cấp mức ưu tiên ngắt tương đối thấp so với mức ưu tiên được gán cho các ngắt của
thiết bị—việc thực hiện lệnh gọi hệ thống thay mặt cho một ứng dụng ít khẩn cấp hơn so
với việc bảo dưỡng bộ điều khiển thiết bị trước khi hàng đợi FIFO của nó bị tràn và mất dữ
liu.
gián đoạn cũng có thể được sử dụng để quản lý luồng điều khiển bên trong hạt nhân. Ví
dụ, hãy xem xét trường hợp xử lý cần thiết để hoàn thành việc đọc đĩa. Một bước có thể
sao chép dữ liệu từ không gian nhân vào bộ đệm người dùng. Việc sao chép này tốn thời
gian nhưng không khẩn cấp—nó không nên chặn việc xử lý ngắt ưu tiên cao khác. Một
ớc khác là bắt đầu I/O đang chờ xử lý tiếp theo cho ổ đĩa đó. Bước này có mức độ ưu
tiên cao hơn. Nếu các đĩa được sử dụng hiệu quả, chúng ta cần bắt đầu I/O tiếp theo ngay
sau khi hoàn thành I/O trước đó. Do đó, một cặp trình xử lý ngắt thực hiện mã hạt nhân để
hoàn thành việc đọc đĩa. Trình xử lý ưu tiên cao ghi lại trạng thái I/O, xóa ngắt thiết bị, bt
đầu I/O đang chờ xử lý tiếp theo và tăng ngắt ưu tiên thấp để hoàn thành công việc. Sau
đó, khi CPU không bận rộn với công việc có mức ưu tiên cao, ngắt có mức ưu tiên thấp sẽ
được gửi đi. Trình xử lý tương ứng hoàn thành I/O cấp người dùng bằng cách sao chép dữ
liệu từ bộ đệm nhân vào không gian ứng dụng, sau đó gọi bộ lập lịch để đặt ứng dụng vào
hàng đợi sẵn sàng.
Kiến trúc nhân luồng rất phù hợp để thực hiện nhiều ưu tiên ngắt và để thực thi quyền
ưu tiên xử lý ngắt đối với xử lý nền trong các thường trình ứng dụng và nhân. Chúng tôi
minh họa điểm này với nhân Solaris. Trong Solaris, các trình xử lý ngắt được thực thi dưới
dạng các luồng nhân. Một loạt các ưu tiên lập lịch trình cao được dành riêng cho các chủ
đề này. Các ưu tiên này cung cấp cho các trình xử lý ngắt ưu tiên hơn mã ứng dụng và
quản lý hạt nhân và thực hiện các mối quan hệ ưu tiên giữa các trình xử lý ngắt. Các ưu
tiên khiến bộ lập lịch luồng Solaris ưu tiên các trình xử lý ngắt có mức độ ưu tiên thấp để ưu
tiên các trình xử lý có mức độ ưu tiên cao hơn và việc triển khai theo luồng cho phép phần
cứng đa bộ xử lý chạy đồng thời một số trình xử lý ngắt. Chúng tôi mô tả kiến trúc ngt ca
Linux trong Chương 20, Windows10 trong Chương 21 và UNIX trong Phụ lục C.
Tóm lại, các ngắt được sử dụng trên khắp các hệ điều hành hiện đại để xử lý các sự
kiện không đồng bộ và bẫy các thói quen chế độ giám sát trong nhân. Để cho phép thực
hiện công việc khẩn cấp nhất trước tiên, các máy tính hiện đại sử dụng một hthống ưu
tiên ngắt. Bộ điều khiển thiết b, lỗi phần cứng và lệnh gọi hệ thống đều gây ra các ngt đ
kích hoạt các quy trình nhân. Bởi vì các ngắt được sử dụng rất nhiều cho quá trình xử lý
lOMoARcPSD| 58833082
nhạy cảm với thời gian, nên việc xử lý ngắt hiệu quả là cần thiết để có hiệu suất hthống
tốt. I/O điều khiển ngắt giờ phbiến hơn nhiều so với bỏ phiếu, với việc bỏ phiếu được sử
dụng cho I/O thông lượng cao. Đôi khi cả hai được sử dụng cùng nhau. Một số trình điều
khiển thiết bị sử dụng các ngắt khi tốc độ I/O thấp và chuyển sang chế độ hỏi vòng khi tốc
độ tăng đến điểm mà việc hỏi vòng nhanh hơn và hiệu quả hơn
12.2.4 Truy cập bộ nhtrực tiếp
Đối với một thiết bị có khả năng truyền dữ liệu lớn, chẳng hạn như ổ đĩa, việc sử dụng
một bộ xử lý đa năng đắt tiền để xem các bit trạng thái và nạp dữ liệu vào một thanh ghi bộ
điều khiển mỗi lần một byte là một quá trình lãng phí—một quy trình được gọi là I/lập trình
được lập trình. O (PIO). Máy tính tránh tạo gánh nặng cho CPU chính với PIO bằng cách
giảm tải một số công việc này cho bộ xử lý có mục đích đặc biệt được gọi là bộ điều khiển
truy cập bộ nhtrực tiếp (DMA). Để bắt đầu truyền DMA, máy chủ ghi khối lệnh DMA vào
bộ nhớ. Khối này chứa một con trỏ tới nguồn truyền, một con trỏ tới đích truyền và đếm số
byte sẽ được truyền. Một khối lệnh có thể phức tạp hơn, bao gồm danh sách các địa chỉ
nguồn và đích không liền kề nhau. Phương thức thu thập phân tán này cho phép thực hiện
nhiều lần truyền thông qua một lệnh DMA đơn lẻ. CPU ghi địa chỉ của khối lệnh này vào bộ
điều khiển DMA, sau đó tiếp tục với công việc khác. Bộ điều khiển DMA tiến hành vận hành
bus bộ nhtrực tiếp, đặt các địa chỉ trên bus để thc hiện chuyển giao mà không cần sự
trợ giúp của CPU chính. Bộ điều khiển DMA đơn giản là một thành phần tiêu chuẩn trong
tất cả các máy tính hiện đại, từ điện thoại thông minh đến máy tính lớn.
Lưu ý rằng cách đơn giản nhất là địa chỉ đích nằm trong không gian địa chỉ kernel. Nếu
ở trong không gian người dùng, chẳng hạn, người dùng có thể sửa đổi nội dung của
không gian đó trong quá trình truyền, làm mất một số bộ dữ liệu. Tuy nhiên, để có được dữ
liệu được truyền DMA đến không gian người dùng để truy cập luồng, thao tác sao chép thứ
hai, lần này là từ bộ nhớ nhân sang bộ nhớ người dùng, là cần thiết. Bộ đệm đôi này không
hiệu quả. Theo thời gian, các hệ điều hành đã chuyển sang sử dụng ánh xạ bộ nhớ (xem
Phần 12.2.1) để thực hiện chuyển giao I/O trực tiếp giữa các thiết bị và không gian địa chỉ
người dùng.
Quá trình bắt tay giữa bộ điều khiển DMA và bộ điều khiển thiết bị được thực hiện thông
qua một cặp dây gọi là yêu cầu DMA và xác nhận DMA. Bộ điều khiển thiết bị đặt một tín
hiệu trên dây yêu cầu DMA khi có sẵn một từ dữ liệu để truyền. Tín hiệu này khiến bộ điu
khiển DMA chiếm bus bộ nhớ, đặt địa chỉ mong muốn trên dây địa chỉ bộ nhớ và đặt tín
hiệu trên dây xác nhận DMA. Khi bộ điều khiển thiết bnhận được tín hiệu xác nhận DMA,
nó sẽ chuyển từ dữ liệu sang bộ nhớ và xóa tín hiệu yêu cầu DMA.
Khi toàn bộ quá trình truyền hoàn tất, bộ điều khiển DMA sẽ ngắt CPU. Quá trình này
được mô tả trong Hình 12.6. Khi bộ điều khiển DMA chiếm bus bộ nh, CPU tạm thời bị
ngăn không cho truy cập bộ nhớ chính, mặc dù nó vẫn có thể truy cập các mục dữ liu
trong bộ đệm của nó. Mặc dù việc đánh cắp chu kỳ này có thể làm chậm quá trình tính toán
của CPU, nhưng việc giảm tải công việc truyền dữ liệu sang bộ điều khiển DMA nhìn chung
sẽ cải thiện hiệu năng tổng thể của hệ thống. Một số kiến trúc máy tính sử dụng địa chỉ bộ
nhớ vật lý cho DMA, nhưng một số khác thực hiện truy cập bộ nhớ ảo trực tiếp (DVMA), sử
dụng địa chỉ ảo trải qua quá trình dịch sang địa chỉ vật lý. DVMA có thể thực hiện truyền
giữa hai thiết bđược ánh xạ bộ nhớ mà không cần sự can thiệp của CPU hoặc sử dụng bộ
nhớ chính.
lOMoARcPSD| 58833082
Trên các nhân chế độ được bảo vệ, hệ điều hành thường ngăn các quy trình phát lệnh
trực tiếp cho thiết bị. Kluật này bảo vệ dữ liệu khỏi các vi phạm kiểm soát truy cập và cũng
bảo vệ hệ thống khỏi việc sử dụng sai bộ điều khiển thiết bị, điều này có thể gây ra sự cố
hệ thống. Thay vào đó, hệ điều hành xuất các chức năng mà một quy trình đủ đặc quyền có
thể sử dụng để truy cập các hoạt động cấp thấp trên phần cứng bên dưới. Trên các nhân
không có bảo vệ bộ nhớ, các tiến trình có thể truy cập trực tiếp vào bộ điều khiển thiết b.
Truy cập trực tiếp này có thể được sử dụng để đạt được hiệu suất cao, vì nó có thể tránh
giao tiếp hạt nhân, chuyển ngữ cảnh và các lớp phần mềm hạt nhân. Thật không may, nó
cản trở sự ổn định và bảo mật của hệ thống. Các hệ điều hành có mục đích chung chung
bảo vệ bộ nhớ và thiết bị để hệ thống có thể cố gắng bảo vệ chống lại các ứng dụng có lỗi
hoặc độc hại
lOMoARcPSD| 58833082
12.2.5 Tom tắt phần cứng I/O
Mặc dù các khía cạnh phần cứng của I/O rất phức tạp khi được xem xét ở mức độ chi
tiết của thiết kế phần cứng điện tử, các khái niệm mà chúng tôi vừa mô tả là đủ để cho
phép chúng tôi hiểu nhiều tính năng I/O của các hệ điều hành. Hãy xem lại các khái niệm
chính:
Bus
Bộ điều khiển
Một cổng I/O và các thanh ghi của nó
Mối quan hệ bắt tay giữa máy chủ và bộ điều khiển thiết bị
Việc thực hiện bắt tay này trong một vòng polling hoặc thông qua các ngắt
Giảm tải công việc này cho bộ điều khiển DMA cho các lần truyền lớn
Chúng tôi đã đưa ra một ví dụ cơ bản về quá trình bắt tay diễn ra giữa bộ điều khiển
thiết bị và máy chủ trước đó trong phần này. Trên thực tế, sự đa dạng của các thiết bị có
sẵn đặt ra một vấn đề cho những người triển khai hệ điều hành. Mỗi loại thiết bị có bộ khả
năng, định nghĩa bit điều khiển và công cụ chuyên nghiệp riêng để tương tác với máy chủ—
và tất cả chúng đều khác nhau. Làm thế nào hệ điều hành có thể được thiết kế để chúng ta
có thể gắn các thiết bị mới vào máy tính mà không cần viết lại hệ điều hành? Và khi các
thiết bị khác nhau rất nhiều, làm thế nào hệ điều hành có thể cung cấp giao diện I/O thống
nhất, thuận tiện cho các ứng dụng? Chúng tôi giải quyết những câu hỏi tiếp theo
12.3 Ứng dụng vào/ra thiết bị ngoài
Trong phần này, chúng ta sẽ thảo luận về các kỹ thuật cấu trúc và giao diện cho hệ điu
hành, cho phép các thiết bị I/O được xử lý một cách chuẩn và đồng nhất. Chúng tôi sẽ gii
thích, ví dụ như: làm thế nào một ứng dụng có thể mở một tệp trên đĩa mà không cần biết
loại đĩa hoặc thiết bị I/O là gì, và làm thế nào để thêm các thiết bị mới vào máy tính mà
không làm gián đoạn hệ điều hành.
Giống như những vấn đề phức tạp khác trong kỹ thuật phần mềm, phương pháp ở đây
bao gồm trừu tượng hóa, đóng gói và lớp phần mềm. Cụ thể, chúng ta có thể trừu tượng
hóa đi các khác biệt chi tiết trong các thiết bị I/O bằng cách xác định một sloại chung. Mỗi
loại chung được truy cập thông qua một tập hợp các chức năng chuẩn - giao diện. Sự khác
biệt được đóng gói trong các mô-đun kernel được gọi là trình điều khiển thiết bị, bên trong
đó được điều chỉnh tùy chỉnh cho từng thiết bị cụ thể nhưng xuất ra một trong những giao
diện tiêu chuẩn. Hình 12.7 minh họa cách các phần liên quan đến I/O của kernel được cấu
trúc theo các lớp phần mềm.
lOMoARcPSD| 58833082
n
Mục đích của lớp trình điều khiển thiết bị là ẩn đi sự khác biệt giữa các bộ điều khiể
thiết bị khác nhau khỏi hệ thống I/O của kernel, giống như các cuộc gọi hệ thống I/O đón
g
c
gói hành vi của thiết bị trong một vài lớp chung mà ẩn đi sự khác biệt phần cứng khỏi cá
ứng dụng. Làm cho hệ thống I/O độc lập với phần cứng đơn giản hóa công việc của nhà
phát triển hệ điều hành. Nó cũng mang lại lợi ích cho các nhà sản xuất phần cứng. Họ có
thể thiết kế các thiết bị mới để tương thích với một giao diện bộ điều khiển máy chủ hiện có
(
như SATA), hoặc viết trình điều khiển thiết bị để giao tiếp phần cứng mới với các hệ đi
hành phổ biến. Do đó, chúng ta có thể kết nối các phụ kiện mới vào máy tính mà không cần
chờ đợi nhà cung cấp hệ điều hành phát triển mã hỗ trợ.
Thật không may đối với các nhà sản xuất phần cứng thiết b, mỗi loại hệ điều hành có
các tiêu chuẩn riêng cho giao diện trình điều khiển thiết bị. Một thiết bị cụ th có thể được
cung cấp kèm theo nhiều trình điều khiển thiết bị - ví dụ như trình điều khiển cho Windows,
Linux, AIX và macOS. Các thiết bị khác nhau về nhiều mặt, như được minh họa trong Hình
12.8
.
lOMoARcPSD| 58833082
• Thiết bị truyền dữ liệu theo luồng ký tự (Character-stream) hoặc khối (Block). Thiết b
truyền dữ liệu theo luồng ký tự chuyển tiếp các byte một cách độc lập,
trong khi thiết b
truyền dữ liệu theo khối chuyển tiếp một khối các byte như một đơn vị.
• Thiết bị truyền dữ liệu tuần tự (Sequential) hoặc truy cập ngẫu nhiên (Random
access). Thiết bị truyền dữ liệu tuần tự chuyển tiếp dữ liệu theo một trình tự cố định được
ruy cp ngu nhiên có thể ch
quy định bởi thiết bị, trong khi người sử dụng của một thiết bị t
định thiết bị để tìm kiếm bất kỳ vị trí lưu trữ dữ liệu có sẵn nào.
• Thiết bđồng bộ (Synchronous) hoặc bất đồng bộ (Asynchronous). Một thiết bị đồng
bộ thực hiện truyền dữ liệu với thời gian phản hồi đáng tin cậy, được điều phối với các khía
cạnh khác của hệ thống. Một thiết bị bất đồng bộ có thời gian phản hồi k
hông đều hoặc
không thể dự đoán, không được điều phối với các sự kiện máy tính khác.
• Thiết bị chia sẻ (Sharable) hoặc dành riêng (Dedicated). Một thiết bị chia sẻ có thể
được
sử dụng đồng thời bởi nhiều quy trình hoặc luồng, trong khi thiết bị dành riêng thì
không
.
• Tốc độ hot động. Tốc độ của thiết bị dao động từ vài byte mỗi giây đến hàng tỷ byte
mỗi giây
.
• Chế độ đọc-ghi, chỉ đọc, ghi một lần. Một số thiết bị thực hiện cả đầu vào và đầu ra,
như
ng một số khác chỉ hỗ trợ một hướng truyền dữ liệu. Một số thiết bị cho phép dữ liệu
đư
ợc sửa đổi sau khi ghi, nhưng một số khác chỉ có thể ghi một lần và sau đó chỉ có thể
đọc
.
lOMoARcPSD| 58833082
Với mục đích truy cập ứng dụng, nhiều khác biệt này được ẩn đi bởi hệ điều hành và
các thiết bđược nhóm lại thành một vài loại thông thường. Những phong cách truy cập
thiết bị kết quả được tìm thấy là hữu ích và có ứng dụng rộng rãi. Mặc dù các cuộc gọi hệ
thống chính xác có thể khác nhau trên các hệ điều hành, nhưng các loại thiết bị khá chuẩn.
Các thỏa thuận truy cập chính bao gồm truy cập I/O khối, truy cập luồng ký tự, truy cập tệp
được ánh xạ vào bộ nh và các socket mạng. Hệ điều hành cũng cung cấp các cuộc gọi hệ
thống đặc biệt để truy cập một số thiết bị bổ sung, chẳng hạn như đồng hồ thời gian thực và
một bộ đếm thời gian. Một số hệ điều hành cung cấp một bộ cuộc gọi hệ thống cho các thiết
bị hiển thị đồ họa, video và âm thanh.
Hầu hết các hệ điều hành đều có một cơ chế thoát (hoặc cửa sau) cho phép các lệnh
tùy ý từ ứng dụng được truyền một cách mượt mà đến trình điều khiển thiết bị. Trong UNIX,
lệnh hệ thống này được gọi là ioctl() (viết tt của "I/O control"). Lệnh hệ thống ioctl() cho
phép một ứng dụng truy cập bất kỳ chức năng nào có thể được thực hiện bởi bất kỳ trình
điều khiển thiết bị nào mà không cần phải tạo ra một lệnh hệ thống mới. Lệnh hệ thng
ioctl() có ba đối số. Đối số đầu tiên là một định danh thiết bị kết nối ứng dụng với trình điều
khiển thông qua một thiết bphần cứng được quản lý bởi trình điều khiển đó. Đối số thứ hai
là một số nguyên chọn một trong các lệnh được thực hiện trong trình điều khiển. Đối số th
ba là một con trỏ đến một cấu trúc dữ liệu tùy ý trong bộ nhớ cho phép ứng dụng và trình
điều khiển giao tiếp bất kỳ thông tin điều khiển hoặc dữ liệu cần thiết nào.
Trong UNIX và Linux, định danh thiết bị là một bộ hai số "major và minor". Số major là
loại thiết bị, và số minor là phiên bản của thiết bị đó. Ví dụ, xem xét các thiết bị SSD này
trên một hệ thống. Nếu ta thực thi lệnh: % ls -l /dev/sda* Thì đầu ra:
brw-rw---- 1 root disk 8, 0 Mar 16 09:18 /dev/sda brw-rw---
- 1 root disk 8, 1 Mar 16 09:18 /dev/sda1 brw-rw---- 1 root
disk 8, 2 Mar 16 09:18 /dev/sda2 brw-rw---- 1 root disk 8,
3 Mar 16 09:18 /dev/sda3
Cho thấy 8 là số major. Hệ điều hành sử dụng thông tin đó để định tuyến các yêu cầu
I/O đến trình điều khiển thiết bị thích hợp. Số minor 0, 1, 2 và 3 chỉ ra phiên bản của thiết bị,
cho phép các yêu cầu I/O tới một mục nhập thiết bị lựa chọn thiết bị chính xác cho yêu cầu.
12.3.1 Block and Character Devices
Giao diện thiết bị khối (block-device interface) bao gồm tất cả các khía cạnh cần
thiết để truy cập vào ổ đĩa và các thiết bị ớng khối khác. Thiết bị được
mong đợi sẽ hiểu các lệnh như read() và write(). Nếu đó là
một thiết bị truy cập ngẫu nhiên, nó cũng được mong đợi sẽ có một lệnh seek() để
chỉ định khối nào được chuyển tiếp tiếp theo. Các ứng dụng thường truy cập vào
một thiết bị như vậy thông qua một giao diện hệ thống tập tin (file-system
lOMoARcPSD| 58833082
interface). Chúng ta có thể thấy rằng read(), write() và seek() bao gồm những
hành vi cần thiết của các thiết bị lưu trữ khối, để các ứng dụng được cô lập khỏi
những sự khác biệt cấp thấp giữa các thiết bđó.
Hệ điều hành và các ứng dụng đặc biệt như hệ thống quản lý cơ sở dữ liệu, có thể ưu
tiên truy cập vào một thiết bkhối dưới dạng một mảng các khối tuyến tính đơn giản.
Phương pháp truy cập này đôi khi được gọi là I/O. Nếu ứng dụng thực hiện bộ đệm của
riêng mình, thì sử dụng hệ thống tập tin sẽ dẫn đến việc tạo thêm bộ đệm không cần thiết.
Tương tự, nếu một ứng dụng cung cấp khóa của riêng nó cho các khối hoặc khu vực, thì
bất kỳ dịch vụ khóa của hệ điều hành nào sẽ trở nên dư thừa ít nhất và mâu thuẫn ở mức
tệ nht. Để tránh các xung đột này, truy cập thiết btrực tiếp (raw-device access) chuyển
quyền kiểm soát của thiết btrực tiếp cho ứng dụng, cho phép hệ điều hành lui lại phía sau.
Tuy nhiên, không có dịch vụ hệ điều hành nào được thực hiện trên thiết bị này. Một giải
pháp hòa hợp ngày càng phổ biến là cho phép hệ điều hành chạy trong chế độ tệp tin mà
vô hiệu hóa bộ đệm và khóa. Trong thế giới UNIX, điều này được gọi là I/O trực tiếp (direct
I/O).
Truy cập tập tin được ánh xạ vào bộ nh có thể được đặt lên các trình điều khiển thiết
bị khối. Thay vì cung cấp các hoạt động đọc và ghi, giao diện được ánh xạ vào bộ nhớ cung
cấp truy cập vào lưu trữ đĩa thông qua một mảng các byte trong bộ nhớ chính. Lời gọi hệ
thống ánh xạ một tập tin vào bộ nhtrả về địa chỉ bộ nhớ ảo chứa bản sao của tập tin. Các
chuyển dữ liệu thực tế chỉ được thực hiện khi cần thiết để đáp ứng yêu cầu truy cập vào
hình ảnh bộ nhớ. Bởi vì các chuyển dữ liệu được xử lý bởi cơ chế giống như sử dụng cho
truy cập bộ nhớ ảo được phân trang theo yêu cầu, I/O ánh xạ vào bộ nh hiệu quả. Ánh xạ
vào bộ nhớ cũng tiện lợi cho các nhà lập trình - truy cập vào tệp được ánh xạ vào bộ nh
chỉ đơn giản là đọc và ghi vào bộ nh. Hđiều hành cung cấp bộ nhớ ảo thường sử dụng
giao diện ánh xạ cho các dịch vụ nhân (kernel services). Ví dụ, để thực thi một chương
trình, hệ điều hành ánh xạ tập tin thực thi vào bộ nhớ và sau đó chuyển quyền điều khiển
đến địa chỉ nhập của tập tin thực thi. Giao diện ánh xạ cũng được sử dụng phổ biến cho
việc truy cập kernel vào không gian swap trên đĩa.
Bàn phím là một ví dụ về thiết bđược truy cập thông qua giao diện dòng ký tự. Các
lệnh hệ thống cơ bản trong giao diện này cho phép một ứng dụng nhận hoặc gửi một ký tự.
Trên cơ sở giao diện này, các thư viện có thể được xây dựng để cung cấp truy cập theo
dòng, với dịch vụ đệm và chỉnh sửa (ví dụ: khi người dùng gõ một ký tự backspace, ký tự
trước đó sẽ bị xóa khỏi luồng đầu vào). Kiểu truy cập này thuận tiện cho các thiết bị đầu
vào như bàn phím, chuột và modem sản xuất dliệu đầu vào "tự động" - nghĩa là ở các
thời điểm không nhất thiết được dự đoán bởi ứng dụng. Kiểu truy cập này cũng tốt cho các
thiết bị đầu ra như máy in và bảng âm thanh, mà tự nhiên phù hợp với khái niệm của một
luồng byte tuyến tính.
12.3.2 Thiết bị mạng
Bởi vì hiệu suất và đặc tính địa chỉ của I/O mạng khác biệt đáng kể so với I/O đĩa, hầu
hết các hệ điều hành cung cấp một giao diện I/O mạng khác biệt so với giao diện read()–
write()–seek() được sử dụng cho đĩa. Một giao diện có sẵn trong nhiều hệ điều hành, bao
gồm UNIX và Windows, là giao diện socket mạng.
lOMoARcPSD| 58833082
Hãy tưởng tượng về một ổ cắm tường cho điện: bất kỳ thiết bđiện nào đều có thể
được cắm vào. Tương tự, các cuộc gọi hệ thống trong giao diện socket cho phép một ng
dụng tạo ra một socket, kết nối một socket cục bộ đến một địa chỉ từ xa (điều này kết nối
ứng dụng này vào một socket được tạo bởi một ứng dụng khác), lắng nghe bất kỳ ứng
dụng từ xa nào cắm vào socket cục bộ, và gửi và nhận các gói tin qua kết nối. Để hỗ tr
việc triển khai các máy chủ mạng, giao diện socket cũng cung cấp một hàm được gọi là
select() để quản lý một tập hợp các socket. Cuộc gọi đến select() trả về thông tin về các
socket có một gói tin đang đợi để nhận và các socket có chỗ để chấp nhận một gói tin để
gửi. Việc sử dụng select() loại bỏ việc đang chờ và đợi bận rộn mà sẽ cần thiết cho I/O
mạng nếu không có nó. Các hàm này bao gồm những hành vi cần thiết của các mạng, rất
hữu ích cho việc tạo ra các ứng dụng phân tán có thể sử dụng bất kỳ phần cứng mạng và
bộ giao thức nào.
Nhiều cách tiếp cận khác nhau cho việc giao tiếp giữa các tiến trình và mạng đã được
triển khai. Ví dụ, Windows cung cấp một giao diện cho thẻ giao diện mạng và một giao diện
khác cho các giao thức mạng. Trong UNIX, mà có mt lịch sử dài trong việc phát triển công
nghệ mạng, chúng ta tìm thấy các pipe nửa-duplex, FIFO toàn-duplex, STREAMS
toànduplex, hàng đợi tin nhắn và sockets. Thông tin về mạng UNIX được đưa ra trong phần
C.9.
12.3.3 Đồng hồ và bộ đếm thời gian
Hầu hết các máy tính đều có đồng hồ và bộ đếm thời gian phần cứng cung cấp ba chức
năng cơ bản:
Cung cấp thời gian hiện tại.
Cung cấp thời gian đã trôi qua.
Thiết lập một bộ hẹn giờ để kích hoạt hoạt động X tại thời điểm T.
Các chức năng này được sử dụng nhiều bởi hệ điều hành cũng như các ứng dụng
nhạy cảm với thời gian. Thật không may, các lời gọi hệ thống thực hiện các chức năng này
không được tiêu chuẩn hóa trên các hệ điều hành khác nhau.
Thiết bị để đo thời gian trôi qua và kích hoạt các thao tác gọi là bộ định thời khoảng thời
gian có thể lập trình được. Nó có thể được thiết lập để chờ một khoảng thời gian nhất định
và sau đó tạo ra một ngắt, và nó có thể được thiết lập để làm điều này một lần hoặc lặp lại
quá trình để tạo ra các ngắt định kỳ. Trình lập lịch sử dụng cơ chế này để tạo ra một ngắt s
ngắt một tiến trình vào cuối một lát cắt thời gian của nó. Hệ thống I/O đĩa sử dụng nó để gọi
việc xả các bộ đệm bẩn định kỳ xuống đĩa, và hệ thống mạng sử dụng nó để hủy các thao
tác đang tiến hành quá chậm do tắc nghẽn hoặc lỗi mạng. Hệ điều hành cũng có thể cung
cấp một giao diện cho các tiến trình người dùng sử dụng đồng hồ bấm giờ. Hđiều hành
có thể hỗ trnhiều yêu cầu đồng hồ bấm giờ hơn số kênh phần cứng đồng hồ bấm giờ
bằng cách mô phỏng các đồng hồ ảo. Để làm như vậy, hạt nhân (hoặc trình điều khiển thiết
bị hồ bấm giờ) duy trì một danh sách các ngắt được yêu cầu bởi các chức năng của chính
nó và các yêu cầu của người dùng, sắp xếp theo thứ tự từ sớm nhất trước. Nó thiết lập bộ
định thời cho thời gian sớm nhất. Khi ngắt đồng hồ, hạt nhân thông báo cho người yêu cầu
và tải lại đồng hồ bấm giờ với thời gian sớm nhất tiếp theo.
lOMoARcPSD| 58833082
Máy tính có phần cứng đồng hồ được sử dụng cho nhiều mục đích khác nhau. Các PC
hiện đại bao gồm một bộ định thời sự kiện hiệu suất cao (HPET), chạy ở tốc độ khoảng 10
megahertz. Nó có một vài bộ so sánh có thể được thiết lập để kích hoạt một lần hoặc lặp đi
lặp lại khi giá trị mà chúng giữ khớp với HPET. Trình kích hoạt tạo ra một ngắt, và các rutin
quản lý đồng hồ của hệ điều hành xác định trình kích hoạt đó là cho mục đích gì và hành
động nào sẽ được thực hiện. Độ chính xác của trình kích hoạt bị giới hạn bởi độ phân giải
của bộ định thời sự kiện, cùng với chi phí của việc duy trì các đồng hồ ảo. Hơn nữa, nếu
các tick của bộ định thời được sử dụng để duy trì đồng hồ thời gian của hệ thống, đồng hồ
hệ thống có thể bị trôi lệch. Trôi lệch có thể được sửa chữa thông qua các giao thức được
thiết kế cho mục đích đó, chẳng hạn như NTP, giao thức đồng hồ thời gian mạng, sử dụng
tính toán độ trễ tinh vi để giữ cho đồng hồ máy tính chính xác gần như đạt đến mức độ
đồng hồ nguyên tử. Trong hầu hết các máy tính, đồng hồ phần cứng được xây dựng từ một
bộ đếm tần số cao. Trong một số máy tính, giá trị của bộ đếm này có thể được đọc từ một
thanh ghi thiết bị, trong trường hợp đó, bộ đếm có thể được coi là một đồng hồ độ phân giải
cao. Mặc dù đồng hồ này không tạo ra các ngắt, nó cung cấp các phép đo chính xác của
khoảng thời gian.
12.3.4 Non blocking and Asynchronous I/O
Một khía cạnh khác của giao diện hệ thống gọi liên quan đến việc lựa chọn giữa I/O
chặn và I/O không chặn. Khi một ứng dụng thực hiện một lệnh hệ thống chặn, việc thực thi
của luồng gọi bị tạm dừng. Luồng được di chuyển từ hàng đợi chạy của hệ thống điều hành
đến một hàng đợi chờ đợi. Sau khi lệnh hệ thống hoàn thành, luồng được chuyển trở lại
hàng đợi chạy, nơi nó có thể tiếp tục thực thi. Khi nó tiếp tục thực thi, nó sẽ nhận các giá trị
được trả về bởi lệnh hệ thống. Các hành động vật lý được thực hiện bởi các thiết bị I/O
thường là không đồng bộ - chúng mất một thời gian biến đổi hoặc không đoán trước được.
Tuy nhiên, hệ điều hành cung cấp các lệnh hệ thống chặn cho giao diện ứng dụng, vì mã
ngun ứng dụng chặn dễ viết hơn so với mã nguồn ứng dụng không chặn.
Một stiến trình cấp người dùng cần I/O không chặn. Một ví dụ là giao diện người dùng
nhận đầu vào từ bàn phím và chuột trong khi xử lý và hiển thị dữ liệu trên màn hình. Một ví
dụ khác là một ứng dụng video đọc các khung hình từ một tệp trên đĩa cứng trong khi đồng
thời giải nén và hiển thị đầu ra trên màn hình.
Một cách để tác giả ứng dụng chồng lấn thực thi với I/O là viết mt ứng dụng đa luồng.
Một sluồng có thể thực hiện các lệnh hệ thống chặn, trong khi các luồng khác tiếp tục
thực thi. Một số hệ điều hành cung cấp các lệnh hệ thống I/O không chặn. Một lệnh gọi
không chặn không dừng thực thi của luồng trong thời gian dài. Thay vào đó, nó trả về
nhanh chóng, với một giá trị trả về cho biết có bao nhiêu byte được truyền.
Một lựa chọn khác cho hệ thống gọi không chặn là hệ thống gọi bất đồng bộ. Một cuộc
gọi bất đồng bộ trả về ngay lập tức mà không chờ cho I/O hoàn thành. Luồng tiếp tục thực
hiện mã của nó. Việc hoàn thành I/O tại một thời điểm trong tương lai được truyền tải đến
luồng, thông qua việc đặt một số biến trong không gian địa chỉ của luồng hoặc thông qua
kích hoạt tín hiệu hoặc ngắt phần mềm hoặc chức năng gọi lại được thực thi bên ngoài
luồng điều khiển tuyến tính của luồng. Sự khác biệt giữa gọi không chặn và gọi bất đồng bộ
là read() không chặn trả về ngay lập tức với bất kỳ dữ liệu nào có sẵn - số byte được yêu
cầu đầy đủ, ít hơn hoặc không có. Một cuộc gọi đọc bất đồng bộ yêu cầu một bản sao hoàn
lOMoARcPSD| 58833082
toàn sẽ được thực hiện nhưng sẽ hoàn thành tại một thời điểm trong tương lai. Hai phương
pháp I/O này được hiển thị trong Hình 12.9.
Các hoạt động bất đồng bộ xuất hiện trong các hệ điều hành hiện đại. Thường thì,
chúng không được phơi bày cho người dùng hay ứng dụng mà được chứa trong các hoạt
động của hệ điều hành. Các thiết bị lưu trữ thứ cấp và I/O mạng là những ví dụ hữu ích.
Theo mặc định, khi một ứng dụng yêu cầu gửi dữ liệu qua mạng hoặc ghi dliệu vào thiết
bị lưu trữ, hệ điều hành sẽ ghi nhận yêu cầu đó, đưa dữ liệu vào bộ đệm I/O, và trả về cho
ứng dụng. Nếu có thể, để tối ưu hiệu suất toàn hệ thống, hệ điều hành sẽ hoàn thành yêu
cầu đó. Nếu có sự cố hệ thống xảy ra trong thời gian này, ứng dụng sẽ mất bất kỳ yêu cầu
"đang chuyển" nào. Do đó, các hệ điều hành thường đặt giới hạn thời gian cho bộ đệm I/O.
Một số phiên bản của UNIX xóa các bộ đệm lưu trữ phụ của họ mỗi 30 giây, ví dụ, hoặc
mỗi yêu cầu sẽ được xóa trong vòng 30 giây sau khi nó xảy ra. Hệ thống cung cấp một
cách để cho phép các ứng dụng yêu cầu xóa một số bộ đệm (như bộ đệm lưu trữ phụ) để
dữ liệu có thể được ép buộc vào lưu trữ phụ mà không phải chờ đợi cho khoảng thời gian
xóa bộ đệm. Sự nhất quán của dữ liệu trong các ứng dụng được duy trì bởi kernel, mà đọc
dữ liệu từ bộ đệm của nó trước khi yêu cầu I/O đến thiết bị, đảm bảo rằng dữ liệu chưa
được ghi vẫn được trả về cho bộ đọc yêu cầu. Lưu ý rằng nhiều luồng thực hiện I/O đến
cùng một tệp có thể không nhận được dữ liệu nhất quán, tùy thuộc vào cách mà kernel
thực hiện I/O của nó. Trong tình huống này, các luồng có thể cần sử dụng giao thức khóa.
Một số yêu cầu I/O cần được thực hiện ngay lập tức, vì vậy các lệnh hệ thống I/O thường
có một cách để chỉ ra rằng một yêu cầu cụ thhoặc I/O đến một thiết bị cụ thể nên được
thực hiện đồng bộ.
Một ví dụ tốt vhành vi không chặn là lời gọi hệ thống select() cho socket mạng. Lời gọi
hệ thống này có đối số chỉ định thời gian chờ tối đa. Bằng cách đặt nó thành 0, một luồng

Preview text:

lOMoAR cPSD| 58833082 Chương 12 : I/O SYSTEM
Hai công việc chính của máy tính là I/O và tính toán. Trong nhiều trường hợp, công việc
chính là I/O và việc tính toán hoặc xử lý chỉ là ngẫu nhiên. Chẳng hạn, khi chúng ta duyệt
một trang web hoặc chỉnh sửa một tệp, mối quan tâm ngay lập tức của chúng ta là đọc
hoặc nhập một số thông tin, chứ không phải tính toán câu trả lời.
Vai trò của hệ điều hành trong I/O máy tính là quản lý và kiểm soát các hoạt động I/O và
thiết bị I/O. Mặc dù các chủ đề liên quan xuất hiện trong các chương khác, nhưng ở đây
chúng tôi tập hợp các phần lại với nhau để vẽ nên một bức tranh hoàn chỉnh về I/O. Đầu
tiên, chúng tôi mô tả những điều cơ bản về phần cứng I/O, bởi vì bản chất của giao diện
phần cứng đặt ra những hạn chế đối với các cơ sở bên trong của hệ điều hành. Tiếp theo,
chúng ta thảo luận về các dịch vụ I/O được cung cấp bởi hệ điều hành và hiện thân của các
dịch vụ này trong giao diện I/O của ứng dụng. Sau đó, chúng tôi giải thích cách hệ điều
hành thu hẹp khoảng cách giữa giao diện phần cứng và giao diện ứng dụng. Chúng tôi
cũng thảo luận về cơ chế UNIX System V STREAM, cho phép một ứng dụng lắp ráp các
đường dẫn mã trình điều khiển một cách linh hoạt. Cuối cùng, chúng ta thảo luận về các
khía cạnh hiệu suất của I/O và các nguyên tắc thiết kế hệ điều hành giúp cải thiện hiệu suất I/O CHƯƠNG MỤC TIÊU
• Khám phá cấu trúc của hệ thống con I/O của một hệ điều hành.
• Thảo luận về các nguyên tắc và sự phức tạp của phần cứng I/O.
• Giải thích các khía cạnh hiệu suất của phần cứng và phần mềm I/O 12.1 Tổng quan
Việc kiểm soát các thiết bị kết nối với máy tính là mối quan tâm chính của các nhà thiết
kế hệ điều hành. Bởi vì các thiết bị I/O rất khác nhau về chức năng và tốc độ của chúng (ví
dụ như chuột, đĩa cứng, ổ đĩa flash và rô-bốt băng từ), nên cần có nhiều phương pháp khác
nhau để điều khiển chúng. Các phương thức này tạo thành hệ thống con I/O của kernel,
phân tách phần còn lại của kernel khỏi sự phức tạp của việc quản lý các thiết bị I/O
Công nghệ thiết bị I/O thể hiện hai xu hướng trái ngược nhau. Một mặt, chúng ta thấy
tiêu chuẩn hóa ngày càng tăng của giao diện phần mềm và phần cứng. Xu hướng này giúp
chúng tôi kết hợp các thế hệ thiết bị cải tiến vào các máy tính và hệ điều hành hiện có. Mặt
khác, chúng ta thấy ngày càng có nhiều loại thiết bị I/O. Một số thiết bị mới không giống các
thiết bị trước đây đến mức khó kết hợp chúng vào máy tính và hệ điều hành của chúng ta.
Thách thức này được giải quyết bằng sự kết hợp giữa kỹ thuật phần cứng và phần mềm.
Các phần tử phần cứng I/O cơ bản, chẳng hạn như cổng, bus và bộ điều khiển thiết bị, phù
hợp với nhiều loại thiết bị I/O. Để đóng gói các chi tiết và điểm kỳ lạ của các thiết bị khác
nhau, nhân của một hệ điều hành được cấu trúc để sử dụng các mô-đun trình điều khiển
thiết bị. Trình điều khiển thiết bị trình bày giao diện truy cập thiết bị thống nhất cho hệ thống
con I/O, giống như các lệnh gọi hệ thống cung cấp giao diện tiêu chuẩn giữa ứng dụng và hệ điều hành lOMoAR cPSD| 58833082 12.2 Phần cứng I/O
Máy tính vận hành rất nhiều loại thiết bị. Hầu hết phù hợp với các loại thiết bị lưu trữ
chung (đĩa, băng), thiết bị truyền dẫn (kết nối mạng, Bluetooth) và thiết bị giao diện người
dùng (màn hình, bàn phím, chuột, đầu vào và đầu ra âm thanh). Các thiết bị khác chuyên
dụng hơn, chẳng hạn như những thiết bị liên quan đến việc điều khiển máy bay phản lực.
Trong những chiếc máy bay này, con người cung cấp đầu vào cho máy tính chuyến bay
thông qua cần điều khiển và bàn đạp chân, đồng thời máy tính gửi các lệnh đầu ra khiến
động cơ di chuyển bánh lái và cánh tà cũng như nạp nhiên liệu cho động cơ. Tuy nhiên, bất
chấp sự đa dạng đáng kinh ngạc của các thiết bị I/O, chúng ta chỉ cần một vài khái niệm để
hiểu cách các thiết bị được gắn vào và cách phần mềm có thể điều khiển phần cứng.
Một thiết bị giao tiếp với hệ thống máy tính bằng cách gửi tín hiệu qua cáp hoặc thậm
chí qua không khí. Thiết bị giao tiếp với máy thông qua điểm kết nối hoặc cổng— ví dụ:
cổng nối tiếp. (Thuật ngữ PHY, viết tắt của lớp vật lý mô hình OSI, cũng được sử dụng để
chỉ các cổng nhưng phổ biến hơn trong danh pháp trung tâm dữ liệu.) Nếu các thiết bị chia
sẻ một bộ dây chung, kết nối được gọi là b. Một bus, giống như bus PCI được sử dụng
trong hầu hết các máy tính ngày nay, là một tập hợp các dây và một giao thức được xác
định rõ ràng để chỉ định một tập hợp các thông báo có thể được gửi trên các dây. Về mặt
điện tử, các thông điệp được truyền tải bằng các mẫu điện áp đặt vào dây dẫn với thời gian
xác định. Khi thiết bị A có cáp cắm vào thiết bị B và thiết bị B có cáp cắm vào thiết bị C và
thiết bị C cắm vào một cổng trên máy tính, sự sắp xếp này được gọi là chuỗi xích. Một
chuỗi daisy thường hoạt động như một chiếc xe buýt
Bus được sử dụng rộng rãi trong kiến trúc máy tính và khác nhau về phương thức
truyền tín hiệu, tốc độ, thông lượng và phương thức kết nối. Một cấu trúc bus PC điển hình
xuất hiện trong Hình 12.1. Trong hình, một bus PCIe (bus hệ thống PC phổ biến) kết nối hệ
thống con bộ xử lý-bộ nhớ với các thiết bị nhanh và một bus mở rộng kết nối các thiết bị
tương đối chậm, chẳng hạn như bàn phím, cổng nối tiếp và cổng USB. Ở phần phía dưới
bên trái của hình, bốn đĩa được kết nối với nhau trên một bus SCSI (SAS) gắn nối tiếp
được cắm vào bộ điều khiển SAS. PCIe là một bus linh hoạt gửi dữ liệu qua một hoặc nhiều
“làn”. Một làn bao gồm hai cặp tín hiệu, một cặp để nhận dữ liệu và cặp kia để truyền. Do
đó, mỗi làn bao gồm bốn dây và mỗi làn được sử dụng làm luồng byte song công hoàn
toàn, vận chuyển các gói dữ liệu ở định dạng byte tám bit đồng thời theo cả hai hướng. Về
mặt vật lý, các liên kết PCIe có thể chứa 1, 2, 4, 8, 12, 16 hoặc 32 làn, được biểu thị bằng
tiền tố “x”. Ví dụ: thẻ hoặc đầu nối PCIe sử dụng 8 làn được chỉ định là x8. Ngoài ra, PCIe
đã trải qua nhiều “thế hệ”, và sẽ có nhiều thế hệ hơn trong tương lai. Vì vậy, ví dụ, một thẻ
có thể là “PCIe gen3 x8”, có nghĩa là nó hoạt động với thế hệ 3 của PCIe và sử dụng 8 làn.
Một thiết bị như vậy có thông lượng tối đa là 8 gigabyte mỗi giây. Thông tin chi tiết về PCIe
có thể được tìm thấy tại https://pcisig.com lOMoAR cPSD| 58833082
Bộ điều khiển là một tập hợp các thiết bị điện tử có thể vận hành một cổng, một xe buýt
hoặc một thiết bị. Bộ điều khiển cổng nối tiếp là bộ điều khiển thiết bị đơn giản. Đó là một
con chip (hoặc một phần của con chip) trong máy tính điều khiển các tín hiệu trên dây của
cổng nối tiếp. Ngược lại, bộ điều khiển bus kênh sợi quang (FC) không đơn giản. Do giao
thức FC phức tạp và được sử dụng trong các trung tâm dữ liệu hơn là trên PC nên bộ điều
khiển bus FC thường được triển khai dưới dạng một bảng mạch riêng—hoặc bộ điều hợp
bus chủ (HBA)— kết nối với bus trong máy tính. Nó thường chứa một bộ xử lý, vi mã và
một số bộ nhớ riêng để cho phép nó xử lý các thông báo giao thức FC. Một số thiết bị có bộ
điều khiển tích hợp riêng. Nếu bạn nhìn vào ổ đĩa, bạn sẽ thấy một bảng mạch được gắn ở
một bên. Bảng này là bộ điều khiển đĩa. Nó thực hiện phía đĩa của giao thức cho một số
loại kết nối — chẳng hạn như SAS và SATA. Nó có vi mã và bộ xử lý để thực hiện nhiều tác
vụ, chẳng hạn như lập bản đồ khu vực xấu, tìm nạp trước, tạo bộ đệm và lưu vào bộ nhớ đệm.
12.2.1 I/O ánh xạ bộ nhớ
Làm thế nào để bộ xử lý đưa ra các lệnh và dữ liệu cho bộ điều khiển để thực hiện
chuyển giao I/O? Câu trả lời ngắn gọn là bộ điều khiển có một hoặc nhiều thanh ghi dữ liệu
và tín hiệu điều khiển. Bộ xử lý giao tiếp với bộ điều khiển bằng cách đọc và ghi các mẫu bit
trong các thanh ghi này. Một cách mà giao tiếp này có thể xảy ra là thông qua việc sử dụng
các lệnh I/O đặc biệt chỉ định việc truyền một byte hoặc một từ tới một địa chỉ cổng I/O.
Lệnh I/O kích hoạt các đường bus để chọn thiết bị thích hợp và di chuyển các bit vào hoặc
ra khỏi thanh ghi thiết bị. Ngoài ra, thiết bị có thể hỗ trợ I/O được ánh xạ bộ nhớ. Trong
trường hợp này, các thanh ghi điều khiển thiết bị được ánh xạ vào không gian địa chỉ của lOMoAR cPSD| 58833082
bộ xử lý. CPU thực thi các yêu cầu I/O bằng cách sử dụng các lệnh truyền dữ liệu tiêu
chuẩn để đọc và ghi các thanh ghi điều khiển thiết bị tại các vị trí được ánh xạ của chúng trong bộ nhớ vật lý
Trước đây, PC thường sử dụng các lệnh I/O để điều khiển một số thiết bị và I/O được
ánh xạ bộ nhớ để điều khiển các thiết bị khác. Hình 12.2 hiển thị các địa chỉ cổng I/O thông
thường cho PC. Bộ điều khiển đồ họa có các cổng I/O cho các hoạt động điều khiển cơ
bản, nhưng bộ điều khiển có vùng ánh xạ bộ nhớ lớn để chứa nội dung màn hình. Một
luồng gửi đầu ra tới màn hình bằng cách ghi dữ liệu vào vùng ánh xạ bộ nhớ. Bộ điều khiển
tạo ra hình ảnh màn hình dựa trên nội dung của bộ nhớ này. Kỹ thuật này là đơn giản để sử
dụng. Hơn nữa, việc ghi hàng triệu byte vào bộ nhớ đồ họa nhanh hơn việc đưa ra hàng
triệu lệnh I/O. Do đó, theo thời gian, các hệ thống đã chuyển sang I/O được ánh xạ bộ nhớ.
Ngày nay, hầu hết I/O được thực hiện bởi bộ điều khiển thiết bị sử dụng I/O ánh xạ bộ nhớ
Điều khiển thiết bị I/O thường bao gồm bốn thanh ghi, được gọi là trạng thái, các
thanh ghi điều khiển, nhập dữ liệu và xuất dữ liệu.
• Thanh ghi dữ liệu vào được máy chủ đọc để nhận đầu vào.
• Thanh ghi dữ liệu ra được ghi bởi máy chủ để gửi đầu ra.
• Thanh ghi trạng thái chứa các bit mà máy chủ có thể đọc được. Các bit này cho biết
các trạng thái, chẳng hạn như liệu lệnh hiện tại đã hoàn thành hay chưa, liệu một byte có
sẵn để đọc từ thanh ghi dữ liệu vào hay không và liệu có xảy ra lỗi thiết bị hay không. lOMoAR cPSD| 58833082
• Thanh ghi điều khiển có thể được ghi bởi máy chủ để bắt đầu một lệnh hoặc để thay
đổi chế độ của thiết bị. Chẳng hạn, một bit nhất định trong thanh ghi điều khiển của cổng
nối tiếp chọn giữa giao tiếp song công hoàn toàn và bán song công, bit khác cho phép
kiểm tra chẵn lẻ, bit thứ ba đặt độ dài từ thành 7 hoặc 8 bit và các bit khác chọn một trong
các tốc độ được hỗ trợ bởi cổng nối tiếp.
Các thanh ghi dữ liệu thường có kích thước từ 1 đến 4 byte. Một số bộ điều khiển có
chip FIFO có thể chứa vài byte dữ liệu đầu vào hoặc đầu ra để mở rộng dung lượng của bộ
điều khiển ngoài kích thước của thanh ghi dữ liệu. Chip FIFO có thể chứa một loạt dữ liệu
nhỏ cho đến khi thiết bị hoặc máy chủ có thể nhận những dữ liệu đó. 12.2.2 Polling
Giao thức hoàn chỉnh cho sự tương tác giữa máy chủ và bộ điều khiển có thể phức tạp,
nhưng khái niệm bắt tay cơ bản thì đơn giản. Chúng tôi giải thích việc bắt tay bằng một ví
dụ. Giả sử rằng 2 bit được sử dụng để điều phối mối quan hệ người sản xuất-người tiêu
dùng giữa bộ điều khiển và máy chủ. Bộ điều khiển cho biết trạng thái của nó thông qua bit
bận trong thanh ghi trạng thái. (Nhớ lại rằng để thiết lập một bit có nghĩa là ghi 1 vào bit và
xóa một bit có nghĩa là ghi 0 vào nó.) Bộ điều khiển thiết lập bit bận khi nó đang bận làm
việc và xóa bit bận khi nó sẵn sàng hoạt động. chấp nhận lệnh tiếp theo. Máy chủ báo hiệu
mong muốn của nó thông qua bit sẵn sàng cho lệnh trong thanh ghi lệnh. Máy chủ đặt bit
sẵn sàng cho lệnh khi có lệnh để bộ điều khiển thực thi. Đối với ví dụ này, máy chủ ghi đầu
ra thông qua một cổng, phối hợp với bộ điều khiển bằng cách bắt tay như sau. 1.
Máy chủ liên tục đọc bit bận cho đến khi bit đó trở nên rõ ràng. 2.
Máy chủ đặt bit ghi trong thanh ghi lệnh và ghi một byte vào thanh ghi dữ liệu ra. 3.
Máy chủ đặt bit sẵn sàng cho lệnh. 4.
Khi bộ điều khiển thông báo rằng bit sẵn sàng cho lệnh được đặt, nó sẽ đặt bit bận. 5.
Bộ điều khiển đọc thanh ghi lệnh và xem lệnh ghi. Nó đọc thanh ghi xuất dữ
liệu để lấy byte và thực hiện I/O cho thiết bị. 6.
Bộ điều khiển xóa bit sẵn sàng cho lệnh, xóa bit lỗi trong thanh ghi trạng thái
để cho biết rằng I/O của thiết bị đã thành công và xóa bit bận để cho biết rằng nó đã kết thúc
Vòng lặp này được lặp lại cho mỗi byte
Ở bước 1, máy chủ đang bận chờ đợi hoặc bỏ phiếu: nó ở trong một vòng lặp, đọc đi
đọc lại thanh ghi trạng thái cho đến khi bit bận trở nên rõ ràng. Nếu bộ điều khiển và thiết bị
nhanh, phương pháp này là một phương pháp hợp lý. Nhưng nếu thời gian chờ đợi có thể
lâu, chủ nhà có lẽ nên chuyển sang nhiệm vụ khác. Sau đó, làm thế nào để máy chủ biết
khi nào bộ điều khiển không hoạt động? Đối với một số thiết bị, máy chủ phải nhanh chóng
phục vụ thiết bị, nếu không dữ liệu sẽ bị mất. Chẳng hạn, khi dữ liệu đang truyền vào cổng lOMoAR cPSD| 58833082
nối tiếp hoặc từ bàn phím, bộ đệm nhỏ trên bộ điều khiển sẽ tràn và dữ liệu sẽ bị mất nếu
máy chủ đợi quá lâu trước khi quay lại đọc byte
Trong nhiều kiến trúc máy tính, ba chu kỳ hướng dẫn CPU là đủ để thăm dò một thiết
bị: đọc thanh ghi thiết bị, logic-và để trích xuất một bit trạng thái và phân nhánh nếu không
phải là 0. Rõ ràng, hoạt động bỏ phiếu cơ bản là hiệu quả. Tuy nhiên, việc bỏ phiếu trở nên
không hiệu quả khi nó được thử lặp đi lặp lại nhưng hiếm khi tìm thấy một thiết bị sẵn sàng
hoạt động, trong khi quá trình xử lý CPU hữu ích khác vẫn chưa hoàn thành. Trong những
trường hợp như vậy, có thể hiệu quả hơn nếu sắp xếp để bộ điều khiển phần cứng thông
báo cho CPU khi thiết bị sẵn sàng hoạt động, thay vì yêu cầu CPU thăm dò liên tục để hoàn
thành I/O. Cơ chế phần cứng cho phép thiết bị thông báo cho CPU được gọi là ngắt. 12.2.3 Ngắt
Cơ chế ngắt cơ bản hoạt động như sau. Phần cứng CPU có một dây được gọi là dây
yêu cầu ngắt mà CPU cảm nhận được sau khi thực hiện mọi lệnh. Khi CPU phát hiện ra
rằng bộ điều khiển đã xác nhận tín hiệu trên dòng yêu cầu ngắt, CPU sẽ thực hiện lưu trạng
thái và chuyển sang quy trình xử lý ngắt tại một địa chỉ cố định trong bộ nhớ. Trình xử lý
ngắt xác định nguyên nhân gây ra ngắt, thực hiện quá trình xử lý cần thiết, thực hiện khôi
phục trạng thái và thực hiện quay lại lệnh ngắt để đưa CPU về trạng thái thực thi trước khi
ngắt. Chúng tôi nói rằng bộ điều khiển thiết bị tạo ra một ngắt bằng cách xác nhận tín hiệu
trên dòng yêu cầu ngắt, CPU bắt ngắt và gửi nó đến bộ xử lý ngắt, và bộ xử lý sẽ xóa ngắt
bằng cách bảo dưỡng thiết bị. Hình 12.3 tóm tắt chu trình I/O điều khiển ngắt.
Chúng tôi nhấn mạnh đến việc quản lý ngắt trong chương này vì ngay cả các hệ thống
hiện đại chỉ có một người dùng cũng quản lý hàng trăm ngắt mỗi giây và các máy chủ lên
tới hàng trăm nghìn ngắt mỗi giây. Ví dụ: Hình 12.4 hiển thị đầu ra lệnh có độ trễ trên
macOS, cho thấy rằng trong hơn 10 giây, một máy tính để bàn yên tĩnh đã thực hiện gần 23.000 lần ngắt.
Cơ chế ngắt cơ bản vừa được mô tả cho phép CPU phản hồi sự kiện không đồng bộ,
chẳng hạn như khi bộ điều khiển thiết bị sẵn sàng hoạt động. Tuy nhiên, trong một hệ điều
hành hiện đại, chúng ta cần các tính năng xử lý ngắt phức tạp hơn.
1. Chúng tôi cần khả năng trì hoãn xử lý ngắt trong quá trình xử lý quan trọng.
2. Chúng ta cần một cách hiệu quả để gửi đến trình xử lý ngắt phù hợp cho một thiết bị
mà không cần bỏ phiếu trước cho tất cả các thiết bị để xem thiết bị nào gây ra ngắt.
3. Chúng ta cần các ngắt đa cấp để hệ điều hành có thể phân biệt giữa các ngắt có
mức độ ưu tiên cao và thấp và có thể phản hồi với mức độ khẩn cấp thích hợp khi có nhiều ngắt đồng thời. lOMoAR cPSD| 58833082
4. Chúng tôi cần một cách để hướng dẫn thu hút sự chú ý của hệ điều hành một cách
trực tiếp (tách biệt với các yêu cầu I/O), đối với các hoạt động như lỗi trang và lỗi chẳng
hạn như chia cho 0. Như chúng ta sẽ thấy, nhiệm vụ này được thực hiện bằng “bẫy”.
Trong phần cứng máy tính hiện đại, các tính năng này được cung cấp bởi CPU và phần
cứng của bộ điều khiển ngắt.
Hầu hết các CPU đều có hai dòng yêu cầu ngắt. Một là ngắt không che được, được
dành riêng cho các sự kiện như lỗi bộ nhớ không thể phục hồi. Dòng ngắt thứ hai có thể
che được: CPU có thể tắt nó trước khi thực hiện các chuỗi lệnh quan trọng không được
ngắt. Ngắt có thể che dấu được bộ điều khiển thiết bị sử dụng để yêu cầu dịch vụ. lOMoAR cPSD| 58833082
Cơ chế ngắt chấp nhận một địa chỉ—một số chọn một thói quen xử lý ngắt cụ thể từ
một tập hợp nhỏ. Trong hầu hết các kiến trúc, địa chỉ này là một phần bù trong bảng được
gọi là vectơ ngắt. Vectơ này chứa các địa chỉ bộ nhớ của các trình xử lý ngắt chuyên biệt.
Mục đích của cơ chế ngắt theo vectơ là giảm nhu cầu về một trình xử lý ngắt duy nhất để
tìm kiếm tất cả các nguồn ngắt có thể có để xác định cái nào cần dịch vụ. Tuy nhiên, trong
thực tế, máy tính có nhiều thiết bị hơn (và do đó, các trình xử lý ngắt) hơn là chúng có các
thành phần địa chỉ trong vectơ ngắt. Một cách phổ biến để giải quyết vấn đề này là sử dụng
chuỗi ngắt, trong đó mỗi phần tử trong vectơ ngắt trỏ đến phần đầu của danh sách các trình
xử lý ngắt. Khi một ngắt được đưa ra, các trình xử lý trong danh sách tương ứng được gọi
từng cái một, cho đến khi tìm thấy một trình xử lý có thể phục vụ yêu cầu. Cấu trúc này là
sự thỏa hiệp giữa chi phí hoạt động của một bảng ngắt khổng lồ và sự kém hiệu quả của
việc gửi tới một trình xử lý ngắt duy nhất
Hình 12.5 minh họa thiết kế của vectơ ngắt cho bộ xử lý Intel Pen tium. Các sự kiện từ
0 đến 31, không thể ẩn được, được sử dụng để báo hiệu các tình trạng lỗi khác nhau (gây
ra sự cố hệ thống), lỗi trang (cần hành động ngay lập tức) và yêu cầu gỡ lỗi (dừng hoạt
động bình thường và chuyển sang ứng dụng trình gỡ lỗi). Các sự kiện từ 32 đến 255, có
thể che được, được sử dụng cho các mục đích như ngắt do thiết bị tạo.
Cơ chế ngắt cũng thực hiện một hệ thống các mức ưu tiên ngắt. Các mức này cho
phép CPU trì hoãn việc xử lý các ngắt có mức độ ưu tiên thấp mà không che dấu tất cả các
ngắt và giúp cho ngắt có mức độ ưu tiên cao có thể chiếm trước việc thực hiện ngắt có mức độ ưu tiên thấp. lOMoAR cPSD| 58833082
Một hệ điều hành hiện đại tương tác với cơ chế ngắt theo một số cách. Tại thời điểm
khởi động, hệ điều hành thăm dò các bus phần cứng để xác định thiết bị nào có mặt và cài
đặt các trình xử lý ngắt tương ứng vào vectơ ngắt. Trong quá trình I/O, các bộ điều khiển
thiết bị khác nhau sẽ tăng các ngắt khi chúng sẵn sàng hoạt động. Các ngắt này biểu thị
rằng đầu ra đã hoàn thành hoặc dữ liệu đầu vào có sẵn hoặc một lỗi đã được phát hiện. Cơ
chế ngắt cũng được sử dụng để xử lý nhiều loại ngoại lệ, chẳng hạn như chia cho 0, truy
cập địa chỉ bộ nhớ lều được bảo vệ hoặc không tồn tại hoặc cố gắng thực hiện lệnh đặc
quyền từ chế độ người dùng. Các sự kiện kích hoạt các ngắt có một thuộc tính chung:
chúng là các sự kiện xảy ra khiến hệ điều hành thực thi một thói quen khẩn cấp, khép kín.
Do việc xử lý ngắt trong nhiều trường hợp bị hạn chế về thời gian và tài nguyên, do đó
phức tạp để thực hiện, các hệ thống thường phân chia việc quản lý ngắt giữa trình xử lý
ngắt cấp một (FLIH) và trình xử lý ngắt cấp hai (SLIH). FLIH thực hiện chuyển đổi ngữ
cảnh, lưu trữ trạng thái và xếp hàng của thao tác xử lý, trong khi SLIH được lập lịch trình
riêng biệt thực hiện xử lý thao tác được yêu cầu lOMoAR cPSD| 58833082
Các hệ điều hành cũng có những cách sử dụng tốt khác cho các ngắt. Ví dụ, nhiều hệ
điều hành sử dụng cơ chế ngắt để phân trang bộ nhớ ảo. Lỗi trang là một ngoại lệ làm tăng
ngắt. Ngắt sẽ tạm dừng tiến trình hiện tại và nhảy tới trình xử lý lỗi trang trong hạt nhân.
Trình xử lý này lưu trạng thái của quy trình, di chuyển quy trình vào hàng đợi, thực hiện
quản lý bộ đệm trang, lên lịch cho thao tác I/O để tìm nạp trang, lên lịch cho một quy trình
khác để tiếp tục thực thi và sau đó quay lại từ ngắt.
Một ví dụ khác được tìm thấy trong việc thực hiện các cuộc gọi hệ thống. Thông
thường, một chương trình sử dụng lời gọi thư viện để thực hiện lời gọi hệ thống. Các
thường trình thư viện kiểm tra các đối số do ứng dụng đưa ra, xây dựng cấu trúc dữ liệu để
truyền các đối số tới nhân, sau đó thực hiện một lệnh đặc biệt gọi là ngắt phần mềm hoặc
bẫy. Hướng dẫn này có một toán hạng xác định dịch vụ hạt nhân mong muốn. Khi một quy
trình thực thi lệnh bẫy, phần cứng ngắt sẽ lưu trạng thái của mã người dùng, chuyển sang
chế độ nhân và gửi đến thủ tục nhân hoặc luồng thực hiện dịch vụ được yêu cầu. Cái bẫy
được cấp mức ưu tiên ngắt tương đối thấp so với mức ưu tiên được gán cho các ngắt của
thiết bị—việc thực hiện lệnh gọi hệ thống thay mặt cho một ứng dụng ít khẩn cấp hơn so
với việc bảo dưỡng bộ điều khiển thiết bị trước khi hàng đợi FIFO của nó bị tràn và mất dữ liệu.
gián đoạn cũng có thể được sử dụng để quản lý luồng điều khiển bên trong hạt nhân. Ví
dụ, hãy xem xét trường hợp xử lý cần thiết để hoàn thành việc đọc đĩa. Một bước có thể
sao chép dữ liệu từ không gian nhân vào bộ đệm người dùng. Việc sao chép này tốn thời
gian nhưng không khẩn cấp—nó không nên chặn việc xử lý ngắt ưu tiên cao khác. Một
bước khác là bắt đầu I/O đang chờ xử lý tiếp theo cho ổ đĩa đó. Bước này có mức độ ưu
tiên cao hơn. Nếu các đĩa được sử dụng hiệu quả, chúng ta cần bắt đầu I/O tiếp theo ngay
sau khi hoàn thành I/O trước đó. Do đó, một cặp trình xử lý ngắt thực hiện mã hạt nhân để
hoàn thành việc đọc đĩa. Trình xử lý ưu tiên cao ghi lại trạng thái I/O, xóa ngắt thiết bị, bắt
đầu I/O đang chờ xử lý tiếp theo và tăng ngắt ưu tiên thấp để hoàn thành công việc. Sau
đó, khi CPU không bận rộn với công việc có mức ưu tiên cao, ngắt có mức ưu tiên thấp sẽ
được gửi đi. Trình xử lý tương ứng hoàn thành I/O cấp người dùng bằng cách sao chép dữ
liệu từ bộ đệm nhân vào không gian ứng dụng, sau đó gọi bộ lập lịch để đặt ứng dụng vào hàng đợi sẵn sàng.
Kiến trúc nhân luồng rất phù hợp để thực hiện nhiều ưu tiên ngắt và để thực thi quyền
ưu tiên xử lý ngắt đối với xử lý nền trong các thường trình ứng dụng và nhân. Chúng tôi
minh họa điểm này với nhân Solaris. Trong Solaris, các trình xử lý ngắt được thực thi dưới
dạng các luồng nhân. Một loạt các ưu tiên lập lịch trình cao được dành riêng cho các chủ
đề này. Các ưu tiên này cung cấp cho các trình xử lý ngắt ưu tiên hơn mã ứng dụng và
quản lý hạt nhân và thực hiện các mối quan hệ ưu tiên giữa các trình xử lý ngắt. Các ưu
tiên khiến bộ lập lịch luồng Solaris ưu tiên các trình xử lý ngắt có mức độ ưu tiên thấp để ưu
tiên các trình xử lý có mức độ ưu tiên cao hơn và việc triển khai theo luồng cho phép phần
cứng đa bộ xử lý chạy đồng thời một số trình xử lý ngắt. Chúng tôi mô tả kiến trúc ngắt của
Linux trong Chương 20, Windows10 trong Chương 21 và UNIX trong Phụ lục C.
Tóm lại, các ngắt được sử dụng trên khắp các hệ điều hành hiện đại để xử lý các sự
kiện không đồng bộ và bẫy các thói quen chế độ giám sát trong nhân. Để cho phép thực
hiện công việc khẩn cấp nhất trước tiên, các máy tính hiện đại sử dụng một hệ thống ưu
tiên ngắt. Bộ điều khiển thiết bị, lỗi phần cứng và lệnh gọi hệ thống đều gây ra các ngắt để
kích hoạt các quy trình nhân. Bởi vì các ngắt được sử dụng rất nhiều cho quá trình xử lý lOMoAR cPSD| 58833082
nhạy cảm với thời gian, nên việc xử lý ngắt hiệu quả là cần thiết để có hiệu suất hệ thống
tốt. I/O điều khiển ngắt giờ phổ biến hơn nhiều so với bỏ phiếu, với việc bỏ phiếu được sử
dụng cho I/O thông lượng cao. Đôi khi cả hai được sử dụng cùng nhau. Một số trình điều
khiển thiết bị sử dụng các ngắt khi tốc độ I/O thấp và chuyển sang chế độ hỏi vòng khi tốc
độ tăng đến điểm mà việc hỏi vòng nhanh hơn và hiệu quả hơn
12.2.4 Truy cập bộ nhớ trực tiếp
Đối với một thiết bị có khả năng truyền dữ liệu lớn, chẳng hạn như ổ đĩa, việc sử dụng
một bộ xử lý đa năng đắt tiền để xem các bit trạng thái và nạp dữ liệu vào một thanh ghi bộ
điều khiển mỗi lần một byte là một quá trình lãng phí—một quy trình được gọi là I/lập trình
được lập trình. O (PIO). Máy tính tránh tạo gánh nặng cho CPU chính với PIO bằng cách
giảm tải một số công việc này cho bộ xử lý có mục đích đặc biệt được gọi là bộ điều khiển
truy cập bộ nhớ trực tiếp (DMA). Để bắt đầu truyền DMA, máy chủ ghi khối lệnh DMA vào
bộ nhớ. Khối này chứa một con trỏ tới nguồn truyền, một con trỏ tới đích truyền và đếm số
byte sẽ được truyền. Một khối lệnh có thể phức tạp hơn, bao gồm danh sách các địa chỉ
nguồn và đích không liền kề nhau. Phương thức thu thập phân tán này cho phép thực hiện
nhiều lần truyền thông qua một lệnh DMA đơn lẻ. CPU ghi địa chỉ của khối lệnh này vào bộ
điều khiển DMA, sau đó tiếp tục với công việc khác. Bộ điều khiển DMA tiến hành vận hành
bus bộ nhớ trực tiếp, đặt các địa chỉ trên bus để thực hiện chuyển giao mà không cần sự
trợ giúp của CPU chính. Bộ điều khiển DMA đơn giản là một thành phần tiêu chuẩn trong
tất cả các máy tính hiện đại, từ điện thoại thông minh đến máy tính lớn.
Lưu ý rằng cách đơn giản nhất là địa chỉ đích nằm trong không gian địa chỉ kernel. Nếu
nó ở trong không gian người dùng, chẳng hạn, người dùng có thể sửa đổi nội dung của
không gian đó trong quá trình truyền, làm mất một số bộ dữ liệu. Tuy nhiên, để có được dữ
liệu được truyền DMA đến không gian người dùng để truy cập luồng, thao tác sao chép thứ
hai, lần này là từ bộ nhớ nhân sang bộ nhớ người dùng, là cần thiết. Bộ đệm đôi này không
hiệu quả. Theo thời gian, các hệ điều hành đã chuyển sang sử dụng ánh xạ bộ nhớ (xem
Phần 12.2.1) để thực hiện chuyển giao I/O trực tiếp giữa các thiết bị và không gian địa chỉ người dùng.
Quá trình bắt tay giữa bộ điều khiển DMA và bộ điều khiển thiết bị được thực hiện thông
qua một cặp dây gọi là yêu cầu DMA và xác nhận DMA. Bộ điều khiển thiết bị đặt một tín
hiệu trên dây yêu cầu DMA khi có sẵn một từ dữ liệu để truyền. Tín hiệu này khiến bộ điều
khiển DMA chiếm bus bộ nhớ, đặt địa chỉ mong muốn trên dây địa chỉ bộ nhớ và đặt tín
hiệu trên dây xác nhận DMA. Khi bộ điều khiển thiết bị nhận được tín hiệu xác nhận DMA,
nó sẽ chuyển từ dữ liệu sang bộ nhớ và xóa tín hiệu yêu cầu DMA.
Khi toàn bộ quá trình truyền hoàn tất, bộ điều khiển DMA sẽ ngắt CPU. Quá trình này
được mô tả trong Hình 12.6. Khi bộ điều khiển DMA chiếm bus bộ nhớ, CPU tạm thời bị
ngăn không cho truy cập bộ nhớ chính, mặc dù nó vẫn có thể truy cập các mục dữ liệu
trong bộ đệm của nó. Mặc dù việc đánh cắp chu kỳ này có thể làm chậm quá trình tính toán
của CPU, nhưng việc giảm tải công việc truyền dữ liệu sang bộ điều khiển DMA nhìn chung
sẽ cải thiện hiệu năng tổng thể của hệ thống. Một số kiến trúc máy tính sử dụng địa chỉ bộ
nhớ vật lý cho DMA, nhưng một số khác thực hiện truy cập bộ nhớ ảo trực tiếp (DVMA), sử
dụng địa chỉ ảo trải qua quá trình dịch sang địa chỉ vật lý. DVMA có thể thực hiện truyền
giữa hai thiết bị được ánh xạ bộ nhớ mà không cần sự can thiệp của CPU hoặc sử dụng bộ nhớ chính. lOMoAR cPSD| 58833082
Trên các nhân chế độ được bảo vệ, hệ điều hành thường ngăn các quy trình phát lệnh
trực tiếp cho thiết bị. Kỷ luật này bảo vệ dữ liệu khỏi các vi phạm kiểm soát truy cập và cũng
bảo vệ hệ thống khỏi việc sử dụng sai bộ điều khiển thiết bị, điều này có thể gây ra sự cố
hệ thống. Thay vào đó, hệ điều hành xuất các chức năng mà một quy trình đủ đặc quyền có
thể sử dụng để truy cập các hoạt động cấp thấp trên phần cứng bên dưới. Trên các nhân
không có bảo vệ bộ nhớ, các tiến trình có thể truy cập trực tiếp vào bộ điều khiển thiết bị.
Truy cập trực tiếp này có thể được sử dụng để đạt được hiệu suất cao, vì nó có thể tránh
giao tiếp hạt nhân, chuyển ngữ cảnh và các lớp phần mềm hạt nhân. Thật không may, nó
cản trở sự ổn định và bảo mật của hệ thống. Các hệ điều hành có mục đích chung chung
bảo vệ bộ nhớ và thiết bị để hệ thống có thể cố gắng bảo vệ chống lại các ứng dụng có lỗi hoặc độc hại lOMoAR cPSD| 58833082
12.2.5 Tom tắt phần cứng I/O
Mặc dù các khía cạnh phần cứng của I/O rất phức tạp khi được xem xét ở mức độ chi
tiết của thiết kế phần cứng điện tử, các khái niệm mà chúng tôi vừa mô tả là đủ để cho
phép chúng tôi hiểu nhiều tính năng I/O của các hệ điều hành. Hãy xem lại các khái niệm chính: • Bus • Bộ điều khiển
• Một cổng I/O và các thanh ghi của nó
• Mối quan hệ bắt tay giữa máy chủ và bộ điều khiển thiết bị
• Việc thực hiện bắt tay này trong một vòng polling hoặc thông qua các ngắt
• Giảm tải công việc này cho bộ điều khiển DMA cho các lần truyền lớn
Chúng tôi đã đưa ra một ví dụ cơ bản về quá trình bắt tay diễn ra giữa bộ điều khiển
thiết bị và máy chủ trước đó trong phần này. Trên thực tế, sự đa dạng của các thiết bị có
sẵn đặt ra một vấn đề cho những người triển khai hệ điều hành. Mỗi loại thiết bị có bộ khả
năng, định nghĩa bit điều khiển và công cụ chuyên nghiệp riêng để tương tác với máy chủ—
và tất cả chúng đều khác nhau. Làm thế nào hệ điều hành có thể được thiết kế để chúng ta
có thể gắn các thiết bị mới vào máy tính mà không cần viết lại hệ điều hành? Và khi các
thiết bị khác nhau rất nhiều, làm thế nào hệ điều hành có thể cung cấp giao diện I/O thống
nhất, thuận tiện cho các ứng dụng? Chúng tôi giải quyết những câu hỏi tiếp theo
12.3 Ứng dụng vào/ra thiết bị ngoài
Trong phần này, chúng ta sẽ thảo luận về các kỹ thuật cấu trúc và giao diện cho hệ điều
hành, cho phép các thiết bị I/O được xử lý một cách chuẩn và đồng nhất. Chúng tôi sẽ giải
thích, ví dụ như: làm thế nào một ứng dụng có thể mở một tệp trên đĩa mà không cần biết
loại đĩa hoặc thiết bị I/O là gì, và làm thế nào để thêm các thiết bị mới vào máy tính mà
không làm gián đoạn hệ điều hành.
Giống như những vấn đề phức tạp khác trong kỹ thuật phần mềm, phương pháp ở đây
bao gồm trừu tượng hóa, đóng gói và lớp phần mềm. Cụ thể, chúng ta có thể trừu tượng
hóa đi các khác biệt chi tiết trong các thiết bị I/O bằng cách xác định một số loại chung. Mỗi
loại chung được truy cập thông qua một tập hợp các chức năng chuẩn - giao diện. Sự khác
biệt được đóng gói trong các mô-đun kernel được gọi là trình điều khiển thiết bị, bên trong
đó được điều chỉnh tùy chỉnh cho từng thiết bị cụ thể nhưng xuất ra một trong những giao
diện tiêu chuẩn. Hình 12.7 minh họa cách các phần liên quan đến I/O của kernel được cấu
trúc theo các lớp phần mềm. lOMoAR cPSD| 58833082
Mục đích của lớp trình điều khiển thiết bị là ẩn đi sự khác biệt giữa các bộ điều khi n ể
thiết bị khác nhau khỏi hệ thống I/O của kernel, giống như các cuộc gọi hệ thống I/O đón g
gói hành vi của thiết bị trong một vài lớp chung mà ẩn đi sự khác biệt phần cứng khỏ c i cá
ứng dụng. Làm cho hệ thống I/O độc lập với phần cứng đơn giản hóa công việc của nhà
phát triển hệ điều hành. Nó cũng mang lại lợi ích cho các nhà sản xuất phần cứng. Họ có
thể thiết kế các thiết bị mới để tương thích với một giao diện bộ điều khiển máy chủ hiện có
( như SATA), hoặc viết trình điều khiển thiết bị để giao tiếp phần cứng mới với các hệ điề u
hành phổ biến. Do đó, chúng ta có thể kết nối các phụ kiện mới vào máy tính mà không cần
chờ đợi nhà cung cấp hệ điều hành phát triển mã hỗ trợ.
Thật không may đối với các nhà sản xuất phần cứng thiết bị, mỗi loại hệ điều hành có
các tiêu chuẩn riêng cho giao diện trình điều khiển thiết bị. Một thiết bị cụ thể có thể được
cung cấp kèm theo nhiều trình điều khiển thiết bị - ví dụ như trình điều khiển cho Windows,
Linux, AIX và macOS. Các thiết bị khác nhau về nhiều mặt, như được minh họa trong Hình 12.8 . lOMoAR cPSD| 58833082
• Thiết bị truyền dữ liệu theo luồng ký tự (Character-stream) hoặc khối (Block). Thiết bị
truyền dữ liệu theo luồng ký tự chuyển tiếp các byte một cách độc lập, trong khi thiết bị
truyền dữ liệu theo khối chuyển tiếp một khối các byte như một đơn vị.
• Thiết bị truyền dữ liệu tuần tự (Sequential) hoặc truy cập ngẫu nhiên (Random
access). Thiết bị truyền dữ liệu tuần tự chuyển tiếp dữ liệu theo một trình tự cố định được
quy định bởi thiết bị, trong khi người sử dụng của một thiết bị ruy t
cập ngẫu nhiên có thể chỉ
định thiết bị để tìm kiếm bất kỳ vị trí lưu trữ dữ liệu có sẵn nào.
• Thiết bị đồng bộ (Synchronous) hoặc bất đồng bộ (Asynchronous). Một thiết bị đồ ng
bộ thực hiện truyền dữ liệu với thời gian phản hồi đáng tin cậy, được điều phối với các khía
cạnh khác của hệ thống. Một thiết bị bất đồng bộ có thời gian phản hồi k hông đều hoặ c
không thể dự đoán, không được điều phối với các sự kiện máy tính khác.
• Thiết bị chia sẻ (Sharable) hoặc dành riêng (Dedicated). Một thiết bị chia sẻ có thể
được sử dụng đồng thời bởi nhiều quy trình hoặc luồng, trong khi thiết bị dành riêng thì không .
• Tốc độ hoạt động. Tốc độ của thiết bị dao động từ vài byte mỗi giây đến hàng tỷ byte mỗi giây .
• Chế độ đọc-ghi, chỉ đọc, ghi một lần. Một số thiết bị thực hiện cả đầu vào và đầu ra,
như ng một số khác chỉ hỗ trợ một hướng truyền dữ liệu. Một số thiết bị cho phép dữ liệu
đư ợc sửa đổi sau khi ghi, nhưng một số khác chỉ có thể ghi một lần và sau đó chỉ có thể đọc . lOMoAR cPSD| 58833082
Với mục đích truy cập ứng dụng, nhiều khác biệt này được ẩn đi bởi hệ điều hành và
các thiết bị được nhóm lại thành một vài loại thông thường. Những phong cách truy cập
thiết bị kết quả được tìm thấy là hữu ích và có ứng dụng rộng rãi. Mặc dù các cuộc gọi hệ
thống chính xác có thể khác nhau trên các hệ điều hành, nhưng các loại thiết bị khá chuẩn.
Các thỏa thuận truy cập chính bao gồm truy cập I/O khối, truy cập luồng ký tự, truy cập tệp
được ánh xạ vào bộ nhớ và các socket mạng. Hệ điều hành cũng cung cấp các cuộc gọi hệ
thống đặc biệt để truy cập một số thiết bị bổ sung, chẳng hạn như đồng hồ thời gian thực và
một bộ đếm thời gian. Một số hệ điều hành cung cấp một bộ cuộc gọi hệ thống cho các thiết
bị hiển thị đồ họa, video và âm thanh.
Hầu hết các hệ điều hành đều có một cơ chế thoát (hoặc cửa sau) cho phép các lệnh
tùy ý từ ứng dụng được truyền một cách mượt mà đến trình điều khiển thiết bị. Trong UNIX,
lệnh hệ thống này được gọi là ioctl() (viết tắt của "I/O control"). Lệnh hệ thống ioctl() cho
phép một ứng dụng truy cập bất kỳ chức năng nào có thể được thực hiện bởi bất kỳ trình
điều khiển thiết bị nào mà không cần phải tạo ra một lệnh hệ thống mới. Lệnh hệ thống
ioctl() có ba đối số. Đối số đầu tiên là một định danh thiết bị kết nối ứng dụng với trình điều
khiển thông qua một thiết bị phần cứng được quản lý bởi trình điều khiển đó. Đối số thứ hai
là một số nguyên chọn một trong các lệnh được thực hiện trong trình điều khiển. Đối số thứ
ba là một con trỏ đến một cấu trúc dữ liệu tùy ý trong bộ nhớ cho phép ứng dụng và trình
điều khiển giao tiếp bất kỳ thông tin điều khiển hoặc dữ liệu cần thiết nào.
Trong UNIX và Linux, định danh thiết bị là một bộ hai số "major và minor". Số major là
loại thiết bị, và số minor là phiên bản của thiết bị đó. Ví dụ, xem xét các thiết bị SSD này
trên một hệ thống. Nếu ta thực thi lệnh: % ls -l /dev/sda* Thì đầu ra:
brw-rw---- 1 root disk 8, 0 Mar 16 09:18 /dev/sda brw-rw---
- 1 root disk 8, 1 Mar 16 09:18 /dev/sda1 brw-rw---- 1 root
disk 8, 2 Mar 16 09:18 /dev/sda2 brw-rw---- 1 root disk 8, 3 Mar 16 09:18 /dev/sda3
Cho thấy 8 là số major. Hệ điều hành sử dụng thông tin đó để định tuyến các yêu cầu
I/O đến trình điều khiển thiết bị thích hợp. Số minor 0, 1, 2 và 3 chỉ ra phiên bản của thiết bị,
cho phép các yêu cầu I/O tới một mục nhập thiết bị lựa chọn thiết bị chính xác cho yêu cầu.
12.3.1 Block and Character Devices
Giao diện thiết bị khối (block-device interface) bao gồm tất cả các khía cạnh cần
thiết để truy cập vào ổ đĩa và các thiết bị hướng khối khác. Thiết bị được
mong đợi sẽ hiểu các lệnh như read() và write(). Nếu đó là
một thiết bị truy cập ngẫu nhiên, nó cũng được mong đợi sẽ có một lệnh seek() để
chỉ định khối nào được chuyển tiếp tiếp theo. Các ứng dụng thường truy cập vào
một thiết bị như vậy thông qua một giao diện hệ thống tập tin (file-system lOMoAR cPSD| 58833082
interface). Chúng ta có thể thấy rằng read(), write() và seek() bao gồm những
hành vi cần thiết của các thiết bị lưu trữ khối, để các ứng dụng được cô lập khỏi
những sự khác biệt cấp thấp giữa các thiết bị đó.
Hệ điều hành và các ứng dụng đặc biệt như hệ thống quản lý cơ sở dữ liệu, có thể ưu
tiên truy cập vào một thiết bị khối dưới dạng một mảng các khối tuyến tính đơn giản.
Phương pháp truy cập này đôi khi được gọi là I/O. Nếu ứng dụng thực hiện bộ đệm của
riêng mình, thì sử dụng hệ thống tập tin sẽ dẫn đến việc tạo thêm bộ đệm không cần thiết.
Tương tự, nếu một ứng dụng cung cấp khóa của riêng nó cho các khối hoặc khu vực, thì
bất kỳ dịch vụ khóa của hệ điều hành nào sẽ trở nên dư thừa ít nhất và mâu thuẫn ở mức
tệ nhất. Để tránh các xung đột này, truy cập thiết bị trực tiếp (raw-device access) chuyển
quyền kiểm soát của thiết bị trực tiếp cho ứng dụng, cho phép hệ điều hành lui lại phía sau.
Tuy nhiên, không có dịch vụ hệ điều hành nào được thực hiện trên thiết bị này. Một giải
pháp hòa hợp ngày càng phổ biến là cho phép hệ điều hành chạy trong chế độ tệp tin mà
vô hiệu hóa bộ đệm và khóa. Trong thế giới UNIX, điều này được gọi là I/O trực tiếp (direct I/O).
Truy cập tập tin được ánh xạ vào bộ nhớ có thể được đặt lên các trình điều khiển thiết
bị khối. Thay vì cung cấp các hoạt động đọc và ghi, giao diện được ánh xạ vào bộ nhớ cung
cấp truy cập vào lưu trữ đĩa thông qua một mảng các byte trong bộ nhớ chính. Lời gọi hệ
thống ánh xạ một tập tin vào bộ nhớ trả về địa chỉ bộ nhớ ảo chứa bản sao của tập tin. Các
chuyển dữ liệu thực tế chỉ được thực hiện khi cần thiết để đáp ứng yêu cầu truy cập vào
hình ảnh bộ nhớ. Bởi vì các chuyển dữ liệu được xử lý bởi cơ chế giống như sử dụng cho
truy cập bộ nhớ ảo được phân trang theo yêu cầu, I/O ánh xạ vào bộ nhớ hiệu quả. Ánh xạ
vào bộ nhớ cũng tiện lợi cho các nhà lập trình - truy cập vào tệp được ánh xạ vào bộ nhớ
chỉ đơn giản là đọc và ghi vào bộ nhớ. Hệ điều hành cung cấp bộ nhớ ảo thường sử dụng
giao diện ánh xạ cho các dịch vụ nhân (kernel services). Ví dụ, để thực thi một chương
trình, hệ điều hành ánh xạ tập tin thực thi vào bộ nhớ và sau đó chuyển quyền điều khiển
đến địa chỉ nhập của tập tin thực thi. Giao diện ánh xạ cũng được sử dụng phổ biến cho
việc truy cập kernel vào không gian swap trên đĩa.
Bàn phím là một ví dụ về thiết bị được truy cập thông qua giao diện dòng ký tự. Các
lệnh hệ thống cơ bản trong giao diện này cho phép một ứng dụng nhận hoặc gửi một ký tự.
Trên cơ sở giao diện này, các thư viện có thể được xây dựng để cung cấp truy cập theo
dòng, với dịch vụ đệm và chỉnh sửa (ví dụ: khi người dùng gõ một ký tự backspace, ký tự
trước đó sẽ bị xóa khỏi luồng đầu vào). Kiểu truy cập này thuận tiện cho các thiết bị đầu
vào như bàn phím, chuột và modem sản xuất dữ liệu đầu vào "tự động" - nghĩa là ở các
thời điểm không nhất thiết được dự đoán bởi ứng dụng. Kiểu truy cập này cũng tốt cho các
thiết bị đầu ra như máy in và bảng âm thanh, mà tự nhiên phù hợp với khái niệm của một luồng byte tuyến tính. 12.3.2 Thiết bị mạng
Bởi vì hiệu suất và đặc tính địa chỉ của I/O mạng khác biệt đáng kể so với I/O đĩa, hầu
hết các hệ điều hành cung cấp một giao diện I/O mạng khác biệt so với giao diện read()–
write()–seek() được sử dụng cho đĩa. Một giao diện có sẵn trong nhiều hệ điều hành, bao
gồm UNIX và Windows, là giao diện socket mạng. lOMoAR cPSD| 58833082
Hãy tưởng tượng về một ổ cắm tường cho điện: bất kỳ thiết bị điện nào đều có thể
được cắm vào. Tương tự, các cuộc gọi hệ thống trong giao diện socket cho phép một ứng
dụng tạo ra một socket, kết nối một socket cục bộ đến một địa chỉ từ xa (điều này kết nối
ứng dụng này vào một socket được tạo bởi một ứng dụng khác), lắng nghe bất kỳ ứng
dụng từ xa nào cắm vào socket cục bộ, và gửi và nhận các gói tin qua kết nối. Để hỗ trợ
việc triển khai các máy chủ mạng, giao diện socket cũng cung cấp một hàm được gọi là
select() để quản lý một tập hợp các socket. Cuộc gọi đến select() trả về thông tin về các
socket có một gói tin đang đợi để nhận và các socket có chỗ để chấp nhận một gói tin để
gửi. Việc sử dụng select() loại bỏ việc đang chờ và đợi bận rộn mà sẽ cần thiết cho I/O
mạng nếu không có nó. Các hàm này bao gồm những hành vi cần thiết của các mạng, rất
hữu ích cho việc tạo ra các ứng dụng phân tán có thể sử dụng bất kỳ phần cứng mạng và bộ giao thức nào.
Nhiều cách tiếp cận khác nhau cho việc giao tiếp giữa các tiến trình và mạng đã được
triển khai. Ví dụ, Windows cung cấp một giao diện cho thẻ giao diện mạng và một giao diện
khác cho các giao thức mạng. Trong UNIX, mà có một lịch sử dài trong việc phát triển công
nghệ mạng, chúng ta tìm thấy các pipe nửa-duplex, FIFO toàn-duplex, STREAMS
toànduplex, hàng đợi tin nhắn và sockets. Thông tin về mạng UNIX được đưa ra trong phần C.9.
12.3.3 Đồng hồ và bộ đếm thời gian
Hầu hết các máy tính đều có đồng hồ và bộ đếm thời gian phần cứng cung cấp ba chức năng cơ bản:
• Cung cấp thời gian hiện tại.
• Cung cấp thời gian đã trôi qua.
• Thiết lập một bộ hẹn giờ để kích hoạt hoạt động X tại thời điểm T.
Các chức năng này được sử dụng nhiều bởi hệ điều hành cũng như các ứng dụng
nhạy cảm với thời gian. Thật không may, các lời gọi hệ thống thực hiện các chức năng này
không được tiêu chuẩn hóa trên các hệ điều hành khác nhau.
Thiết bị để đo thời gian trôi qua và kích hoạt các thao tác gọi là bộ định thời khoảng thời
gian có thể lập trình được. Nó có thể được thiết lập để chờ một khoảng thời gian nhất định
và sau đó tạo ra một ngắt, và nó có thể được thiết lập để làm điều này một lần hoặc lặp lại
quá trình để tạo ra các ngắt định kỳ. Trình lập lịch sử dụng cơ chế này để tạo ra một ngắt sẽ
ngắt một tiến trình vào cuối một lát cắt thời gian của nó. Hệ thống I/O đĩa sử dụng nó để gọi
việc xả các bộ đệm bẩn định kỳ xuống đĩa, và hệ thống mạng sử dụng nó để hủy các thao
tác đang tiến hành quá chậm do tắc nghẽn hoặc lỗi mạng. Hệ điều hành cũng có thể cung
cấp một giao diện cho các tiến trình người dùng sử dụng đồng hồ bấm giờ. Hệ điều hành
có thể hỗ trợ nhiều yêu cầu đồng hồ bấm giờ hơn số kênh phần cứng đồng hồ bấm giờ
bằng cách mô phỏng các đồng hồ ảo. Để làm như vậy, hạt nhân (hoặc trình điều khiển thiết
bị hồ bấm giờ) duy trì một danh sách các ngắt được yêu cầu bởi các chức năng của chính
nó và các yêu cầu của người dùng, sắp xếp theo thứ tự từ sớm nhất trước. Nó thiết lập bộ
định thời cho thời gian sớm nhất. Khi ngắt đồng hồ, hạt nhân thông báo cho người yêu cầu
và tải lại đồng hồ bấm giờ với thời gian sớm nhất tiếp theo. lOMoAR cPSD| 58833082
Máy tính có phần cứng đồng hồ được sử dụng cho nhiều mục đích khác nhau. Các PC
hiện đại bao gồm một bộ định thời sự kiện hiệu suất cao (HPET), chạy ở tốc độ khoảng 10
megahertz. Nó có một vài bộ so sánh có thể được thiết lập để kích hoạt một lần hoặc lặp đi
lặp lại khi giá trị mà chúng giữ khớp với HPET. Trình kích hoạt tạo ra một ngắt, và các rutin
quản lý đồng hồ của hệ điều hành xác định trình kích hoạt đó là cho mục đích gì và hành
động nào sẽ được thực hiện. Độ chính xác của trình kích hoạt bị giới hạn bởi độ phân giải
của bộ định thời sự kiện, cùng với chi phí của việc duy trì các đồng hồ ảo. Hơn nữa, nếu
các tick của bộ định thời được sử dụng để duy trì đồng hồ thời gian của hệ thống, đồng hồ
hệ thống có thể bị trôi lệch. Trôi lệch có thể được sửa chữa thông qua các giao thức được
thiết kế cho mục đích đó, chẳng hạn như NTP, giao thức đồng hồ thời gian mạng, sử dụng
tính toán độ trễ tinh vi để giữ cho đồng hồ máy tính chính xác gần như đạt đến mức độ
đồng hồ nguyên tử. Trong hầu hết các máy tính, đồng hồ phần cứng được xây dựng từ một
bộ đếm tần số cao. Trong một số máy tính, giá trị của bộ đếm này có thể được đọc từ một
thanh ghi thiết bị, trong trường hợp đó, bộ đếm có thể được coi là một đồng hồ độ phân giải
cao. Mặc dù đồng hồ này không tạo ra các ngắt, nó cung cấp các phép đo chính xác của khoảng thời gian.
12.3.4 Non blocking and Asynchronous I/O
Một khía cạnh khác của giao diện hệ thống gọi liên quan đến việc lựa chọn giữa I/O
chặn và I/O không chặn. Khi một ứng dụng thực hiện một lệnh hệ thống chặn, việc thực thi
của luồng gọi bị tạm dừng. Luồng được di chuyển từ hàng đợi chạy của hệ thống điều hành
đến một hàng đợi chờ đợi. Sau khi lệnh hệ thống hoàn thành, luồng được chuyển trở lại
hàng đợi chạy, nơi nó có thể tiếp tục thực thi. Khi nó tiếp tục thực thi, nó sẽ nhận các giá trị
được trả về bởi lệnh hệ thống. Các hành động vật lý được thực hiện bởi các thiết bị I/O
thường là không đồng bộ - chúng mất một thời gian biến đổi hoặc không đoán trước được.
Tuy nhiên, hệ điều hành cung cấp các lệnh hệ thống chặn cho giao diện ứng dụng, vì mã
nguồn ứng dụng chặn dễ viết hơn so với mã nguồn ứng dụng không chặn.
Một số tiến trình cấp người dùng cần I/O không chặn. Một ví dụ là giao diện người dùng
nhận đầu vào từ bàn phím và chuột trong khi xử lý và hiển thị dữ liệu trên màn hình. Một ví
dụ khác là một ứng dụng video đọc các khung hình từ một tệp trên đĩa cứng trong khi đồng
thời giải nén và hiển thị đầu ra trên màn hình.
Một cách để tác giả ứng dụng chồng lấn thực thi với I/O là viết một ứng dụng đa luồng.
Một số luồng có thể thực hiện các lệnh hệ thống chặn, trong khi các luồng khác tiếp tục
thực thi. Một số hệ điều hành cung cấp các lệnh hệ thống I/O không chặn. Một lệnh gọi
không chặn không dừng thực thi của luồng trong thời gian dài. Thay vào đó, nó trả về
nhanh chóng, với một giá trị trả về cho biết có bao nhiêu byte được truyền.
Một lựa chọn khác cho hệ thống gọi không chặn là hệ thống gọi bất đồng bộ. Một cuộc
gọi bất đồng bộ trả về ngay lập tức mà không chờ cho I/O hoàn thành. Luồng tiếp tục thực
hiện mã của nó. Việc hoàn thành I/O tại một thời điểm trong tương lai được truyền tải đến
luồng, thông qua việc đặt một số biến trong không gian địa chỉ của luồng hoặc thông qua
kích hoạt tín hiệu hoặc ngắt phần mềm hoặc chức năng gọi lại được thực thi bên ngoài
luồng điều khiển tuyến tính của luồng. Sự khác biệt giữa gọi không chặn và gọi bất đồng bộ
là read() không chặn trả về ngay lập tức với bất kỳ dữ liệu nào có sẵn - số byte được yêu
cầu đầy đủ, ít hơn hoặc không có. Một cuộc gọi đọc bất đồng bộ yêu cầu một bản sao hoàn lOMoAR cPSD| 58833082
toàn sẽ được thực hiện nhưng sẽ hoàn thành tại một thời điểm trong tương lai. Hai phương
pháp I/O này được hiển thị trong Hình 12.9.
Các hoạt động bất đồng bộ xuất hiện trong các hệ điều hành hiện đại. Thường thì,
chúng không được phơi bày cho người dùng hay ứng dụng mà được chứa trong các hoạt
động của hệ điều hành. Các thiết bị lưu trữ thứ cấp và I/O mạng là những ví dụ hữu ích.
Theo mặc định, khi một ứng dụng yêu cầu gửi dữ liệu qua mạng hoặc ghi dữ liệu vào thiết
bị lưu trữ, hệ điều hành sẽ ghi nhận yêu cầu đó, đưa dữ liệu vào bộ đệm I/O, và trả về cho
ứng dụng. Nếu có thể, để tối ưu hiệu suất toàn hệ thống, hệ điều hành sẽ hoàn thành yêu
cầu đó. Nếu có sự cố hệ thống xảy ra trong thời gian này, ứng dụng sẽ mất bất kỳ yêu cầu
"đang chuyển" nào. Do đó, các hệ điều hành thường đặt giới hạn thời gian cho bộ đệm I/O.
Một số phiên bản của UNIX xóa các bộ đệm lưu trữ phụ của họ mỗi 30 giây, ví dụ, hoặc
mỗi yêu cầu sẽ được xóa trong vòng 30 giây sau khi nó xảy ra. Hệ thống cung cấp một
cách để cho phép các ứng dụng yêu cầu xóa một số bộ đệm (như bộ đệm lưu trữ phụ) để
dữ liệu có thể được ép buộc vào lưu trữ phụ mà không phải chờ đợi cho khoảng thời gian
xóa bộ đệm. Sự nhất quán của dữ liệu trong các ứng dụng được duy trì bởi kernel, mà đọc
dữ liệu từ bộ đệm của nó trước khi yêu cầu I/O đến thiết bị, đảm bảo rằng dữ liệu chưa
được ghi vẫn được trả về cho bộ đọc yêu cầu. Lưu ý rằng nhiều luồng thực hiện I/O đến
cùng một tệp có thể không nhận được dữ liệu nhất quán, tùy thuộc vào cách mà kernel
thực hiện I/O của nó. Trong tình huống này, các luồng có thể cần sử dụng giao thức khóa.
Một số yêu cầu I/O cần được thực hiện ngay lập tức, vì vậy các lệnh hệ thống I/O thường
có một cách để chỉ ra rằng một yêu cầu cụ thể hoặc I/O đến một thiết bị cụ thể nên được thực hiện đồng bộ.
Một ví dụ tốt về hành vi không chặn là lời gọi hệ thống select() cho socket mạng. Lời gọi
hệ thống này có đối số chỉ định thời gian chờ tối đa. Bằng cách đặt nó thành 0, một luồng