Từ 1 tới 10 trên tổng số 10 kết quả

Đề tài: Hướng Dẫn Lập Trình Driver Trên Hệ Điều Hành Linux

  1. #1
    Ngày gia nhập
    02 2008
    Bài viết
    1,009

    Mặc định Hướng Dẫn Lập Trình Driver Trên Hệ Điều Hành Linux

    Xin chào tất cả các anh em congdongcviet,công việc hiện tại của tôi là lập trình nhúng sử dụng nhân linux, qua thời gian tìm hiểu về cấu trúc linux tôi có đúc rút được một chút kinh nghiệm, hôm nay ngẫu hứng muốn chia sẻ cùng anh em yêu thích công nghệ nói chung và những thành viên lập trình nhúng nói riêng. Toàn bộ bài viết là những hiểu biết của tôi về linux, có rất nhiều trong số đó là đi sưu tầm ( kiến thức tôi tìm tòi trên mạng ), xin được viết lại tại đây

    Mở đầu bài viết, tôi xin nêu một vài ý chính về nhân linux,các bạn sẽ hiểu hơn về hệ điều hành mã nguồn mở này, bài viết tiếp theo sẽ hướng chi tiết các thao tác để lập trình 1 driver nho nhỏ trên Linux

    I) Hiểu sơ bộ về nhân Linux

    Linux đang ngày càng trở nên một hệ điều hành phổ biến trên thế giới. Việc phân tích và tìm hiểu hoạt động của Linux đang trở nên một nhu cầu thiết yếu đối với những người nghiên cứu CNTT. Trong bài viết này chúng tôi muốn giới thiệu cho các bạn các thành phần và hoạt động của nhân Linux (Linux Kernel)

    Nhân (kernel) của Linux gồm 5 tiểu hệ thống chính:

    1. Bộ phân thời cho tiến trình (Process Scheduler-SCHED):


    Như bạn biết về cơ bản PC vẫn là một hệ thống xử lý đơn tức là chỉ có 1 lệnh thực thi tại một thời điểm. Tuy nhiên các hệ điều hành đa nhiệm(multi-task) như Windows, Linux v.v đều cho phép nhiều chương trình chạy cùng một lúc. Làm sao chúng làm được như vậy? Bằng cách chuyển quyền thực thi qua lại giữa các chương trình thật nhanh làm cho chúng ta có cảm giác các chương trình chạy cùng lúc với nhau. Ví dụ bạn vừa đánh Winword vừa chơi Winamp thì thật ra SCHED sẽ chạy Winword 5,10 lệnh xong chuyển qua Winamp 5,10 lệnh rồi chuyển lại v.v Việc này cực kì nhanh nên bạn không có cảm giác gì.
    Hệ điều hành MSDOS ngày xưa thật sự là hệ điều hành đơn nhiệm, tuy nhiên bạn vẫn có thể bẩy ngắt 1Ch (hook interrupt) để giả lập đa nhiệm. Interrupt 1Ch thực chất được Timer IRQ (6 hay 8 gì quên mất rồi) gọi. Timer IRQ là một ngắt cứng tức là tín hiệu do bộ phát xung gởi tới CPU. Mặc định là 1/13 giây 1 lần bộ phát xung này gởi 1 tín hiệu IRQ đến CPU. Khi đó CPU sẽ ngưng lệnh đang thi hành chuyển qua xử lý ngắt. Timer IRQ sau đó sẽ gọi ngắt 1Ch. Nếu bạn viết 1 chương trình con hook int 1Ch, thì bạn sẽ có cảm tưởng nó chạy song song với chương trình chính (thật ra 1/13s nó mới chạy 1 lần). Ứng dụng cái này tôi có viết một chương trình chạy banner trong màn hình DOS, hay chương trình ping pong gồm 1 hay nhiều trái tim chạy va đập vào các cạnh của màn hình, trong khi vẫn chạy DOS.
    Các hệ điều hành đa nhiệm sau này đều xử dụng nguyên tắc này để làm SCHED. Tuy nhiên 1/13s thì không đủ nhanh để switch qua nhiều tiến trình nhưng xài hàm của BIOS ta có thể tăng tốc cho SCHED 1/100s 1 lần chẳng hạn.

    2. Bộ quản lý bộ nhớ (Memory Manager-MM):

    Bộ nhớ qui ước (conventional memory) của PC chỉ có 640K thôi. Do chương trình BIOS chỉ quản lý được tới FFFFF, mà vùng nhớ cao (High memory từ A0000 trở lên) dùng để ánh xạ (map) BIOS, Video card memory và các thiết bị ngoại vi khác, vùng nhớ còn xài được (Low memory) là từ 9FFFF trở xuống. Dùng calculator đổi 9FFFF ra decimal bạn sẽ có đúng 640K . Chắc bạn ngạc nhiên hỏi rằng cây RAM 512M mua hết $70 của tôi biến đâu mất rồi??? Hihihi nó bây giờ teo lại còn có 1 page 64K trong vùng nhớ cao. Tuy nhiên bạn có thể thay đổi ánh xạ để truy xuất hết 512M. RAM card màn hình cũng tương tự như vậy. Ở chế độ bảo vệ (protect mode) của CPU 32bít đưa ra khái niệm virtual memory (bộ nhớ ảo). Lúc này mỗi process được cấp cho 4G virtual memory từ 00000000-FFFFFFFF. Nhưng kernel sẽ giữ 1 table mô tả ánh xạ từng page của virtual memory với physical memory. Physical memory bây giờ bao gồm cả RAM và swap disk space. Tất nhiên là 4G virtual memory không bao giờ được ánh xạ đầy đủ (ánh xạ hết lấy gì cho mấy process khác chạy). Phần lớn mặc dù có đánh địa chỉ , nhưng chỉ khi bạn đọc hoặc ghi lên đó thì kernel mới allocate từ physical memory.

    3. Hệ thống file ảo (Virtual File System – VFS)

    Hệ thống này không chỉ cung cấp truy xuất đến hệ thống file trên harddisk mà còn cho tất cả các thiết bị ngoại vi. Nếu như Triump tất cả là thời trang thì ta có thể nói ở Linux tất cả là file. Ý tưởng này bắt nguồn từ Unix và các hệ điều hành sau này điều thiết lập theo hướng đấy. Đừng quên là trong DOS bạn dùng copy xxx con để in file xxx ra màn hình. Khi đó “con “ là filehandler cho thiết bị xuất chuẩn (console).

    4. Giao diện mạng (Network Interface-NET).

    Linux dựng sẵn TCP/IP trong kernel. Do chưa hiểu phần này nên để trống, sau sẽ viết tiếp

    5. Bộ truyền thông nội bộ (Inter-process communication IPC)

    Cung cấp các phương tiện truyền thông giữa các tiến trình trong cùng hệ thống Linux. Chúng ta sẽ cùng nhau tìm hiểu sau.


    Các cấu trúc dữ liệu hệ thống.
    Hệ điều hành Linux hoạt động nhờ vào các dữ liệu này
    1. Task list (Danh sách tác vụ)
    SCHED lưu 1 bộ dữ liệu cho mỗi tiến trình đang hoạt động. Các bộ dữ liệu này làm thành 1 danh sách liên kết gọi là danh sách tác vụ. SCHED còn có 1 con trỏ current để chỉ tác vụ nào đang active. Theo tôi nghĩ thì các dữ liệu này phải có các giá trị của các thanh ghi của process đó ngay lúc nó bị switch. Khi một tiến trình được active trở phải SCHED sẽ khôi phục các giá trị này.

    2. Memory map(Ánh xạ bộ nhớ)
    Như giải thích ở trên MM cần 1 ánh xạ từ bộ nhớ vật lý cho bộ nhớ ảo 4G của mỗi tiến trình. Ngoài ra còn các thông tin để chỉ cách lấy và thay cho từng trang cụ thể. Tất cả các thông tin này chứa trong memory map và memory map được chứa trong trong task list.

    3. I-nodes
    VFS dùng i-nodes để định vị các file. Cấu trúc dữ liệu i-nodes dùng để ánh xạ các file block thành các địa chỉ vật lý ở trường hợp đĩa cứng và đĩa mềm là các sector, cyclinder và head.

    4. Data connection
    Mô tả network connection đang mở

    Tất cả các cấu trúc dữ liệu này đều bắt nguồn từ task list. Mỗi 1 process có một con trỏ chỉ tới cấu trúc memory map, 1 con trỏ chỉ tới danh sách các i-node của các file đang mở cho riêng process đó, và 1 con trỏ chỉ tới danh sách các data connection cho tất cả các network connection đang mở.


    Hình vẽ trên mô phỏng kiến trúc nhân Linux. Trên cùng là giao diện lời gọi hệ thống (read, write,...). Bốn lớp ở giữa chính là phần cơ bản của nhân Linux, bao gồm Quản lý tiến trình, Quản lý bộ nhớ, Hệ thống file ảo và Ngăn xếp mạng. Hai lớp ở tầng dưới cùng là Driver thiết bị (chiếm phần lớn nhân!) và Mã lệnh phụ thuộc kiến trúc (Architecture-dependent code - ADP). Chính ADP chính là phần mã thay đổi theo từng kiến trúc phần cứng cụ thể, do đó khi mang nhân Linux sử dụng cho các phần cứng khác nhau, chúng ta chỉ cần thay đổi phần ADP rồi biên dịch lại. Đây là tính khả chuyển của Linux.

    **************************
    Cấu trúc của SCHED
    Bây giờ ai cũng biết đây là bộ phận trung tâm của hệ điều hành. Nó chịu trách nhiệm chia sẽ thời gian xử dụng CPU cho tất cả các process , process bình thường cũng như các tiểu hệ thống.

    SCHED được chia thành 4 module
    1. Module luật định thời (scheduling policy): chịu trách nhiệm phân xử xem process nào được quyền truy xuất CPU. Hệ thống hoạt động có thông suốt hay không nhờ vào bộ luật này, tránh trường hợp 1 process lợi dụng sơ hở của điều luật mà chiếm thời gian hệ thống qua nhiều làm các process khác bị đóng băng (freeze)
    2. Module phụ thuộc kiến trúc (architeture-specific): module này gồm các code assembly phụ thuộc vào mỗi loại CPU dùng để suspend hay assume process.
    3. Module độc lập kiến trúc (architeture-independent): module gọi các hàm từ module phụ thuộc kiến trúc và module luật để switch giửa các process đồng thời nó còn gọi các hàm ở MM để thiết lập virtual memory cho các process được resume. Nên nhớ module phụ thuộc kiến trúc sẽ khác nhau ở mỗi loại CPU (ỉ386, apha, v.v) nhưng module độc lập kiến trúc thì không đổi kĩ thuật này ai lập trình hướng đối tượng sẽ biết nó là abstract
    4. Module hàm gọi hệ thống (system call). Gồm các hàm mà user có thể dùng để tương tác với SCHED. Ai lập trình Linux và Unix sẽ quen với các system call này.

    Cấu trúc dữ liệu
    Task list: chứa dữ liệu đủ để suspend và resume 1 process. Ngoài ra còn có các dữliệu dùng để thống kê trạng thái hệ thống. Các dữ liệu này được public. Các bạn có thể dùng nó để phân tích hệ thống đang chạy
    Cấu trúc Memory Map
    MM chịu trách nhiệm điểu khiển tiến trình truy xuất tài nguyên bộ nhớ. Bản thân CPU cũng có một hệ thống quản lý bộ nhớ vật lý mà cho phép ánh xạ giửa bộ nhớ process với bộ nhớ vật lý. MM phải lưu trữ ánh xạ này cho từng process. Thêm vào đó MM còn cho phép swap; nó sẽ di chuyển những trang bộ nhớ không dùng xuống ổ cứng cho phép PC dùng bộ nhớ RAM còn trống.

    Memory Map có 3 module
    1. Module phụ thuộc kiến trúc: code gọi các lệnh của hệ thống quản lý bộ nhớ của CPU
    2. Module độc lập kiến trúc: ánh xạ cho từng process và swap bộ nhớ ảo. Nó cũng quyết định xem phải remove trang nào , load trang nào. Các lập trình viên Linux không thiết kế 1 module policy riêng vì policy cho MM sẽ không thay đổi.
    3. System call cho phép các process tác động lên MM bao gồm xin cấp phát vùng nhớ

    Cấu trúc VFS


    VFS thiết kế để thống nhất cách truy xuất tất cả các thiết bị phần cứng. Ngoài ra VFS còn chịu trách nhiệm load các chương trình thực thi.

    Modules
    1. Các module thiết bị điều kiển, mỗi một phần cứng sẽ có 1 module thiết bị điều kiển riêng, cái này thường gọi là driver. Linux cũng như các hệ điều hành khác cho phép thêm vào 1 driver mới.
    2. Module giao diện độc lập thiết bị. Cái này là abstract cho tất cả các driver
    3. Các module hệ thống file logic: Trên thực tế có nhiều hệ thống fiel khác nhau, mỗi hệ thống file có một module điều khiển riêng.
    4. Module độc lập hệ thống file: Cái này là abstract không những cho các loại file system mà còn cho tất cả các driver. Ai lập trình driver cho DOS thì có lẽ nhớ chổ này tất cả các driver qui về 2 loại loại block mỗi lần đọc hay ghi đều đi theo block dữ liệu ví dụ nhưng disk và loại kí tự mỗi lần đọc và ghi 1 kí tự như máy in bàn phím v.v
    5. Cuối cùng là system call các hàm gọi hệ thống cho VFS
    Cấu trúc NET

    NET cho phép Linux connect với các hệ thống khác bằng mạng. Ai cũng biết mạng thì rất nhiều loại thiết bị và giao thức mạng. NET abstract tất cả cho phép những phần khác có thể truy xuất qua mạng mà không quan tâm đền các thiết bị và giao thức được sử dụng

    NET có 5 module
    1. Các Driver cho thiết bị mạng, mỗi module cho mỗi loại thiết bị
    2. Module độc lập thiết bị: abstract tất cả các thiết bị mạng
    3. Các Module giao thức mạng: mỗi module cho mỗi loại giao thức truyền
    4. Module độc lập giao thức mạng: abstract cho tất cả các module giao thức mạng và các driver.
    5. System call
    Booting (khởi động)Ở đây ta chỉ phân tích cho hệ thống máy tính PC i386 còn các loại khác thì không biết
    Có 6 bước khởi động máy tính
    1. Chương trình BIOS chọn thiết bị boot
    2. Chương trình BIOS đọc bootsector từ thiết bị boot lên bộ nhớ
    3. Quyền điều khiển chuyển qua cho chương trình bootsector, chương trình này đọc tiếp phần setup , các chương trình giải nén và kernel image đã được nén
    4. Kernel sẽ được giải nén ở trong protected mode
    5. Khởi động mức thấp bởi assembly code
    6. Khởi động mức cao cho C code
    Bước 1: BIOS POST (Khởi động của BIOS)
    Theo bản thiết kế đầu tiên của máy tính – máy Turing, máy tính là 1 thiết bị cho phép chạy đúng 1 chương trình thôi. Các máy tinh Casio chẳng hạn cũng chạy 1 chương trình calculator. Các máy tinh chạy băng giấy cũng vậy bạn nạp chương trình bằng tay vào chạy kết thúc rồi lại nạp chương trình khác. Bản thân máy PC Pent4 hiện đại nhất cũng vậy cũng chỉ cho phép chạy đúng 1 chương trình thôi. Tuy nhiên chương trình này có khả năng nạp những chương trình khác lớn hơn phức tạp (khác với việc nạp tay như hồi xưa) hơn rồi chuyển quyền điều khiển cho chúng để tiếp tục những dòng lệnh thực thi không ngừng cho đến khi tắt máy tính. Chương trình ban đầu này được gọi là chương trình khởi động, để thuận tiện người ta nạp nó vào con BIOS và gắn chung vào hệ thống PC.
    Mục đích của chương trình khởi động là nạp cho được chương trình hệ điều hành (OS). OS sẽ là chương trình lập vô tận (infinite loop) nó chờ lệnh của user để nạp các chương trình ứng dụng , khi các chương trình ứng dụng kết thúc thì quyền thực thị trả về cho OS ... OS lại tiếp tục chờ để nạp chương trình khác. Vậy từ khả năng chỉ chạy được 1 chương trình người ta đã phát minh ra hệ điều hành là 1 chương trình cho phép nạp tự động những chương trình khác giúp cho máy tính có khả năng to lớn hơn.
    BIOS -----> OS --------> Application
    ...................|____________|

    a. Khi bật điện, Bộ nguồn sẽ chạy bộ tạo xung (đồng hồ nhịp - tốc độ Hetz của memboard phục thuộc vào đồng hồ này), và tín hiệu POWERGOOD được gởi vào bus báo cho các thiết bị trong PC.
    b. Đồng thời đường #RESET của CPU on, CPU khởi động ở real mode(8086)
    c. Các thanh ghi %ds=%es=%fs=%gs=0, %cs=0xFFFF, %ip=FFF0
    d. Chương trình kiểm tra thiết bị chạy (giá trị RAM chạy vèo vèo trên màn hình)
    e. Bảng interrupt được khởi động tại 0
    f. Chương trình BIOS Bootstrap Loader chạy qua int 0x19 %dl=dsố hiệu ổ đĩa khởi động. Chương trình này tải track 0 sector 1 (boot sector) lên địa chỉ 0x7C00

    Kết luận:


    Hột nhưng Linux được xây dựng theo các khái niệm hệ điều hành thông thường thôi, tuy nhiên các programmer của Linux cố gắng phân chia thành nhiều lớp, Lớp sau là abstract cho lớp trước. Cách này cho phép nhiều người khác nhau cùng làm việc trên các phần khác nhau mà hệ thống vẩn đảm bảo tính thống nhất và ổn định.

    Okê phần này chủ yếu dịch từ Conceptual Architecture of the Linux Kernel http://plg.uwaterloo.ca/~itbowman/CS746G/a1/. Tức là cấu trúc khái niệm. Chúng ta sẽ đi sâu vào chi tiết ở các phần sau.

    Giài thích:

    Ở đây là những từ kĩ thuật về cấu trúc máy tính , tôi không tiện giải thích cặn kẽ vì sẽ tốn rất nhiều thời gian.

    Bootsector và phần setupĐĩa cứng và đĩa mềm lưu trữ ghi dữ liệu thành từng rãnh (track) là những vòng tròn đồng tâm. Đầu từ của ổ đĩa không đọc hay ghi 1 bit hay 1 byte dữ liệu mà là 1 đoạn trên 1 track, học hình học ai cũng biết 1 đoạn trên 1 đường tròn gọi là cung (sector). Mỗi sector thường lưu trữ được 512 bytes.
    Track đầu tiên còn gọi là track 0 trên đĩa mềm được định vị bằng 1 lổ tròn to tướng. Sector đầu tiên sector 1 của track 0 (đánh số hơi bị kì nhưng phải nhớ) gọi là bootsector vì nó sẽ được BIOS tải lên khi boot. Toàn bộ chương trình khởi động mềm 512bytes nằm trong sector này.

    Bây giờ hãy download source code linux kernel 2.4 từ site www.kernel.org khoảng 34M không thôi chép từ đĩa cài đặt Linux cũng được.
    Mở file: arch/i386/boot/bootsect.S. Đây là source code viết bằng ngôn ngữ assembly cho Linux bootsector .

    Ở đây các programmer Linux chú thích khá kĩ
    1. Chép 512 bytes bootsector từ vị trí khởi đầu mà BIOS POST tải lên 0x7C0:0 đến ví trí cuối cùng của vùng nhớ qui ước 0x9000:0, rồi jmp tới đó. Chổ này chưa hiểu ý định của người lập trình vì chương trình khởi động của DOS không làm vậy. Có lẽ vùng nhớ 0x7C00 nhanh chóng sẽ bị chép đè bởi các chương trình tải lên sau.
    2. Stack được khỏi động tại khoảng giữa của segment 0x9000
    3. Khởi tạo bảng tham số ổ dĩa mới (disk parameters table). Phần cứng của ổ đĩa hoạt động phụ thuộc bảng tham số này. Mặc định có bảng tham số nằm trong BIOS, tuy nhiên nhiều BIOS chỉ set chế độ đọc từng sector. Linux programmer set lại chế độ đọc nhiều sector cũng lúc để tăng tốc độ tải HĐH. Bảng tham số này được chép về ngay dưới đoạn stack (stack chạy ngược lên nên bảo đảm không đụng hàng) gồm 12 bytes. Bytes 0x4 là dữ liệu chứa số sector maxium đọc cùng 1 lúc được patch thành 36 36 là số sector trong 1 track của ổ đĩa 2.88M, set cao như vậy nhưng tuy loại ổ đĩa giá trị thực tế sẽ khác; trích từ chú thích của người lập trình high doesn’t hurt but low does.
    4. Tính toán số sector trên 1 track. Hồi xa xưa đây là phần nhức đầu với các bác viết bootvirus. Gần như không có cách chính xác cái này với cái xác định FAT12 và FAT16. Tuy nhiên cách giải quyết của mấy tay Linux programmer này cũng hay. Lần lựa đọc các sector cao nhất của ổ đĩa. 36 là 2.88M. 18 là 1.44M thường dùng. 15 là 1.2M đĩa to như bánh tráng và 9 là 760K cũng to như vậy như chỉ có 1 mặt thôi. Có điều nếu đĩa 1.44M bị lỗi tại sector 18, chẳng lẽ Linux lại hiểu nó là 1.2M sao smilie, mà kệ hỏng bất cứ sector nào ở track 0 thì coi như vứt.
    Lúc này Linux sẽ in dòng chử : Loading lên màn hình
    5. Chương trình setup của Linux nằm ở các sector tiếp theo bootsector sẽ được tải lên ngay sau bootsector: 0x90200. Số sector cho phần setup mặc định là 4. Mỗi lần đọc 1 track nó in dấu chấm ra màn hình.
    6. Tiếp theo chương trình system ở dạng nén(compressed kernel image) sẽ được tải ở vị trí 0x10000 - chừa 64K low memory. Kích thước mặc định là 0x7F00 tính theo đơn vị 16-bytes = 508K. Linux còn có chể độ big-kernel khi này kernel image sẽ vượt quá bộ nhớ qui ước 640K. Khi đó trong code của phần setup có hàm bootsect_kludge nằm tại offset 0x220 chịu trách nhiệm tải kernel vào bộ nhớ cao.
    7. Kết thúc quá trình tải setup và system, chương trình setup sẽ được thực hiện tại segment 0x9020
    Mở file: arch/i386/boot/setup.S. Đây là source code viết bằng ngôn ngữ assembly cho Linux setup.
    1. Phần setup này có thể được tải bởi các loader khác nhau bootsect.S chỉ là loader chuẩn của Linux thôi ngoài ra còn còn LILO hay Loadin v.v. Mở đầu setup check xem nó có được tải đầy đủ không. Nên không nó sẽ cố gắng tải lại cho đủ. Nếu vẫn không được nó đành báo lỗi loader sai. Pó tay ...
    2. Kiểm tra kích thước bộ nhớ. Nhưng ta đã đề cập bộ nhớ cao của máy tính (từ 640K trở lên) khá phức tạp. Linux dùng 3 cách khác nhau để detect. Cách đầu tiên là E820h, dùng hàm ax=E820, int 15h để kiểm tra. Nếu thất bại dùng hàm ax=E801h, int 15h. Cuối cùng là dùng cách ax=8800h, int 15h đây là cách cổ điển chỉ cho memory dưới 64M.
    3. Tiếp theo setup gọi hàm video nằm trong file video.S đây là code assembly để detect chế độ đồ hoạ
    4. Lấy disk parameter table của harddisks. Các bảng này nằm trong BIOS, trong quá trình BIOS detect thiết bị hay là bạn set tay trong bảng BIOS. Sau quá trình BIOS POST, vị trí của các bảng này được đặt tại 0smilie4 * 41h) cho hd0 và 0smilie4 * 46h) cho hd1.
    5. Check MCA bus bằng hàm c0h của int 15h
    6. Check con chuột PS/2 dùng int 11h
    7. Check APM bios: cách này không biết
    8. Thiết lập protected mode. Chuyển từ real mode sang protected mode

    Protected mode.
    Thật khó để định nghĩa protected mode là cái gì. Chúng ta quay trở lại thời kì CPU 8086. 8086 bao gồm các thanh ghi 16 bít (2 bytes).
    AX: Accumulate thanh ghi tích lũy vì ax thường lưu giữ kết quả các phép tính toán học.
    BX: Base thanh ghi cơ sở vì bx thường dùng để định vị [bx+??]
    CX: Count thanh ghi đếm vì cx thường dùng chứa số đếm trong các lệnh loop rep
    DX: Data thanh ghi dữ liệu vì dx thường chứa dữ liệu trong phép tinh toán học
    SI,DI: source index, destination index-thanh ghi chỉ mục địa chỉ nguồn và địa chỉ đích
    SP: stack pointer: thanh ghi con trỏ stack
    IP: intruction pointer: thanh ghi con trỏ lệnh
    BP: base pointer: thanh ghi con trỏ cơ sở dùng định stack frame trong cấu trúc ngôn ngử bậc cao.
    CS: code segment: thanh ghi đoạn lệnh
    DS: data segment: thanh ghi đoạn dữ liệu
    ES: extra segment: thanh ghi đoạn dữ liệu
    SS: stack segment: thanh ghi đoạn stack

    Để xác định 1 vị trí trong bộ nhớ cần 2 cặp thanh ghi seg và index:
    CS:IP con trỏ đến code sắp thi hành
    DS:SI: con trỏ địa chỉ dữ liệu nguồn
    ESsmilieI: con trỏ địa chỉ dữ liệu đích
    SS:SP: con trỏ stack
    SS:BP: con trỏ stack frame

    8086 sẽ không phân biệt các đâu trên bộ nhớ là code, data hay stack. Nếu CS=DS thì code cũng là data mà data cũng là code. Ngoài ra không có hạn chế gì, bạn tự do đọc thi hành hay thay đổi code, dữ liệu cũng tất cả trong memory. vị dụ kernel của DOS được tải lên ở vùng nhớ 0x40000, bảng Interupt 0x0, đều có thể thay đổi bởi bất cứ 1 chương trình bình thường nào. Rõ ràng 8086 chỉ thích hợp cho hệ thống 1 người dùng.

    Bắt đầu từ 80286 CPU 16 bít protected mode rồi 80386 CPU 32 bít protected mode. Tất cả các dòng CPU Intel sau này cho đến Pent IV đều chung kiến trúc protected nên người ta gọi là dòng i386.

    Ở chế độ protected mode, bộ nhớ máy tính được bảo vệ chặt chẽ việc truy xuất được phần cứng CPU kiểm soát. Lúc này 1 địa chỉ được xác định bởi 1 thanh ghi segment selector 16 bít và chỉ mục 32 bít.. Có 6 thanh ghi segment selector cs,ss,ds,es,gs,fs.Điểm khác của thanh ghi segment selector và thanh ghi segment ở 8086 ở chổ thanh ghi segment xác định trực tiếp vùng nhớ còn thanh ghi segment selector lại xác định 1 segment descriptor (mô tả đoạn) trong bảng mô tả. Rồi giá trị trong segment descriptor mới giúp chúng ta xác định bộ nhớ vật lý.
    Bảng mô tả bộ nhớ:
    Bộ nhớ máy tính được chia nhỏ thành nhiều đoạn. Cách thức chia bộ nhớ được mô tả trong 1 bảng mô tả (Nếu bạn không hình dung được thì hãy nhớ đến việc chia đĩa cứng thành nhiều đĩa logic cần bảng partion , bảng này mô tả cách thức chia đĩa). Có 2 loại bảng mô tả - GDT(Global Descriptior Table) và LDT(Local Descriptor Table). Chỉ có 1 bảng GDT được build trong bộ nhớ và vị trí của bảng này được chứa trong thanh ghi gdtr của CPU. Mỗi process sẽ có 1 bảng LDT riêng của nó mô tả cách phân chia riêng cho process đó -tất nhiên cho vùng nhớ mà nó có quyền thôi. Địa chỉ LDT nằm trong thanh ghi ldtr. Mỗi khi swap process ta chỉ cần load lại ldtr thì sẽ có phân vùng bộ nhớ cho process đó ...
    Mỗi một đoạn được mô tả bởi 8 bytes trong bảng mô tả. Bao gồm:
    1. 32 bít Base: là địa chỉ phẳng của byte đầu tiên của đoạn trong vùng nhớ 4G
    2. Cờ G 1 bít: là các định kích thước đoạn 0 là tính theo byte, 1 là tính theo 4096 bytes
    3. 20 bít Limit: là kích thước đoạn nếu G=0 thì đoạn có size=1bytes-1Mbyte(2^20) nếu G=1 thì đoạn có size=4K-4G
    4. Cờ S 1 bít: S=0: đoạn kernel, S=1: đoạn bình thường
    5. 4 bít Type: có các type sau đây: Code, Data, Task State đây là đoạn đặc biệt chỉ có trong GDT dùng để chỉ nơi chứa các dữ liệu liên quan các task. Khi SCHED muốn swap task sẽ đọc trong đây là phục hồi các giá trị của task. Local Descriptor Table đây cũng là đoạn đặt biệt chuyên chưa các LDT của các task và chỉ có trong GDT.
    6. 2 bít DPL: Quyền thấp nhất được truy xuất đoạn này. CPU i386 cho 4 mức quyền 0 là cao nhất tương đương với kernel. Còn 3 là User Application. Nếu DPL cho 1 segment là 0 thì user application không thể truy xuất vào đoạn này.
    7. Các bít còn lại không dùng.
    Các thanh ghi segment selector (cs,ds,ss,es,gs,fs)
    Mỗi khi 1 giá trị được set trong 1 thanh ghi segment selector thì CPU sẽ tìm 8 bytes mô tả tương ứng của selector tải vào thanh ghi nội bộ. Vì vậy việc chuyển địa chỉ không cần truy xuất GDT hay LDT 1 cách thường xuyên. Mỗi thanh ghi segment selector có 16 bít trong đó:
    1. 13 bít chỉ mục:chỉ 1 mục trong GDT hay LDT
    2. Cờ 1 bit TI : 0: đoạn này trong GDT, 1: đoạn này trong LDT
    3. 2 bít RPL: Từ 0-3 chỉ mức yêu cầu truy cập đoạn này chỉ có tác dụng với CS Theo tài liệu Intel thì khi 1 process set thanh ghi CS thì RPL có thể nạp với những mức thấp hơn hay bằng mức mà process có quyền. Theo tôi hiểu thì như vậy 1 process kernel có thể chạy 1 đoạn code với quyền thấp hơn ví dụ set RPL=3 chẳng hạn.

    Vậy 1 địa chỉ sẽ là Base của đoạn đó + giá trị của thanh ghi chỉ mục (ebx,esi,edi v.v) nhưng theo tôi thường thấy các chương trình 32bít thường set base của tất cả các đoạn =0 hết.
    Mục đầu tiên của bảng mô tả luôn toàn là số 0, vì vậy nếu 1 segment selector =0 thì nó sẽ invalid. Do 1 segment selector có 13 bít chỉ mục nên số segment trong bảng mô tả tối đa là 2^13 1 = 8191.

    Linux Protected Mode.

    Nếu bạn choáng về sự phức tạp của chế độ protected mode (thực ra còn nhiều nữa), thì may mắn cho bạn cho tôi và cho cả lập trình viên Linux là không ai mà implement hết. Giống Windows, Linux cũng làm protected mode đơn giản thôi chỉ có 2 mức 0 cho Kernel và 3 cho User Application. Vấn đề là tương thích với các kiến trúc CPU khác nữa. Tất cả các phân đoạn của Linux dùng GDT không cần LDT. Có duy nhất 1 bảng GDT bao gồm:
    1. Null
    2. Kernel Code Segment: Chứa code của kernel. Base=0. Limit=0xFFFF. G=1. S=1. Type=A có thể đọc và thi hành (không modify được). DPL=0 Kernel mode only.
    3. Kernel Data Segment: Chứa dữ liệu kernel. Base=0. Limit=0xFFFF.G=1.S=1. Type=2 có thể đọc và ghi (không thi hành được). DPL=0
    4. User Code Segment: Chứa code segment cho tất cả user proccess. Base=0. Limit=0xFFFF.G=1.S=1. Type=A. DPL=3
    5. User Data Segment: Chứa data segment cho tất cả user process. Base=0. Limit=0xFFFF.G=1.S=1. Type=2. DPL=3
    6. Default null LDT
    7. Ngoài ra còn 4 segment cho APM (advenced power mangement) , 4 segment để dành.
    8. Mỗi process sẽ có 1 TSS Segment ở đây: Base lúc này sẽ là địa chỉ của bảng ghi process đó. G=0. Limit=236 vậy mỗi đoạn sẽ có 236 bytes. DPL=0 kernel only. Mỗi process có 1 LDT segment. Default là null để chỉ rằng không dùng LDT. Ai thích dùng thì phải tự viết. GDT có tối đa 2^13 là 8192 mục. Trừ 14 mục system là còn 8178 /2= 4089 process. Như vậy Linux không thể có hơn 4089 proccess.
    Ebook tham khảo:

    Ref Links Code:
    còn tiếp
    Đã được chỉnh sửa lần cuối bởi AlexF : 09-03-2011 lúc 05:18 PM.

  2. #2
    Ngày gia nhập
    02 2008
    Bài viết
    1,009

    II) Phương Pháp Cài Driver Cho Linux Bằng Tay
    1) Trước hết, húng ta sẽ đi tới hiểu 1 Linux device driver là gì ?
    --> Device driver đóng một vai trò quan trọng trong Linux kernel, chúng như những “hộp đen” làm cho phần cứng thực thi những hàm API được định nghĩa sẵn trong kernel.
    Device driver được hiện thực trong Linux kernel thông qua các loadable module. Loadable modules là các đoạn chương trình không cần liên kết tĩnh vào kernel image mà có thể liên kết động, nhờ vậy mà cái driver sẽ được load vào kernel chỉ khi cần thiết. Để có thể link một module vào kernel ta gọi lệnh insmod, ngược lại để tháo một module ra khỏi kernel ta dùng lệnh rmmod. Đối với những loại device driver ở dạng stack (driver này sử dụng các dịch vụ của một driver khác), thay vì phải load thủ công với insmod ta có thể gọi modprobe, hàm này sẽ tự động liên kết các module theo đúng thứ tự bằng cách tra /lib/modules/kernel_version/modules.dep. Hàm modprobe -r có thể dùng thay thế cho rmmod.Vì thế, để load 1 driver của 1 thiết bị lên, người ta hay dùng lệnh modprobe( sẽ dễ hơn rất nhiều khi phải load bằng lệnh insmod)
    Linux phân chia device thành ba nhóm chính:
    --- Character device: truy cập vào device để thể hiện như một byte stream, và truy cập là tuần tự, driver của character device có nhiệm vụ phải hiện thực chức năng này.
    --- Block device: thao tác IO thực hiện theo block (thường là 512 bytes/block).
    --- Network device: thể hiện dưới dạng các interface, các interface có thể là một NIC (Network Interface Card) hoặc một interface ảo (loopback interface), driver của network device chủ yếu xử lý dữ liệu theo packets mà không cần quan tâm đến các tầng cao hơn như connection của TCP.

    Ngoại trừ network device, các device khác được truy cập thông qua một loại file đặc biệt gọi là device file. Device file không giống như file thông thường khác ở điểm các truy cập vào device file sẽ được thực hiện bởi các hàm tương ứng do driver cung cấp thay vì truy xuất đĩa như các file thông thường.
    Để tạo một device file chúng ta dùng lệnh mknod và cần quan tâm đến 3 thông số: device là character device hay block device, major number và minor number của device, để remove một device file chúng ta dùng lệnh rm như cho file thông thường. Thông số major number chỉ ra device file này do driver nào quản lý. Chúng ta có thể tra các major number đã cấp phát sẵn trong file /linux/Document/devices.txt, khi viết một driver mới và nếu đăng ký thành công vào source tree của kernel chúng ta sẽ được cấp 1 major number ứng với driver của mình. Nếu chỉ cần viết driver cho thiết bị của mình mà không cần đăng ký vào source tree thì cách thức đơn giản nhất là cấp phát major number động. File /proc/device sẽ chứa thông tin về các character deviceblock device có trong hệ thống cùng với major number tương ứng, chúng ta có thể dùng file này để tra major number nếu cấp phát major number động và dùng kết quả này để tạo device file phục vụ việc truy cập. Minor number có ý nghĩa khác nhau đối với mỗi loại device, ví dụ như với ổ đĩa minor number sẽ tương ứng với các partition còn đối với các device như serial port minor number lại dùng để chỉ port vật lý thứ mấy.
    Nhầm mục đích tiện lợi chúng ta có thể viết một script đơn giản để load và unload các module

    PHP Code:
    #!/bin/sh
    # load module script
    module="my_driver"
    device="my_device"
    mode="664"

    if grep -'^staff:' /etc/groupthen
        group
    ="staff"
    else
       
    group="wheel"
    fi

    # insmode or exit on error
    /sbin/insmod ./$module.ko $* || exit 1

    # find driver major number
    major=$(awk "\$2==\"$module\" {print \$1}" /proc/devices)
    minor=0

    # create device file
    rm -/dev/${device}0
    mknod 
    /dev/${device}0 c $major $minor

    chgrp $group 
    /dev/${device}
    chmod $mode  
    /dev/${device}0
    #!/bin/sh
    # unload module script
    module="my_driver"
    device="my_device"

    # invoke rmmod with all arguments we got
    /sbin/rmmod $module $* || exit 1

    # delete device file
    rm -/dev/${device}

    còn tiếp

  3. #3
    Ngày gia nhập
    02 2008
    Bài viết
    1,009

    III) Hướng dẫn viết module cho Linux

    Như đã biết, Linux là 1 hệ điều hành có tính module rất cao, cho phép người dùng có thể chỉnh sửa các module, driver cần thiết tương ứng với thiết bị đang sử dụng

    Đối với ng dùng bình thường, modularity cho phép chọn lựa cách biên dịch các drivers của nhân theo dạng modules hay theo dạng biên dịch trực tiếp vào nhân.

    Có những "driver" không thể biên dịch như một module vì nó phải được load and link trực tiếp ngay khi nhân khởi động . Cũng có những "driver" cho phép chọn như một module và được tải trong khi và sau khi nhân được khởi động. Điểm chính yếu cần nắm là khi nào nên chọn M (cho module), Y (cho biên dịch trực tiếp) và N (không dùng) các drivers này.

    Biên dịch trực tiếp nghĩa là các driver có dùng hay không cũng đều được load ngày từ khi hđh khởi động, tất nhiên nó sẽ chiếm 1 phần memory. Lợi điểm là "tính trung thực" của nhân và driver. Các hệ thống bảo mật cao thường biên dịch trực tiếp để tránh các module "lạ" bị cài vào nhân trong quá trình hoạt động của máy.

    Biên dịch module nghĩa là biên dịch các driver như những module, chỉ khi cần sử dụng mới được tải vào nhân. Lợi điểm của phương pháp này là hiệu quả sử dụng tài nguyên. Bạn có thể tạo ra 1 nhân rất nhỏ gọn và thuận tiện trong việc biên dịch lại 1 số module nào đó (thay vì phải biên dịch lại toàn bộ nhân).

    Các bạn nào muốn biết thêm về biên dịch nhân và cách viết driver và biên dịch driver cho Linux (cơ bản là cho máy vi tính nhé) thì tham khảo các tài liệu sau:

    Biên dịch nhân linux - Hoàng Ngọc Diệu
    Linux device driver - Third Edition - Jonathan Corbet

    Hoặc vào website http://kernelnewbies.org/ để làm quen với kernel.


    Nào Cùng Viết 1 Module loadable thui

    PHP Code:
    // hello_world_module.c
    #include <linux/init.h>
    #include <linux/module.h>
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR("abc@googlegroups.com"); 
    MODULE_DESCRIPTION("Hello world module");
    MODULE_VERSION("v1.0");

    static 
    char *whom "world";
    module_param(whomcharpS_IRUGO);
    MODULE_PARM_DESC(whom"Say hello to whom");

    static 
    int array[4];
    static 
    int array_length;
    module_param_array(array, int, &array_lengthS_IRUGO);
    MODULE_PARM_DESC(array, "array of 4 integers");

    static 
    int hello_init(void)
    {
        
    printk(KERN_ALERT "Hello, %s\n"whom);
        return 
    0;
    }
    static 
    void hello_exit(void)
    {
        
    printk(KERN_ALERT "Goodbye, %s\n"whom);
    }
    module_init(hello_init);
    module_exit(hello_exit); 

    Module này không làm việc gì khác ngoài gọi lệnh printk (tương đương printf trong user space, printk chạy trong kernel space), nội dung được in ra file /var/log/messges và có thể được xem bằng lệnh dmesg. Xem xét module này chúng ta thấy được một module căn bản nhất cần include 2 file <linux/init.h> và <linux/module.h>, và cần hiện thực ít nhất 2 hàm module_init và module_exit. Hàm module_init sẽ được gọi khi lệnh insmod thực thi và hàm module_exit sẽ được gọi khi chúng ta rmmod module. Linux kernel cung cấp nhiều macro cho module, sau đây là một số macro thường gặp:

    Code:
    module_init(hello_init);
    module_exit(hello_exit); 
    MODULE_LICENSE("Dual BSD/GPL");
    MODULE_AUTHOR(“author”); 
    MODULE_DESCRIPTION(“description”);
    MODULE_VERSION(“version”);

    Các thông tin này sau khi biên dịch module thành file .ko có thể được xem với lệnh:
    @host:# modinfo ./hello_world.ko
    filename: ./hello_world.ko
    version: v1.0
    description: Hello world module
    author: abc@googlegroups.com
    license: Dual BSD/GPL
    srcversion: D18FE01C06E9DB883BF5E18
    depends:
    vermagic: 2.6.32.2-FriendlyARM mod_unload ARMv4
    parm: whom:Say hello to whom (charp)
    parm: array:array of 4 integers (array of int)

    Module còn có thể nhận biến được truyền từ lệnh insmod với macro

    Code:
    #include <linux/moduleparam.h>
    module_param(variable_name, type, permission);
    module_param_array(name,type,num,perm);
    Hiện tại các type được hỗ trợ gồm bool, invbool, charp, int, long, short, uint, ulong, ushort. Permission được định nghĩa trong file <linux/stat.h>. Phiên bản khác của module_param là module_param_array cho phép chúng ta truyền vào một mảng các biến thay vì một biến riêng lẻ. Các biến này có thể được truyền vào module thông quan hàm inmode với cú pháp sau
    Code:
    @host:# insmod  my_module my_variable=value
    @host:# insmod ./hello_world.ko whom="foo" array=1,2,3,4
    IV) Phương pháp biên dịch

    Việc đầu tiên cần thiết để compile được module là chúng ta phải có một kernel cùng phiên bản với kernel hệ thống chúng ta sử dụng và kernel này phải được config và compile thành công .
    Có hai cách để compile một loadable module: compile trong kernel source và compile bên ngoài kernel source. Cách thứ nhất có thể dùng khi ta thêm driver vào source tree của kernel và biến nó thành một tùy chọn trong Kconfig. Cách thứ hai có tính linh hoạt hơn khi phát triển driver, nhưng module không phải là một option của kernel source nên chúng ta phải compile riêng và chép file kết quả (file có đuôi .ko) vào hệ thống Linux của mình.
    Hai cách được lần lượt trình bày như sau:

    ---------Compile trong kernel:
    Trong ví dụ này chúng ta giả sử muốn add driver hello_world phía trên của mình vào source tree. Trước hết chúng ta sẽ tạo một directory riêng cho module của mình
    (Chú ý: bạn sẽ nhìn thấy các config của bạn khi gõ make menuconfig )

    @host:# mkdir /linux/driver/hello_world
    Để kernel có thể nhận ra thư mục này chúng ta cần chỉnh sửa 2 file thuộc thư mục cha của hello_world (ở đây là thư mục driver) là Kconfig và Makefile


    @host:# gedit /linux/driver/Kconfig
    Thêm dòng này vào file Kconfig

    source "drivers/hello_world/Kconfig"
    @host:# gedit /linux/driver/Makefile
    Thêm dòng này vào Makefile


    obj-y += hello_world/
    Bây giờ chúng ta phải vào thư mục hello_world và tạo ra 2 file Kconfig và Makefile.


    @host:# gedit /linux/driver/hello_world/Kconfig
    config EXAMPLE_HELLO_WORLD
    tristate "Example driver"
    default n
    help
    Say yes if you want to compile this example driver...
    @host:# gedit /linux/driver/hello_world/Makefile

    obj-$(CONFIG_EXAMPLE_HELLO_WORLD) += hello_world.o
    Sau này khi config kernel chúng ta sẽ thấy module của mình là một option, chúng ta có thể chọn link static vào kernel hoặc tạo thành loadable module hoặc không đưa module này vào kernel.


    Nếu chỉ cần compile module này mà không cần compile kernel, chúng ta có thể dùng lệnh

    @host:# make M=./drivers/hello_world/ modules
    File biên dịch thành công là hello_world.ko đặt trong cùng thư mục với file source hello_world.c


    ----------------Compile bên ngoài kernel:
    Chúng ta cần tạo một Makefile trong cùng thư mục source của module có nội dung như sau:


    @host:# gedit /linux/driver/hello_world/Makefile


    KERNELDIR ?= path_to_kernel
    PWD := $(shell pwd)

    ifneq ($(KERNELRELEASE),)
    #hello_world-objs := file1.c file2.c ... filen.c
    obj-m := hello_world.o

    else
    modules:
    $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
    endif

    clean:
    rm -rf *.o *~ core .depend .*.cmd *.ko *.mod.c .tmp_versions
    Ta thay thế “path_to_kernel” bằng đường dẫn tới thư mục kernel. hello_world.o bằng tên module cần compile và nếu module phụ thuộc vào nhiều file source C thì cần chỉnh thêm objs. Để thực hiện compile đơn giản ta chỉ sử dụng lệnh make tại thư mục chứa source của module.
    Code:

    @host:/linux/driver/hello_world# make







    source code được đính kèm phía dưới
    Attached Thumbnails Attached Thumbnails 5.png  
    Attached Files Attached Files
    Đã được chỉnh sửa lần cuối bởi AlexF : 16-04-2011 lúc 04:15 PM.

  4. #4
    Ngày gia nhập
    12 2010
    Bài viết
    115

    anh ơi anh có sách nào nói về quản lý tiến trình trong linux hong? Nhất là nói về các tín hiệu trong linux. Em tìm trên mạng cũng có rất nhiều,nhưng nó nói về cái này ít quá lại không chi tiết cho lắm.Mong anh giúp đỡ.em cám ơn anh nhiều!!!!!

  5. #5
    Ngày gia nhập
    02 2008
    Bài viết
    1,009

    Trích dẫn Nguyên bản được gửi bởi tetuongrua Xem bài viết
    anh ơi anh có sách nào nói về quản lý tiến trình trong linux hong? Nhất là nói về các tín hiệu trong linux. Em tìm trên mạng cũng có rất nhiều,nhưng nó nói về cái này ít quá lại không chi tiết cho lắm.Mong anh giúp đỡ.em cám ơn anh nhiều!!!!!
    mời bạn đọc 1 chương trong sách này: http://forums.congdongcviet.com/showthread.php?t=50048

  6. #6
    Ngày gia nhập
    12 2010
    Bài viết
    115

    Mặc định Hướng Dẫn Lập Trình Driver Trên Hệ Điều Hành Linux

    Trích dẫn Nguyên bản được gửi bởi AlexF Xem bài viết
    mời bạn đọc 1 chương trong sách này: http://forums.congdongcviet.com/showthread.php?t=50048
    em đã download về rồi,nhưng đang chuẩn bị thi vài môn khác nên chưa xem qua.Thank anh nhiều.

  7. #7
    Ngày gia nhập
    09 2010
    Nơi ở
    Hải Phòng city
    Bài viết
    527

    cám ơn anh AlexF nhiều.
    bài viết của anh cho em nhiều kiến thức cơ bản.
    ****************************************
    *****Trường Sa, Hoàng Sa là của Việt Nam*****
    ****************************************

  8. #8
    Ngày gia nhập
    03 2011
    Bài viết
    6

    thanks bạn AlexF,nhưng giờ bạn đã bị xóa sổ rùi :-??

  9. #9
    Ngày gia nhập
    01 2010
    Nơi ở
    #root(-xD~)
    Bài viết
    5

    Nếu có tài liệu tiếng anh thì tốt quá.
    Mơ về một cuộc sống tươi đẹp
    huongcvit@gmail.com

  10. #10
    Ngày gia nhập
    04 2011
    Bài viết
    2

    Rất hay đúng cái em đang tìm

