24
NGẮT VÀ TRÌNH XỬ LÝ NGẮT Trách nhiệm cốt lõi của hạt nhân bất kỳ hệ điều hành nào đó là quản lý phần cứng đã kết nối tới máy tính như: Đĩa cứng, đĩa blu-ray, bàn phím, chuột, vi xử lý 3D, đài phát không dây... Để đáp ứng khả năng này, hạt nhân hệ điều hành cần phải giao tiếp với các thiết bị riêng biệt của máy tính. Việc cho vi xử lý có tốc độ xử lý lớn hơn các phần cứng chúng giao tiếp, đó không phải là ý tưởng của nhân hệ điều hành cho vấn đề yêu cầu và đợi phản ứng từ các phần cứng chậm hơn một cách đáng kể. Thay vào điều này, bởi vì phần cứng tương đối chậm để phản ứng, hạt nhân cần được giải phóng để giải quyết các công việc khác, chỉ giao tiếp với phần cứng khi chúng đã hoàn thành công việc của mình. Làm thế nào để vi xử lý làm việc với phần cứng mà không làm ảnh hưởng tới hiệu xuất tổng thể của máy tính? Một câu trả lởi cho câu hỏi này đó là “bỏ phiếu”. Một cách định kỳ, hạt nhân có thể kiểm tra trạng thái của phần cứng trong hệ thống và phản ứng lại một cách phù hợp. Bỏ phiếu phải mất chi phí, tuy nhiên nó phải diễn ra liên tục bất kể phần cứng đang hoạt động hoặc đã sẵn sàng. Một giải pháp tốt hơn đó là cung cấp một cơ chế để báo hiệu cho hạt nhân khi cần được chú ý. Cơ chế này được gọi là “Ngắt”. Trong chương này, chúng ta đề cập tới các loại ngắt và làm thế nào để hạt nhân phản ứng với chúng, với một hàm đặc bệt gọi là “Trình xử lý ngắt”. 1.Các loại ngắt Ngắt cho phép phần cứng gửi tín hiệu tới vi xử lý. Ví dụ: Khi bạn gõ phím, trình quản lý bàn phím có vấn đề với tín hiệu điện tới vi xử lý để báo cho hệ điều hành phím vừa được ấn. Những tín hiệu điện đó được gọi là những “Ngắt”. Vi xử lý nhận những ngắt và tín hiệu của hệ điều hành để cho phép hệ điều hành phản ứng với dữ liệu mới. Các thiết bị phần cứng tạo ra các ngắt không đồng bộ với xung nhịp vi xử lý – nó có thể xảy ra bất cứ lúc nào. Do đó hạt nhân có thể bị gián đoạn bất cứ lúc nào để xử lý ngắt. Ngắt được tạo ra từ tín hiệu điện có nguồn gốc từ các thiết bị phần cứng và hướng vào các chân đầu vào của bộ điều khiển ngắt, một chip đơn giản có nhiều luồng nhiều yêu cầu ngắt đưa vào một dòng đơn tới vi xử lý. Khi nhận được một ngắt, trình quản lý ngắt gửi tín hiệu tới vi xử lý. Vi xử lý nhận dạng tín hiệu này và ngắt các thi hành

NGẮT VÀ TRÌNH XỬ LÝ NGẮT

Embed Size (px)

Citation preview

Page 1: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

NGẮT VÀ TRÌNH XỬ LÝ NGẮT

Trách nhiệm cốt lõi của hạt nhân bất kỳ hệ điều hành nào đó là quản lý phần cứng đã kết nối tới máy tính như: Đĩa cứng, đĩa blu-ray, bàn phím, chuột, vi xử lý 3D, đài phát không dây... Để đáp ứng khả năng này, hạt nhân hệ điều hành cần phải giao tiếp với các thiết bị riêng biệt của máy tính. Việc cho vi xử lý có tốc độ xử lý lớn hơn các phần cứng chúng giao tiếp, đó không phải là ý tưởng của nhân hệ điều hành cho vấn đề yêu cầu và đợi phản ứng từ các phần cứng chậm hơn một cách đáng kể. Thay vào điều này, bởi vì phần cứng tương đối chậm để phản ứng, hạt nhân cần được giải phóng để giải quyết các công việc khác, chỉ giao tiếp với phần cứng khi chúng đã hoàn thành công việc của mình.

Làm thế nào để vi xử lý làm việc với phần cứng mà không làm ảnh hưởng tới hiệu xuất tổng thể của máy tính? Một câu trả lởi cho câu hỏi này đó là “bỏ phiếu”. Một cách định kỳ, hạt nhân có thể kiểm tra trạng thái của phần cứng trong hệ thống và phản ứng lại một cách phù hợp. Bỏ phiếu phải mất chi phí, tuy nhiên nó phải diễn ra liên tục bất kể phần cứng đang hoạt động hoặc đã sẵn sàng. Một giải pháp tốt hơn đó là cung cấp một cơ chế để báo hiệu cho hạt nhân khi cần được chú ý. Cơ chế này được gọi là “Ngắt”. Trong chương này, chúng ta đề cập tới các loại ngắt và làm thế nào để hạt nhân phản ứng với chúng, với một hàm đặc bệt gọi là “Trình xử lý ngắt”.

1.Các loại ngắt

Ngắt cho phép phần cứng gửi tín hiệu tới vi xử lý. Ví dụ: Khi bạn gõ phím, trình quản lý bàn phím có vấn đề với tín hiệu điện tới vi xử lý để báo cho hệ điều hành phím vừa được ấn. Những tín hiệu điện đó được gọi là những “Ngắt”. Vi xử lý nhận những ngắt và tín hiệu của hệ điều hành để cho phép hệ điều hành phản ứng với dữ liệu mới. Các thiết bị phần cứng tạo ra các ngắt không đồng bộ với xung nhịp vi xử lý – nó có thể xảy ra bất cứ lúc nào. Do đó hạt nhân có thể bị gián đoạn bất cứ lúc nào để xử lý ngắt.

Ngắt được tạo ra từ tín hiệu điện có nguồn gốc từ các thiết bị phần cứng và hướng vào các chân đầu vào của bộ điều khiển ngắt, một chip đơn giản có nhiều luồng nhiều yêu cầu ngắt đưa vào một dòng đơn tới vi xử lý. Khi nhận được một ngắt, trình quản lý ngắt gửi tín hiệu tới vi xử lý. Vi xử lý nhận dạng tín hiệu này và ngắt các thi hành hiện tại để xử lý ngắt. Vi xử lý có thể báo cho hệ điều hành có một ngắt đã xảy ra, và hệ điều hành có thể xử lý ngắt một cách thích hợp.

Những thết bị khác nhau có thể được ghép với các loại ngắt khác bởi một giá trị duy nhất được kết hợp với mỗi loại ngắt. Bằng cách này, ngắt từ bàn phím là khác biệt với ngắt từ ổ đĩa cứng. Điều này cho phép hệ điều hành có thể phân biệt giữa các loại ngắt và biết thiết bị phần cứng nào gây ra ngắt. Trong trường hợp này, hệ điều hành có thể phục vụ mỗi ngắt với bộ xử lý tương ứng của nó.

