Device context là trong lập trình window là gì

Để phân biệt, khi xài API function trực tiếp, thêm dấu :: đằng trước. Bây giờ khi đã có device context của Static Text. Vì device context này chỉ được release khi thoát chương trình, nên khai báo thêm  m_StaticWnd và m_StaticDC trong phần global.

1.2 – Thay đổi pixel format của device context

Device context cần được thay đổi phù hợp để OpenGL có thể vẽ lên đó.

PIXELFORMATDESCRIPTOR pfd = { sizeof(PIXELFORMATDESCRIPTOR), 1, PFD_DRAW_TO_WINDOW | PFD_SUPPORT_OPENGL | PFD_DOUBLEBUFFER, PFD_TYPE_RGBA, 24, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 32, 0, 0, PFD_MAIN_PLANE, 0, 0, 0, 0 }; 
int PixelFormat = ::ChoosePixelFormat(m_StaticDC, &pfd); 
::SetPixelFormat(m_StaticDC, PixelFormat, &pfd);

Các tham số có thể tham khảo trong MSDN.PFD_DOUBLEBUFFER
– Khi có tham số này, OpenGL không vẽ trực tiếp lên device context, mà sẽ vẽ lên buffer. Muốn hiện buffer này lên màn hình, sẽ swap buffer này với device context, xem bước 2. Nếu OpenGL vẽ trực tiếp lên device context, tốc độ sẽ rất chậm, vì vẽ được cái gì thì sẽ phải update lên màn hình cái đấy ngay.
– PFD_SUPPORT_GDI và PFD_DOUBLEBUFFER là mutually exclusive, nên các thao tác GDI trên device context này sẽ không có tác dụng.

1.3 – Tạo renderring context cho OpenGL

Bước này báo cho OpenGL biết sẽ vẽ lên device context nào.
HGLRC m_OpenGLRC;
// global m_OpenGLRC = wglCreateContext(m_StaticDC); 
wglMakeCurrent(m_StaticDC, m_OpenGLRC);
Muốn OpenGL vẽ lên device context nào, wglMakeCurrent đến device context đó. Rendering context này chỉ được delete khi thoát chương trình, nên khai báo trong phần global.

1.4 – Setup trạng thái cho OpenGL

Điều chỉnh góc nhìn, màu nền,.. như sau:

// xóa đen window 
               glClearColor(0, 0, 0, 0); 
               glClear(GL_COLOR_BUFFER_BIT); 
               glFlush(); 
// chỉnh viewport 
               RECT rect; 
               ::GetWindowRect(m_StaticWnd, &rect); 
               int width = rect.right - rect.left; int height = rect.bottom - rect.top; 
               glViewport(0, 0, width, height); 
// chỉnh góc nhìn 
               double aspect = (double)width/height; 
               glMatrixMode(GL_PROJECTION); 
               glLoadIdentity(); 
               gluPerspective(60, aspect, 0.1, 1000); 
// chọn rendering mode 
               glMatrixMode(GL_MODELVIEW); 
               glLoadIdentity(); glDrawBuffer(GL_BACK); 
               glEnable(GL_DEPTH_TEST); 
               glEnable(GL_BLEND); 
               glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);

Để hiểu rõ, tham khảo thêm MSDN. Nếu size của window bị thay đổi, nên gọi lại phần “chỉnh viewport” và “chỉnh góc nhìn”.

Bước 2 – Update window

Chỉ nên update window khi chương trình ở trong trạng thái idle. Xem ghi chú về idle ở cuối bài viết này.

Render(); // gọi Render SwapBuffers(m_StaticDC); // swap buffer với device context -> hiện buffer lên màn hình

Muốn vẽ cái gì lên window thì vẽ ở Render. Vì phần vẽ này thường rất dài và phức tạp, nên mới được tách ra thành 1 function.

void Render() { 
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT); // xóa toàn bộ
glPushMatrix(); // OpenGL painting commands go here, dài và phức tạp lắm nghe
glPopMatrix(); 
}