Các đề tài tương tự

  1. Tool để lập trình linux device driver?
    Gửi bởi bigbang1b0 trong diễn đàn Công cụ, Tài liệu lập trình trên Linux
    Trả lời: 1
    Bài viết cuối: 13-10-2012, 10:48 PM
  2. Muốn học về Linux để phát triển theo hướng mã nguồn mở trên Linux thì phải làm những gì?
    Gửi bởi vncoder trong diễn đàn Thắc mắc lập trình C/C++ trên Linux
    Trả lời: 7
    Bài viết cuối: 06-01-2012, 07:58 AM
  3. Cách load Driver Trong Linux khi có file firmware
    Gửi bởi haian trong diễn đàn Thắc mắc lập trình shell Linux
    Trả lời: 0
    Bài viết cuối: 02-03-2011, 04:19 PM
  4. Xin code về việc viết driver chuột cho linux
    Gửi bởi 5primes trong diễn đàn Thắc mắc lập trình C/C++ trên Linux
    Trả lời: 3
    Bài viết cuối: 01-04-2008, 06:49 AM
  5. Memory Device Driver trong lập trình C trên Linux
    Gửi bởi Xcross87 trong diễn đàn Thắc mắc chung
    Trả lời: 3
    Bài viết cuối: 26-12-2007, 12:42 PM

Quyền hạn của bạn

  • Bạn không thể gửi đề tài mới
  • Bạn không thể gửi bài trả lời
  • Bạn không thể gửi các đính kèm
  • Bạn không thể chỉnh sửa bài viết của bạn