Những giá trị của ngắt thường được gọi là những dòng Interrupt Request (IRQ). Mỗi một dòng IRQ được gán một giá trị bằng số. Ví dụ: Trong classic PC, IRQ 0 là bộ đếm ngắt, IRQ 1 là ngắt bàn phím. Không phải tất đều là số, đó là một định nghĩa cứng nhắc, ví dụ: các ngắt được gắn với các PCI bus được gán một cách tự động. Trên một số kiến trúc không phải PC cũng có cách gán động giá trị cho các ngắt. Điều quan trọng cần chú ý đó là “một ngắt cụ thể được gắn với một thiết bị cụ thể, và hạt nhân biết điều này”. Phần cứng

Page 2: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

đưa các ngắt để báo cho hạt nhân: “Này, tôi có một phím mới bấm và đang chờ đây, đọc và xử lý đi cậu bé ”.

2.Ngoại lệ:

Trong những văn bản hệ điều hành, những ngoại lệ thường được đề cập song hành với ngắt. Không giống như ngắt, ngoại lệ xảy ra khi đồng bộ với xung nhịp của vi xử lý. Thực vậy, chúng thường được gọi là đồng bộ ngắt (synchronous interrupts). Ngoại lệ được đưa ra bởi vi xử lý trong khi đang thi hành những chỉ thị hoặc để phản ứng với lỗi lập trình (ví dụ như lỗi chia cho số 0). Bởi vì kiến trúc vi xử lý xử lý ngoại lệ giống với ngắt, cơ sở hạ tầng của hạt nhân cũng xử lý giống vậy. Đã có nhiều đề cập về ngắt trong chương này (Ngắt không đồng bộ được tạo ra bởi phần cứng), cũng gắn liền với ngoại lệ (ngắt đồng bộ được tạo ra bởi vi xử lý).

Trình xử lý ngắt

Chức năng của hạt nhân là chạy để phản ứng với các loại ngắt được gọi là “interrupt handler” (xử lý ngắt) hoặc ISR (interrupt service routine). Mỗi loại thiết bị tạo những lại ngắt có một liên kết tới Interrupt handler. Ví dụ một hàm quản lý ngắt từ hệ thống timer, trong khi một hàm quản lý ngắt khác được tạo ra bởi bàn phím. Quản lý ngắt cho thiết bị là một phần của driver thiết bị - nhân quản lý thiết bị.

Trong Linux, quản lý ngắt là những hàm C thông thường. Chúng có một nguyên mẫu cụ thể, cho phép hạt nhân xử lý thông tin trong một chuẩn, nhưng nó cũng là nhưng hàm thông thường. Có những điểm gì khác giữa Interrupt handlers và những hàm khác của hạt nhân? Đó là hạt nhân gọi chúng để phản ứng lại các ngắt và chúng được chạy trong một ngữ cảnh đặc biệt được gọi là interrupt context (ngữ cảnh ngắt). Ngữ cảnh đặc biệt này là ngẫu nhiên, được gọi là atomic context bởi vì, như chúng ta đã thấy, đoạn mã đang được thi hành trong ngữ cảnh này không thể bị block.

Bở bì một ngắt có thể xảy ra bất cứ lúc nào, một xử lý ngắt có thể lần lượt được thực thi. Điều bắt buộc là quá trình xử lý phải thật nhanh, để quay trở lại thi hành nhanh nhất có thể những đoạn mã đã bị gián đoạn. Vì vậy điều quan trọng là phần cứng phải làm cho hệ điều hành phục vụ ngắt không chậm trễ. Điều quan trọng nữa đó là hệ điều hành cần xử lý ngắt một cách ngắn nhất có thể.

Ít nhất, một công việc xử lý ngắt là để xác nhận ngắt tới phần cứng: “Này, phần cứng, tôi đã nghe bạn nói, hãy quay lại làm việc đi”. Thông thường, mà đôi khi xử lý ngắt có khối lượng lớn công việc để xử lý.

Top Halves so với Bottom Halves

Có 2 mục tiêu: Một là xử lý ngắt thực thi nhanh và xử lý khối lượng lớn công việc. Hai là giải quyết rõ ràng các xung đột với nhau. Bởi vì để hoàn thành những mục tiêu này, quá trình xử lý của các ngắt được chia làm 2 phần, hoặc 2 nửa. Xử lý ngắt được gọi là top half (nửa trên). Nửa trên được chạy ngay lập tức khi có xác nhận của ngắt và chỉ xử lý khi nó là quan trọng. Chẳng hạn như việc xác nhận của ngắt hoặc quay trở lại phần cứng. Công việc có thể được xử lý sau đó, hoãn lại cho tới phần dưới (bottom half). Phần dưới chạy sau, tại

Page 3: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

một thời gian thuận tiện hơn, với tất cả các ngắt. Linux cung cấp rất nhiều cơ chế thực hiện bottom halves (phần sau).(Nó sẽ được đề cập trong chương 8 của tài liệu này).

Nào bây giờ chúng ta cùng xem một ví dụ về sự phân đôi top-half/bottom-half, Khi card mạng nhận những gói tin từ mạng, chúng cần thông báo cho hạt nhân của chúng về khả năng của chúng. Card mạng cần làm việc này ngay lập tức, để tối ưu thông lượng, độ trễ và tránh timeout. Ngay lập tức có một vấn đề ngắt: “Này, hạt nhân, tôi có một số gói tin mới!”. Hạt nhân phản ứng lại bằng cách thi hành đăng ký ngắt của card mạng. Ngắt được chạy, xác nhận phần cứng, sao chép gói tin mới vào bộ nhớ chính, và card mạng sẵn sàng cho những gói tin mới. Nhưng công việc này là quan trọng, thời gian quan trọng, và là công việc riêng của phần cứng. Hạt nhân cần sao chép nhanh những gói tin mạng vào trong bộ nhớ chính bởi vì bộ đệm dữ liệu mạng trong card mạng là giới hạn và có kích thước nhỏ, đặc biệt là so sánh với bộ nhớ chính. Thời gian trễ trong quá trình copy gói tin có thể dẫn tới kết quả là bộ đệm bị tràn. Các gói tin đến bị chôn vùi trong mạng và bị mất. Sau khi dữ liệu mạng đã ở an toàn trong bộ nhớ chính, công việc ngắt hoàn thành, và nó có thể quay trở về điều khiển hệ thống thi hành tiếp các đoạn mã đã bị gián đoạn trước đó. Phần còn lại của quá trình xử lý gói tin được xảy ra sau này, trong phần dưới (bottom half). Trong phần này chúng ta chỉ xem về phần trên (top half).

Đăng ký một trình xử lý ngắt.

Những trình xử lý ngắt có trách nhiệm trong việc điều khiển quản lý thiết bị. Mỗi loại thiết bị được ghép với một trình điều khiển, và nếu thiết bị đó sử dụng ngắt, sau đó trình điều khiển cần đăng ký một trình xử lý ngắt.

Trình điều khiển có thể đăng ký một trình xử lý ngắt và cho một yêu cầu ngắt để xử lý với hàm request_irq(), đã được khai báo trong <linux/interrupt.h>:

/* request_irq: allocate a given interrupt line */

int request_irq(unsigned int irq,

irq_handler_t handler,

unsigned long flags,

const char *name,

void *dev)

Tham số đầu tiên irq để chỉ số của ngắt được cấp phát. Một vài thiết bị ví dụ như system timer hoặc bàn phím, những giá trị này thường được có định. Những thiết bị khác được lập trình hoàn toàn tự động.

