Giáo trình Lập trình hệ thống

pdf 371 trang ngocly 100
Bạn đang xem 20 trang mẫu của tài liệu "Giáo trình Lập trình hệ thống", để tải tài liệu gốc về máy bạn click vào nút DOWNLOAD ở trên

Tài liệu đính kèm:

  • pdfgiao_trinh_lap_trinh_he_thong.pdf

Nội dung text: Giáo trình Lập trình hệ thống

  1. LẬP TRÌNH HỆ THỐNG Biên tập bởi: Khoa CNTT ĐHSP KT Hưng Yên
  2. LẬP TRÌNH HỆ THỐNG Biên tập bởi: Khoa CNTT ĐHSP KT Hưng Yên Các tác giả: Khoa CNTT ĐHSP KT Hưng Yên Phiên bản trực tuyến:
  3. MỤC LỤC 1. Bài 1: TỔNG QUAN VỀ LẬP TRÌNH HỆ THỐNG 1.1. Khái niệm về lập trình hệ thống 1.2. Tổng quan về lập trình hệ thống 1.3. Lịch sử về lập trình hệ thống 1.4. Cấu trúc tổng quan lập trình hệ thống 2. Bài 2: CÔNG CỤ LẬP TRÌNH HỆ THỐNG 2.1. Các ngôn ngữ lập trình 2.2. Giới thiệu về C++ 2.3. Giới thiệu về Visual C++ 3. Bài 3: THỰC HÀNH MỘT SỐ BÀI TẬP CƠ BẢN TRÊN C++ 3.1. Thực hành một số bài tập cơ bản trên C++ 4. Bài 4: CƠ BẢN VÀ CẤU TRÚC VỀ DRIVER 4.1. Tóm lược lịch sử các bộ điều khiển thiết bị 4.2. Tổng quan về các Hệ điều hành (An Overview of the Operating Systems) 4.3. Các kiểu Driver 4.4. Tổng quan về quản lý và kiểm tra danh sách 5. Bài 5: THỰC HÀNH MỘT SỐ BÀI TẬP CƠ BẢN TRÊN VC++ 5.1. Thực hành một số bài tập cơ bản trên VC++ 6. Bài 6: CÁC KỸ THUẬT LẬP TRÌNH CƠ BẢN 6.1. Môi trường lập trình kiểu Kernel – Mode 6.2. Trình bày lỗi (Lỗi xử lý) 6.3. Quản lý bộ nhớ (Memory Management ) 6.4. Trình bày chuỗi (String Handling) 6.5. Kỹ thuật lập trình hỗn hợp (Miscellaneous Programming Techniques ) 7. Bài 7: THỰC HÀNH MỘT SỐ BÀI TẬP TRÊN VC++ 7.1. Thực hành một số bài tập cơ bản trên Visual C++ 8. Bài 8: LẬP TRÌNH GIAO TIẾP QUA CỔNG LPT 8.1. Giới thiệu cổng LPT 8.2. Cấu trúc cổng LPT 9. Bài 9: THỰC HÀNH VỚI CÁC CHƯƠNG TRÌNH GIAO TIẾP QUA CỔNG LPT 9.1. Thực hành với các chương trình giao tiếp qua cổng LPT 10. Bài 10: THỰC HÀNH VỚI CÁC CHƯƠNG TRÌNH GIAO TIẾP QUA CỔNG COM 10.1. Giới thiệu cổng COM 1/369
  4. 10.2. Cấu trúc cổng COM 11. Bài 11: THỰC HÀNH VỚI CÁC CHƯƠNG TRÌNH GIAO TIẾP QUA CỔNG COM 11.1. Thực hành với các chương trình giao tiếp qua cổng COM 12. Bài 12: VẤN ĐỀ ĐỒNG BỘ 12.1. Vấn đề đồng bộ hóa nguyên mẫu (An Archetypal Synchronization Problem ) 12.2. Mức yêu cầu Ngắt (Interrupt Request Level ) 12.3. Khóa xoay vòng (Spin Locks ) 12.4. Các đối tượng Kernel Dispatcher (Kernel Dispatcher Objects ) 12.5. Một số phương pháp đồng bộ khác (Other Kernel-Mode Synchronization Primitives ) 13. Bài 13: THỰC HÀNH LẬP TRÌNH DRIVER CƠ BẢN 13.1. Thực hành lập trình driver cơ bản 14. Bài 14: GÓI DỮ LIỆU VÀO RA 14.1. Các cấu trúc dữ liệu (Data Structures ) 14.2. Hàng đợi yêu cầu Vàora (Queuing IO Requests) 14.3. Hủy bỏ yêu cầu vàora (Cancelling IO Requests ) 14.4. Tóm lược các kịch bản xử lý (Summary—Eight IRP-Handling Scenarios) 15. Bài 15: THỰC HÀNH LẬP TRÌNH DRIVER CHO XỬ LÝ IRP 15.1. Thực hành một số bài lập trình driver cơ bản 16. Bài 16: ĐỌC VÀ GHI DỮ LIỆU 16.1. Cấu hình thiết bị của bạn (Configuring Your Device ) 16.2. Địa chỉ một Bộ đệm dữ liệu (Addressing a Data Buffer ) 16.3. Các cổng và các thanh ghi (Ports and Registers ) 16.4. Phục vụ ngắt (Servicing an Interrupt ) 16.5. Truy nhập bộ nhớ trực tiếp (Direct Memory Access ) 17. Bài 17: ĐIỀU KHIỂN VÀO/RA VÀ HÀM ĐIỀU KHIỂN PLUG AND PLAY 17.1. Hàm DeviceIoControl API (The DeviceIoControl API) 17.2. Điều khiển IRP MJ DEVICE CONTROL 17.3. Những thao tác bên trong điều khiển IO (Internal IO Control Operations) 18. Bài 18: THỰC HÀNH LẬP TRÌNH DRIVER CHO ĐIỂU KHIỂN VÀO/ RA 18.1. Thực hành lập trình driver cho điều khiển Vàora 19. Bài 19: TRÌNH ĐIỀU KHIỂN CHO USB 19.1. Giới thiệu cổng USB 20. Bài 20: THỰC HÀNH ĐIỀU KHIỂN QUA CỔNG USB 20.1. Thực hành với các chương trình ví dụ điều khiển qua cổng USB 2/369
  5. 21. Bài 21: TRÌNH ĐIỀU KHIỂN CHO HID 21.1. Những bộ điều khiển cho thiết bị HID (Drivers for HID Devices ) 21.2. Những mô tả báo cáo và những báo cáo (Reports and Report Descriptors ) 21.3. Những điều khiển nhỏ HIDCLASS (HIDCLASS Minidrivers) 22. Bài 22: THỰC HÀNH LẬP TRÌNH HID 22.1. Thực hành với các chương trình ví dụ điều khiển cho HID 23. Bài 23: THỰC HÀNH LẬP TRÌNH DRIVER GIAO TIẾP CÁC CỔNG 23.1. Thực hành một số bài tập tổng hợp 24. TÀI LIỆU THAM KHẢO 24.1. Lập trình hệ thống: Tài liệu tham khảo 25. MỤC LỤC 25.1. Lập trình hệ thống: Mục lục Tham gia đóng góp 3/369
  6. Bài 1: TỔNG QUAN VỀ LẬP TRÌNH HỆ THỐNG Khái niệm về lập trình hệ thống Lập trình hệ thống (hoặc chương trình hệ thống) là hoạt động của các phần mềm hệ thống. Đầu tiên chỉ ra sự khác biệt tiêu biểu của các chương trình hệ thống khi đã so sánh tới lập trình ứng dụng là ở đó nhắm vào lập trình ứng dụng để sản sinh phần mềm mà cung cấp những dịch vụ tới người dùng (ví du: bộ xử lý văn bản), trong khi những nhà lập trình hệ thống nhắm vào việc sản xuất phần mềm mà cung cấp những dịch vụ tới phần cứng máy tính (ví dụ: phần mềm chống phân mảnh đĩa). Nó cũng yêu cầu một độ lớn hơn của sự ý thức phần cứng. 4/369
  7. Tổng quan về lập trình hệ thống Những điểm đặc biệt hơn trong lập trình hệ thống: • Những nhà lập trình sẽ tạo những gánh vác về phần cứng và một số thuộc tính khác của những chương trình chạy trên hệ thống đó, và sẽ thường khai thác những thuộc tính đó (cho ví dụ bởi việc sử dụng một giải thuật mà được biết mà hiệu quả khi nào được sử dụng với phần cứng đặc biệt). • Thông thường một ngôn ngữ lập trình cấp thấp hoặc tiếng địa phương ngôn ngữ lập trình sử dụng đó là: ◦ có thể hoạt động trong những môi trường tài nguyên bắt buộc ◦ là rất hiệu quả và có thể thực hiện một ít ở trên đầu ◦ có một thư viện thực hiện nhỏ, hoặc không ở mọi thứ ◦ cho phép trực tiếp và “thô” mà điều khiển qua truy cập bộ nhớ và điều khiển chảy tràn ◦ để cho người lập trình viết những phần của chương trình ngay tức khắc trên ngôn ngữ assembly. • Gỡ rối có thể là khó nếu nó là thật không có thể để chạy chương trình trong một chương trình gỡ rối vì những sự ràng buộc tài nguyên. Chạy chương trình bên trong một môi trường giả có thể sử dụng để giảm bớt vấn đề này. Những nhà lập trình hệ thống một cách đầy đủ thì khác với lập trình ứng dụng mà những người lập trình hướng tới chuyên về một hoặc cái khác. Trong lập trình hệ thống, những phương tiện lập trình có hạn thường sẵn có. Sự sử dụng của tập hợp rác tự động thì không phải là phổ biến và gỡ rối là không đổi đôi khi để làm. Thư viện thực hiện, nếu sẵn có ở mọi nơi, thì ít mạnh hơn nhiều thông thường, và làm ít sợ kiểm tra lỗi hơn. Bởi vì những sự hạn chế đó, màn hình và sự đăng ký thường được sử dụng; những hệ điều hành có thể có vô cùng chi tiết hóa những hệ thống con đăng ký. Thực hiện những phần nhất định trong Hệ điều hành và những quy định hoạt động mạng những nhà lập trình hệ thống (cho ví dụ thi hành phân trang (bộ nhớ ảo) hoặc một trình điều khiển thiết bị cho một hệ điều hành). 5/369
  8. Lịch sử về lập trình hệ thống Trước đây những nhà lập trình hệ thống không thay đổi điền thêm ngôn ngữ assembly. Những cuộc thử nghiệm với việc hỗ trợ phần cứng ở những ngôn ngữ bậc cao vào cuối những năm 1960 dẫn dắt với những ngôn ngữ như BLISS và BCPL, trừ C, giúp đỡ bởi sư tăng của UNIX, trở thành là ở khắp nơi vào những năm 1980. C++ nhúng mới đây hơn đã nhìn thấy sự sử dụng nào đó, cho thể hiện trong bộ dụng cụ các trình điều khiển Vào/ra của Mac OS X. 6/369
  9. Cấu trúc tổng quan lập trình hệ thống Hình 1-1 là biểu đồ thu gọn của hệ điều hành Windows XP, nó rất quan trọng đối với người lập trình driver. Các hoạt động của Windows XP đều được hỗ trợ bởi hai chế độ. Đó là User Mode và Kernel mode. Ví dụ : Một User Mode muốn tới, đọc dữ liệu từ thiết bị thì phải gọi đến chương trình giao tiếp ứng dụng (API) qua ReadFile. 7/369
  10. Bài 2: CÔNG CỤ LẬP TRÌNH HỆ THỐNG Các ngôn ngữ lập trình Lập trình hệ thống không nhất thiết phải sử dụng ngôn ngữ assembly. Thật vậy, tuyệt đại đa số các module chức năng cấu thành hệ điều hành Windows, Unix, Linux đều được viết bằng ngôn ngữ C. Ưu điểm của các ngôn ngữ cấp cao là rõ ràng, dễ đọc, dễ diễn đạt giải thuật, diễn đạt giải thuật cô đọng Như vậy, nếu chưa thật cần thiết phải dùng assembly hay ngôn ngữ máy, bạn nên dùng 1 ngôn ngữ cấp cao như C, C++ để viết các ứng dụng của bạn. Ngược lại, ngôn ngữ Assembly hay ngôn ngữ máy không nhất thiết chỉ để dùng cho lập trình hệ thống mà có thể được dùng để viết ứng dụng bất kỳ. Tuy nhiên do nhược điểm của assembly và ngôn ngữ máy là quá yếu để diễn đạt giải thuật nên rất ít người dùng chúng trực tiếp. Như trên đã nói, ngay cả khi viết hệ điều hành hay các hệ thống nhúng (chương trình điều khiển thiết bị và được ghi trên ROM của thiết bị đó), người ta cũng cố gắng dùng ngôn ngữ cấp cao như C, trừ những đoạn code đặc biệt mới dùng assembly hay mã máy. Trong trường hợp buộc phải dùng assembly hay mã máy, bạn phải lưu ý rằng các ngôn ngữ này phụ thuộc hoàn toàn vào CPU được dùng. Bạn không thể viết đoạn code assembly hay mã máy mà có thể chạy trên nhiều loại CPU được. Riêng đối với CPU Intel từ 80386 trở lên, nó có thể hoạt động ở 1 trong 3 chế độ quản lý bộ nhớ khác nhau như: Real-mode (chế độ mặc định khi bị reset ban đầu), protected-mode (quản lý theo segment) và 386-enchanced mode (quản lý vừa theo segment, vừa theo page, đây là chế độ quản lý bộ nhớ hoàn hảo nhất). Thí dụ khi mới boot máy hay khi máy được boot và chạy MSDOS, CPU Intel sẽ chạy ở chế độ Real-mode, còn khi máy đang chạy Linux, Windows XP thì CPU Intel chạy ở chế độ 386-enchanced mode. Nếu bạn lập trình bằng ngôn ngữ cấp cao, bạn không cần biết chế độ quản lý bộ nhớ nào sẽ được dùng để chạy ứng dụng. Còn nếu lập trình bằng assembly hay mã máy, bạn cần phải nắm vững các chế độ quản lý bộ nhớ của CPU, phải quyết định chế độ quản lý nào sẽ dùng để chạy ứng dụng, từ đó mới bắt đầu viết lệnh tương thích với chế độ quản lý bộ nhớ mong muốn. Thí dụ nếu bạn lập trình ở chế độ real-mode (1 trong những chế độ 16-bit), bạn chỉ có thể dùng các thanh ghi 16-bit của CPU như ax, bx, cx, dx, ds, cs, es, ss). Còn nếu bạn lập trình ở chế độ 386-enchanced mode, bạn có thể dùng các thanh ghi 32 bit của CPU, trong đó có 2 thanh ghi fs, gs như bạn đề cập. Lưu ý rằng các ứng dụng viết ở chế độ 32bit chỉ có thể chạy trên môi trường Windows (hay Linux), chứ không thể chạy trên MSDOS được. Chi tiết về các chế độ quản lý bộ nhớ cũng như tập lệnh CPU ở từng chế độ quản lý bộ nhớ được trình bày trong tài liệu kỹ thuật giới thiệu CPU tương ứng. Bạn có thể tìm tài liệu kỹ thuật trên Internet hay liên hệ trực tiếp với các đại lý của Intel. 8/369
  11. Giới thiệu về C++ C++ là một ngôn ngữ lập trình tiến tiến, mạnh trong các ngôn ngữ lập trình hiện nay, nó được sử dụng bởi hàng triệu lập trình viên trên thế giới. Nó là một trong những ngôn ngữ phổ biến để viết các ứng dụng máy tính – và ngôn ngữ thông dụng nhất để lập trình games. Được sáng tạo bởi Bjarne Stroustrup, C++ là thế hệ sau của ngôn ngữ C. Thực tế, C++ giữ lại hầu hết các đặc điểm của C. Như thế nào đi nữa, C++ đem đến cho chúng ta những thuận lợi hơn trong việc lập trình. Dùng C++ cho Games: C++ là ngôn ngữ được các lập trình viên games lựa chọn. Hầu hết các games được giới thiệu hiện nay đều được viết bởi C++. Có nhiều lí do khác nhau để giải thích vì sao những người lập trình games sử dụng C++. Đây là một vài lí do: Nhanh: Nếu bạn rành C++ thì bạn có thể lập trình nhanh. Một trong những mục tiêu của C++ là khả năng thực thi. Và nếu bạn cần thêm các tính năng cho chương trình, C++ cho phép bạn dùng ngôn ngữ Assembly (Hợp ngữ) – Ngôn ngữ lập trình bậc thấp nhất – để giao tiếp trực tiếp với phần cứng của máy tính. Dễ điều khiển: C++ là một ngôn ngữ biến hóa, nó hỗ trợ các phong cách lập trình khác nhau, bao gồm lập trình hướng đối tượng. Không giống các ngôn ngữ khác, C++ không ép buộc lập trình viên phải đi theo một phong cách nào cả. Hỗ trợ nhiều: Vì nó là ngôn ngữ vượt trội các ngôn ngữ khác, có rất nhiều tài nguyên cho người lập trình bằng C++, bao gồm đồ họa API và 2D, 3D, vật lý, các thiết bị âm thanh chính vì điều này đã giúp cho lập trình viên tăng tốc độ lập trình games. 9/369
  12. Tạo File thực thi: (.exe) File mà bạn dùng để chạy chương trình – dù bạn đang nói đến game hay các ứng dụng windows – gọi là file thực thi (Executable File). Có rất nhiều bước để tạo một file thực thi từ mã nguồn của C++ (tập hợp các lệnh trong ngôn ngữ C++). Quá trình này được mô tả ở hình 1.1. 1. Đầu tiên, người lập trình dùng editor (trình soạn thảo) để viết mã nguồn C++, file đó thường có đuôi .cpp. Trình soạn thảo giống như bộ xử lý ngôn ngữ cho chương trình, nó cho phép lập trình viên tạo, chỉnh sửa, và lưu trữ mã nguồn. 2. Sau khi lập trình viên lưu lại mã nguồn, anh (chị) ta sẽ gọi compiler (trình biên dịch) – một ứng dụng có chức năng đọc mã nguồn và dịch nó sang file đối tượng (object file). Object files thường có đuôi mở rộng là .obj. 10/369
  13. 3. Tiếp theo, bộ phận kết nối (Linker) sẽ kết nối file object đến những file ngoài nếu cần thiết, sau đó tạo file thực thi (executable file), thường có đuôi mở rộng là .exe. Đến lúc này, người dùng có thể chạy chương trình bằng cách chạy file thực thi. Lưu ý: Quá trình tôi miêu tả ở trên chỉ là một trường hợp đơn giản. Để tạo nên một ứng dụng phức tạp trong C++ thường liên quan đến rất nhiều file mã nguồn được viết bởi lập trình viên (hay một nhóm lập trình viên). Để tự động hóa quá trình này, lập trình viên dùng một công cụ tổng hợp, đó là IDE (Integrated Development Environment – môi trường tương thích khai triển). IDE thường bao gồm editor, compiler, linker và một số công cụ khác. Phiên bản thương mại IDE cho Windows bao gồm Visual Studio.NET và C++ Builder Studio. Dev-C++ là một ngôn ngữ mà nguồn mở miễn phí cho Windows (hyutar: strong CD ROM kèm quyển sách gốc có cái Dev-C++, nhưng sách này là đồ lậu nên hổng có, mọi người có thể tải cái này từ trên net về, hoặc dùng C++ 6.0 hay Visual C++ cũng được, mình nghĩ hầu hết mọi người đều có bản thương mại của C rồi). Xử lý lỗi: (error) Khi miêu tả quá trình tạo file thực thi từ mã nguồn C++, tôi đã bỏ qua một chi tiết nhỏ: đó là lỗi. Lỗi là một chuyện hay gặp của các chương trình máy tính. Lập trình viên chính là người thường xuyên mắc lỗi nhất. Ngay cả lập trình viên giỏi nhất đều có thể mắc lỗi ở lần thứ 1 (hoặc nhiều hơn) chạy chương trình. Lập trình viên phải sửa lỗi và chạy lại quá trình tạo file thực thi. Sau đây là một vài loại lỗi cơ bản bạn thường mắc phải khi chạy chương trình: Lỗi biên dịch (Compile Errors): Nó xảy ra trong quá trình biên dịch. Kết quả, file object không được tạo ra. Lỗi này thường do lỗi cú pháp, có nghĩa là trình biên dịch không hiểu cái gì đó. Nó có thể đơn giản như gõ sai lệnh chẳng hạn, hay thiếu dấu “;”. Trình biên dịch còn đưa ra những cảnh báo (warning). Mặc dù bạn thường không cần phải chú ý đến warning, nhưng bạn nên giải quyết nó như là lỗi, sữa chửa, sau đó biên dịch lại, đây là một thói quen tốt đấy ^^. Lỗi liên kết (Link Errors): Nó xảy ra trong quá trình kết nối và có thể cho biết có vài thứ mà chương trình liên kết đến không thể tìm thấy. Lỗi này thường được giải quyết bằng cách đặt đúng vị trí các liên kết và bắt đầu quá trình biên dịch/kết nối lần nữa. Lỗi Run-time (Run-time errors): Nó xảy ra khi đang chạy file thực thi. Nếu chương trình làm một cái gì đó không hợp lý, nó có thể phá hủy hệ thống. Nhưng một dạng lỗi tinh vi, khó phát hiện hơn của lỗi run-time: logical error (lỗi logic), có thể làm chương trình làm những việc mà ta không định trước. Nếu bạn đã từng chơi game mà trong đó nhân vật có thể bước trên không khí (trong kịch bản nhân vật không thể bước trên không khí), khi đó bạn đã thấy lỗi logical trong hành động. 11/369
  14. Trong thực tế: Như các nhà tạo phần mềm, công ty game thường gặp rắc rối với các sản phẩm bị lỗi. Biện pháp khắc phục của họ là trước khi đem ra ngoài thị trường, họ thuê những người chơi game thử (game testers). Những người này chỉ chơi games, nhưng công việc của họ không thú vị như bạn tưởng đâu. Họ phải chơi đi chơi lại một phần nào đó của game – có thể lên đến hàng trăm – cố gắng tìm xem có lỗi nào không. Và với công việc buồn tẻ này, lương của họ cũng bèo nhèo. Nhưng trở thành game testers là một nấc thang để bạn có thể vào làm việc tại công ty làm games. Ví dụ: #include #include void main() { printf(“chao cac ban”); getch(); } 12/369
  15. Giới thiệu về Visual C++ Visual C++ là môt phần mềm lập trình hướng đối tượng được phát triển trên cơ sở là ngôn ngữ lập trình C và C++. Phương pháp thiết kế hướng đối tượng vừa mới phát triển nhằm giúp nhà phát triển khai thác được sức mạnh của đối tượng và ngôn ngữ lập trình hướng đối tượng, dùng các lớp và đối tượng như là khối xây dựng cơ sở. OOP (ngôn ngữ lập trình hướng đối tượng ) là ngôn ngữ hiện thực trong đó chương trình được tổ chức như tập hợp những đối tượng hợp tác với nhau, mỗi đối tượng đại diện cho một instance của một vài lớp và những lớp mà chúng là thành viên của một lớp phân cấp thông qua quan hệ thừa kế. 1. Abstraction ( tính trừu tượng) : Sự trừu tượng thể hiện những đặc tính cốt yếu của một đối tượng mà những đặc tính này dùng để phân biệt đối tượng này với tất cả các loại đối tượng khác và do vậy cung cấp một cách rõ ràng giới hạn ý niệm được ý nghĩa, liên quan đến viễn tượng của người nhìn. Trừu tượng tập trung vào cái nhìn bề ngoài của đối tượng và do đó thỏa mãn được các hành vi chủ yếu của đối tượng riêng biệt từ hiện thực của nó. Ta có thể có các loại trừu tượng sau: - Thực thể trừu tượng : một đối tượng mà nó đại diện cho một mô hình hữu dụng của một miền vấn đề hay thực thể miền giải quyết. - Operation abstraction (hoạt động trừu tượng ) : một đối tượng mà nó cung cấp một tập tổng quát của các operation, tất cả các operation thực thi cùng một loại chức năng. - Máy ảo trừu tượng : một đối tượng mà nó nhóm các hoạt động với nhau, và tất cả các hoạt động này được sử dụng bởi mức điều khiển cao hơn. Hoặc các hoạt động mà chúng sử dụng tập hợp các hoạt động của mức thấp hơn. - Trừu tượng ngẫu nhiên : một đối tượng mà nó đóng gói một tập hợp các hoạt động mà chúng không có quan hệ lẫn nhau. Một client là bất kì đối tượng nào sử dụng nguồn tài nguyên của đối tượng khác ( gọi là server). Chúng ta có thể biểu thị đặc điểm hành vi của đối tượngbằng cách xem xét những dịch vụ mà nó cung cấp cho những đối tương khác, cũng như những hoạt động 13/369
  16. mà nó có thể thực thi cho đối tượng khác. Quan điểm này buộc chúng ta phải tập trung vào cái nhìn bề ngoài của đối tượng. Chúng ta gọi toàn bộ tập hợp của các hoạt động mà một client có thể thi hành cho một đối tượng, cùng với những câu lệnh hợp lệ mà chúng có thể được gọi là protocol. Một protocol biểu thị những cách thức mà một đối tượng có thể hành động và phản ứng, và do vậy có thể cấu thành tổng thể bề ngoài tĩnh và động của trừu tượng. 2. Encap sulation (sự đóng kín) : Tính đóng kín là quá trình phân chia các phần tử của một trừu tượng để cấu thành nên cấu trúc và hành vi của chính nó, đóng kín cho phép hoạt động giao tiếp của một trừu tượng và hiện thực của nó. Sự đóng kín là cơ chế liên kết mã và dữ liệu mà nó thao tác, và giữ cho cả 2 được an toàn khỏi sự can thiệp từ bên ngoài và do sử dụng sai. Trong ngôn ngữ hướng đối tượng, mã và dữ liệu liên kết với nhau để tạo thành một “hộp đen” độc lập. Trong hộp này là tất cả mã và data cần thiết. Khi mã và data liên kết với nhau như thế một đối tượng sẽ được tạo ra. Nói cách khác, đối tượng là một dụng cụ hỗ trợ cho sự đóng kín. Trong một đối tượng , mã, data, hoặc cả 2 có thể là private (riêng) của đối tượng đó hay public (chung). Mã hoặc data riêng là thuộc về đối tượng đó và chỉ được truy cập với bộ phận của đối tượng. Nghĩa là mã hoặc data riêng không thể truy cập bởi các phần khác của chương trình tồn tại ngoài đối tượng. Khi data là chung, các bộ phận khác có thể truy cập đến nó mặc dù nó được định nghĩa trong một đối tượng. Các phần chung của một đối tượng dùng để cung cấp một giao diện có điều khiển cho các phần riêng của đối tượng. Nói chung, một đối tượng là một biến thuộc kiểu do người sử dụng định nghĩa. Mỗi lần ta định nghĩa một đối tượng mới, ta tạo ra một loại data mới. Đặc biệt các thành viên có thể được đặt vào public, private, hay protectedcủa lớp. Thành viên được khai báo là public thì thấy được đối với mọi client, những thành viên khai báo là private thì hoàn toàn đóng kín, và những thành viên khai báo là protected thì chỉ thấy với chính lớp của nó và những lớp con của nó. C++ cũng còn cho phép kí hiệu friend : là lớp cùng hợp tác được phép thấy những phần privated của nhau. 3. Modularity (modun hóa) : Modularity là tính chất của một hệ thống đã được phân tích thành một tập hợp dính kết và ghép lại một cách lỏng lẻo thành những modun. 14/369
  17. Việc phân chia chương trình thành những phần riêng lẻ có thể làm giảm độ phức tạp. Ở ngôn ngữ này lớp, đối tượng hình thành cấu trúc luận lý của hệ thống. Chúng ta đặt những trừu tượng này trong modun để tạo ra những kiến trúc vật lý của hệ thống. Đối với những vấn đề nhỏ, nhà phát triển có thể quyết định vấn đề khai báo tất cả các lớp và đối tượng trong cùng một gói. Ta thường nhóm những lớp vàø đối tượng có quan hệ trong cùng một modun, và chỉ phơi bày những phần tử mà modun khác cần thấy. Trong các hệ thống lớn, thì nó có quá nhiều modun và thật là khó cho người sử dụng tìm thấy những lớp mà họ cần. Hơn nữa , khi quyết định thay đổi hàng trăm modun phải được hiệu chỉnh hoặc biên dịch lại. Như vậy, modun hóa đôi khi cũng không tốt. Modun phục vụ như là những phần tử và những đơn vị không thể phân chia được của phần mềm mà có thể sử dụng lại được qua các ứng dụng. Nhà phát triển sẽ chọn cách đóng gói đối tượng thành các modun sao cho chúng thuận tiện để sử dụng lại. 4. Hierachy (hệ thống phân cấp) : Hệ thống phân cấp là một sự sắp xếp theo thứ bậc hay sắp đặt các trừu tượng. Có hai phân cấp quan trọng nhất trong một hệ thống phức tạp là : cấu trúc lớp của nó(phân cấp “is a”), và cấu trúc đối tượng của nó ( phân cấp “part of “). Thừa kế đơn giản: thừa kế là mối quan hệ “ is a “. thừa kế định nghĩa một mối quan hệ giữa các lớp, trong đó một lớp chia sẻ cấu trúc và hành vi đã được định nghĩa trong một lớp hay nhiều lớp ( tương ứng với thừa kế đơn giản và đa thừa kế ). Do đó thừa kế như là sự phân cấp của các trừu tượng, trong đó một lớp con thừa kế một hay nhiều lớp cha. Đa thừa kế đưa đến một số vấn đề phức tạp cho ngôn ngữ lập trình. Những ngôn ngữ phải đưa ra hai vấn đề: xung đột giữa tên của các lớp cha khác nhau, và thừa kế được lập lại nhiều lần. Sự xung đột xảy ra khi hai hay nhiều lớp cha cùng cấp cùng chia sẻ một lớp cha chung. Và trong trường hợp như vậy, lớp lá có một hoặc nhiều bản sao cấu trúc của lớp cha được dùng chung đó. Một số ngôn ngữ thì không cho phép thừa kế lập lại, một số khác thì chọn hướng khác. C++ cho phép người lập trình quyết định: virtual base class (lớp cơ sở ảo ) được dùng để chỉ ra sự dùng chung của cấu trúc lập lại, trong khi đó nonvirtual base class có kết quả là trùng bản sao ở lớp con. 5. Kiểm tra kiểu ( typing ) : Typing là sự tuân theo bắt buộc của lớp đối tượng, như là những đối tượng của các lớp khác nhau không được thay thế lẫn nhau hoặc chúng có thể thay thế lẫn nhau chỉ theo một cách thức rất hạn chế. C++ có khuynh hướng kiểm tra kiểu chặt chẽ nhưng ta có thể lờ đi hay cấm kiểm tra.Tính đa dạng (polymorphism): 15/369
  18. Là tính chất cho phép một tên được dùng cho hai hoặc nhiều mục đích khác nhau có quan hệ về phương diện kỹ thuật. Mục đích của tính đa dạng khi được áp dụng cho OOP là cho phép một tên được dùng để chỉ rõ một lớp tác động tổng quát, một tác động cụ thể được xác định bởi loại data. Ví dụ, trong C, không có hỗ trợ tính đa dạng nên một tác động giá trị tuyệt đối cần phải có ba hàm khác nhau: abs(), labs(), và fabs(). Các hàm này tính toán và lần lượt trả về một giá trị tuyệt đối của số nguyên, một số nguyên dài, một giá trị dấu phẩy động. Tuy nhiên, trong C++ có đặc tính đa dạng nên có hàm abs(). Loại data được dùng để gọi hàm xác định loại hàm nào thực sự được dùng. Trong C++ có thể sử dụng một tên hàm cho nhiều mục đích khác nhau. Điều này được gọi là quá tải hàm(function overloading). Tổng quan hơn khái niệm tính đa dạng là ý tưởng “một giao diện, nhiều phương pháp”. Điều này có nghĩa là có thể thiết kế một giao diện chung cho một nhóm các tác động có liên quan. Tuy nhiên, tác động cụ thể được thi hành phụ thuộc vào data. Ưu điểm của tính đa dạng là chúng làm giảm tính phức tạp bằng cách cho phép cùng một giao diện được sử dụng để ghi rõ một lớp tác động tổng quát, chính trình biên dịch sẽ lựa chọn tác động cụ thể khi áp dụng cho từng trường hợp. Là người lập trình ta không phải thực hiện sự lựa chọn này bằng tay. Ta chỉ cần nhớ và sử dụng giao diện chung. Liên kết tĩnh và kiên kết động (Static and Dynamic Binding): Khái niệm trong typing và static typing liên kết tĩnh là hoàn toàn khác nhau. Strong typing đề cập tới sự nhất quán của kiểu. Stactic binding có nghĩa là kiểu của mọi biến và biểu thức là cố định vào lúc biên dịch. Dynamic binding nghĩa là các kiểu của mọi biến và biểu thức không cho biết cho tới khi chạy chương trình. Bởi vì Strong typing và Dynamic binding là khái niệm độc lập cho nên một ngôn ngữ có thể vừa Strong và Static typing. 6. Bản chất của đối tượng: Một đối tượng có trạng thái, hành vi, đặc tính nhân dạng; cấu trúc và hành vi của các đối tượng giống nhau được định nghĩa trong một lớp chung của chúng, thuật ngữ instance và đối tượng là có thể thay thế lẫn nhau được. Trạng thái (state) : của một đối tượng bao gồm tất cả các đặc tính (thường là tĩnh) của đối tượng trừ đi giá trị hiện tại ( thường là động ) của mỗi đặc tính đó. Hành vi (behavior ) : là đối tượng hoạt động và phản ứng như thế nào theo điều kiện trạng thái của nó thay đổi và message truyền đến. Operations (hoạt động) : là một dịch vụ mà một lớp cung cấp cho khách hàng của nó. Chúng ta thấy rằng một client thường thực thi năm loại opearation đối tượng. Ba loại operation thông thường nhất là : 16/369
  19. • Modifier : là operation thay đổi trạng thái của đối tượng. Selector: là Operation truy xuất trạng thái của đối tượng nhưng không làm thay đổi trạng thái của nó. • Iterrator : là operation cho phép tất cả các phần của một đối tượng được truy xuất theo một thứ tự đã định nghĩa trước. Hai loại operation thông dụng khác cần thiết để tạo và hủy những instance của một lớp là : • Constructor: là operation tạo đối tượng và (hoặc) khởi động trạng thái của nó. • Destructor là operation giải phóng trạng thái của một đối tượng và (hoặc) hủy chính đối tượng đó. • Indentify: (đặc điểm nhận dạng) : indentify là đặc tính của một đối tượng để phân biệt nó với các đối tượng khác. 7. Mối quan hệ giữa các đối tượng: Links( liên kết) : Link là nối kết vật lý hay ý niệm giữa các đối tượng. Một đối tượng cộng tác với một đối tượng khác thông qua link của nó tới những đối tượng này. Hay nói cách khác, một link vó nghĩa là một hợp tác cụ thể qua đó một đối tượng ( client) áp dụng những dịch vụ của đối tượng khác (nhà cung cấp) hoặc qua đó một đối tượng có thể điều khiển đối tượng khác. Một đối tượng có thể đóng các vai trò sau: • Actor : một đối tượng có thể làm việc trên một đối tượng khác nhưng nó không bao giờ cho một đối tượng khác làm việc trên nó, thuật ngữ actor và active object có thể thay thế lẫn nhau. • Server: một đối tượng không bao giờ làm việc trên một đối tượng khác, nó chỉ cho đối tượng khác làm việc trên nó. • Agent : một đối tượng vừa làm việc trên đối tượng khác vừa cho đối tượng khác làm việc trên nó, một đại lý thường được tạo ra để làm công việc cho một actor hay một agent khác . • Đối tượng được cung cấp là global đối với client. • Đối tượng cung cấp là một tham số cho các operation của client. • Đối tượng cung cấp là một phần của đối tượng client. • Đối tượng cung cấp được khai báo cục bộ trong operation của client. 17/369
  20. Khi có multiple thread control, việc truyền thông tin giữa các đối tượng cần xử lý vấn đề multual exclusion trong hệ thống xử lý đồng thời. Vấn đề đồng bộ hóa cũng phải xem xét. Aggregation: Aggregation có nghĩa là một phân cấp tổng thể / bộ phận, với khả năng điều khiển từ tổng thể ( còn gọi là aggregate ) tới những bộ phận (được hiểu là những thuộc tính của nó). 8. Bản chất của lớp: Class (lớp) : là tập hợp của các đối tượng có cùng cấu trúc chung hay hành vi chung. Một đối tượng đơn giản chỉ là một instance của lớp. Interface: của một lớp cho thấy bên ngoài của đối tượng và do đó nhấn mạnh đến vấn đề trừu tượng trong khi che dấu cấu trúc và hành vi bên trong. Implemention : của môt lớp là cái nhìn bên trong của nó. Implemention của một lớp cơ bản bao gồm tât cả các operation được định nghĩa trong interface. Chúng ta chia interface của lớp thành ba phần: • Public : là khai báo được truy xuất đến mọi client. • Protected : là khai báo được truy xuất chỉ chính lớp đó, lớp con của nó, và lớp friend. • Private: là khai báo được truy xuất chỉ chính lớp đó và lớp friend. 9. Mối quan hệ giữa các lớp: Phần lớn ngôn ngữ hướng đối tượng hỗ trợ trực tiếp một vài kết hợp của những quan hệ sau đây: • Association : gồm có quan hệ một-một, một-nhiều, nhiều-nhiều. • Inheritance (thừa kế) : là một lớp chia sẽ cấu trúc và (hoặc) hành vi được định nghĩa bởi một lớp (đơn thừa kế) hoặc nhiều lớp (đa thừa kế). • Aggregation (kết tập) : quan hệ kết tập giữa các lớp giống như quan hệ kết tập giữa các đối tượng tượng giữa các lớp đó. Đa thừa kế thường bị lẫn lộn với kết tập. Trong C++ thừa kế protected hay private dễ dàng bị thay thế bởi kết tập protected hay private của instace của lớp cha, mà không mất tính ngữ nghĩa của nó. Ví dụ: 18/369
  21. #include "stdafx.h" #include "conio.h" #include "stdio.h" int main(int argc, char* argv[]) { printf("chao"); getch(); return 0; } 19/369
  22. Bài 3: THỰC HÀNH MỘT SỐ BÀI TẬP CƠ BẢN TRÊN C++ Thực hành một số bài tập cơ bản trên C++ Bài 1 Viết cấu trúc, các kiểu dữ liệu, các cấu trúcđiều kiện, cấu trúc lặp trong C ++ Bài 2 Viết chương trình giải phương trình bậc 2 trên C++ 20/369
  23. Bài 4: CƠ BẢN VÀ CẤU TRÚC VỀ DRIVER Tóm lược lịch sử các bộ điều khiển thiết bị Những thế hệ PC đầu tiên chạy trên một chip xử lý Intel ở đó đã cung cấp khả năng định vị cho 640KB của bộ nhớ “thực tế”—gọi như vậy bởi vì bộ nhớ là thật sự ở đó trong mẫu của những chip nhớ mà bộ xử lý trực tiếp hướng vào bởi những phương tiện của một địa chỉ vật lý 20 bit. Chính bộ xử lý đề nghị cho cái đúng kiểu của quá trình hoạt động, cái gọi là kiểu thực tế, ở chỗ nào bộ xử lý đã kết hợp thông tin từ hai thanh nghi 16-bit để hình thành một địa chỉ bộ nhớ 20-bit cho mỗi chỉ dẫn bộ nhớ đã nhắc đến đó. Kiến trúc máy tính bao gồm khái niệm của những khe mở rộng mà một vùng có thể những người dùng bất chấp với những card mua riêng rẽ từ bản thân máy tính. Những card tự chúng thường xuyên đến với những chỉ thị về làm sao để đặt những công tắc DIP (trễ, các jumper giữa các pin) ở thứ tự tạo ra những thay đổi không đáng kể ở cấu hình Vào/ra. Bạn phải giữ một sơ đồ của tất cả sự Vào/ra và những sự ấn định ngắt cho PC của bạn ở thứ tự cho sự phù hợp này. MS-DOS hợp nhất một lược đồ cơ sở trên file CONFIG.SYS nhờ đó hệ điều hành hệ thống có thể nạp vào những bộ điều khiển thiết bị kiểu chế độ thực cho thiết bị nguyên bản và cho các card thêm vào. Chắc chắn, đây là những trình điều khiển theo hình thức lập trình ở ngôn ngữ assembly và tin vào một phạm vi lớn hơn hoặc kém hơn trên chỉ thị INT để gọi tới BIOS và tới những phục vụ hệ thống bên trong bản thân MS-DOS. Những người dùng kết thúc bắt buộc học làm sao để cầu khẩn những ứng dụng qua các lệnh. Các phần mềm ứng dụng cần thiết đã học làm sao để lập trình hiển thị video, bàn phím, và con chuột ngay lập tức bởi vì không một MS-DOS nào mà BIOS hệ thống đã làm đầy đủ như vậy. Sau này, IBM được giới thiệu dòng AT của những máy tính cá nhân đặt cơ sở trên hệ vi xử lý Intel 80286. Bộ xử lý 286 thêm một kiểu bảo vệ của thao tác ở chỗ những chương trình có thể hướng vào lên trên tới 16 MB của bộ nhớ mở rộng và bộ nhớ chính sử dụng địa chỉ đoạn 24-bit (theo danh nghĩa gián tiếp qua một bộ chọn lọc đoạn ở một thanh ghi đoạn 16-bit) và một địa chỉ offset 16-bit. Bản thân MS-DOS vẫn là một Hệ điều hành hệ thống chế độ thực, như vậy vài nhà cung cấp phần mềm xây dựng các sản phẩm mở rộng DOS tới cho phép những người lập trình để di trú những ứng dụng kiểu thực tế của họ tới kiểu bảo vệ và chấp nhận lợi ích to tất cả bộ nhớ mà đang trở nên sẵn có trên thị trường. Từ đó MS-DOS đã đứng yên ở vị trí đầu của máy tính, công nghệ điều khiển đã không phát triển ở thời điểm này. Sự thay đổi bước ngoặt của công nghệ PC xuất hiện—trong tổng quan của tôi, dù sao đi nữa—khi mà Intel đưa ra bán chip xử lý 80386. Dòng 386 cho phép những chương trình để truy nhập lên trên tới 4 GB của địa chỉ bộ nhớ ảo gián tiếp qua các bảng trang nhớ, và 21/369
  24. nó đã cho phép những chương trình để dễ dàng sử dụng những số lượng cho 32-bit số học và địa chỉ. Đó là một cơn gió mạnh của sự hoạt động trong thị trường những công cụ phần mềm như những nhà cung cấp chương trình biên dịch và sự cạnh tranh giữa các công ty đưa ra để bắt khối lượng tăng trưởng liên tục khao khát những ứng dụng lớn hơn cho tốc độ xử lý và tốc độ bộ nhớ. Những trình điều khiển thiết bị là vẫn còn viết ra những chương trình chế độ thực 16-bit trong ngôn ngữ assembly và đã cài đặt qua tệp CONFIG.SYS, và những người dùng cuối vẫn còn cần để các card được định cấu hình điều khiển bằng tay. Kế tiếp tiến tới các chip của Bộ vi xử lý chủ yếu trong vùng của sự thực thi và dung lượng. Trong khi tôi viết chương này, những máy tính hoạt động nhanh hơn 1 GHz với những ổ cứng 50-GB và 512 MB (hoặc hơn) của bộ nhớ là cũ rích và có thể cung cấp dễ dàng bằng những phân đoạn lớn hơn của mẫu. Song song với sự tiến hóa của nền tảng công nghệ máy tính, sự tiến hóa khác đang xuất hiện với công nghệ hệ điều hành. Hầu hết mọi người, thậm chí bao gồm những người lập trình của phần mềm hệ thống, thích những biện pháp trên nền đồ họa của ảnh hưởng lẫn nhau với các máy tính tới những biện pháp trên nền văn bản. Microsoft đã muộn tới nhóm Hệ điều hành đồ họa—Apple đánh trúng với hệ Macintosh đầu tiên—nhưng có đến và thống trị nó với gia đình Windows của những Hệ điều hành. Trong sự bắt đầu, Windows chỉ là một tiện ích đồ họa cho MS-DOS kiểu thực tế. Trong cả thời gian, một tập hợp của các trình điều khiển Windows cho phần cứng phổ biến, kể cả màn hình , bàn phím, và chuột, cho đến sự tồn tại. Những bộ điều khiển này là những tệp tin có thể thực hiện với một đuôi mở rộng .DRV, và chúng đã được viết đầu tiên trong ngôn ngữ assembly. Đến với lớp AT của máy tính, Microsoft đã thiết lập một phiên bản chế độ bảo vệ của Windows. Microsoft mang chuyển các trình điều khiển .DRV chế độ thực để chế độ bảo vệ là tốt nhất. Phần cứng khác hơn những thiết bị Windows chuẩn (màn hình, bàn phím, và con chuột) tiếp tục sẽ được điều khiển bằng các trình điều khiển MS-DOS chế độ thực. Kết luận lại, một khoảng thời gian sau đó các PC với các bộ xử lý 386 đã trở nên sẵn dùng mọi nơi, Microsoft đã phát hành Windows 3.0, của chế độ “tăng cường” các thao tác bắt lấy đầy đủ sự thuận lợi của các khả năng bộ nhớ ảo. Thậm chí như vậy, nó vẫn còn đúng với mọi bộ phận mới đó của nhu cầu phần cứng một điều khiển chế độ thực. Nhưng bây giờ nó là một vấn đề lớn. Từ một sự hỗ trợ đa nhiệm của các ứng dụng MS- DOS (một nhu cầu chấp nhận cho người sử dụng cuối của Windows), Microsoft là đã xây dựng một hệ điều hành cho máy ảo. Mỗi ứng dụng MS-DOS chạy trong máy ảo của chính mình, như đã làm môi trường đồ họa cho Windows. Nhưng tất cả các ứng dụng MS-DOS đó trực tiếp đang cố gắng nói tới phần cứng bằng sự đưa ra các cấu trúc IN và OUT, bộ nhớ thiết bị đọc và ghi, và việc dùng các ngắt từ phần cứng. Hơn nữa, hai hoặc nhiều hơn các ứng dụng đang chia sẻ thời gian xử lý có thể đang đưa ra những chỉ dẫn 22/369
  25. xung đột tới phần cứng. Chúng chắc chắn xung đột qua sự sử dụng của màn hình, bàn phím, và con chuột, của quá trình diễn biến. Tới việc chấp nhận các ứng dụng phức tạp tới việc chia sẻ phần cứng vật lý, Microsoft giới thiệu khái niệm của một bộ điều khiển thiết bị ảo, mục đích rộng của ai tới “thực tế” một thiết bị phần cứng. Những bộ điều khiển như vậy là khái quát gọi là VxDs bởi vì hầu hết chúng cho phép những tên file phù hợp với những mẫu VxD.386, ở đấu đó chỉ báo kiểu của thiết bị đã quản lý chúng. Sử dụng khái niệm này, Windows 3.0 đã tạo sự xuất hiện của trang bị những cỗ máy ảo với những thể hiện riêng biệt của một vài thiết bị phần cứng. Nhưng tự mình những thiết bị tiếp tục, trong hầu hết các trường hợp, tới việc điều khiển bằng bộ điều khiển MS-DOS chế độ thực. Một vai trò của VxD sẽ điều đình sự truy cập tới phần cứng bằng việc chặn đứng đầu tiên những sự cố gắng của các ứng dụng tới chi tiết nhỏ phần cứng và khóa chuyển đổi ngắn gọn Bộ xử lý tới một sắp xếp của chế độ thực gọi cho chế độ 8086 ảo tới việc chạy trình điều khiển MS-DOS. Để không đặt một mặt quá tinh tế trên nó, khóa chuyển đổi chế độ để chạy những bộ điều khiển kiểu thực tế là một sự tấn công công dụng duy nhất của chúng là đã chấp nhận nó cho một sự tăng trôi chảy hợp lý ở trong nền phần cứng và hệ điều hành. Windows 3.0 đã có một vài hỏng hóc tính năng gốc nguyên nhân là cái đó rất đặc trưng của cấu trúc. Microsoft trả lời là để tới OS/2, khi nó là đang phát triển ở sự cấn đối (đó là sử dụng ở thế kỷ 20) với IBM. Microsoft có phiên bản của OS/2 trở thành là Windows NT, phiên bản dùng chung đầu tiên của chúng là vào những năm 1990, không lâu sau Windows 3.1. Microsoft xây dựng Windows NT từ trên nền với ý định của việc tạo nó một cách lâu bền và nền tảng bảo đảm trên việc chạy những hệ điều hành. Những trình điều khiển cho Windows NT đã sử dụng một công nghệ nhân-kiểu mới toanh đó là không được chia sẻ trên thực tế với hai bộ công nghệ điều khiển khác khi ấy đang được thịnh hành. Những trình điều khiển Windows NT đã sử dụng gần như chỉ riêng ngôn ngữ lập trình C vì vậy chúng có thể được biên dịch lại cho những cấu trúc CPU mới mà không yêu cầu bất kỳ những sự thay đổi nguồn nào. Vấn đề khác xảy ra dọc theo khung thời gian về Windows 3.0 ở đó có một sự phân nhánh quan trọng cho chúng ta hôm nay. Windows 3.0 chính thức bị tách ra khỏi thế giới phần mềm vào trong các chương trình User-mode và Kernel-mode. Những chương trình User-mode bao gồm tất cả các chương trình ứng dụng và các trò chơi ở đó mọi người mua các máy tính để chạy, nhưng chúng là không được lòng tin để giao du mạnh (hoặc thậm chí trung thực) với phần cứng hoặc với các chương trình khác. Những chương trình Kernel-mode kể cả chính Hệ điều hành và tất cả các trình điều khiển thiết bị mà ở đó mọi người thích bạn và tôi viết. Những chương trình Kernel-mode là có thể tin cẩn được hoàn toàn và có thể chạm bất kỳ tài nguyên hệ thống nào mà chúng thích. Mặc dù vậy Windows 3.0 tách riêng các chương trình bằng kiểu của họ của Hệ điều hành, không phiên bản nào của Windows (không chỉ là Window Me) đã thật sự đặt sự bảo vệ bộ nhớ 23/369
  26. trong chỗ để nhượng bộ một hệ thống bảo mật. Bảo mật là một lĩnh vực của Windows NT và những người kế nghiệp nó, mà ngăn cấm các chương trinhg user-mode từ việc nhìn thấy hoặc thay đổi những tài nguyên quản lý bởi nhân. Nguồn máy tính là không thật sự tiến bộ tới điểm nơi mà một PC trung bình có thể chạy Windows NT tốt cho đến khi mới đây được ưa chuộng. Microsoft vì thế phải giữ cho dòng sản phẩm Windows hoạt động. Windows 3.0 phát triển thành 3.1, 3.11, và 95. Đang bắt đầu với Windows 95, nếu bạn muốn viết một trình điều khiển thiết bị, bạn viết cái gì đó gọi một VxD cái đó thật sự là một bộ điều khiển bảo vệ chế độ 32-bit. Cũng bắt đầu với Windows 95, những người dùng cuối cùng có thể ném ra khỏi những sơ đồ Vào/ra của họ bởi vì tính năng Plug và Play mới của nhận biết hệ điều hành hệ thống và có phần tự động cấu hình phần cứng. Như một nhà sản xuất phần cứng, tuy nhiên bạn có thể phải viết một trình điều khiển real-mode để giữ những vui lòng của các khách hàng của bạn mà ai không phải nâng cấp từ Windows 3.1. trong lúc đó, Windows NT phát triển thành 3.5, 4.0. Bạn đã cần một bộ điều khiển thứ ba để hỗ trợ những hệ thống này, và không phải biết nhiều kiến thức lập trình của bạn đã có thể chuyển giữa những dự án. Microsoft đã thiết kế một công nghệ mới cho các trình điều khiển thiết bị, chế độ điều khiển Windows (WDM), và đưa vào Windows 98 và Windows Me, những người nối nghiệp tới Windows 95. Họ cũng đưa những công nghệ này tới Windows 2000 và Windows XP, những người nối nghiệp tới Windows NT 4.0. Bằng thời gian của Windows Me, MS-DOS hiện hữu chỉ do sự giúp đỡ và đó là sự không cần thiết cuối cùng cho một nhà sản xuất phần cứng để lo lắng về những bộ điều khiển thiết bị real- mode. Vì WDM, ít nhất bởi mục đích ban đầu, thực tế là cũng như thế về tất cả các nền tảng, nó trở nên có thể thực hiện được để viết đúng cho một bộ điều khiển. Tổng kết, chúng ta hôm nay đứng ở hình bóng của kiến trúc PC nguyên bản và của những phiên bản đầu tiên của MS-DOS. Những người dùng cuối vẫn còn thỉnh thoảng phải mở lớp vỏ của các PC khác để cài đặt các card mở rộng, nhưng chúng ta sử dụng một bus mạnh hơn và khác ngày nay hơn chúng ta làm trước đây. Plug và Play và bus PCI đã phần lớn loại bỏ nhu cầu cho những người dùng cuối để giữ lại rãnh ghi của Vào/ ra, bộ nhớ, và cách sử dụng yêu cầu dùng ngắt. Vẫn còn có một BIOS đúng chỗ, trừ phi ngày nay công việc của nó phần lớn để khởi động hệ thống và để khai báo hệ điều hành thực tế (Windows XP hoặc Windows Me) khoảng cấu hình những chi tiết phát hiện ra dọc theo đường đi. Và những trình điều khiển WDM vẫn còn có tệp có phần mở rộng .SYS, đúng như những bộ điều khiển real-mode đầu tiên đã làm. 24/369
  27. Tổng quan về các Hệ điều hành (An Overview of the Operating Systems) Windows Driver Model cung cấp một khung cho các bộ điều khiển thiết bị đó hoạt động ở 2 hệ điều hành hệ thống—Windows 98/Windows Me và Windows 2000/Windows XP. Như được thảo luận về trong tóm lược lịch sử trước đây, hai cặp này của những hệ điều hành là các sản phẩm của hai con đường của sự phát triển song song. Thật ra, tôi sẽ tham chiếu tới cặp trước kia của những hệ thống với sự tóm tắt “Windows 98/Me” để nhấn mạnh di sản chung của họ và để and to cái đó ghép đôi đơn giản như XP. Mặc dù tới người dùng cuối hai cặp này của những hệ thống là giống nhau, chúng làm việc khá khác nhau ở trong. Ở đoạn này, Tôi sẽ giới thiệu tổng quan và ngắn gọn của hai hệ thống này. Tổng qua về Windows XP Hình 1-1 là một tóm tắt cao sơ đồ chức năng của hệ điều hành Windows XP, ở khía cạnh nào đó tôi nhấn mạnh những đặc trưng mà quan trọng tới những người mà viết những bộ điều khiển thiết bị. Mọi nền tảng mà Windows XP chạy hỗ trợ hai kiểu của sự thi hành. Phần mềm thực hiện hay trong user-mode hoặc ở trong kernel-mode. Một chương trình user-mode điều đó muốn tới, nói, đọc dữ liệu nào đó từ một thiết bị gọi là một giao diện lập trình ứng dụng (API) như ReadFile. Một module hệ thống con như những sự 25/369
  28. thi hành KERNEL32.DLL API này bằng việc kéo theo một chức năng API bản ngữ như NtReadFile. Đề cập tới khía cạnh để có thêm thông tin về API bản ngữ. Chúng tôi thường nói NtReadFile được gọi là quản lý Vào/ra. Thuật ngữ quản lý Vào/ra là có thể một ít lạc đường bởi vì ở đó không là bất kỳ mô đun có thể thực hiện đơn nào với tên đó. Hệ điều hành sẽ giao tiếp với thiết bị bằng trình điều khiển của riêng mình. Nhiều thủ tục phục vụ như NtReadFile. Họ vận hành ở kernel-mode ở thứ tự để phục vụ một vài ứng dụng để tương tác với một thiết bị trong cách nào đó. API gốc NtReadFile thuộc API của Windows XP. Hệ điều hành Windows NT bao gồm một số hệ thống để thực thi những ngữ nghĩa của vài cái mới và những hệ điều hành đang tồn tại. Đã có một hệ thống con OS/2, POSIX, và hệ thống con Win32. Những hệ thống con được thực hiện bằng việc tạo mà user-mode hướng tới API gốc, mà chính nó được thực hiện trong kernel-mode. Một user-mode DLL có tên (rather redundantly, I’ve always thought) NTDLL.DLL những bổ sung API gốc cho những đối tượng gọi Win32. Mỗi mục vào trong DLL này là một trình bao bọc mỏng xung quanh một sự gọi tới một chức năng kernel-mode maf thật sự thực hiện chức năng. Việc gọi sử dụng một giao diện cộng tác hệ thống phụ thuộc nền để chuyển điều khiển ngang qua ranh giới user-mode/kernel-mode. Trên những bộ xử lý Intel mới hơn, giao diện công tác hệ thống này sử dụng chỉ dẫn SYSENTER. Trên những bộ xử lý Intel cũ hơn, những sự sử dụng giao diện cấu trúc INT với với chức năng viết mã 0 x2E. Trên những bộ xử lý khác, vẫn còn những cơ chế khác được sử dụng. Mặc dù, bạn và tôi không cần hiểu những chi tiết của cơ chế để viết những bộ điều khiển thiết bị. Tất cả chúng tôi cần hiểu là cơ chế cho phép một chương trình được chạy trong user mode gọi một chương trình con mà thực hiện ở kernel mode và ý định đó dần dần trở lại người gọi user-mode của nó. Không có sự chuyển mạch ngữ cảnh luồng xuất hiện trong thời gian xử lý: tất cả những sự thay đổi là mức đặc quyền của thực hiện mã (cùng với vài chi tiết khác những lập trình viên hợp ngữ duy nhất đó từng chú ý hoặc quan tâm xung quanh). Hệ thống Win32 là một trong đa số những lập trình viên ứng dụng quen thuộc với vì nó thi hành những chức năng một trong những sự kết hợp phổ biến với hệ giao diện đồ hoạ Windows. Một bộ điều khiển thiết bị có thể dần dần cần truy nhập thật sự phần cứng của nó để thực hiện một IRP. Trong những trường hợp của một IRP_MJ_READ tới một loại vào/ ra (PIO) được chương trình hóa của thiết bị, sự truy nhập có lẽ đã đưa mẫu (dạng) (của) một thao tác đọc được định hướng đi một cổng vào/ra hoặc một thanh ghi bộ nhớ được đăng ký bằng thiết bị. Những bọ phận điều khiển, mặc dù họ thực hiện trong kernel- 26/369
  29. mode và có thể bởi vậy nói trực tiếp tới phần cứng của họ, sử dụng những phương tiện được cung cấp bởi lớp trừu tượng hóa phần cứng (HAL) để truy nhập phần cứng. Một thao tác đọc có lẽ đã bao gồm sự gọi EAD_PORT_UCHAR để đọc một byte dữ liệu từ một cổng Vào/ra. HAL thường sử dụng một phương pháp platform-dependent thật sự thực hiện thao tác HAL thường lệ sử dụng một phương pháp phụ thuộc nền tảng để thật sự thực hiện thao tác. Trên một máy tính x86, HAL sử dụng trong lệnh IN; trong một nền tảng Windows XP tương lai khác nào đó, nó có lẽ đã thực hiện một công việc nạp vào bộ nhớ. Sau khi một trình điều khiển đã kết thúc với một thao tác Vào/ra, nó hoàn thành IRP bằng việc gọi một thủ tục dịch vụ kernel-mode đặc biệt. Hoàn thành là màn cuối cùng trong việc xử lý một IRP, và nó cho phép ứng dụng chờ đợi thực hiện lại. 27/369
  30. Các kiểu Driver Một số loại của mẫu các trình điều khiển một hệ thống XP Windows đầy đủ. Hình 1-4 những sơ đồ riêng biệt của chúng. Hình 1-4. Các kiểu của các trình điều khiển thiết bị trong Windows XP. • Một trình điều khiển thiết bị ảo(VDD) là một thành phần user-mode điều đó cho phép các ứng dụng MS-DOS-based truy cập phần cứng trên các nền tảng Intel x86. Một VDD tin vào mặt nạ cho phép trên vào/ra để bẫy sự truy nhập cổng, và nó thực chất mô phỏng thao tác của phần cứng cho lợi ích của các ứng dụng điều đó trực tiếp trước đấy được chương trình hóa để nói tới phần cứng trên một máy tối thiểu. Đừng nhầm lẫn một Windows XP VDD với một Windows 98/Me VxD. Cả hai được gọi là những bộ điều khiển thiết bị thực tế, và họ phục vụ cùng mục đích cơ bản phần cứng thực tế, nhưng họ thuê công nghệ phần mềm hoàn toàn khác nhau. • Những loại trình điều khiển thiết bị kernel-mode bao gồm một số loại con. Một trình điều khiển PnP là một trình điều khiển kernel-mode mà hiểu các giao thức Plug and Play của Windows XP. Để hoàn toàn chính xác, sách này liên quan những trình điều khiển PnP và không có gì khác. • Một trình điều khiển WDM là một trình điều khiển PnP mà đồng thời hiểu các giao thức power-management và là nguồn thích hợp với cả Windows 98/Me và Windows 2000/XP. Bên trong phạm trù của trình điều khiển WDM, bạn có thể cũng phân biệt giữa lớp những trình điều khiển (mà quản lý một thiết bị thuộc về lớp được định nghĩa kỹ nào đó của thiết bị) và những trình điều khiển nhỏ (trợ giúp vendor-specific về nhà cung cấp tới một lớp bộ phận điều khiển), và giữa những trình điều khiển chức năng nguyên khối (mà gồm mọi thứ chức năng cần hỗ trợ một thiết bị phần cứng) và lọc những trình điều khiển (Những thao tác vào/ra “lọc” nào cho một thiết bị đặc biệt trong thứ tự để thêm hay sửa đổi hành vi). 28/369
  31. • Những trình điều khiển tập tin hệ thống thực thi mẫu tệp tin hệ thống PC chuẩn (mà bao gồm những khái niệm của một cấu trúc thư mục phân cấp việc chứa đặt tên những tệp tin) trên những đĩa cứng địa phương hay qua những kết nối mạng. Đây cũng, là những trình điều khiển kernel-mode. • Những bộ điều khiển thiết bị kế thừa là các trình điều khiển kernel-mode mà trực tiếp kiểm soát một thiết bị phần cứng không có trợ giúp từ những trình điều khiển khác. Phạm trù này thực chất bao gồm những trình điều khiển cho những phiên bản trước đó của Windows NT mà đang chạy không có sự thay đổi trong Windows XP. Có một số thiết bị, bạn không cần viết bất kỳ trình điều khiển nào bởi vì Microsoft đã cung cấp một trình điều khiển chung mà sẽ làm việc với thiết bị của bạn. Đây là một vài ví dụ: • Thiết bị tồn trữ tin lớn SCSI-compatible hoặc ATAPI-compatible • Bất kỳ thiết bị nào nối tới một cổng USB mà hoàn toàn thích hợp với một thuyết minh đã phê chuẩn, cung cấp bạn hạnh phúc với bất kỳ hạn chế nào trong trình điều khiển Microsoft tiêu chuẩn • Cổng tuần tự chuẩn hay cổng chuột PS/2 • Bàn phím chuẩn • Card điều hợp video không có gia tốc hay những tính năng đặc biệt • Cổng song song hay tuần tự chuẩn • Ổ đĩa mềm chuẩn WDM Drivers Cho đa số những thiết bị mà Microsoft không trực tiếp sự hỗ trợ, bạn cần viết một trình điều khiển WDM. Bạn sẽ viết một trình điều khiển chức năng nguyên khối, một trình điều khiển lọc, hay đơn giản là một trình điều khiển nhỏ. WDM minidrivers Nếu Microsoft đã viết một trình điều khiển cho thiết bị, bạn cố gắng cho thiết bị để hỗ trợ, bạn cần phải viết một Minidriver để làm việc với trình điều khiển lớp đó. Minidriver của bạn trên danh nghĩa trong việc phụ trách của, trừ phi bạn sẽ gọi những chương trình con trong trình điều khiển lớp mà về cơ bản lấy lại quản lý của phần cứng và gọi quay trở lại bạn để làm những thứ device-dependent khác nhau. Số lượng của công việc bạn cần để làm một minidriver thay đổi ghê gớm từ một lớp của thiết bị đến thiết bị khác. Ở đây là một số ví dụ của những lớp thiết bị mà bạn cần phải lập kế hoạch để viết một minidriver: 29/369
  32. • Các HID Non-USB, bao gồm chuột, các bàn phím, các cần điều khiển, những bánh lái, vân vân. Nếu bạn có một thiết bị USB (cho) hành vi chung nào của HIDUSB.SYS (những trình điều khiển Microsoft cho những thiết bị USB HID còn thiếu, bạn cũng nên viết một minidriver HIDCLASS minidriver. Phần chủ yếu điển hình của những thiết bị này là họ báo cáo người sử dụng được nhập vào bằng phương pháp những báo cáo mà có thể được mô tả bởi một cấu trúc dữ liệu bộ mô tả. Cho những thiết bị như vậy, phục vụ HIDCLASS.SYS như trình điều khiển lớp và thực hiện nhiều chức năng điều đó Direct-Input và những lớp bậc cao khác của phần mềm tiếp tục phụ thuộc, vì thế là bạn’re tương đối bị kẹt nhiều với việc sử dụng IDCLASS.SYS. Đây đủ khó khăn để tôi dành cho không gian đáng kể tận tâm tới nó sau đó trong sách này. Như một sự nói riêng, HIDUSB.SYS chính nó là một HIDCLASS minidriver. • Những thiết bị Windows Image Acquisition (WIA), bao gồm những scanner và những camera. Bạn sẽ viết một WIA minidriver mà thực chất thực hiện một vài mạch ghép nối COM-style tới những diện mạo hỗ trợ vendor-specific của phần cứng của bạn. • Những thiết bị Luồng, như âm thanh, DVD, và những thiết bị video, và những bộ lọc software-only cho những dòng dữ liệu đa phương tiện. Bạn sẽ viết một dòng minidriver. • Những thiết bị giao diện Mạng trên những bus không truyền thống, như USB hay 1394. Cho một thiết bị như vậy, bạn sẽ viết một một trình điều khiển miniport Network Driver Interface Specification (NDIS) “với một khía cạnh thấp hơn WDM,” tới sử dụng những cụm từ giống như tài liệu DDK trên chủ đề này. Một trình điều khiển như vậy không chắtrinhflinh động giữa những hệ điều hành, vì thế là bạn cần phải lập kế hoạch trên sự ghi riêng biệt của họ với những sự khác nhau nhỏ hơn đối phó với những phần phụ thuộc nền tảng. • Những card Video. Những thiết bị này yêu cầu một video minidriver mà làm việc với bộ điều khiển lớp cổng video. • Những máy in, mà đòi hỏi thay cho các DLL user-mode của các trình điều khiển kernel-mode. • Những nguồn pin, Microsoft cung cấp một trình điều khiển lớp chung. Bạn viết một minidriver (DDK nào gọi là một trình điều khiển miniclass, nhưng cùng thứ của nó) để làm việc với BATTC.SYS. Những trình điều khiển lọc WDM Một thiết bị vận hành sao cho gần tới một tiêu chuẩn được thừa nhận một trình điều khiển Microsoft chung đó gần như thích hợp. Trong một số hoàn cảnh, bạn có thể có khả năng để viết một trình điều khiển lọc mà sửa đổi hành vi của trình điều khiển chung đủ phải làm công việc phần cứng của bạn. Điều này xảy ra rất thường xuyên, nhân tiện, vì thường của nó không phải dễ dàng để thay đổi cách mà một trình điều khiển chung truy nhập phần cứng. 30/369
  33. Những trình điều khiển chức năng WDM nguyên bản Với một số ngoại lệ sẽ được ghi chép trong mục tiếp theo, đa số những kiểu khác (của) thiết bị yêu cầu cái gì Tôi có gọi cho ở đây một trình điều khiển chức năng WDM nguyên khối. Một trình điều khiển như vậy thực chất đứng một mình và xử lý tất cả các chi tiết của kiểm soát phần cứng của bạn. Khi kiểu này của trình điều khiển thích hợp, Tôi khuyến cáo cách tiếp cận sau đây vì thế mà bạn có thể kết luận với một bit nhị phận đó là sẽ làm việc việc trên các nền tảng Intel x86 trong tất cả các hệ điều hành. Đầu tiên, xây dựng với nhiều DDK gần đây nhất — Tôi sử dụng một phiên bản beta của DDK .NET cho những ví dụ tiêu biểu trong nội dung sách hướng dẫn. Bạn có thể sử dụng IoIsWdmVersionAvailableto quyết định hệ điều hành nào mà bạn tình cờ đang sử dụng. Nếu bạn tình cờ được chạy trong Windows 2000 hay Windows XP, bạn có thể gọi là MmGetSystemRoutineAddress để có một con trỏ tới một chức năng Windows XP-only. Tôi cũng gợi ý ở một hải cảng WDMSTUB.SYS, mà được bàn luận trong Phụ lục A, để định nghĩa MmGetSystemRoutineAddress và những hàm nhân then chốt khác trong Windows 98/Me; cách khác, trình điều khiển của các bạn đơn giản không tải được trong Windows 98/Me bởi vì những nhập khẩu không xác định. Đây là một số ví dụ của những thiết bị mà cho bạn có lẽ đã viết một chức năng trình điều khiển WDM nguyên khối: • Bất cứ loại SmartCard loại ra ngoại trừ một loại được gán cho một cổng tuần tự • Bộ biến đổi Digital-to-analog • Card ISA hỗ trợ quyền xác minh sở hữu thẻ đọc/ghi bộ chuyển đổi Những kiểu của các trình điều khiển khác Vài vị trí tồn tại trong đó một trình điều khiển chức năng WDM nguyên khối không chiến thắng đủ bởi vì những sự khác nhau kiến trúc giữa Windows 98/ Me và Windows 2000/ XP. Trong những trường hợp sau đây, bạn cần viết hai trình điều khiển: một trình điều khiển WDM cho Windows 2000/XPvà một trình điều khiển VxD cho Windows 98/ Me: • Một trình điều khiển cho một cổng nối tiếp. Trình điều khiển trên Windows 98/ Me là một VxD mà đưa ra giao diện trình điều khiển cổng VCOMM tại cạnh trên của nó, trong khi mà trình điều khiển Windows 2000/ XP là một trình điều khiển WDM đó là những đề xuất rất hay và nghiêm khắc chỉ rõ giao diện IOCTL tại mép trên của nó. Hai thuyết minh upper-edge không có cái gì chung. • Một trình điều khiển cho một thiết bị nối tới một cổng nối tiếp. Trình điều khiển Windows 98/Me là một VxD mà gọi là VCOMM để nói chuyện với cổng. Trình điều khiển Windows 2000/XP là một trình điều khiển WDM mà 31/369
  34. nói chuyện với SERIAL.SYS hay một vài trình điều khiển cổng nối tiếp khác mà thực hiện cùng giao diện IOCTL. • Một trình điều khiển cho một thiết bị trữ tin lớn USB không tiêu chuẩn. Dành cho Windows 98/Me, bạn sẽ viết một VxD mà điều chỉnh vào trong sự phân cấp giám sát viên Vào/ra của những trình điều khiển phân tầng. Dành cho Windows 2000/XP, bạn sẽ viết một trình điều khiển chức năng WDM nguyên khối đó là các khối yêu cầu SCSI chấp nhận ở cạnh trên của nó và giao tiếp với thiết bị USB tại mép thấp hơn của nó. Dành cho hai lớp của thiết bị, Microsoft định nghĩa một kiến trúc trình điều khiển di động dài trước WDM: • Những bộ điều hợp Computer System Interface (SCSI) nhỏ sử dụng một trình điều khiển " SCSI miniport ", không sử dụng bất kỳ cái nào của những chức năng hỗ trợ nhân tiêu chuẩn và tin cậy thay vào đó trên một API đặc biệt được xuất khẩu bởi SCSIPORT.SYS hay SCSIPORT.VXD, như trường hợp có thể. Miniport thì di động giữa những hệ thống. • Những card giao diện Mạng sử dụng một “NDIS miniport driver,” mà tin cậy chỉ riêng trên một API đặc biệt được xuất khẩu bởi NDIS.SYS hay NDIS.VXD, như trường hợp có thể. Tại một thời gian, các trình điều khiển NDIS miniport là di động giữa các hệ thống, nhưng tính di chuyển được đã khá nhiều bị mất cho đến bây giờ. Những trình điều khiển giao thức mạng và những cái gọi là trình điều khiển " trung gian " mà cung cấp chức năng lọc cũng đi theo quỹ đạo xung quanh NDIS. 32/369
  35. Tổng quan về quản lý và kiểm tra danh sách Nếu bạn là một giám đốc phát triển, hay nếu bạn chiụ trách nhiệm khác về chuyển một thiết bị phần cứng để kinh doanh, ở đó là vài suy nghĩ bạn cần để biết về những bộ điều khiển thiết bị. Bạn cần quyết định, trước hết, liệu có phải bạn cần một trình điều khiển tùy biến và, như vậy thì, cách thức nào. Phần có trước cần phải giúp đỡ bạn với quyết định đó, nhưng bạn có lẽ đã muốn thuê một cố vấn chuyên gia cho mục đích hạn chế của việc khuyên bạn trên điểm đó. Nếu sự đánh giá của các bạn dẫn dắt bạn để tin bạn cần một trình điều khiển tùy thích, bạn rồi cần định vị một lập trình viên thích hợp. Sự thật buồn rầu là lập trình trình điều khiển WDM tương đối cứng, và chỉ có kinh nghiệm (và đắt tiền!) những lập trình viên mới có khả năng làm nó tốt. Một số công ty có những lập trình viên trình điều khiển vững chắc, nhưng hầu như không có thể tới. Nếu bạn trong chỗ làm mới đây, sự lựa chọn cơ bản của các bạn giữa việc huấn luyện người nào đó đã là nhân viên của các bạn, việc thuê một lập trình viên mà đã có cần thiết thành vấn đề, hứa hẹn một lập trình viên cố vấn hay hợp đồng, hay một công ty chuyên về nhận linh kiện, phụ tùng phát triển tới một công ty mà chuyên về lập trình trình điều khiển. Tất cả những giải pháp này có những dấu cộng và những số trừ, và bạn sẽ có cơ sở trọng lượng của chúng trên những cần thiết độc nhất của các bạn. Lập trình Driver cần phải bắt đầu ngay khi có một đặc điểm kỹ thuật vững chắc hợp lý cho phần cứng sẽ làm việc như thế nào. Bạn cần phải mong đợi sửa đổi đặc điểm kỹ thuật không rõ của những khám phá khó ưa trong thời gian sự phát triển trình điều khiển, và bạn cần phải cũng mong đợi lặp lại phần cứng vi chương trình của các bạn và thiết kế trình điều khiển vài lần. Tính linh hoạt và một sự bằng lòng trên để bắt đầu thực sự sẽ giúp bạn. Chuẩn bị cơ sở hạ tầng doanh nghiệp của các bạn để làm việc với WHQL. Tại một mức tối thiểu, (cái) này sẽ yêu cầu đang tồn tại một số Data Universal Numbering System (DUNS) từ Dun and Bradstreet (hay việc cung cấp tương đương chứng minh của tổ chức kinh doanh) và một chứng chỉ chữ ký số hóa từ Verisign. Kể từ sự ghi này, những số DUNS là tự do, trừ phi chứng chỉ Verisign không phải. Và việc làm việc xuyên qua tất cả các quá trình của nhiều công ty sẽ mất thời gian. Những trình điều khiển có thể Cung cấp Thống kê và thông tin quản lý khác trong hai cách. Hệ thống con Windows Management Instrumentation (WMI) cung cấp một ngôn ngữ - và truyền tải- đường mòn độc lập cho những phân loại khác nhau của dữ liệu nhị phân. Microsoft đã thiết lập những lớp WMI tiêu chuẩn cho những kiểu nhất định của thiết bị, và nhóm con công nghiệp của riêng các bạn có thể đã thiết lập những tiêu chuẩn khác mà tới cái đó trình điều khiển của các bạn cần phải làm phù hợp. 33/369
  36. Hai cách của việc cung cấp thông tin quản lý bởi những phương tiện của bản ghi sự kiện hệ thống, đây là bộ phận của Windows NT từ sự bắt đầu và mà đưa cho những người quản trị một cách nhanh phạm vi của kiến thức về những điều kiện khác thường mà đã xuất hiện trong quá khứ gần đây. Trình điều khiển của các bạn cần phải báo cáo những sự kiện mà một người quản trị quan tâm đến và có thể làm cái gì đó. Người nào mà lập trình driver của các bạn cần phải tranh luận với với một quản trị hệ thống giàu kinh nghiệm để quyết định những bản ghi sự kiện nào, như như vậy tránh làm bừa bộn bản ghi với thủ tục và thông tin không nổi bật. Tập tin khả thi driver của các bạn sẽ cũng có lẽ bao gồm văn bản của thông báo trong một tài nguyên thông báo đa ngữ đặc biệt, và nó là một ý tưởng tốt để có một người viết có huấn luyện biên soạn văn bản đó. Cuối cùng, đừng coi những trình điều khiển của các bạn như những chi tiết không quan trọng. Việc có một driver tốt với một sự cài đặt phẳng là ít nhất quan trọng như vẻ ngoài của sản phẩm. Để đặt nó đơn giản, nếu driver của các bạn phá hủy hệ điều hành, những nhà phê bình sẽ cảnh báo công khai, và bất cứ ai không đọc những tổng quan sẽ nổi giận đang trả lại sản phẩm của các bạn cho những cửa hàng. Bạn không chiến thắng có những bất kỳ sự đặt hàng lại nào từ những người mà có hệ thống đã phá hủy, thậm chí một lần, bởi vì driver của các bạn. Vì thế là một quyết định cận thị tới sự phát triển trình điều khiển short-fund đã có thể dễ dàng có một kịch tính, hiệu ứng tiêu cực trên hàng dưới của các bạn trong nhiều năm đển. Lời khuyên này là một cách đặc biệt quan trọng cho những nhà sản xuất phần cứng trong những nước đang phát triển, ở đâu những nhà quản lý có một xu hướng để tìm kiếm mọi cách khả dĩ để cắt (xén, ngắt) những chi phí. Tôi gợi ý rằng sự phát triển driver là một chỗ nơi sự ra quyết định trên nền chi phí không thích hợp. Tổng kết, lập kế hoạch dự án của các bạn với những cột mốc sau đây trong tâm trí: • Sự Đánh giá của trình điều khiển và sự chọn lọc của đòi hỏi tài năng lập trình • Đặc tả lập trình cho phần cứng đủ hoàn thành đầy đủ cho công việc trình điều khiển để bắt đầu • Mẫu ban đầu phần cứng sẵn sàng cho sự thử driver • Driver và hardware/firmware hoạt động cùng nhau như trước đấy được dự định • File cài đặt (INF) được kiểm tra trên tất cả các hệ điều hành • Những bảng điều khiển và phần mềm phụ trợ khác làm • WMI và sự kiện bản ghi chức năng được làm và kiểm tra • Những tự thử WHQL được đi qua và quyết định được làm • Chương trình cài đặt tùy chỉnh đã làm (không phải quyết định bộ phận của WHQL) • Sẵn sàng để đốt cháy những đĩa và sản phẩm nghề nghiệp! 34/369
  37. Bài 5: THỰC HÀNH MỘT SỐ BÀI TẬP CƠ BẢN TRÊN VC++ Thực hành một số bài tập cơ bản trên VC++ Bài 1 Các tạo Project trên VC++ Bài 2 Viết được chương trbàinh đơn gảin trên VC++; cộng 2 số Bài 3 Tạo chương trình VC++, viết chương trình giải phương trình bậc 2. 35/369
  38. Bài 6: CÁC KỸ THUẬT LẬP TRÌNH CƠ BẢN Môi trường lập trình kiểu Kernel – Mode Hình 3-1 minh họa một số những thành phần tạo ra hệ điều hành XP Microsoft Windows. Mỗi thành phần hàm dịch vụ nó có tên bắt đầu với một tiền tố đặc biệt 2- ký tự, 3- ký tự: • Quản lý vào/ra (Tiền tố Io) chứa đựng nhiều hàm dịch vụ mà những trình điều khiển sử dụng, và đó là điều mà tôi bàn luận trong cuốn sách này. Mô đun Cấu trúc Quá trình (Tiền tố Ps ) tạo ra và quản lý những luồng kiểu nhân. Một trình điều khiển WDM bình thường có lẽ đã sử dụng Một luồng độc lập để nhiều lần cắt ngọn Một thiết bị không có khả năng của việc phát sinh những ngắt, và (cho) những mục đích khác. • Quản lý bộ nhớ ( Tiền tố Mm) điều khiển những bảng trang mà định nghĩa ánh xạ (của) những địa chỉ ảo lên trên bộ nhớ vật lý. • Thi hành ( Tiền tố Ex) cung cấp quản lý tất cả các dịch vụ đồng bộ hóa. Tôi sẽ bàn tới các vấn đề thực hiện các chỉ lệnh ở chương tiếp theo, bao hàm những dịch vụ đồng bộ hóa. • Quản lý đối tượng ( Tiền tố Ob ) cung cấp kiểm soát tập trung qua nhiều đối tượng dữ liệu mà khi Windows XP hoạt động. Những điều khiển WDM tin cậy trong quản lý đối tượng để làm cho một tính thử lại mà ngăn ngừa một đối tượng không biến mất trong khi người nào đó vẫn còn sử dụng nó và để chuyển đổi phần đối tượng qua con trỏ tới những đối tượng đại diện. • Bảo mật tham chiểu mà hình ( Tiền tố Be) cho phép điều khiển hệ thống tập tin thực hiện những sự kiểm tra bảo mật. Một vài điều khiển khác thì phân phối với những sự liên quan bảo mật trước thời gian một yêu cầu vào/ra đạt đến điều khiển WDM, vì vậy Tôi đang bàn luận những hàm đó trong sách này. • Thành phần " thư viện thực thi " ( Tiền tố Rtl) chứa đựng những chương trình thông dụng, như danh sách và những thủ tục quản lý chuỗi, những trình điều khiển Kernel - mode có thể sử dụng thay vì ANSI- tiêu chuẩn bình thường những thủ tục thư viện. Phần lớn, thao tác những hàm này hiển nhiên thông qua những tên của nó và những hiểu biết của bạn khi sử dụng chúng. • Windows XP thi hành những ngôn ngữ API của Kernel - mode được gọi khi sử dụng những tên thủ tục mà bắt đầu với tiền tố Zw. Những tài liệu DDK dung một vài ký hiệu hàn ZwXxx, Tức là là thứ mà gắn liền với nơi đăng ký và sự truy nhập hồ sơ. Những hàm đó được bàn luận trong chương này. 36/369
  39. • Nhân XP Windows ( Tiền tố Ke ) là tất cả sự đồng bộ hóa mức thấp (của) những hoạt động giữa những luồng và những bộ xử lý xuất hiện. KeXxx được bàn tới trong chương tiếp theo. • Nhiều lớp dưới của hệ điều hành, nó có tác dụng hỗ tợ hệ thống khi về chế độ nghỉ ngơi, là lớp trừu tượng hóa phần cứng ( hay HAL, tiền tố Hal). Tất cả những thành phần hệ điều hành của máy tính được liên kết với nhau trong HAL. HAL gọi ngắt trong những quá trình làm việc, để kết nối các thiết bị vào/ ra và những thiết bị lưu trũ khác. Thay vì việc kết nối trực tiếp tới phần cứng của máy tính, điều khiển WDM gọi là những hàm trong HAL để thực hiện. Hình 3-1. Tổng quan của Kernel – mode hỗ trợ những thủ tục Sử dụng các thư viện chức năng chuẩn Run-Time Windows NT, kiến trúc sư ưa thích mà không sử dụng trình điều khiển run_time các thư viện được cung cấp bởi nhà cung cấp của chương trình biên dịch C. Trong một phần, ban đầu không chấp nảy sinh từ thời gian rất đơn giản. Windows NT đã được thiết kế tại một thời gian khi không có ANSI tiêu chuẩn cho những gì thuộc một hàm chuẩn thư viện và khi rất nhiều trình biên dịch tồn tại, với mỗi ý tưởng riêng của những gì bao gồm sự bình tĩnh và độc đáo riêng chuẩn chất lượng. Yếu tố khác là thư viện chuẩn run_time đôi khi dựa vào khởi tạo rằng có thể xảy ra chỉ trong một người sử dụng chế độ-ứng dụng và đôi khi được triển khai thực hiện trong một sợi-đa-không an toàn hay không an toàn cách. 37/369
  40. Một Nhắc nhở về những tác dụng phụ Các "functions" mà bạn sử dụng trong một trình điều khiển được định nghĩa là macros trong DDK tiêu đề file. Ví dụ, các mã sau đây: int a = 2, b = 42, c; c = min(a++, b); Cơ bản bạn có thể không biết khi nào các DDK sẽ sử dụng một macro và nó sẽ khi tuyên bố một thực tế hàm bên ngoài. Đôi khi một dịch vụ hàm sẽ là một nền tảng vĩ mô cho một số hàm và một cuộc gọi các nền tảng. Hơn nữa, Microsoft là miễn phí để thay đổi ý nghĩa của nó trong tương lai. Do đó, bạn nên làm theo quy định này khi lập trình cho trình điều khiển WDM : Không bao giờ sử dụng một biểu hiện có phản ứng phụ như là một đối số cho một hạt nhân-chế độ dịch vụ hàm 38/369
  41. Trình bày lỗi (Lỗi xử lý) Lỗi luôn luôn phát sinh trong chương trình. Một số người trong số họ bắt đầu với chương trình lỗi, hoặc trong mã riêng của chúng tôi hoặc các thành viên trong-chế độ gọi các ứng dụng mà mã của chúng tôi. Một số người trong số họ liên quan đến hệ thống nạp hoặc làm ngay trong tiểu bang của phần cứng. Bất nguyên nhân, hoàn cảnh không bình thường là một nhu cầu linh hoạt của chúng tôi phản hồi từ mã Trong phần này, tôi sẽ mô tả ba khía cạnh của xử lý lỗi: tình trạng mã số, ngoại trừ cấu trúc xử lý, và kiểm tra lỗi. Nhìn chung, chế độ hỗ trợ hạt nhân-thói quen báo cáo lỗi không mong muốn bằng cách trả ra một mã trạng thái, trong khi họ báo cáo dự kiến các biến thể bình thường trong dòng chảy bằng cách trả ra một giá trị số Boolean hoặc khác hơn là một trạng thái thức mã Cấu trúc xử lý ngoại lệ cung cấp một tiêu chuẩn nhất để dọn sạch sau khi thực sự không mong muốn các sự kiện, chẳng hạn như dereferencing một người dùng không hợp lệ- chế độ trỏ, hoặc để tránh đụng xe mà các hệ thống thông thường ensues sau khi những sự kiện. Một lỗi kiểm tra nội bộ là tên gọi cho một thất bại thảm cho đó là một hệ thống tắt là chỉ chữa khỏi. Codes Kernel_mode hỗ trợ thói quen (và mã của bạn ,cho rằng vấn đề) cho biết thành công hay thất bại bằng cách trả ra một mã trạng thái của họ để gọi. NTSTATUS một giá trị là 32-bit integer gồm một số subfields, như minh họa trong hình 3-2. Cao-bit 2 trật tự biểu các mức độ nghiêm trọng của các điều kiện được báo cáo-thành công, thông tin, cảnh báo, hoặc lỗi. Tôi sẽ giải thích sự tác động của các khách hàng sớm cờ Các thiết bị mã đó cho biết thành phần hệ thống nguồn gốc tin nhắn và decouple cơ bản để phục vụ phát triển các nhóm từ mỗi khi nói đến số điện thoại để gán mã. Trong phần còn lại trong tình trạng mã 16-bit 'giá trị-cho biết chính xác điều kiện được báo cáo. 39/369
  42. Bạn nên luôn luôn kiểm tra tình trạng tra ra từ thủ tục mà họ cung cấp. Tôi đang dừng kết quả nguyên tắc này thường xuyên trong một số các đoạn mã tôi cho bạn bởi vì bao gồm tất cả các lỗi cần thiết xử lý mã thường xuyên che lấp các expository đích của đoạn này. Nhưng bạn không mô phỏng thực hành động Nếu bit-cao, trật tự của một mã trạng thái là 0, bất kỳ số bit còn lại có thể được thiết lập và các mã vẫn còn cho biết sẽ thành công. Do đó, không bao giờ cần so sánh với tình trạng mã số 0 để xem liệu bạn đang đối phó với sự thành công-thay vì, sử dụng các macro NT_SUCCESS: NTSTATUS status = SomeFunction( ); if (!NT_SUCCESS(status)) { //handle error } Không chỉ làm bạn muốn kiểm tra tình trạng mã số mà bạn nhận được từ thói quen bạn gọi , nhưng bạn cũng muốn trả ra trạng thái mã số thủ tục bạn gọi. Trong chương trước, tôi xử lý với hai trình điều khiển subroutines-DriverEntry và AddDevice-được định nghĩa là cả hai lại NTSTATUS mã Như tôi đã thảo luận, bạn muốn trả về STATUS_SUCCESS như sự thành công từ những thói quen. Nếu có gì đó sai, bạn thường muốn trả ra một mã trạng thái thích hợp, mà 40/369
  43. đôi khi là cùng một giá trị trả ra cho bạn. Ví dụ, đây là một số bước đầu tiên trong hàm AddDevice , với tất cả các lỗi kiểm tra ở bên trái: NTSTATUS AddDevice(PDRIVER_OBJECT DriverObject, PDEVICE_OBJECT pdo) { NTSTATUS status; PDEVICE_OBJECT fdo; status = IoCreateDevice(DriverObject, sizeof(DEVICE_EXTENSION), NULL, FILE_DEVICE_UNKNOWN, FILE_DEVICE_SECURE_OPEN, FALSE, &fdo); if (!NT_SUCCESS(status)) { KdPrint(("IoCreateDevice failed - %X\n", status)); return status; } PDEVICE_EXTENSION pdx = (PDEVICE_EXTENSION) fdo->DeviceExtension; pdx->DeviceObject = fdo; pdx->Pdo = pdo; pdx->state = STOPPED; IoInitializeRemoveLock(&pdx->RemoveLock, 0, 0, 0); status = IoRegisterDeviceInterface(pdo, &GUID_SIMPLE, NULL, &pdx->ifname); if (!NT_SUCCESS(status)) { 41/369
  44. KdPrint(("IoRegisterDeviceInterface failed - %X\n", status)); IoDeleteDevice(fdo); return status; } } 1. IoCreateDevice chúng tôi sẽ trở lại trong cùng một mã trạng thái nó đã cho chúng tôi. Lưu ý việc sử dụng các macro NT_SUCCESS như được mô tả trong văn bản. 2. Nó đôi khi là một ý tưởng tốt, đặc biệt là một khả năng gỡ lỗi trong trình điều khiển , để in tình trạng lỗi nào bạn khám phá. Tôi sẽ thảo luận về cách sử dụng chính xác của KdPrint sau trong chương này (trong phần "Mẫu Debugging Easier" ). 3. IoInitializeRemoveLock, thảo luận trong Chương 6, không thể không thành công. Do đó, không cần phải kiểm tra mã trạng thái. Nói chung, hầu hết các hàm tuyên bố với các loại VOID đang có trong cùng một loại "không thể không" . Một số hàm VOID có văn bản bị thất bại do nuôi một ngoại lệ, nhưng các tài liệu DDK rằng hành vi rất rõ ràng. 4. IoRegisterDeviceInterface nên không thành công, chúng tôi có một số ngẫu nhiên để làm trước khi chúng tôi quay trả lại gọi ; cụ thể,chúng tôi phải gọi IoDeleteDevice để tiêu diệt các thiết bị đối tượng, chúng tôi chỉ cần tạo ra. Bảng 3-1 cho thấy những thư quan trọng nhất đối với tình trạng mã số. 42/369
  45. Sự khác biệt giữa một lỗi và cảnh báo có thể được đáng kể. Ví dụ, không một METHOD_BUFFERED kiểm soát hoạt động (xem Chương 9) với STATUS_BUFFER_OVERFLOW-a-gây ra các cảnh báo I / O Trưởng đại diện để sao chép dữ liệu đối với người sử dụng chế độ-buffer. Việc không cùng một hoạt động với STATUS_BUF ¬ FER_TOO_SMALL-một lỗi-nguyên nhân của I / O Trưởng đại diện cho bất kỳ không được sao chép dữ liệu. Giải quyết Các ngoại lệ có cấu trúc Gia đình của hệ điều hành Windows cung cấp một phương pháp xử lý điều kiện đặc biệt giúp bạn tránh những tiềm năng hệ thống treo. Tích hợp chặt chẽ với các biên của mã Máy phát điện, cấu trúc xử lý ngoại lệ cho phép bạn dễ dàng đặt ra một sự bảo vệ trên các phần của mã của bạn và xử lý gọi ngoại trừ khi có gì đó sai trong phần guarded Cấu trúc ngoại lệ xử lý cũng cho phép bạn dễ dàng cung cấp các tài liệu kiếm mà bạn có thể chắc chắn sẽ luôn luôn được thực hiện như thế nào không có vấn đề kiểm soát lá guarded một phần của mã. Rất ít các buổi hội thảo của tôi đã được học sinh quen thuộc với cấu trúc ngoại lệ, vì thế tôi sẽ giải thích một số Những vấn đề cơ bản của đây. Mà bạn có thể viết tốt hơn, thêm bulletproof mã nếu bạn sử dụng những tiện nghi. Trong nhiều tình huống, các tham số mà bạn nhận được trong một trình điều khiển WDM đã được quán triệt vetted do các mã và sẽ không cau Tốt hương vị có thể, do đó, các chỉ được đà để bạn có thể sử dụng các công cụ Tôi mô tả trong phần này. Như là một quy luật chung, tuy nhiên, bạn luôn luôn muốn bảo vệ tài liệu tham khảo trực tiếp với người sử dụng bộ nhớ ảo-chế độ với một cấu trúc khung ngoại lệ. Những tài liệu tham khảo xảy ra khi bạn trực tiếp tham chiếu bộ nhớ và khi bạn gọi MmProbeAndLockPages, ProbeForRead, và ProbeForWrite, và có lẽ ở những thời gian khác Mã số mẫu Các SEHTEST mẫu minh hoạ driver cơ khí của các cấu trúc ngoại lệ trong một trình điều khiển WDM. Ngoại lệ nào có thể trở thành trapped Gary Nebbett nghiên cứu các câu hỏi trong đó có trường hợp ngoại lệ có thể được trapped với các cấu trúc ngoại trừ cơ chế và báo cáo kết quả của mình trong một newsgroup đăng bài nhiều năm trước đây. SEHTEST các mẫu hợp những gì ông được học. Nói tóm lại, các trường hợp ngoại lệ sau đây sẽ được khi họ bị bắt lại xảy ra tại IRQL nhỏ hơn hoặc bằng DISPATCH_LEVEL (lưu ý rằng một số trong số này là cụ thể cho các bộ xử lý Intel x86): • Bất kỳ ký do ExRaiseStatus và liên quan hàm để lại dự 43/369
  46. • dereference không hợp lệ cho người dùng trỏ-chế độ bộ nhớ • Debug hoặc Breakpoint ngoại lệ • Integer overflow (hướng dẫn nhập) • opcode không hợp lệ Lưu ý rằng một tham chiếu đến một chế độ không hợp lệ hạt nhân-trỏ dẫn trực tiếp đến một lỗi kiểm tra và có thể không được trapped. Tương tự, một chia-do-hay không ngoại trừ một ngoại lệ RÀNG BUỘC hướng dẫn đã dẫn tới một lỗi kiểm tra. Chương trình hạt nhân-chế độ sử dụng cấu trúc ngoại trừ trường hợp ngoại lệ bằng cách xây dựng khung trên cùng một cây rơm được sử dụng cho các đối số qua, subroutine gọi điện thoại, và tự động các biến. Một dành để xử lý đăng ký điểm hiện tại ngoại trừ khung. Mỗi phòng trong khung điểm đến trước khung. Bất cứ khi nào một trong các ngoại lệ xảy ra, hạt nhân tìm kiếm trong danh sách các ngoại lệ cho một ngoại trừ khung handler. Nó sẽ luôn luôn tìm thấy một bởi vì có một ngoại lệ khung tại rất trên cùng của stack mà sẽ xử lý bất kỳ khác unhandled ngoại lệ. Một khi hạt nhân nằm một ngoại lệ handler, nó unwinds việc thực hiện và ngoại trừ khung stacks song song, kêu gọi sự xử lý dọc theo đường đi. Sau đó, nó điều khiển cho các ngoại lệ handler. Khi bạn sử dụng biên của Microsoft, bạn có thể sử dụng Microsoft mở rộng cho C / C + + ngôn ngữ mà ẩn một số trong các phức tạp của nguyên liệu làm việc với các hệ điều hành primitives. Bạn sử dụng __try tuyên bố để chỉ một phức hợp tuyên bố như là guarded cơ thể cho một ngoại trừ khung, và bạn sử dụng hoặc các __finally tuyên bố thành lập một handler hoặc chấm dứt các __except tuyên bố thành lập một ngoại lệ handler. Đầu tiên các máy tính thi guarded trong cơ thể. Khi kiểm soát các lá guarded cơ thể cho bất cứ lý do gì, máy tính handler thi chấm dứt. Xem Hình 3-3. 44/369
  47. Hình 3-3. Kiểm soát dòng chảy của trong một thử-cuối cùng khối. Đây là một trong rất đơn giản Illustration: LONG counter = 0; __try { ++counter; } __finally { counter; } KdPrint(("%d\n", counter)); Guarded đầu tiên trong cơ thể và thi increments quầy biến từ 0 đến 1. Khi kiểm soát "bỏ qua" quyền brace ở cuối của guarded cơ thể, chấm dứt handler thi và decrements cập quay lại 0. Giá trị do đó sẽ được in ra 0. Đây là biến thể hơi phức tạp hơn: VOID RandomFunction(PLONG pcounter) { 45/369
  48. __try { ++*pcounter; return; } __finally { *pcounter; } } Kết quả của hàm này, không có thay đổi để các nguyên ở cuối của pcounter trỏ: bất cứ khi nào kiểm soát các lá guarded cơ thể cho bất cứ lý do gì, bao gồm một tuyên bố trở lại hoặc một goto, chấm dứt handler thi. Đây là những sự tăng dần thận trọng trong cơ thể truy cập và thực hiện một trở lại. static LONG counter = 0; __try { ++counter; BadActor(); } __finally { counter; } 46/369
  49. Hãy thử-Ngoại trừ Blocks Các cách khác để sử dụng cấu trúc ngoại lệ bao gồm việc xử lý một thử-ngoại trừ khối: __try { } __except( ) { } EXCEPTION_EXECUTE_HANDLER được nhiều bằng 1 và cho các hệ điều hành để chuyển giao kiểm soát của bạn ngoại trừ handler. Nếu handler té ngã của bạn thông qua việc kết thúc quyền brace, tiếp tục kiểm soát của bạn trong chương trình tại các tuyên bố ngay lập tức sau đó phải brace. (Tôi đã thấy Platform SDK tài liệu hướng dẫn cho rằng có hiệu lực trở về để kiểm soát các điểm của các ngoại lệ, nhưng đó là không chính xác • EXCEPTION_CONTINUE_SEARCH được nhiều bằng 0 và cho các hệ điều hành mà bạn có thể không xử lý các ngoại lệ. Hệ thống giữ hàm quét up the stack đang tìm kiếm các handler khác. Nếu không có người đã cung cấp một handler cho các ngoại lệ, một hệ thống sụp đổ sẽ xảy ra. • EXCEPTION_CONTINUE_EXECUTION được nhiều bằng -1 và cho các hệ điều hành để trở lại điểm, nơi các ngoại lệ đã được nâng lên. Tôi sẽ có thêm một chút để nói về việc này biểu hiện giá trị thêm vào một ít. Hãy xem ở Hình 3-4 cho các con đường có thể kiểm soát trong và xung quanh là một thử-trừ khối. Bottom of Form Bottom of Form 47/369
  50. Hình 3-3. Luồng của điều khiển khối try - finally . Đây là một ví dụ đơn giản: LONG counter = 0; __try { ++counter; } __finally { counter; } KdPrint(("%d\n", counter)); Trước tiên thực thi phần thân và tăng biến đếm từ 0 lên 1. Khi điều khiển “drops through” ở cuối của phần thân, kết thúc quá trình xử lý và giảm biến đếm tới 0. Bởi vậy giá trị in ra sẽ là 0. Đây là một ví dụ phức tạp hơn. 48/369
  51. VOID RandomFunction(PLONG pcounter) { __try { ++*pcounter; return; } __finally { *pcounter; } } Kết quả của hàm này là không thay đổi giá trị nguyên ở cuói của con trỏ: bất cứ lúc nào điều khiển dời dến phần thân với bất kỳ lý do gi, bao gồm một câu lệnh trả về hoặc một goto, viếc thực thi kết thúc. Đây là phần tăng biến đếm và trả ra giá trị. Ở đoạn code tiếp theo thực thi và giảm biến đếm. sau đó chương trình con sẽ thực hiện lại. Một ví dụ hoàn chỉnh của khối try-finally: static LONG counter = 0; __try { ++counter; BadActor(); } __finally 49/369
  52. { counter; } Chúng ta gọi đây là một hàm, BadActor, nó sẽ xuất hiện một vài sự sắp xếp của ngoại lệ kích hoạt một stack hoạt động. Như một phần của tiến trình nghỉ thực hiện và ngoại lệ ngăn xếp, hệ điều hành sẽ gọi mã code của chúng ta để khôi phục lại biến đếm tới giá trị trước nó. hệ thống sẽ tiếp tục giải phóng stack, vì vậy bất cứ câu lệnh nào viết sau khối finally sẽ không được thực hiện. Khối Try – Except. Một cách khác để sử dụng cấu trúc xử lý ngoại lệ là khối try-Except __try { } __except( ) { } Phần thân của khối try-except là mã có sức mạnh phá vỡ những phát sinh ngoại lệ. Có lẽ là bạn đang gọi tới phần chính của hàm như MmProbeAndLockPages sử dụng con trỏ chuyển hoá từ chế độ người dùng không cần kiểm tra giá trị rõ ràng. Có thể bạn có lý do khác. Trong mọi trường hợp nếu bạn quản lý tất cả các cách thông qua phần thân không có lỗi, điều khiển sẽ tiếp tục mã code xử lý ngoại lệ ở phần sau. Bạn sẽ nghĩ trường hợp này trở lên bình thường. Nếu một ngoại lệ xuất hiện trong mã code của bạn hoặc trong bất kỳ tiến trình con nào mà bạn gọi, tuy nhiên, hệ điều hành sẽ giải phóng stack, đánh giá biểu thức trong câu lệnh Except. Những biểu thức này sinh ra một trong những giá trị sau: • EXCEPTION_EXECUTE_HANDLER có giá trị bàng 1 và thông báo cho hệ điều hành để chuyển điều khiển tới việc xử lý ngoại lệ của bạn. Nếu việc xử lý 50/369
  53. của bạn thất bại, đièu khiẻn sẽ tiếp tục chương trình của bạn ở câu lệnh ngay sau đó.( Tôi đã xem tài liệu Platform SDK nói về hiệu ứng mà điều khiển trả ra ngoại lệ tại điểm của ngoại lệ, nhưng đièu đó không đúng) • EXCEPTION_CONTINUE_SEARCH có giá trị bằng 0 và thông báo với hệ điều hành rằng bạn không thể xử lý ngoại lệ. Hệ thống sẽ quét qua stack để tìm kiếm xử lý khác. Nếu không ai cung cấp một xử lý nào cho ngoại lệ thì một hệ thống đổ vỡ sẽ xảy ra. • EXCEPTION_CONTINUE_EXECUTION có giá trị bằng -1 và thông báo cho hệ điều hành trả ra một diểm nơi mà ngoại lệ được sinh ra. Tôi sẽ có mọt bài để nói về giá trị biểu thức này nhièu hơn nữa. Nhìn hình 3-4 có thể điều khiển đường dẫn trong và xung quanh một khối try-except. Hình 3-4. Flow of control in a try-except block. Cho ví dụ, bạn có thể bảo vệ chính bạn khỏi nhận một con trơ sai bằng sử dụng mã code sau đây. PVOID p = (PVOID) 1; __try { KdPrint(("About to generate exception\n")); ProbeForWrite(p, 4, 4); KdPrint(("You shouldn't see this message\n")); 51/369
  54. } __except(EXCEPTION_EXECUTE_HANDLER) { KdPrint(("Exception was caught\n")); } KdPrint(("Program kept control after exception\n")); ProbeForWrite kiểm tra một vùng dữ liệu cho tính hợp lệ. Ở ví dụ này, nó sẽ gây ra một ngoại lệ bởi vì con trỏ mà chúng ta cung cấp chưa được liên kết tới 4 byte. Việc xử lý ngoại lệ tăng tốc đièu khiển. Điều khiển sau đó sẽ thực hiện câu lệnh sau xử lý ngoại lệ và tiếp tục chương trình của bạn. Trong ví dụ trước, có bạn được trả ra giá trị EXCEPTION_CONTINUE_SEARCH, hệ điều hành tiếp tục giải phóng stack tìm kiếm một xử lý ngoại lệ. Không có mã code xử lý ngoại lệ của bạn cũng không có mã sau nó được thực thi: Mọi hệ thống sẽ bị phá huỷ hoặc sẽ được xử lý ngoại lệ ở mức cao hơn. Bạn không nên trả về EXCEPTION_CONTINUE_EXECUTION trong phần nhân vì bạn không có cách nào dể thay đổi những nguyên nhân gây ra ngoại lệ để cho phép thử lại. Chú ý rằng bạn không thể bẫy những ngoại lệ số học, hoặc những trang lỗi liên kết một con trỏ kiểu nhân sai, bằng việc sử dụng cấu trúc ngoại lệ. Bạn chỉ cần viết mã để không phát sinh những ngoại lệ như vậy. Đó là làm thế nào để tránh chia cho 0. Như ví dụ sau đây. ULONG numerator, denominator; // else quotient = numerator / denominator; Nhưng làm thế nào để con trỏ có thể đến tới bạn từ phần khác của nhân? Đó không phải là hàm mà bạn có thể sử dụng để kiểm tra giá trị của một nhân con trỏ. Bạn cần chú ý những quy tắc dưới đây: 52/369
  55. Theo cách thông thường, giá trị thực mà một nhân thành phần đưa cho bạn. Tôi không cho rằng bạn không nên chia nhỏ đoận mã của bạn với câu lệnh ASSERT - bạn nên bởi vì bạn có thể không hiểu tất cả bên trong và bên ngoài cách làm việc của thành phần nhân khác như thế nào. Tôi cho rằng bạn không cần đè nặng trình điều khiẻn của bạn với sự phòng thử quá mức chống lại lỗi ở chỗ khác, được kiểm tra kỹ, một phần của hệ thống trừ khi bạn cần làm việc xung quanh một lỗi. Vì những hạn chế trên bạn sử dụng hai biểu thức này như thế nào trong chương trình của bạn, bạn sẽ hầu như chắc chắn muốn sử dụng chúng trong một hàm để gọi một vài hàm điều kiện, giống như: LONG EvaluateException(NTSTATUS status, PEXCEPTION_POINTERS xp) { . . . } . . . __except(EvaluateException(GetExceptionCode(), GetExceptionInformation())) Đưa ra ngoại lệ Chương trình lỗi là một cách bạn có thể đưa ra ngoại lệ mà gọi cấu trúc xử lý ngoại lệ. Chương trình ứng dụng là quen thuộc với Win32 hàm API đưa ra ngoại lệ, cái mà cho phép bạn tạo ra một cái gì đó ngoại lệ cho chính bạn. Ở trình điều khiển WDM bạn có thể gọi tiến trình lắng nghe trong bảng 3-2. tôi không đưa cho bạn một ví dụ cụ thể để gọi hàm này bởi vì những lý do sau: Nói cụ thể, đưa ra ngoại lệ không phải là cách tốt để nói cho đối tượng gọi của bạn thông tin mà bạn khám phá ra trong việc xử lý các tiến trình thông thường. Nó tốt hơn nhiều để 53/369
  56. trả về một trạng thái mã, thậm chí như thể là sự chỉ dẫn từ bên ngoài khó đọc hơn. Bạn nên tránh những ngoại lệ bởi vì máy móc rất dắt tiền. Thậm chí giá của khung ngoại lệ là quan trọng và một vài thứ cần tránh khi bạn có thể. Bảng 3-2. Dịch vụ hàm cho Raising Exceptions Service Function Description ExRaiseStatus Raise exception with specified status code ExRaiseAccessViolation Raise STATUS_ACCESS_VIOLATION Raise ExRaiseDatatypeMisalignment STATUS_DATATYPE_MISALIGNMENT Ví dụ thực tế Mặc dù phí tổn của việc cài đặt lên và phá hỏng một ngoại lệ, bạn phải sử dụng quy tắc cấu trúc ngoại lệ trong một trình điều khiển trong trường hợp đặc biệt. Một trong những lần bạn phải thiết lập một xử lý ngoại lệ là khi bạn gọi MmProbeAndLockPages để tìm kiếm trang cho một bộ nhớ danh sách mieu tả(MDL) bạn tạo ra: PMDL mdl = MmCreateMdl( ); __try { MmProbeAndLockPages(mdl, ); } __except(EXCEPTION_EXECUTE_HANDLER) { NTSTATUS status = GetExceptionCode(); IoFreeMdl(mdl); return CompleteRequest(Irp, status, 0); 54/369
  57. } (CompleteRequest là một hàm giúp đỡ tôi sử dụng để xử lý đòi hỏi hoàn thành I/O. Chương 5 giải thích tất cả về yêu cầu I/O và hoàn thành chúng.) Lần khác khi sử dụng một xử lý ngoại lệ là khi bạn muốn truy cập bộ nhớ người dùng sử dụng một con trỏ từ nguồn không đáng tin. Trong ví dụ dưới đây, cho rằng bạn thu được con trỏ p từ một chương trình người dùng và chắc chắn nó trỏ tới một số nguyên: PLONG p; // from user-mode __try { ProbeForRead(p, 4, 4); LONG x = *p; . . . } __except(EXCEPTION_EXECUTE_HANDLER) { NTSTATUS status = GetExceptionCode(); . . . } Bug Checks 55/369
  58. Những lỗi không thể phục hồi đươc trong kiểu nhân có thể biêur thị chính nó trong cái goi là màn hình xanh của sự chết(BSOD) đó là tất cả quá quen thuộc đối với lập trình viên. Hình 3-5 là một ví dụ . Bên trong, những lỗi này được gọi là kiẻm tra rối,sau đó dịch vụ hàm bạn sử dụng để chuẩn đoán sự cố của chúng: KeBugCheckEx. Đặc tính chính của sự kiểm tra rối là hệ thống đóng chính nó trong thứ tự một cách như hợp lý và hiện tại BSOD. Một BSOD xuất hiện hệ thống sẽ không hoạt động và phải khởi động lại Hình 3-5. Mà hình xanh của sự không hoạt động Bạn có thể gọi KeBugCheckEx như sau: KeBugCheckEx(bugcode, info1, info2, info3, info4); bugcode là một giá trị số xác định nguyên nhân lỗi và info1, info2, vân vân là những số mà xuất hiện trên màn hình BSOD để giúp người lập trình hiểu được các lỗi. Hàm này không trả ra giá trị. Như một người phát triển bạn không có thông tin từ màn hình xanh. Nếu bạn may mắn. thông tin sẽ bao gồm cả hướng dẫn been trong trình điều khiển của bạn. Sau đó bạn có thể xem xét vị trí này trong nhân trình gỡ rối, có lẽ, suy luận có thể thực hiện được vì kiểm tra rối. Chính kiểm tra rối của Microsoft xuất hiện trong bugcodes.h Sụ giải thích về mã của họ có thể tìm thấy trong Những kiến thức cơ bản Q103059. a fuller 56/369
  59. explanation of the codes and their various parameters can be found in Knowledge Base article Q103059, “Descriptions of Bug Codes for Windows NT,” cái mà có sẵn trong MSDN, giữa những chỗ khác. Ví dụ Ví dụ điều khiển BUGCHECK cung cấp cách làm thế nào để gọi KeBugCheckEx. Tôi sử dụng nó để phát sinh hình ảnh ở hình 3-5. Bạn có thể tạo debug-check cho chính bạn nếu bạn muốn. Những giá trị của Microsoft là những giá trị đơn giản bắt dầu từ 1(APC_INDEX_MISMATCH) và (hiện giờ) mở rộng dến 0xF6 (PCI_VERIFIER_DETECTED_VIOLATION), cùng với một số cái khác. Để tạo ra mã kiểm tra của mình, dịnh nghĩa một hằng số số nguyên như nếu bạn ở trạng thái STATUS_SEVERITY_SUCCESS , nhưng những yêu cầu khác của khách hàng hoặc một mã khác không. Ví dụ: #define MY_BUGCHECK_CODE 0x002A0001 KeBugCheckEx(MY_BUGCHECK_CODE, 0, 0, 0, 0); Bạn sử dụng một mã trạng thái khác 0 (42 trong ví dụ) hoặc cờ của khách hàng(cái mà tôi viết 0 trong ví dụ này) vì vậy mà bạn có thể thông báo chính mã của bạn từ một người sử dụng Microsoft. Bây giơ tôi nói cho bạn cách làm thế nào tạo BSOD cho chính bạn, cho phép tôi nói khi nào bạn làm nó: không bao giờ. Hoặc trong khi xây dựng kiểm tra của trình điều khiển đẻ sử dụng trong suốt bên trong quá trình gỡ rối. Bạn và tôi đều không chắc chắn để viết một trình diều khiển mà sẽ phát hiện một lỗi nghiêm túc đến nỗi lấy xuống hệ thống là cách giải quyết duy nhất. Nó sẽ tốt hơn nhiều gỡ lỗi( sử dụng gỡ lỗi tôi sẽ mô tả trong chương 14 ) và trả ra một mã tình trạng. Chú ý rằng người dùng cuối có thể cấu hình hành động của KeBugCheckEx trong những sự thiết lập cho máy tính của tôi. Người sử dụng có thể chọn khởi động một cách tự động hoặc phát sinh BSOD. Người dùng cuối có thể như vậy chọn vài mức của chi tiết( kể cả không) cho một file. 57/369
  60. Quản lý bộ nhớ (Memory Management ) Ở chương này, tôi sẽ bàn về chủ đề quản lý bộ nhớ. Windows XP chia cắt vùng địa chỉ thực tế chia cắt bên trong bằng vài cách. Một cách chia - rất bảo mật và toàn vẹn là địa chỉ người dùng và địa chỉ nhân. Một cách chia khác, mà sử dụng hầu hết nhưng không tốt lắm là giữa trang nhớ và không phaitrang nhớ. Hầu hết địa chỉ người sử dụng và vài địa chỉ kiểu nhân tham chiếu dến ô nhớ mà Quản lý bộ nhớ đổi tới và từ đĩa trong toàn bộ thời gian, trong khi một vài địa chỉ kiểu nhân luôn luôn liên kết tới ô nhớ trong bộ nhớ vật lý. Từ Windows XP cho phép nhưững phần của trình điều khiển tớ ô nhớ, tôi sẽ giải thích cách làm thế nào điều khiển ô nhớ của trình điều khiển của bạn tại một thời điểm bạn xây dựng trình điều khiển và chạy nó. Windows XP cung cấp một số phưoơg thức để quản lý bộ nhớ. Tôi sẽ mô tả hai hàm dịch vụ cơ bản —ExAllocatePoolWithTag và ExFreePool— mà bạn sử dụng để phân chia và giải phóng một cách ngẫu nhiên. Tôi sẽ giải thích nguyên thuỷ mà bạn sử dụng để tổ chức bộ nhớ và trong những danh sách liên kết của cấu trúc. Cuối cùng tôi sẽ giải thích khái niệm một danh sách mà cho phép bạn phân chia có hiệu quả. User-Mode and Kernel-Mode Address Spaces Windows XP và Microsoft Windows 98/Me chạy trên nhữngmáy tính mà hỗ trợ một vùng địa chỉ thực tế nơi mà địa chỉ ảo được vẽ tới bộ nhớ vật lý hoặc một ô nhớ bên trong file tráo đổi trên dĩa cứng. Thủ tục đơn giản hoá những vấn đề, bạn có thể nghĩ về địa chỉ ảo như chia cắt thành hai phần: một phaanf kiêu nhân và một phần kiểu người dùng. Nhìn hình 3-6. Hình 3-6. Thành viên-hạt nhân và chế độ-chế độ của phần không gian địa chỉ. Mỗi người sử dụng chế độ xử lý có địa chỉ bối cảnh riêng của mình, mà bản đồ của người sử dụng chế độ ảo-địa chỉ duy nhất cho một bộ sưu tập của vật chất trang khung. Nói cách khác, ý nghĩa của bất kỳ địa chỉ riêng ảo thay đổi từ một chút thời gian để tiếp theo như là Windows XP Scheduler tắc từ một sợi ở một trong quá trình vào một sợi khác trong quá 58/369
  61. trình. Một phần của công việc trong khâu chuyển đổi là để thay đổi các trang bảng được sử dụng bởi một xử lý để họ giới thiệu đến các luồng của quá trình bối cảnh. It's thường không WDM đó, một driver sẽ thực hiện trong cùng một bối cảnh như là sợi xướng của I / O yêu cầu nó xử lý. Chúng ta nói rằng chúng tôi đang chạy trong bối cảnh arbitrary sợi, nếu chúng ta không biết chắc chắn sẽ cho quá trình mà hiện nay người sử dụng chế độ địa chỉ bối cảnh thuộc. Trong bối cảnh arbitrary sợi, chúng tôi chỉ đơn giản có thể không được sử dụng một địa chỉ mà ảo thuộc với người sử dụng chế độ bởi vì chúng tôi có thể không có bất kỳ ý tưởng với những gì vật lý bộ nhớ nó có thể điểm. Trong số này xem không chắc chắn, nhìn chung chúng tôi phải tuân theo các quy định sau đây trong một chương trình driver: Không bao giờ (tốt, mấy khi) người trực tiếp tham chiếu-chế độ bộ nhớ Nói cách khác, không có một địa chỉ mà một người sử dụng chế độ-cung cấp các ứng dụng và chữa trị cho rằng như là một địa chỉ trỏ rằng chúng tôi có thể trực tiếp dereference. Tôi sẽ thảo luận trong chương sau một vài kỹ thuật để truy cập dữ liệu đệm mà bắt nguồn ở chế độ người dùng. Tất cả chúng tôi cần phải biết ngay bây giờ, tuy nhiên, chúng tôi đang là (gần như) luôn luôn có được bằng cách sử dụng hạt nhân-chế độ địa chỉ ảo bất cứ khi nào chúng tôi muốn truy cập vào bộ nhớ của máy tính. Bao lớn Có một trang? Trong một hệ thống bộ nhớ ảo, hệ điều hành tổ chức vật lý bộ nhớ và các vùng trao đổi vào tập tin như-kích thước trang khung. Trong một WDM trình điều khiển, bạn có thể sử dụng để tỏ cố PAGE_SIZE cho bạn biết làm thế nào là một trang lớn. Trong một số máy tính Windows XP, một trang là 4096 bytes dài; trong những người khác, it's 8192 bytes dài. A liên quan đến việc đặt tên PAGE_SHIFT kích thước bằng các trang như là một sức mạnh của 2. Đó là: PAGE_SIZE == 1 << PAGE_SHIFT Cho sự tiện nghi của bạn, bạn có thể sử dụng một vài preprocessor macros mã của bạn trong khi bạn đang làm việc với các kích thước của một trang web: • ROUND_TO_PAGES trong vòng một kích thước byte đến trang kế tiếp cao hơn-ranh giới. Ví dụ, ROUND_TO_PAGES (1) là 4.096 trên một 4-KB-trang máy tính. • BYTES_TO_PAGES xác định bao nhiêu trang được yêu cầu tổ chức một số byte, bắt đầu từ bắt đầu của một trang. Ví dụ, BYTES_TO_PAGES (42) sẽ là 1 trên tất cả các nền tảng, và BYTES_TO_PAGES (5000) sẽ là 2 trên một số nền tảng và 1 về những người khác. • BYTE_OFFSET sẽ trả về byte bù đắp phần của một địa chỉ ảo. Đó là, tính toán của nó bắt đầu bù đắp trong vòng một số trang khung của một địa chỉ. Ngày 4-KB-trang, máy tính, BYTE_OFFSET (0x12345678) sẽ được 0x678 59/369
  62. PAGE_ALIGN vòng một ảo địa chỉ xuống để một trang ranh giới. Ngày 4-KB-trang, máy tính, PAGE_ALIGN (0x12345678) sẽ được 0x12345000. • ADDRESS_AND_SIZE_TO_SPAN_PAGES trả ra số trang khung chiếm được xác định bởi một số byte, bắt đầu từ một địa chỉ ảo. Ví dụ, các tuyên bố ADDRESS_AND_SIZE_TO_SPAN_PAGES (0x12345FFF, 2) là 2 trên một 4-KB- trang máy vì 2 byte chiều dài một trang ranh giới. Đánh số trang và đánh số trang Bộ nhớ Toàn bộ số điểm của một hệ thống bộ nhớ ảo là bạn có thể có được một không gian địa chỉ ảo đó là nhiều lớn hơn số lượng bộ nhớ vật lý trên máy tính. Để thực hiện điều này feat, Trưởng đại diện các Bộ nhớ nhu cầu để trao đổi trang khung trong và ngoài của vật lý bộ nhớ. Các thể loại "phải được cư dân" công cụ rộng lớn hơn nhiều hơn là chỉ các trang lỗi xử lý. Windows XP cho phép phần cứng interrupts để xảy ra ở gần bất kỳ lúc nào, bao gồm một trang lỗi trong khi đang được phục vụ. Nếu điều này không được như vậy, các trang lỗi handler sẽ không thể đọc hoặc viết trang từ một thiết bị sử dụng một gián đoạn Vì vậy, mỗi phần cứng gián đoạn dịch vụ thường phải được nonpaged trong bộ nhớ. Các thiết kế của Windows NT, bao gồm các quyết định ngay cả thêm thói quen trong nonpaged danh mục bằng cách sử dụng một nguyên tắc đơn giản: Mã thi hành tại hoặc gián đoạn ở trên mức yêu cầu (IRQL) DISPATCH_LEVEL trang có thể không gây ra lỗi lầm. Tôi sẽ xây dựng trên nguyên tắc này trong chương kế tiếp. Bạn có thể sử dụng PAGED_CODE preprocessor vĩ mô (trong wdm.h) để giúp bạn khám phá các hành vi vi phạm các quy định này trong kiểm tra xây dựng của driver của bạn. Ví dụ: NTSTATUS DispatchPower(PDEVICE_OBJECT fdo, PIRP Irp) { PAGED_CODE() } PAGED_CODE chứa các điều kiện biên soạn. Trong khi kiểm tra-xây dựng, môi trường, nó in một tin nhắn và tạo ra một đồng thất bại nếu IRQL hiện nay là quá cao. Trong miễn phí-xây dựng, môi trường, nó không làm bất cứ điều gì. Để hiểu lý do tại sao PAGED_CODE rất hữu ích, tưởng tượng rằng DispatchPower nhu cầu cho một số lý do để được ở nonpaged nhưng bộ nhớ rằng bạn có misplaced nó trong bộ nhớ paged 60/369
  63. Nếu hệ thống xảy ra DispatchPower để gọi tại một thời gian khi trang chứa nó không phải là hiện nay, một trang lỗi sẽ xảy ra, theo sau là một lỗi kiểm tra. Lỗi mã kiểm tra sẽ được pretty uninformative (IRQL_NOT_LESS_OR_EQUAL hay DRIVER_IRQL_NOT_LESS_OR_EQUAL), nhưng ít nhất bạn sẽ tìm ra rằng bạn có một vấn đề. Nếu bạn kiểm tra trình điều khiển của bạn trong một tình hình trong đó các trang có chứa DispatchPower sẽ xảy ra fortuitously để được trong bộ nhớ, tuy nhiên, sẽ không có một trang lỗi. PAGED_CODE sẽ phát hiện ngay cả vấn đề như vậy Cài đặt các Driver Verifier "IRQL lực lượng kiểm tra" lựa chọn sẽ làm tăng đáng kể cơ hội khám phá ra rằng bạn đã tan vỡ các quy định về paging và IRQL. Tùy chọn lực lượng pageable trang ra khỏi bất cứ khi nào xác nhận trình điều khiển bộ nhớ nâng cao IRQL để DISPATCH_LEVEL hay ngoài. Biên-Thời gian kiểm soát của Pageability Cho rằng một số phần của trình điều khiển của bạn phải luôn luôn được cư dân và một số phần có thể được paged, bạn cần một cách để kiểm soát các giao của bạn và mã dữ liệu vào paged và nonpaged pool. Bạn pháp phần của công việc này bằng cách hướng dẫn làm thế nào để biên apportion mã của bạn và dữ liệu khác nhau giữa các phần The run-tải thời gian sử dụng tên của các phần để đưa phần của trình điều khiển của bạn trong những nơi bạn có ý. Bạn cũng có thể pháp phần của công việc này tại thời gian chạy bằng cách gọi các bộ nhớ Trưởng đại diện thói quen mà tôi sẽ thảo luận trong phần kế tiếp. Chú ý: Win32 executable file, bao gồm cả hạt nhân-chế độ điều khiển, đang có nội bộ thành từ một hay nhiều phần. Một phần có thể chứa dữ liệu và mã hay, nói chung, có thêm thuộc tính như là đang được đọc, ghi, shareable, thi hanh, và như vậy trên. Một phần nhỏ cũng là đơn vị mà bạn có thể chỉ khi bạn đã xác định khả năng trang ¬ Khi driver tải một hình ảnh, hệ thống đã nhận phần chữ có tên bắt đầu với Trang hay. Eda (bắt đầu. EDATA) vào paged pool, trừ khi DisablePagingExecutive giá trị trong HKLM \ System \ CurrentControlSet \ Control \ Session Manager \ Memory Quản lý chủ chốt sẽ xảy ra để được thiết lập (trong đó có trường hợp không có bằng driver paging xảy ra). ). Lưu ý rằng các tên này là trường hợp nhạy cảm! Trong một trong những twists ít có ảnh hưởng đến số phận của chúng tôi tất cả theo thời gian, chạy mềm-Ice / W trên Windows XP yêu cầu bạn để vô hiệu hóa hạt nhân trong paging bằng cách này. Điều này 61/369
  64. chắc chắn làm cho khó hơn để tìm thấy lỗi gây ra bởi misplacement của driver hoặc mã dữ liệu vào paged pool! Nếu bạn sử dụng debugger này, tôi khuyên bạn nên religiously sử dụng PAGED_CODE vĩ mô và Driver Verifier. Cách truyền thống cho các biên để đưa mã vào một phần là để sử dụng những alloc_text pragma. Kể từ mỗi biên sẽ không nhất thiết phải hỗ trợ các pragma, các DDK tiêu đề hoặc xác định hay không xác định việc ALLOC_PRAGMA để cho biết nên sử dụng pragma. Sau đó bạn có thể gọi các pragma để xác định vị trí của phần subroutines cá nhân của bạn trong trình điều khiển, như sau: #ifdef ALLOC_PRAGMA #pragma alloc_text(PAGE, AddDevice) #pragma alloc_text(PAGE, DispatchPnp) #endif Những tài liệu phục vụ cho các nơi AddDevice và DispatchPnp hàm vào paged pool. Của Microsoft C / C + + biên nơi hai gây phiền nhiễu hạn chế về việc sử dụng các alloc_text: • Các pragma phải làm theo các công bố của một hàm nhưng việc định nghĩa. Một cách để phải tuân theo các quy định này là để tuyên bố tất cả các hàm trong trình điều khiển của bạn trong một tiêu chuẩn tiêu đề tập tin và gọi alloc_text tại đầu của các tập tin nguồn có chứa một hàm nhưng sau khi bạn bao gồm các tiêu đề. • Các pragma chỉ có thể được sử dụng với hàm có liên kết C-. Nói cách khác, nó sẽ không làm việc cho các thành viên lớp học hoặc hàm cho các hàm trong một C + + mã nguồn tệp tin mà bạn đã không khai báo bằng cách sử dụng extern "C". Để kiểm soát các vị trí của các biến dữ liệu, bạn sử dụng một pragma khác nhau dưới sự kiểm soát của là một biểu tượng khác nhau preprocessor vĩ mô: #ifdef ALLOC_DATA_PRAGMA #pragma data_seg("PAGEDATA") #endif Data_seg pragma nguyên nhân của tất cả các dữ liệu tĩnh tuyên bố các biến trong một mô-đun nguồn sau khi xuất hiện của pragma để đi vào paged pool. Bạn sẽ nhận thấy rằng này pragma khác trong một cách căn bản từ alloc_text. Một phần pageable bắt đầu, nơi # pragma data_seg ( "PAGEDATA") xuất hiện và kết thúc, nơi một countervailing # pragma data_seg () xuất hiện. Alloc_text, mặt khác, áp dụng cho một hàm cụ thể. 62/369