glPushMatrix và glPopMatrix dùng để lưu và phục hồi trạng thái của OpenGL. Nếu trong  painting commands có command nào đó làm thay đổi trạng thái của OpenGL, mà Render không lưu và phục hồi trạng thái ban đầu, thì sau vài lần gọi Render, hình vẽ hiển thị lên màn hình sẽ không như ý muốn. Nhớ rằng OpenGL là state machine.

Bước 3 – Dọn dẹp

Khi chương trình chương trình kết thúc, cần release device context và delete rendering context.ReleaseDC(m_StaticWnd, m_StaticDC); // đây là lí do tôi khai m_StaticWnd trong phần global wglMakeCurrent(NULL, NULL); wglDeleteContext(m_OpenGLRC);

Ghi chú về idle

Chương trình Dialog based, xài MFC

Không xài được cả WM_ENTERIDLE lẫn CWinApp::OnIdle() => xài WM_KICKIDLE trong afxpriv.hFile header của dialog, phần message map, thêm:

Trong Linux, Initrd (initial ramdisk) là 1 mô hình để load root file system tạm thời vào bộ nhớ để có thể được sử dụng như là 1 phần của quá trình khởi động Linux. initrdinitramfs là 2 phương thức khác nhau để đạt được điều này. Cả 2 thường được sử dụng để chuẩn bị trước khi root file system thực sự được mount.

Nhiều distro được phân phối cùng với linux kernel tổng quát, là kernel được các lập trình viên của bản phân phối tạo ra để boot trên nhiều phần cứng khác nhau. Các device driver cho kernel image tổng quát này được thêm vào như là LKM (Loadable Kernel Module) bởi vì việc biên dịch tĩnh nhiều driver vào 1 kernel sẽ làm cho kernel image có kích thước lớn hơn, có thể quá lớn để boot trên máy tính có memory giới hạn. Điều này phát sinh vấn đề cần phải phát hiện và load các module cần thiết để mount root file system lúc boot nhưng tại thời điểm này root filesystem là gì và ở đâu?

Ngoài ra, root file system có thể nằm trên RAID volume, LVM, NFS hoặc 1 partition đã được mã hóa. Tất cả điều này yêu cầu 1 sự chuẩn bị đặc biệt (kiểu như kernel phải tích hợp thêm nhiều device driver mới hiểu được) mới mount được.

Một sự phức tạp khác là kernel hỗ trợ hibernation sẽ treo máy tính vào disk bằng cách tạo ra (dumping) 1 image của toàn bộ nội dung bộ nhớ vào 1 swap partition hoặc 1 file thông thường sau đó power off. Khi khởi động lại, image này cần phải truy cập được trước khi nó có thể được load ngược lại vào bộ nhớ.

Để tránh việc phải hardcode quá nhiều trường hợp đặc biệt vào trong kernel, giai đoạn boot ban đầu với 1 root file system tạm thời, ta sử dụng cơ chế early user space. Điều này cho phép root file system có thể chứa các user space helper để phát hiện phần cứng, load module và khám phá các thiết bị cần thiết để có thể mount root file sytem thực sự.

Cài đặt

Image của root file system ban đầu (cùng với kernel image) phải được lưu trữ ở đâu đó có thể truy cập được bởi boot loader hoặc boot firmware (trường hợp UEFI). Nó có thể là bản thân root file system, boot image trên đĩa quang, 1 partition nhỏ trên local disk (boot partition, thường là ext2 hoặc FAT) hoặc TFTP server (boot từ Ethernet)