Tham số thứ hai, handler là để chỉ con trỏ hàm tới trình xử lý ngắt hiện thời đang phục vụ cho quá trình ngắt này. Hàm này được gọi ra bất kỳ khi hệ điều hành nhận được ngắt.

Page 4: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

typedef irqreturn_t (*irq_handler_t)(int, void *);

Chú ý khai báo nguyên mâu hàm xử lý: Nó có 2 tham số và có giá trị trả về của irqreturn_t. Hàm này sẽ đề cập ở chương sau.

Interrupt handler flags

Tham số thứ ba, flags, có thể là 0 hoặc một hặc một vài cờ khác được định nghĩa trong <linux/interrupt.h>. Giữa các flag này, điều quan trọng nhất đó là:

IRQF_DISABLED: Khi được thiết lập, flag này chỉ cho hạt nhân phải vô hiệu hóa tất cả các ngắt khi thi hành trình xử lý ngắt nà. Khi không được thiết lập, trình xử lý ngắt chạy với tất cả các ngắt trừ bản thân nó tự kích hoạt. Hầu hết các trình xử lý ngắt không thiết lập giá trị cho flag này, vô hiệu hóa tất cả các ngắt là hình thức xấu. Nó được sử dụng để dành riêng cho xử lý ngắt được thi hành nhanh. Flag này là một biểu hiện của SA_INTERRUPT flag, cái mà khi trước dùng để đánh dấu giữu những ngắt nhanh và chậm.

IRQF_SAMPLE_RAMDOM: Flag này chỉ rõ những ngắt được tạo ra bởi thiết bị góp phần tăng khả năng ngẫu nhiên bầu chọn của hạt nhân. Khả năng ngẫu nhiễn bầu chọn ngẫu nhiên của hạt nhân cung cấp những con số thực sự ngẫu nhiên xuất phát từ rất nhiều sự kiện ngẫu nhiên. Nếu flag này được mô tả, thì thời gian của các ngắt từ thiết bị là nguồn để tạo ra sự bầu chọn giống như entropy. Không thiết lập giá trị cho nó nếu thiết bị của bạn có vấn đề với ngắt tại một tỉ lệ dự đoán. Ở phía khác, hầu hết các phần cứng tạo ngắt tại một thời điểm không xác đinh vì thế nó là một nguồn tài nguyên tốt cho entropy.

IRQF_TIMER: Flag này chỉ ra trình xử lý ngắt xử lý cho hệ thống timer

IRQF_SHARED: Flag này chỉ ra yêu cầu ngắt có thể được chia sẻ giữa các đa trình xử lý ngắt. Mỗi trình xử lý được đăng ký trên một dòng của flag này, nói một cách khác là chỉ có một trình xử lý tồn tại trên mỗi dòng.

Một ví dụ về ngắt:

Trong trình điều khiển, yêu cầu một yêu cầu ngắt và cài đặt trình xử lý được hoàn thành thông qua:

if (request_irq(irqn, my_interrupt, IRQF_SHARED, "my_device", my_dev)) {

printk(KERN_ERR "my_device: cannot register IRQ %d\n", irqn);

return -EIO;

}

Trong ví dụ này, irqn là một dòng yêu cầu được ngắt; my_interrupt là trình xử lý; chúng ta đã chỉ ra thông qua flag rằng dòng này có thể được chia sẻ; thiết bị có tên là my_device; và chúng ta đã hợp thức hóa my_dev cho dev. Trong trường hợp không thực hiện được, đoạn code in ra lỗi và trả về giá trị. Nếu lời gọi trả về 0, trình quản xử lý đã được cài đặt thành công. Từ thời điểm này về sau, trình xử lý được gọi trong việc phản ứng với các ngắt. Nó rất

Page 5: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

quan trọng trong việc khởi tạo và đăng ký trình xử lý ngắt phần cứng theo thứ tự thích hợp để ngăn chặn trình xử lý ngắt chạy trước khi phần cứng được khởi tạo.

Giải phóng một trình xử lý ngắt

Khi thiết bị của bạn được giải phóng, bạn cần hủy những trình xử lý ngắt và phải vô hiệu những yêu cầu ngắt. Để làm được việc này, gọi hàm

void free_irq(unsigned int irq, void *dev)

Nếu như yêu cầu ngắt được chỉ ra không được chia sẻ, hàm trên sẽ loại bỏ trình xử lý và vô hiệu những dòng này. Nếu những yêu cầu ngắt được chia sẻ, trình xử lý được nhận diện thong qua thiết bị nào đã bị loại bỏ, nhưng những yêu cầu ngắt chỉ được vô hiệu khi mà trình xử lý cuối cùng được gỡ bỏ. Bây giờ bạn có thể thấy tại sao một unique dev là rất quan trọng. Với những yêu cầu ngắt được chia sẻ, một unique cookie phải được yêu cầu để phân biệt giữa những đa xử lý có thể tồn tại trong một dòng và cho phép free_irq() chỉ loại bỏ đúng những trình xử lý. Trong trường hợp khác, (có thể được chia sẻ, hoặc không được chia sẻ), nếu dev không phải là Null, nó cần phải phù hợp với trình xử lý muốn có. Một lời gọi tới free_irq() cần phải được tạo ra từ ngữ cảnh xử lý.

Viết một trình xử lý ngắt

Đoạn mã sau đây khai báo một trình xử lý ngắt

static irqreturn_t intr_handler(int irq, void *dev)

Chú ý rằng trong khai báo này phải giống với nguyên mẫu của các tham số truyền vào cho trình xử lý ở request_irq(). Tham số đầu tiên irq, nó là giá trị bằng số của yêu cầu ngắt mà trình xử lý đang phục vụ. Giá trị được chuyển vào cho trình xử lý, nhưng không dược sử dụng thường xuyên, trừ khi in log. Trước phiên bản 2.0 của hạt nhân Linux, không có tham số dev và chỉ có irq được sử dụng để phân biệt giữa nhiều thiết bị sử dụng cùng một trình điều khiển. Chính vì thế nên có cùng một trình xử lý ngắt. Một ví dụ cho điều này đó là việc máy tính sử dụng nhiều ở đĩa cứng với cùng một trình điều khiển.

Tham số thứ 2, dev, là một con trỏ để trỏ tới cùng dev được đưa cho request_irq() khi mà trình xử lý ngắt được đăng ký. Nếu giá trị này là duy nhất( cái mà được yêu cầu hỗ trợ chia sẻ), nó có thể thực thi giống một cookie để phân biệt giữa nhiều thiết bị sử dụng chung một trình xử lý ngắt. dev cugnx có thể trỏ tới một cấu trúc đưcọ sử dụng cho trình xử lý ngắt. Bởi vì cấu trúc thiết bị cũng là duy nhất cho mỗi thiết bị và nó hữu ích cho trình xử lý, thường được truyền vào cho dev.

Giá trị trả về của một trình xử lý ngắt được chỉ ra là irqreturn_t. Một trình xử lý ngắt có thể trả về 2 giá trị, IRQ_NONE hoặc IRQ_HANDLED. Giá trị thứ nhất được trả về khi trình xử lý ngắt phát hiện một ngắt cho thiết bị mà không phải do nó khởi tạo. Giá trị thứ 2 được trả về nếu trình xử lý ngắt được gọi một cách chính xác. Ngoài ra, IRQ_RETVAL(val) có thể được sử dụng. Nếu val không phải là 0, hàm này trả về IRQ_HANDLED, ngược lại trả về IRQ_NONE. Những giá trị được nêu ra ở đây được sử dụng để báo cho hạt nhân biết thiết bị nào được ngắt giả. Nếu tất cả trình xử lý ngắt đều trả về IRQ_NONE, sau đó hạt nhân sẽ phát hiện lỗi. Bạn có thể tò mò về kiểu trả về irqreturn_t, nó rất đơn giản là một kiểu nguyên. Giá trị này được cung cấp tương thích ngược với các hạt nhân cũ. Trước phiên bản 2.6. trình xử lý ngắt trả về giá trị void. Trình điều khiển có thề dễ dàng định nghĩa irqreturn_t

Page 6: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

thành void để làm việc với phiên bản 2.4 mà không cần phải có sự chỉnh sửa nào. Trình xử lý ngắt thường được đánh dấu là static bởi vì nó không bao giờ được gọi trực tiếp từ file khác.

Vai trò của trình xử lý ngát phụ thuộc hoàn toàn vào thiết bị và đó là lý do cho vệc cấp phát ngắt. Một thời gian ngắn, hầu hết tất cả các trình xử lý ngắt cần phải cho biết thiết bị nào được nhận ngắt này. Những thiết bị rất phức tạp, cần phải bổ xung them việc gửi và nhận dữ liệu để làm việc với các trình xử lý ngắt.

Trình xử lý chia sẻ.

Một trình xử lý chia sẻ được đăng ký và được thi hành giống như một trình xử lý không chia sẻ. Nhưng có 3 điểm khác

Cờ IRQF_SHARED cần được thiết lập trong tham số flags tới hàm request_irq()

Tham số dev phải là duy nhất cho mỗi trình xử lý được đăng ký. Một con trỏ bất kỳ cho mỗi thiết bị là một cấu trúc đáp ứng được. Một sự lựa chọn chung là cấu trúc thiết bị cũng duy nhất. Bạn không thể nào truyền NULL vào cho trình xử lý chia sẻ.

Một trình xử lý ngắt cần có khả năng phân biệt bất kể khi thiết bị nó quản lý tạo ra một ngắt. Điều này yêu cầu cả phần cứng hỗ trợ và kết hợp logic với trình xử lý ngắt. Nếu phần cứng không đáp ứng khả năng này, thì không còn cách nào để trình xử lý ngắt biết được thiết bị chia sẻ nào là nguyên nhân sinh ra ngắt.

Tấ cả thiết bị chia sẻ yêu cầu ngắt cần phải đáp ứng các yêu cầu trên. Nếu một thiếu bị không chia sẻ công bằng thì không thể có dòng chia sẻ. Khi hàm request_irq() được gọi vói IRQF_SHARED được mô tả, lời gọi này chỉ thành công khi yêu cầu ngắt chưa được đăng ký, hoặc tất cả các trình xử lý được đăng ký trên yêu cầu ngắt đó cũng được mô tả IRQF_SHARED. Một trình xử lý chia sẻ, thỉnh thoảng có thể kết hợp với IRQF_DISABLED.

Khi hạt nhân nhận được một ngắt, nó gọi ra tuần tự các trình xử lý đã được đăng ký trên dòng đó. Trình xử lý cần phải nhanh chóng thoát nếu nó không được liên kết với thiết bị tạo ra ngắt. Điều này hiêu cầu phần cứng phải có trạng thái đăng ký, và trình xử lý có thể kiểm tra. Hầu hết các phần cứng đều có khả năng này.

Thực tế về một trình xử lý ngắt

Nào chúng ta hãy cùng xem một trình xử lý ngắt từ bộ phận điều khiển thời gian thực(RTC), được tìm thấy trong drivers/char/rtc.c. RTC được tìm thấy trong rất nhiều máy, có cả PC. Nó là một thiết bị được tách ra từ hệ thống timer, cái mà thiết lập giờ hệ thống, cung cấp một cảnh báo, hoặc hỗ trợ bộ đếm định kỳ. Trên hầu hết các kiến trúc, hệ thống thời gian được thiết lập bằng cách viết một yêu cầu thời gian vào một thanh ghi cụ thể hoặc một vùng I/O. Bất kỳ cảnh báo hoặc bộ đếm định kỳ, chức năng thường được cài đặt thong qua ngắt. Ngắt tương đương với một đồng hồ báo thức thời gian thực.

Khi trình điều khiển RTC được nạp, hàm rtc_init() được gọi để khởi tạo trình điều khiển. Một trong những nhiệm vụ của nó là để đăng ký xử lý ngắt.

/* register rtc_interrupt on rtc_irq */

Page 7: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

if (request_irq(rtc_irq, rtc_interrupt, IRQF_SHARED, "rtc", (void *)&rtc_port)) {

printk(KERN_ERR "rtc: cannot register IRQ %d\n", rtc_irq);

return -EIO;

}

Trong ví dụ này yêu cầu ngắt được chứa trong rtc_irq. Biến này được thiết lập cho ngắt RTC cho cấu trúc được đưa ra. Trên PC, RTC được định vị ở IRQ 8. Tham số thứ 2 là trình xử lý, rtc_interrupt, sẵn sang chia sẻ yêu cầu ngắt với trình xử lý khác, thong qua cờ IRQ_SHARED. Thừ tham số thứ 4 bạn có thể thấy tên thiết bị là rtc.

Và đây là đoạn mã của trình xử lý ngắt rtc:

static irqreturn_t rtc_interrupt(int irq, void *dev)

{

/*

* Can be an alarm interrupt, update complete interrupt,

* or a periodic interrupt. We store the status in the

* low byte and the number of interrupts received since

* the last read in the remainder of rtc_irq_data.

*/

spin_lock(&rtc_lock);

rtc_irq_data += 0x100;

rtc_irq_data &= ~0xff;

rtc_irq_data |= (CMOS_READ(RTC_INTR_FLAGS) & 0xF0);

if (rtc_status & RTC_TIMER_ON)

mod_timer(&rtc_irq_timer, jiffies + HZ/rtc_freq + 2*HZ/100);

spin_unlock(&rtc_lock);

/*

* Now do the rest of the actions

*/

spin_lock(&rtc_task_lock);

if (rtc_callback)

rtc_callback->func(rtc_callback->private_data);

Page 8: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

spin_unlock(&rtc_task_lock);

wake_up_interruptible(&rtc_wait);

kill_fasync(&rtc_async_queue, SIGIO, POLL_IN);

return IRQ_HANDLED;

}

Hàm này được gọi bất kể khi nào máy nhận được ngắt RTC. Nhưng thứ nhất phải chú ý rtc_irq_data không được truy cập đồng thời bởi vi xử lý khác trên một hệ thống SMP, và thứ hai là thiết lập bảo bệ rtc_callback.

rtc_irq_data là biến unsigned long dùng để lưu trữ thông tin về RTC và được cập nhật mỗi khi ngắt phản ánh về trạng thái của ngắt đó.

Tiếp theo nếu như RTC preodic timer được thiết lập, nó được cập nhật thông qua hàm mod_timer().

Đoạn cuối của đoạn code, dưới dòng comment là để gọi lại một hàm có sẵn. Trình điều khiển RTC cho phép hàm gọi lại có thể được đăng ký và thi hành trên mỗi ngắt của RTC.