Boot loader sẽ load kernel và initial root files system image vào memory và khởi động kernel, chuyển vào địa chỉ bộ nhớ của image. Vào giao đoạn cuối của quá trình boot này, kernel sẽ cố gắng xác định định dạng của image từ vài block data đầu tiên của nó và có thể dẫn đến thực thi mô hình initrd hoặc initramfs scheme.

  • Trong mô hình initrd: image có thể là 1 file system image (có thể bị nén) có thể truy cập được trong 1 block device đặc biệt (/dev/ram) sau đó được mount như là initial root file system. Driver cho cho file system đó phải được biên dịch tĩnh vào kernel. (Nhiều Linux distros ban đầu sử dụng ext2 file system image trong khi một số khác (gồm Debian 3.1) sử dụng cramfs để boot trên hệ thống có memory giới hạn vì cramfs image có thể được mount mà không cần thêm không gian để giải nén). Khi initial root file system đã up, kernel sẽ thực thi /linuxrc như là process đầu tiên của nó, khi kết thúc, kernel giả định/cho rằng là real root file system đã được mount và thực thi /sbin/init để bắt đầu quá trình boot user space bình thường.

  • Trong mô hình initramfs (từ linux kernel v2.6.13): image có thể lưu trữ theo định dạng của cpio archive (kiểu giống như tar, có thể nén). File lưu trữ archive này sẽ được mở đóng gói (unpack) bởi kernel vào 1 instance đặc biệt của tmpfs và sẽ trở thành initial root file system. Mô hình này có ưu điểm là không yêu cầu 1 file system ngay lập tức hoặc các block driver phải được biên dịch vào kernel. Một số hệ thống sử dụng dracut package để tạo initramfs image. Trong mô hình initramfs, kernel thực thi /init như là process đầu tiên của nó và không chờ đợi phải exit. Đối với một số ứng dụng, initramfs có thể sử dụng tiện ích casper để tạo ra 1 môi trường có thể ghi bằng cách sử dụng unionfs để overlay (che phủ) 1 persistence layer lên trên read only root file system image. VD overlay data có thể được lưu trên USB flash drive trong khi image read only SquashFS đã được nén được lưu trên live CD hoạt động như 1 root file system.

Tùy thuộc vào thuật toán nào đã được sử dụng để biên dịch tĩnh vào kernel, kernel sau đó có thể unpack initrd/intramfs image đã được nén bằng gzip, bzip2, LZMA, XZ, LZO và LZ4.

Chuẩn bị mount

Một số bản phân phối Linux chẳng hạn như Debian sẽ tạo một initrd image tùy biến chỉ chứa những gì cần thiết để khởi động máy tính, chẳng hạn như ATA, SCSI và filesystem kernel module. Chúng thường nhúng vị trí và loại của root filesystem.

Các bản phân phối Linux khác (chẳng hạn như Fedora và Ubuntu) tạo ra một initrd image tổng quát hơn. Chúng chỉ khởi động với tên thiết bị của root filesystem (hoặc UUID của nó) và phải khám phá mọi thứ khác tại thời điểm khởi động. Trong trường hợp này, phần mềm phải thực hiện một loạt các tác vụ phức tạp để có thể moun được root filesystem:

  • Bất kỳ trình điều khiển (driver) phần cứng nào mà quá trình khởi động phụ thuộc vào phải được load. Một cách sắp xếp phổ biến là đóng gói các kernel module cho các thiết bị lưu trữ phổ biến vào initrd và sau đó gọi một hotplug agent để lấy về (pull) vào các module phù hợp với phần cứng được phát hiện của máy tính.

  • Trên các hệ thống có hiển thị màn hình khởi động, phần cứng video phải được khởi tạo và một userspace helper bắt đầu vẽ các hình ảnh lên màn hình trong bước khóa (lockstep) với quá trình khởi động.

  • Nếu root filesystem nằm trên NFS, thì nó phải làm cho network interface chính UP, gọi DHCP client để có thể nhận được DHCP lease, trích xuất tên của NFS share và địa chỉ của NFS server từ DHCP lease, sau đó mount NFS share.

  • Nếu root filesystem xuất hiện trên thiết bị RAID phần mềm, sẽ không có cách nào để biết ổ đĩa RAID trải dài trên những thiết bị nào; các tiện ích MD tiêu chuẩn phải được gọi để quét tất cả các thiết bị block có sẵn và đưa các thiết bị cần thiết online.

  • Nếu root filesystem nằm trên một logical volume thì các tiện ích LVM phải được gọi để quét và kích hoạt volume group chứa nó.

  • Nếu root filesystem nằm trên thiết bị block đã được mã hóa, phần mềm cần gọi đến helper script để nhắc người dùng nhập vào chuỗi mật khẩu và/hoặc chèn token phần cứng (chẳng hạn như smart card hoặc thiết bị bảo mật dạng USB) và sau đó tạo mục tiêu giải mã bằng device mapper.