Cuối cùng hàm trả về IRQ_HANDLED báo hiệu là đã xử lý thiết bị này.

Ngữ cảnh ngắt

Khi thực hiện một bộ xử lý ngắt, hạt nhân đang ở tai ngữ cảnh ngắt. Nhớ lại quá trình ngữ cảnh đang chế độ hoạt động hạt nhân trong khi đang thực hiện thay cho một cuộc gọi một quá trình ví dụ, thực hiện hệ thống hoặc chạy một chủ đề hạt nhân. Trong quá trình ngữ cảnh, các vĩ mô hiện tại chỉ đến nhiệm vụ liên quan. Hơn nữa, vì một quá trình cùng với các hạt nhân ở tại quá trình ngữ cảnh, quá trình ngữ cảnh có thể ngủ hoặc nếu không, gọi các lịch trình.

ngữ cảnh ngắt, mặt khác, không liên kết với một quá trình. Các vĩ mô hiện nay đang không có liên quan (mặc dù nó chỉ đến quá trình ngắt). Nếu không có một quá trình sao lưu, ngữ cảnh ngắt không thể ngủ, làm thế nào mà nó bao giờ sắp xếp lại? Do đó, bạn không thể gọi chức năng nhất định từ ngữ cảnh ngắt. Nếu chức năng ngủ, bạn không thể sử dụng nó từ của bạn ngắt xử lý-điều này hạn chế các chức năng mà ta có thể gọi từ một ngắt xử lý.

Ngữ cảnh ngắt thời gian là quan trọng bởi vì các bộ xử lý ngắt ngắt mã khác.

Mã nên được nhanh chóng và đơn giản. Bận vòng lặp là có thể, nhưng khuyến khích. Đây là một điểm quan trọng, luôn luôn ghi nhớ rằng xử lý ngắt của bạn đã ngắt mã khác (thậm chí có thể xử lý một ngắt trên một dòng khác nhau!). Bởi vì điều này bản chất không đồng bộ, bắt buộc tất cả các bộ xử lý ngắt như nhanh chóng và đơn giản nhất có thể càng nhiều càng tốt, làm việc cần được đẩy ra từ bộ xử lý ngắt và

thực hiện trong một nửa dưới, chạy vào thời gian thuận tiện hơn.

Các thiết lập của ngăn xếp một bộ xử lý ngắt là một lựa chọn cấu hình. Trong lịch sử, ngắt

Page 9: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

xử lý không nhận được ngăn xếp riêng của mình. Thay vào đó, họ sẽ chia sẻ các ngăn xếp của

quá trình mà họ bị gián đoạn. Các hạt nhân ngăn xếp là hai trang về kích thước, thông thường, đó là 8KB

trên 32-bit, kiến trúc và 16KB trên các kiến trúc 64-bit. Bởi vì trong này thiết lập ngắt

xử lý chia sẻ đống, họ phải được đặc biệt tiết kiệm với những dữ liệu họ phân bổ

ở đó. Tất nhiên, hạt nhân ngăn xếp được giới hạn để bắt đầu, vì vậy tất cả mã hạt nhân nên được

thận trọng.

Sớm trong quá trình hạt nhân 2.6, tùy chọn đã được bổ sung để làm giảm kích thước của hai ngăn xếp

trang xuống một, cung cấp chỉ là một 4KB ngăn xếp trên hệ thống 32-bit. Áp lực này giảm trí nhớ bởi vì mỗi quá trình trên hệ thống trước đây cần hai trang của kề nhau, bộ nhớ hạt nhân nonswappable. Để đối phó với các kích thước ngăn xếp giảm, xử lý ngắt được cho riêng ngăn xếp của họ, một ngăn xếp trên bộ vi xử lý, một trang trong kích thước. ngăn xếp này được gọi là ngắt ngăn xếp. Mặc dù kích thước tổng cộng của các ngắt ngăn xếp là một nửa của

ban đầu được chia sẻ ngăn xếp, ngăn xếp trung bình không gian có sẵn là lớn hơn bởi vì ngắt xử lý

có được trang đầy đủ bộ nhớ cho bản thân mình.

Xử lý ngắt của bạn không nên quan tâm những gì ngăn xếp thiết lập được sử dụng hoặc những gì kích thước của hạt nhân ngăn xếp có. Luôn luôn sử dụng một số lượng tối thiểu tuyệt đối ngăn xếp không gian.

Thực hiện xử lý ngắt

Có lẽ không đáng ngạc nhiên, việc thực hiện hệ thống xử lý ngắt trong Linux là kiến trúc phụ thuộc. Thực hiện phụ thuộc vào bộ vi xử lý, loại ngắt

điều khiển được sử dụng, và thiết kế của kiến trúc và máy. Hình 7.1 là biểu đồ của con đường ngắt một có thông qua phần cứng và hạt nhân.

A tuan chen them hinh vao nhe!

Một vấn đề thiết bị ngắt bằng cách gửi một tín hiệu điện trên xe buýt của mình cho các gián đoạn điều khiển. Nếu yêu cầu ngắt được kích hoạt (có thể là đeo mặt nạ ra), bộ điều khiển ngắt gửi ngắt để xử lý. Trong hầu hết các kiến trúc, điều này được thực hiện bởi một tín hiệu điện được gửi trong một pin đặc biệt để xử lý. Trừ khi ngắt bị vô hiệu hóa trong các bộ vi xử lý (mà cũng có thể xảy ra), xử lý ngay lập tức dừng lại những gì nó đang làm gì, vô

Page 10: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

hiệu hóa hệ thống ngắt, và nhảy đến một địa điểm định sẵn trong bộ nhớ và thực thi các mã nằm ở đó. Điểm này được xác định trước được thiết lập bởi hạt nhân và là điểm mấu chốt cho bộ xử lý ngắt.

Các ngắt hành trình trong nhân bắt đầu vào thời điểm này nhập cảnh được xác định trước, cũng giống như

các cuộc gọi vào hệ thống hạt nhân thông qua một bộ xử lý ngoại lệ được xác định trước. Đối với mỗi yêu cầu ngắt, bộ xử lý nhảy đến một địa điểm duy nhất trong bộ nhớ và thực thi các mã nằm ở đó. Theo cách này, hạt nhân biết số IRQ của ngắt đến. Các điểm vào ban đầu chỉ đơn giản là lưu giá trị này và lưu trữ các giá trị đăng ký hiện tại (thuộc nhiệm vụ ngắt) trên ngăn xếp, sau đó nhân các cuộc gọi do_IRQ (). Từ đây trở đi, hầu hết các mã xử lý ngắt được viết bằng C, tuy nhiên, nó là

vẫn còn phụ thuộc vào kiến trúc.

chức năng do_IRQ () được khai báo là

unsigned int do_IRQ (struct regs pt_regs)

Bởi vì C gọi nơi quy ước chức năng lập luận ở trên cùng của ngăn xếp, cơ cấu pt_regs chứa các giá trị đăng ký ban đầu mà trước đây được lưu trong các thủ tục nhập cảnh lắp ráp. Bởi vì các giá trị ngắt cũng được lưu lại, do_IRQ () có thể giải nén nó. Sau khi yêu cầu ngắt được tính toán, do_IRQ () thừa nhận khi nhận được ngắt và vô hiệu hóa cung cấp ngắt trên đường dây. Trên máy PC bình thường, các hoạt động này được xử lý bởi mask_and_ack_8259A ().

Tiếp theo, do_IRQ () đảm bảo cho một bộ xử lý hợp lệ được đăng ký trên đường dây và rằng đó là

kích hoạt và hiện tại không thực hiện. Nếu vậy, nó gọi handle_IRQ_event (), được định nghĩa trong

kernel / irq / handler.c, để chạy cài đặt xử lý ngắt cho đường dây.

/**

* handle_IRQ_event - irq action chain handler

* @irq: the interrupt number

* @action: the interrupt action chain for this irq

*

* Handles the action chain of an irq event

*/

irqreturn_t handle_IRQ_event(unsigned int irq, struct irqaction *action)

{

irqreturn_t ret, retval = IRQ_NONE;

Page 11: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

unsigned int status = 0;

if (!(action->flags & IRQF_DISABLED))

local_irq_enable_in_hardirq();

do {

trace_irq_handler_entry(irq, action);

ret = action->handler(irq, action->dev_id);

trace_irq_handler_exit(irq, action, ret);

switch (ret) {

case IRQ_WAKE_THREAD:

/*

* Set result to handled so the spurious check

* does not trigger.

*/

ret = IRQ_HANDLED;

/*

* Catch drivers which return WAKE_THREAD but

* did not set up a thread function

*/

if (unlikely(!action warn_no_thread(irq, action);

break;

}

/*

* Wake up the handler thread for this

* action. In case the thread crashed and was

* killed we just pretend that we handled the

* interrupt. The hardirq handler above has

* disabled the device interrupt, so no irq

* storm is lurking.

Page 12: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

*/

if (likely(!test_bit(IRQTF_DIED,

&action->thread_flags))) {

set_bit(IRQTF_RUNTHREAD, &action->thread_flags);

wake_up_process(action->thread);

}

/* Fall through to add to randomness */

case IRQ_HANDLED:

status |= action->flags;

break;

default:

break;

}

retval |= ret;

action = action->next;

} while (action);

if (status & IRQF_SAMPLE_RANDOM)

add_interrupt_randomness(irq);

local_irq_disable();

return retval;

}

Trước tiên, vì bộ xử lý ngắt vô hiệu hóa, chúng đang quay trở lại trừ khi

IRQF_DISABLED đã được quy định trong quá trình đăng ký của bộ xử lý. Nhớ lại rằng

IRQF_DISABLED xác định rằng xử lý phải được chạy với ngắt vô hiệu hóa. Tiếp theo,

mỗi xử lý tiềm năng được thực hiện trong một vòng lặp. Nếu dòng này không được chia sẻ, vòng lặp kết thúc sau khi lặp đi lặp lại đầu tiên. Nếu không, tất cả các bộ xử lý được thực thi. Sau đó,

add_interrupt_randomness () được gọi là nếu IRQF_SAMPLE_RANDOM được quy định trong quá trình đăng ký. Chức năng này sử dụng thời gian của các ngắt để tạo ra dữ liệu ngẫu nhiên cho các máy phát điện số ngẫu nhiên. Cuối cùng, ngắt được một lần nữa vô

Page 13: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

hiệu hóa (do_IRQ () hy vọng họ vẫn còn phải được tắt) và hàm trả về. Trở lại do_IRQ (), chức năng dọn dẹp và trả về cho điểm vào ban đầu, sau đó nhảy đến ret_from_intr ().

Các ret_from_intr () là thường, như với mã mục ban đầu, được viết trong lắp ráp.

Điều này thường xuyên kiểm tra xem liệu một lịch lại là chờ. (Nhớ lại từ Chương 4, "Quy trình

Lập kế hoạch, "điều này có nghĩa là need_resched được thiết lập). Nếu một lịch lại là chờ, và các hạt nhân đang trở lại với không gian sử dụng (có nghĩa là, những gián đoạn ngắt một quá trình người dùng), lịch () được gọi. Nếu hạt nhân đang trở lại với không gian hạt nhân (có nghĩa là, những gián đoạn bị gián đoạn trong bản thân), lịch () được gọi là chỉ khi preempt_count là số không. Nếu không nó không phải là an toàn Ðể tránh bị hạt nhân. Sau khi lịch trình () trả về, hoặc nếu không có công việc đang chờ, sổ đăng ký ban đầu được phục hồi và các hồ sơ hạt nhân bất cứ điều gì bị ngắt.

Trên x86, các thói quen lắp ráp ban đầu được đặt tại arch/x86/kernel/entry_64.S

(entry_32.S cho 32-bit x86) và các phương pháp C được đặt tại arch/x86/kernel/irq.c.

Kiến trúc khác có hỗ trợ tương tự.

/ proc / interrupts

Procfs là một hệ thống tập tin ảo chỉ tồn tại trong bộ nhớ hạt nhân và thường được gắn ở proc /. Đọc hoặc viết các tập tin trong procfs gọi chức năng hạt nhân mà mô phỏng đọc hoặc ghi từ một tập tin thực sự. Một ví dụ liên quan được các / proc / interrupts tập tin, mà là dân cư với số liệu thống kê liên quan đến ngắt trên hệ thống. Đây là mẫu đầu ra từ một máy tính uniprocessor:

CPU0

0: 3602371 XT-PIC timer

1: 3048 XT-PIC i8042

2: 0 XT-PIC cascade

4: 2689466 XT-PIC uhci-hcd, eth0

5: 0 XT-PIC EMU10K1

12: 85077 XT-PIC uhci-hcd

15: 24571 XT-PIC aic7xxx

NMI: 0

LOC: 3602236

ERR: 0

Page 14: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

Cột đầu tiên là yêu cầu ngắt. Trên hệ thống này, ngắt đánh số 0-2, 4, 5, 12,

và 15 có mặt. Xử lý không được cài đặt trên các tuyến đường không được hiển thị. Cột thứ hai là truy cập của số ngắt received.A cột là hiện nay cho mỗi bộ vi xử lý trên hệ thống, nhưng máy này chỉ có một processor.As bạn có thể thấy, những ngắt giờ đã nhận được 3.602.371 ngắt, 2 trong khi các card âm thanh (emu10k1) đã nhận được không (đó là một dấu hiệu cho thấy nó không được sử dụng từ máy tính khởi động). Cột thứ ba là bộ điều khiển xử lý ngắt ngắt này. XT-PIC tương ứng với các tiêu chuẩn

PC lập trình điều khiển ngắt. Trên hệ thống với một I / O APIC, hầu hết các ngắt

có danh sách IO-APIC cấp hoặc IO-APIC cạnh là bộ điều khiển ngắt của họ. Cuối cùng, cột cuối cùng là thiết bị liên quan ngắt này. Tên này được cung cấp bởi các tham số devname để request_irq (), như được thảo luận trước đó. Nếu ngắt được chia sẻ, như là trường hợp với số gián đoạn 4 trong ví dụ này, tất cả các thiết bị đã đăng ký trên yêu cầu ngắt được liệt kê.

Đối với những người tò mò, mã procfs nằm chủ yếu trong fs / proc. Các chức năng cung cấp / proc / interrupts là, không đáng ngạc nhiên, kiến trúc phụ thuộc và đặt tên là show_interrupts ().

Các hạt nhân Linux thực thi một họ về các giao diện cho các thao tác trạng thái ngắt trên máy. Các giao diện này cho phép bạn vô hiệu hóa hệ thống ngắt cho các bộ xử lý hiện hành hoặc che đi một đường dây ngắt cho toàn bộ máy. Những thường trình này phụ thuộc tất cả các cấu trúc và có thể được tìm thấy trong <asm/system.h> và <asm/irq.h>. Xem Bảng 7.2, sau đó trong chương này, đối với một danh sách đầy đủ các giao diện. Lý do để kiểm soát các hệ thống ngắt nói chung cần phải cung cấp đồng bộ hóa. Bằng cách vô hiệu hoá ngắt, bạn có thể đảm bảo rằng một bộ xử lý sẽ không tránh ngắt mã hiện tại của bạn. Hơn nữa, vô hiệu hoá ngắt cũng vô hiệu hóa hạt nhân. Không phân phối vô hiệu hóa ngắt hay việc vô hiệu hóa hạt nhân cung cấp bất kỳ sự bảo vệ từ việc truy cập đồng thời từ các bộ vi xử lý khác, tuy nhiên. Bởi vì Linux hỗ trợ nhiều bộ xử lý, mã hạt nhân nói chung cần phải có được một số loại khóa để ngăn ngừa một bộ xử lý truy cập dữ liệu được chia sẻ cùng một lúc. Các khóa này thường được kết hợp với việc vô hiệu hóa ngắt cục bộ. Khóa này cung cấp bảo vệ chống truy cập đồng thời từ các bộ vi xử lý khác, trong khi vô hiệu hoá ngắt cung cấp bảo vệ chống lại các truy cập đồng thời từ một trình xử lý ngắt có thể.

Vô hiệu hóa và kích hoạt ngắt

Để ngắt vô hiệu hóa cục bộ cho các bộ xử lý hiện tại (và chỉ có bộ xử lý hiện hành) và sau đó bật lại chúng, làm như sau:

local_irq_disable (); / ngắt * là vô hiệu hoá .. * / local_irq_enable ();

Page 15: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

Các chức năng này thường được thực hiện như một hoạt động lắp ráp đơn lẻ. (Tất nhiên, điều này phụ thuộc vào kiến trúc.) Thật vậy, trên x86, local_irq_disable () là một local_irq_enable cliand đơn giản () là một chỉ dẫn đơn giản. cli và sti đang lắp ráp các cuộc gọi đến rõ ràng và thiết lập cho phép ngắt lá cờ, tương ứng. Nói cách khác, họ vô hiệu hoá và cho phép phân phối gián đoạn vào bộ xử lý phát hành.

Các local_irq_disable () thường là nguy hiểm nếu ngắt đã bị vô hiệu hoá trước khi gọi của nó. Các cuộc gọi tương ứng với local_irq_enable () vô điều kiện cho phép ngắt, bất chấp thực tế rằng họ đã tắt để bắt đầu. Thay vào đó, cơ chế cần thiết để khôi phục các ngắt về trạng thái trước. Đây là một mối quan tâm chung vì một con đường mã được đưa ra trong hạt nhân có thể đạt được cả hai có và không có các ngắt kích hoạt, tùy thuộc vào chuỗi gọi. Ví dụ, hãy tưởng tượng các đoạn mã trước đây là một phần của một chức năng lớn hơn. Hãy tưởng tượng rằng chức năng này được gọi bằng hai chức năng khác, một trong đó vô hiệu hóa ngắt và một không. Bởi vì nó đang trở nên khó khăn hơn như là hạt nhân phát triển về quy mô và phức tạp để biết tất cả các đường dẫn mã dẫn đến một chức năng, nó là an toàn hơn nhiều để lưu trạng thái của hệ thống ngắt trước khi vô hiệu hóa nó sau, khi bạn đã sẵn sàng để ngắt bật lại , bạn chỉ cần khôi phục lại tình trạng ban đầu của chúng:

local_irq_save (cờ); / * ngắt bây giờ bị vô hiệu hóa * /

local_irq_restore (cờ); / ngắt là khôi phục lại trạng thái trước đó của chúng * /

Lưu ý rằng những phương pháp này được thực hiện ít nhất một phần như các macro, vì thế cờ tham số là có vẻ như thông qua giá trị. Tham số này có chứa dữ liệu cụ thể cấu trúc có chứa các trạng thái của các hệ thống ngắt. Bởi vì ít nhất một hỗ trợ cấu trúc kết hợp ngăn xếp thông tin vào giá trị (ahem, SPARC), cờ không thể được thông qua chức năng khác (đặc biệt, nó phải được giữ trên cùng một khung ngăn xếp). Vì lý do này, các cuộc gọi để lưu lại và ngắt cuộc gọi để khôi phục lại phải xảy ra trong cùng chức năng.

Tất cả các chức năng trước đây có thể được gọi từ cả hai bối cảnh ngắt và quá trình.

No More Global cli()

Các hạt nhân trước đây là cung cấp một phương pháp để ngắt vô hiệu hóa trên tất cả các bộ vi xử lý trong hệ thống. Hơn nữa, nếu một bộ xử lý được gọi là phương pháp này, nó sẽ phải chờ cho đến khi các ngắt được kích hoạt trước khi tiếp tục. Chức năng này được đặt tên là cli () và các cuộc gọi cho phép tương ứng được đặt tên là sti ()-rất x86-trung tâm, mặc dù đã có cho mọi kiến trúc.

Các giao diện này đã bị phản đối trong suốt thời gian 2,5, và do đó tất cả các đồng bộ hóa ngắt hiện nay phải sử dụng một sự kết hợp kiểm soát ngắt cục bộ và quay ổ khóa. Điều này có nghĩa rằng mã mà trước đây chỉ phải các ngắt vô hiệu hóa toàn cầu để đảm bảo độc quyền truy cập lẫn nhau để chia sẻ dữ liệu bây giờ cần phải làm việc nhiều hơn một chút. Trước đây, người viết trình điều khiển có thể giả định một cli () được sử dụng trong xử lý ngắt của họ và bất cứ nơi nào khác chia sẻ dữ liệu được truy cập sẽ cung cấp loại trừ lẫn nhau. Các cli () gọi sẽ đảm bảo rằng không có xử lý ngắt khác (và do đó xử lý riêng của họ) sẽ chạy. Hơn nữa, nếu một bộ xử lý vào một cli () khu vực bảo vệ, nó sẽ không tiếp tục cho đến khi xử lý ban đầu đã thoát cli của nó () khu vực được bảo vệ với một cuộc gọi đến sti (). Loại bỏ các cli toàn cầu () có một số ít các lợi thế. Đầu tiên, nó buộc người viết điều khiển để thực hiện các khóa thực sự. Một khóa xử lý mỗi gói với một mục đích cụ thể là nhanh

Page 16: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

hơn một khóa toàn cầu, mà là hiệu quả những gì cli () là. Thứ hai, việc loại bỏ được sắp xếp rất nhiều mã trong hệ thống ngắt và di chuyển một nhóm nhiều hơn nữa. Kết quả là đơn giản và dễ hiểu

Vô hiệu hóa một yêu cầu ngắt cụ thể

Trong phần trước, chúng tôi đã vô hiệu hóa tất cả các chức năng ngắt cung cấp cho một cả bộ xử lý. Trong một số trường hợp, nó rất hữu ích để vô hiệu hóa chỉ có một dòng cụ thể ngắt cho toàn bộ hệ thống. Điều này được gọi là che đi một đường dây ngắt. Ví dụ, bạn có thể muốn cung cấp vô hiệu hóa của các ngắt của thiết bị trước khi thao tác trạng thái của nó. Linux cung cấp bốn giao diện cho công việc này:

void disable_irq (unsigned int irq);

void disable_irq_nosync (irq int không dấu);

void enable_irq (unsigned int irq);

void synchronize_irq (unsigned int irq);

Hai chức năng đầu tiên được vô hiệu hóa một ngắt dòng trong bộ điều khiển ngắt.

Điều này cung cấp vô hiệu hóa các ngắt cung cấp cho tất cả các bộ vi xử lý trong hệ thống. Ngoài ra, các chức năng disable_irq () không trả lại cho đến khi xử lý bất kỳ hiện đang thực hiện hoàn tất. Như vậy, các lời gọi được đảm bảo rằng không chỉ làm ngắt mới sẽ không được chuyển giao trên đường nhất định, nhưng cũng là bất kỳ bộ xử lý đã được thực hiện có kết thúc.Các chức năng disable_irq_nosync () không chờ xử lý hiện tại để hoàn thành.

Các chức năng synchronize_irq () chờ xử lý cụ thể ngắt để thoát ra, nếu nó là thực hiện, trước khi trở về.

Các cuộc gọi đến các tổ chức năng. Đối với mỗi cuộc gọi đến disable_irq () hoặc disable_irq_nosync () trên một đường dây ngắt nhất định, một cuộc gọi tương ứng với enable_irq ()

là bắt buộc. Chỉ trên các cuộc gọi cuối cùng để enable_irq () là yêu cầu ngắt thực sự kích hoạt.

Ví dụ, nếu disable_irq () được gọi là hai lần, yêu cầu ngắt là không thực sự kích hoạt lại cho đến khi cuộc gọi thứ hai đến enable_irq().

Tất cả ba của các chức năng này có thể được gọi từ thời điểm ngắt hoặc quá trình không dừng. Nếu gọi từ thời điểm ngắt, hãy cẩn thận! Bạn không muốn, ví dụ, để kích hoạt một đường dây ngắt trong khi bạn đang xử lý nó. (Hãy nhớ rằng các yêu cầu ngắt của bộ xử lý được che đậy ra trong khi nó đang phục vụ.)

Nó sẽ là khá thô sơ để vô hiệu hóa một đường dây ngắt chia sẻ giữa nhiều xử lý ngắt. Vô hiệu hóa các dòng vô hiệu hóa cung cấp ngắt cho tất cả các thiết bị trên dòng. Do đó, trình điều khiển cho các thiết bị mới hơn có xu hướng không sử dụng các giao diện này. Bởi vì các thiết bị PCI có để hỗ trợ chia sẻ yêu cầu ngắt bởi đặc điểm kỹ thuật, họ không nên sử dụng các giao diện này ở tất cả. Như vậy, disable_irq () và bạn bè được tìm thấy thường

Page 17: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

xuyên hơn trong các trình điều khiển cho các thiết bị thừa cũ, như cổng song song của máy tính.

Trạng thái của hệ thống ngắt

Nó thường hữu ích để biết tình trạng của hệ thống ngắt (ví dụ, đã ngắt được kích hoạt hay vô hiệu hóa) hoặc liệu bạn đang thực hiện trong bối cảnh ngắt.

Các irqs_disabled (), được định nghĩa trong <asm/system.h>, trả về giá trị khác không nếu ngắt hệ thống trên bộ vi xử lý của địa phương là vô hiệu. Nếu không, nó trả về số không.

Hai macro, quy định tại <linux/hardirq.h>, cung cấp một giao diện để kiểm tra hạt nhân của

bối cảnh hiện nay. Chúng là

in_interrupt ()

in_irq ()

Các hữu ích nhất là lần đầu tiên: Nó trả về giá trị khác không nếu hạt nhân đang thực hiện bất kỳ loại xử lý ngắt. Điều này bao gồm cả thực hiện một bộ xử lý ngắt hoặc nửa dưới xử lý. Các in_irq () trả về giá trị khác không chỉ khi các hạt nhân là cụ thể thực hiện một bộ xử lý ngắt.

Thường xuyên hơn, bạn muốn kiểm tra xem bạn đang ở trong bối cảnh quá trình Đó là,. bạn muốn để đảm bảo bạn không phải là trong bối cảnh ngắt. Điều này thường xảy ra vì mã muốn làm một cái gì đó mà chỉ có thể được thực hiện từ bối cảnh quá trình, chẳng hạn như ngủ. Nếu in_interrupt () trả về số không, hạt nhân là trong bối cảnh quá trình. Có, tên là khó hiểu và không làm gì để truyền đạt ý nghĩa của chúng. Bảng 7.2 là một bản tóm tắt

các phương pháp kiểm soát ngắt và mô tả của họ.

Table 7.2 Interrupt Control Methods

Function Description

local_irq_disable() Disables local interrupt delivery

local_irq_enable() Enables local interrupt delivery

local_irq_save() Saves the current state of local interrupt delivery and then disables it

local_irq_restore() Restores local interrupt delivery to the given state

disable_irq() Disables the given interrupt line and ensures no handler on the line is executing before returning

disable_irq_nosync() Disables the given interrupt line

enable_irq() Enables the given interrupt line

irqs_disabled() Returns nonzero if local interrupt delivery is disabled; otherwise

Page 18: NGẮT VÀ  TRÌNH XỬ LÝ NGẮT

returns zero

in_interrupt() Returns nonzero if in interrupt context and zero if in process context

in_irq() Returns nonzero if currently executing an interrupt handler and zero otherwise

Kết luận:

Chương này xem xét ngắt, một nguồn tài nguyên phần cứng được sử dụng bởi các thiết bị không đồng bộ tín hiệu bộ vi xử lý. Ngắt, có hiệu lực, được sử dụng bởi phần cứng để làm gián đoạn hệ điều hành.

Hầu hết các phần cứng hiện đại sử dụng ngắt để giao tiếp với hệ điều hành. Các thiết bị điều khiển để quản lý một phần nhất định của phần cứng đăng ký một bộ xử lý gián đoạn để đáp ứng và làm gián đoạn quá trình phát hành từ phần cứng liên quan của chung. Công tác thực hiện trong ngắt bao gồm xác nhận và cài đặt lại phần cứng, sao chép dữ liệu từ thiết bị vào bộ nhớ chính và ngược lại, xử lý các yêu cầu phần cứng, và đưa ra các yêu cầu phần cứng mới.

Hạt nhân cung cấp giao diện cho đăng ký và huỷ đăng ký xử lý gián đoạn, vô hiệu hóa ngắt, che ra đường gây cản trở, và kiểm tra tình trạng của hệ thống gián đoạn. Bảng 7.2 cung cấp một cái nhìn tổng quan của nhiều các chức năng này.

Bởi vì làm gián đoạn mã gián đoạn thực hiện các (quy trình, hạt nhân chính nó, và thậm chí xử lý các gián đoạn), họ phải thực thi một cách nhanh chóng. Thông thường, tuy nhiên, có rất nhiều việc phải làm. Để cân đối số lượng lớn công việc với sự cần thiết để thực hiện nhanh chóng, hạt nhân phân chia công việc của các ngắt xử lý thành hai nửa. Việc xử lý gián đoạn, nửa trên, đã được thảo luận trong chương này. Các chương tiếp theo nhìn vào nửa dưới.