Một số bản phân phối sử dụng hotplug agent hướng sự kiện như udev, agent này sẽ gọi các chương trình helper dưới dạng thiết bị phần cứng, phân vùng đĩa và volume lưu trữ phù hợp với các quy tắc nhất định. Điều này cho phép tiến trình khám phá (discovery) chạy song song và xếp tầng thành LVM, RAID lồng vào nhau hoặc mã hóa tùy ý để truy cập vào root filesystem.

Cuối cùng, khi root filesystem được hiển thị, mọi tác vụ bảo trì không thể chạy trên root filesystem sẽ được thực hiện, root filesystem sẽ được mount dưới dạng read only và mọi process phải tiếp tục chạy (chẳng hạn như splash screen helper và lệnh FIFO của nó) sẽ được đưa vào root filesystem mới được mount.

Root filesystem cuối cùng không thể chỉ đơn giản được mount tại /, vì điều đó sẽ làm cho các script và công cụ trên root filesystem ban đầu không thể truy cập được đối với bất kỳ tác vụ dọn dẹp cuối cùng nào:

  • Trên initrd, root mới được mount tại mount point tạm thời và được xoay vòng bằng pivot_root (được giới thiệu đặc biệt cho mục đích này). Điều này để lại root filesystem ban đầu tại mount point (chẳng hạn như /initrd) nơi các boot script bình thường sau đó có thể unmount nó để giải phóng bộ nhớ bị chiếm dụng bởi initrd.

  • Trên initramfs, root filesystem ban đầu không thể bị xoay vòng được. Thay vào đó, nó chỉ đơn giản là làm trống (empty) và root filesystem cuối cùng (final) sẽ được mount lên trên đầu (top).

Hầu hết các root filesystem ban đầu đều triển khai /linuxrc hoặc /init dưới dạng shell script và do đó sẽ bao gồm một shell tối giản (thường là initramfs1) cùng với một số tiện ích userspace thiết yếu (thường là bộ công cụ BusyBox). Để tiết kiệm thêm dung lượng, shell, các tiện ích và các thư viện hỗ trợ của chúng thường được biên dịch với tính năng tối ưu hóa không gian được bật (chẳng hạn như với cờ “initramfs2” của gcc) và được liên kết với klibc, một phiên bản tối giản của thư viện C được viết riêng cho mục đích này.

Các mục đích sử dụng khác

Các trình cài đặt dành cho các bản phân phối Linux thường chạy hoàn toàn từ initramfs vì chúng phải có khả năng lưu trữ giao diện trình cài đặt và các công cụ hỗ trợ trước khi bất kỳ bộ nhớ ổn định nào được thiết lập.

Tiny Core Linux và Puppy Linux có thể chạy hoàn toàn từ initrd.

Điểm tương đồng trong các hệ điều hành khác

Kể từ Windows Vista, Windows có thể khởi động từ file WIM disk image, nó khá giống với định dạng ZIP ngoại trừ việc nó hỗ trợ liên kết cứng (hard link), sử dụng nén chunk-by-chunk và có thể hỗ trợ các chunk không trùng lặp. Trong trường hợp này, toàn bộ WIM lúc đầu được load vào RAM, sau đó là khởi tạo kernel. Tiếp theo, WIM đã được load sẽ có sẵn dưới dạng SystemRoot với ký tự ổ đĩa được chỉ định. Trình cài đặt Windows sử dụng ổ đĩa này để nó khởi động từ initramfs3, sau đó sử dụng initramfs4 là tập hợp các file Windows sẽ được cài đặt.

Ngoài ra, Windows Preinstallation Environment (Windows PE) cũng giống như vậy, nó là cơ sở cho các phiên bản khởi động riêng biệt của một số phần mềm chống virus và sao lưu/phục hồi thảm họa.

Ta cũng có thể cài đặt Windows để nó luôn khởi động từ file WIM hoặc file VHD nằm trên ổ đĩa vật lý. Tuy nhiên, điều này hiếm khi được sử dụng vì Windows bootloader có khả năng load các file initramfs5 cho chính các kernel module tại thời điểm khởi động, đây là tác vụ yêu cầu initrd trong Linux.