<?xml version="1.0" encoding="UTF-8"?><rss xmlns:dc="http://purl.org/dc/elements/1.1/" xmlns:content="http://purl.org/rss/1.0/modules/content/" xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title><![CDATA[U40 Học Code - Lập trình & Hệ điều hành]]></title><description><![CDATA[Chia sẻ kiến thức hệ điều hành, lập trình, C/C++, đa luồng, bộ nhớ, Linux/Windows.  Học để hiểu bản chất, không chỉ chạy được code.]]></description><link>https://u40hoccode.dev</link><generator>RSS for Node</generator><lastBuildDate>Wed, 15 Apr 2026 00:40:15 GMT</lastBuildDate><atom:link href="https://u40hoccode.dev/rss.xml" rel="self" type="application/rss+xml"/><language><![CDATA[en]]></language><ttl>60</ttl><item><title><![CDATA[Mô hình OSI (Open System Interconnection)]]></title><description><![CDATA[Mô hình OSI là mô hình kết nối các hệ thống mở, đây là mô hình căn bản về tiến trình truyền thông, thiết lập các tiêu chuẩn kiến trúc mạng, là cơ sở chung để các hệ thống giao tiếp và truyền thông được với nhau.
Mô hình OSI tổ chức giao thức truyền t...]]></description><link>https://u40hoccode.dev/mo-hinh-osi-open-system-interconnection</link><guid isPermaLink="true">https://u40hoccode.dev/mo-hinh-osi-open-system-interconnection</guid><category><![CDATA[OSI Model]]></category><category><![CDATA[networking]]></category><category><![CDATA[tcp/ip-model]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Sun, 08 Feb 2026 09:14:22 GMT</pubDate><content:encoded><![CDATA[<p>Mô hình OSI là mô hình kết nối các hệ thống mở, đây là mô hình căn bản về tiến trình truyền thông, thiết lập các tiêu chuẩn kiến trúc mạng, là cơ sở chung để các hệ thống giao tiếp và truyền thông được với nhau.</p>
<p>Mô hình OSI tổ chức giao thức truyền thông thành 7 tầng, mỗi tầng giải quyết một phần hẹp của tiến trình truyền thông, chia tiến trình truyền thông thành nhiều tầng và trong mỗi tầng có thể có các giao thức (Protocol) thực hiện các nhu cầu truyền thông cụ thể.</p>
<p>Mô hình OSI gồm 7 tầng như hình dưới.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770538955198/fb921412-30d0-47e3-82a4-cd9510d94711.png" alt class="image--center mx-auto" /></p>
<p>Quá trình một gói tin được gửi từ máy A sang máy B qua mạng diễn ra như sau:</p>
<p>Tại máy A (máy gửi), dữ liệu do ứng dụng tạo ra sẽ lần lượt được đóng gói (encapsulation) khi đi từ tầng 7 (Application Layer) xuống tầng 1 (Physical Layer).<br />Mỗi tầng trong mô hình mạng sẽ thêm vào dữ liệu của tầng trên thông tin điều khiển tương ứng (header) để phục vụ cho việc định tuyến, kiểm soát kết nối và truyền dữ liệu qua mạng.</p>
<p>Sau khi xuống đến tầng vật lý, dữ liệu được chuyển thành các bit 0/1 và truyền đi trên môi trường truyền dẫn (cáp mạng, sóng vô tuyến, v.v.).</p>
<p>Tại máy B (máy nhận), tín hiệu nhận được sẽ được chuyển ngược từ dạng bit thành dữ liệu và lần lượt trải qua quá trình giải đóng gói (decapsulation) từ tầng 1 lên tầng 7.<br />Mỗi tầng sẽ bóc bỏ phần header tương ứng của mình, xử lý thông tin điều khiển, rồi chuyển dữ liệu còn lại lên tầng trên cho đến khi dữ liệu ban đầu được chuyển tới ứng dụng ở tầng ứng dụng.</p>
<h2 id="heading-1-tang-1-physical-layer-tang-vat-ly">1. Tầng 1 – Physical Layer (Tầng vật lý)</h2>
<p><strong>Chức năng</strong></p>
<ul>
<li><p>Truyền bit 0/1 dưới dạng tín hiệu vật lý</p>
</li>
<li><p>Quy định điện áp, sóng, tốc độ truyền</p>
</li>
<li><p>Chuẩn hóa cáp, đầu nối, tần số</p>
</li>
</ul>
<p><strong>Thành phần liên quan</strong></p>
<ul>
<li><p>Môi trường truyền: cáp mạng, cáp quang, sóng Wi-Fi</p>
</li>
<li><p>Thiết bị: Hub, Repeater</p>
</li>
<li><p>Chuẩn vật lý: RJ45, tốc độ 100Mbps, 1Gbps…</p>
</li>
</ul>
<h2 id="heading-2-tang-2-data-link-layer-tang-lien-ket-du-lieu">2. Tầng 2 – Data Link Layer (Tầng liên kết dữ liệu)</h2>
<p>Chức năng</p>
<ul>
<li><p>Truyền frame trong cùng một mạng vật lý</p>
</li>
<li><p>Địa chỉ hóa bằng MAC</p>
</li>
<li><p>Phát hiện lỗi (CRC/FCS)</p>
</li>
<li><p>Điều khiển truy cập đường truyền</p>
</li>
</ul>
<p>Thành phần liên quan</p>
<ul>
<li><p>Giao thức: Ethernet, Wi-Fi (802.11)</p>
</li>
<li><p>Thiết bị: Switch, NIC (card mạng)</p>
</li>
<li><p>Địa chỉ: MAC Address</p>
</li>
<li><p>Frame, FCS (CRC)</p>
</li>
</ul>
<h2 id="heading-3-tang-3-network-layer-tang-mang">3. Tầng 3 – Network Layer (Tầng mạng)</h2>
<p>Chức năng</p>
<ul>
<li><p>Định tuyến gói tin giữa các mạng khác nhau</p>
</li>
<li><p>Xác định địa chỉ logic (IP)</p>
</li>
<li><p>Chọn đường đi cho gói tin</p>
</li>
</ul>
<p>Thành phần liên quan</p>
<ul>
<li><p>Giao thức: IP, ICMP</p>
</li>
<li><p>Thiết bị: Router</p>
</li>
<li><p>Địa chỉ: IPv4, IPv6</p>
</li>
<li><p>Bảng định tuyến (Routing Table)</p>
</li>
</ul>
<h2 id="heading-4-tang-4-transport-layer-tang-giao-van">4. Tầng 4 – Transport Layer (Tầng giao vận)</h2>
<p><strong>Chức năng</strong></p>
<ul>
<li><p>Truyền dữ liệu giữa 2 tiến trình (process-to-process)</p>
</li>
<li><p>Chia nhỏ gói tin/ ghép gói tin</p>
</li>
<li><p>Đảm bảo tin cậy, kiểm soát luồng, kiểm soát lỗi</p>
</li>
<li><p>Định danh ứng dụng bằng port</p>
</li>
</ul>
<p><strong>Thành phần liên quan</strong></p>
<ul>
<li><p>Giao thức: TCP, UDP</p>
</li>
<li><p>Khái niệm: Port, Socket</p>
</li>
<li><p>Thành phần OS: TCP/IP stack trong kernel</p>
</li>
</ul>
<h2 id="heading-5-tang-5-session-layer-tang-phien">5. Tầng 5 – Session Layer (Tầng phiên)</h2>
<p>Chức năng</p>
<ul>
<li><p>Thiết lập, duy trì và kết thúc phiên làm việc</p>
</li>
<li><p>Quản lý trạng thái kết nối (session)</p>
</li>
<li><p>Đồng bộ phiên, khôi phục khi gián đoạn</p>
</li>
</ul>
<p>Thành phần liên quan</p>
<ul>
<li><p>Session trong ứng dụng web</p>
</li>
<li><p>RPC session</p>
</li>
<li><p>Session ID, Cookie</p>
</li>
</ul>
<h2 id="heading-6-tang-6-presentation-layer-tang-trinh-bay">6. Tầng 6 – Presentation Layer (Tầng trình bày)</h2>
<p>Chức năng</p>
<ul>
<li><p>Chuyển đổi định dạng dữ liệu</p>
</li>
<li><p>Mã hóa / giải mã</p>
</li>
<li><p>Nén / giải nén dữ liệu</p>
</li>
</ul>
<p>Thành phần liên quan</p>
<ul>
<li><p>Chuẩn mã hóa: TLS/SSL, Base64</p>
</li>
<li><p>Định dạng dữ liệu: JSON, XML, UTF-8</p>
</li>
<li><p>Thư viện mã hóa / encode trong ứng dụng</p>
</li>
</ul>
<h2 id="heading-7-tang-7-application-layer-tang-ung-dung">7. Tầng 7 – Application Layer (Tầng ứng dụng)</h2>
<p>Chức năng</p>
<ul>
<li><p>Cung cấp giao diện cho ứng dụng người dùng giao tiếp với mạng</p>
</li>
<li><p>Thực hiện các dịch vụ mạng cấp cao: gửi mail, duyệt web, truyền file, gọi API…</p>
</li>
</ul>
<p>Thành phần liên quan</p>
<ul>
<li><p>Giao thức: HTTP/HTTPS, FTP, SMTP, POP3, IMAP, DNS</p>
</li>
<li><p>Ứng dụng: Web browser, Email client, API client</p>
</li>
<li><p>Dịch vụ: Web server, Mail server</p>
</li>
</ul>
<p>Lưu ý: Mô hình OSI chủ yếu dùng để học tập và phân tích hệ thống.<br />Trong triển khai thực tế, Internet sử dụng TCP/IP stack, trong đó các chức năng tầng 5 và 6 của OSI thường được gộp vào tầng ứng dụng.</p>
<h2 id="heading-phan-tich-goi-tin-bang-phan-mem-wireshark">Phân tích gói tin bằng phần mềm Wireshark</h2>
<p>Hình minh họa cho thấy một gói tin HTTP thực tế được bắt bằng Wireshark.<br />Gói tin bao gồm đầy đủ các tầng giao thức trong TCP/IP stack:</p>
<p>Ethernet (tầng 2) → IP (tầng 3) → TCP (tầng 4) → HTTP (tầng 7).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1770541396851/4b51d8b4-8a30-4080-9772-0644e0d2faf8.png" alt class="image--center mx-auto" /></p>
<p>Tầng 2 – Data Link (Ethernet II)</p>
<p>Ethernet II, Src: TPLink_77:23:4d (f0:09:0d:77:23:4d) Dst: LiteonTechno_6e:3e:e3 (14:b5:cd:6e:3e:e3)</p>
<ul>
<li><p>Địa chỉ MAC nguồn / MAC đích</p>
</li>
<li><p>Frame Ethernet (giao thức Ethernet)</p>
</li>
</ul>
<p>Tầng 3 – Network (IP)</p>
<p>Internet Protocol Version 4, Src: 23.203.133.184, Dst: 192.168.0.49</p>
<ul>
<li><p>Địa chỉ IP nguồn / IP đích</p>
</li>
<li><p>Giao thức IP</p>
</li>
</ul>
<p>Tầng 4 – Transport (TCP)</p>
<p>Transmission Control Protocol, Src Port: 80, Dst Port: 54803, Seq, Ack, Len</p>
<ul>
<li><p>TCP</p>
</li>
<li><p>Port nguồn / đích</p>
</li>
<li><p>Sequence, Acknowledgement</p>
</li>
</ul>
<p>Tầng 7 – Application (HTTP)</p>
<p>Hypertext Transfer Protocol Line-based text data: text/plain</p>
<ul>
<li><p>HTTP/1.1</p>
</li>
<li><p>Header + body của gói tin</p>
</li>
</ul>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Quản lý bộ nhớ]]></title><description><![CDATA[1. Địa chỉ vật lý và địa chỉ logic
Địa chỉ vật lý là địa chỉ thực trên RAM, là địa chỉ mà CPU cuối cùng sử dụng để truy cập bộ nhớ, thông qua khối quản lý bộ nhớ (MMU).
Địa chỉ logic là địa chỉ được gán cho các lệnh và dữ liệu khi chương trình chạy. ...]]></description><link>https://u40hoccode.dev/quan-ly-bo-nho</link><guid isPermaLink="true">https://u40hoccode.dev/quan-ly-bo-nho</guid><category><![CDATA[operating system]]></category><category><![CDATA[memory-management]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Fri, 30 Jan 2026 14:31:02 GMT</pubDate><content:encoded><![CDATA[<p><strong>1. Địa chỉ vật lý và địa chỉ logic</strong></p>
<p>Địa chỉ vật lý là địa chỉ thực trên RAM, là địa chỉ mà CPU cuối cùng sử dụng để truy cập bộ nhớ, thông qua khối quản lý bộ nhớ (MMU).</p>
<p>Địa chỉ logic là địa chỉ được gán cho các lệnh và dữ liệu khi chương trình chạy. CPU nhìn thấy và sử dụng các địa chỉ logic này để trỏ đến các phần khác nhau của lệnh và dữ liệu. Vùng không gian địa chỉ này gọi là không gian nhớ logic.</p>
<p>Như chúng ta đã biết mỗi tiến trình có không gian địa chỉ riêng như code/text, data, bss, heap, stack. Những địa chỉ này nằm trên không gian nhớ logic.</p>
<p>Khi chương trình chạy, địa chỉ logic cần được biến đổi thành địa chỉ vật lý nhờ một module phần cứng gọi là khối quản lý bộ nhớ (Memory Management Unit - MMU).</p>
<p>Như vậy để dễ quản lý và lập trình, địa chỉ logic là liên tục nhưng khi ánh xạ sang địa chỉ vật lý các địa chỉ này có thể là các địa chỉ rải rác và không liền mạch.</p>
<p><strong>2. Phân trang (paging)</strong></p>
<p>Bộ nhớ vật lý được chia thành các khối nhỏ có kích thước cố định bằng nhau gọi là khung trang (page frame). Trên hệ điều hành windows, thông thường mỗi khung trang có kích thước 4KB.</p>
<p>Để xem kích thước này trên windows chúng ta có thể xem qua phần mềm RamMap, ở tab Physical Pages.</p>
<p>Đơn vị nhỏ nhất của bộ nhớ RAM là 1 byte. 4KB (0x1000) là kích thước một trang (page) / khung trang (page frame).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769776614756/6636e28a-7e2a-4186-a8f0-e7e9c845a208.png" alt class="image--center mx-auto" /></p>
<p>Không gian địa chỉ logic của tiến trình cũng được chia thành những khối có địa chỉ liền nhau gọi là trang (page) có kích thước bằng với kích thước của khung trang.</p>
<p>Mỗi tiến trình sẽ được cấp phát các khung trang trong bộ nhớ vật lý (RAM) để chứa các trang nhớ của mình. Địa chỉ của các khung trang này có thể nằm rải rác không liên tục trong RAM.</p>
<p>Vì mỗi tiến trình được cấp phát nhiều khung có kích thước nhỏ nằm rải rác trên bộ nhớ vật lý, nhờ vậy giảm đáng kể tình trạng phân mảnh ở trong RAM.</p>
<p><strong>3. Ánh xạ địa chỉ</strong></p>
<p>Để ánh xạ địa chỉ logic thành địa chỉ vật lý, mỗi tiến trình có một bảng gọi là bảng trang (page table), mỗi ô của bảng tương ứng với một trang và chứa số hiệu của khung trang trong bộ nhớ vật lý.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769779067020/24a17765-052d-495e-bde7-74b31a97fa17.png" alt class="image--center mx-auto" /></p>
<p>Ở ví dụ trên tiến trình A được cấp phát 2 trang ảo là 0 và 1. Được ánh xạ qua page table tương ứng tới 2 khung trang A0 và A1 trong bộ nhớ vật lý.</p>
<p>Tiến trình B được cấp phát 3 trang ảo là 0, 1 và 2. Được ánh xạ qua page table tương ứng tới 3 khung trang B0, B1 và B2 trong bộ nhớ vật lý.</p>
<p>Địa chỉ vật lý được CPU truy cập sẽ được tính theo công thức</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769784351976/2ba87d3b-4b01-4c51-886b-caa0e8877af5.png" alt class="image--center mx-auto" /></p>
<p>Page Frame Number là chỉ số của khung trang trong bộ nhớ vật lý.</p>
<p>Khi phần mềm đưa cho CPU một địa chỉ ảo, cách CPU tìm ra địa chỉ vật lý được diễn ra như sau.</p>
<p>Đầu tiên CPU sẽ căn cứ vào địa chỉ này để tính ra địa chỉ này tương ứng với số hiệu trang ảo (Virtual Page Number) là bao nhiêu và tính từ đầu trang ảo thì Offset là bao nhiêu.</p>
<p>CPU tra bảng trang để tìm ra số hiệu khung trang vật lý (Page Frame Number - PFN) tương ứng.</p>
<p>Sau khi có hai thông số này, CPU sử dụng công thức trên để tra ra địa chỉ vật lý.</p>
<p>Trên thực tế để tăng tốc quá trình truy cập địa chỉ, CPU sử dụng bộ nhớ cache TLB là bộ nhớ cache phần cứng dùng để lưu ánh xạ từ Virtual Page Number sang Page Frame Number. CPU sẽ tra TLB trước, nếu TLB miss mới truy cập page table trong RAM để lấy PFN.</p>
<p>Sau khi có địa chỉ vật lý, CPU tiếp tục truy cập cache dữ liệu L1/L2/L3 trước khi truy cập xuống RAM.</p>
<p><strong>4. Lỗi trang (Page Fault) và Demand Paging</strong></p>
<p>Lỗi trang (Page Fault) là một sự kiện xảy ra khi CPU không thể hoàn tất việc ánh xạ từ địa chỉ ảo sang địa chỉ vật lý do chưa có khung trang vật lý hợp lệ trong RAM, hoặc do vi phạm quyền truy cập bộ nhớ.</p>
<p>Page Fault xảy ra khi</p>
<ul>
<li><p>CPU truy cập một địa chỉ ảo</p>
</li>
<li><p>Trang tương ứng <strong>chưa có trong RAM</strong>, hoặc</p>
</li>
<li><p>Trang tồn tại nhưng <strong>không được phép truy cập</strong> (vi phạm quyền read/write/execute)</p>
</li>
<li><p>Bảng trang (page table) <strong>không có ánh xạ hợp lệ</strong> cho trang đó</p>
</li>
</ul>
<p>Demand Paging là một chính sách quản lý bộ nhớ mà hệ điều hành triển khai dựa trên cơ chế Page Fault.<br />Theo chính sách này, hệ điều hành không nạp toàn bộ chương trình vào RAM ngay từ đầu nhằm tránh lãng phí bộ nhớ, thay vào đó, các trang ảo chỉ được nạp vào RAM khi chương trình thực sự truy cập đến chúng.</p>
<p>Khi chương trình truy cập một trang lần đầu tiên, do trang đó chưa được cấp khung trang vật lý, nên Page Fault xảy ra. Lúc này, hệ điều hành sẽ:</p>
<ul>
<li><p>Cấp phát một Page Frame vật lý mới, hoặc</p>
</li>
<li><p>Đọc nội dung trang từ disk (pagefile hoặc file backing) vào RAM</p>
</li>
</ul>
<p>Sau đó, hệ điều hành cập nhật bảng trang và cho phép CPU thực thi lại lệnh gây ra Page Fault.</p>
<p>Việc sử dụng cơ chế Demand Paging giúp tránh lãng phí bộ nhớ vật lý, đồng thời cho phép hệ điều hành chạy song song nhiều chương trình hơn trên cùng một hệ thống.</p>
<p>Cơ chế Demand Paging và Page Fault được ứng dụng trực tiếp trong các API ánh xạ bộ nhớ như mmap (trên Linux/Unix) hoặc memory-mapped file trên Windows.</p>
<p>Cơ chế này cho phép giải quyết các bài toán xử lý file dữ liệu lớn trong khi vẫn tiết kiệm RAM, nhờ chỉ nạp vào bộ nhớ những phần dữ liệu thực sự được truy cập. Chủ đề này sẽ được trình bày chi tiết hơn trong một bài viết riêng về lập trình ánh xạ bộ nhớ.</p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Semaphore và cách sử dụng trong lập trình đa luồng]]></title><description><![CDATA[Semaphore là một cơ chế đồng bộ được sử dụng để điều phối quyền truy cập của nhiều luồng/tiến trình vào một số lượng tài nguyên hữu hạn hoặc để đồng bộ luồng trong môi trường đa luồng.
1. Cách thức hoạt động của semaphore
Semaphore là một tập hợp các...]]></description><link>https://u40hoccode.dev/semaphore-va-cach-su-dung-trong-lap-trinh-da-luong</link><guid isPermaLink="true">https://u40hoccode.dev/semaphore-va-cach-su-dung-trong-lap-trinh-da-luong</guid><category><![CDATA[semaphore]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[C++]]></category><category><![CDATA[multithreading]]></category><category><![CDATA[operating system]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Sat, 24 Jan 2026 06:30:23 GMT</pubDate><content:encoded><![CDATA[<p>Semaphore là một cơ chế đồng bộ được sử dụng để điều phối quyền truy cập của nhiều luồng/tiến trình vào một số lượng tài nguyên hữu hạn hoặc để đồng bộ luồng trong môi trường đa luồng.</p>
<p><strong>1. Cách thức hoạt động của semaphore</strong></p>
<p>Semaphore là một tập hợp các giấy phép (permits).</p>
<p>Để truy cập tài nguyên thi luồng phải yêu cầu một giấy phép. Nếu còn phép thì semaphore sẽ cho luồng đi tiếp và giảm số lượng giấy phép đi 1.</p>
<p>Nếu hết giấy phép thì luồng sẽ phải chờ cho đến khi có môt luồng khác trả lại giấy phép.</p>
<p>Sau khi luồng dùng xong sẽ trả lại giấy phép, khi đó số lượng giấy phép của semaphore sẽ tăng lên 1.</p>
<p>Một luồng sẽ thao tác với semaphore ở 2 trạng thái dưới:</p>
<ul>
<li><p>Wait: luồng kiểm tra semaphre, nếu còn giấy phép thì giảm giá số giá trị xuống 1, nếu không còn giấy phép thì đưa thread vào wait queue (trạng thái ngủ (sleep) không tiêu tốn CPU)</p>
</li>
<li><p>Signal: luồng xử lý xong nếu có luồng đang wait thì đánh thức luồng đó, nếu không có luồng nào đang wait ở trong wait queue thì tăng semaphore lên 1.</p>
</li>
</ul>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769237817135/513096f3-7b39-48a5-add3-f064730aee51.png" alt class="image--center mx-auto" /></p>
<p><strong>2. Các loại semaphore</strong></p>
<p>Binary Semaphore</p>
<p>Binary semaphore là semaphore chỉ có giá trị {0, 1} và chủ yếu được sử dụng để đồng bộ luồng (synchronization).</p>
<p>Chúng ta xem xét bài toán dùng binary semaphore để đồng bộ giữa ngắt của UART và task xử lý dữ liệu nhận trong hệ điều hành thời gian thực RTOS. Ban đầu, semaphore có giá trị khởi tạo là 0, Task này sẽ wait semaphore vô thời hạn trong vòng lặp. UART nhận dữ liệu xong sẽ phát sinh một ngắt nhận. Ngắt này sẽ phát tín hiệu signal cho task nhận dữ liệu.</p>
<p>Nhờ đó, task xử lý dữ liệu chỉ được đánh thức khi có ngắt nhận từ UART, sau khi xử lý xong task lại tiếp tục gọi wait và quay lại trạng thái sleep (blocked/waiting) để không gây tiêu tốn CPU trong thời gian chờ.</p>
<p>Counting Semaphore</p>
<p>Sử dụng khi có một tập hợp các tài nguyên hữu hạn.</p>
<p>Ví dụ về counting semaphore như sau:</p>
<p>Giả sử chúng ta có 2 phòng tắm (tài nguyên) khi đó semaphore ban đầu bằng 2.</p>
<p>Khi có người thứ nhất vào phòng, semaphore giảm xuống 1.</p>
<p>Khi có người thứ hai vào phòng, semaphore giảm xuống 0.</p>
<p>Khi người thứ 2 muốn vào phòng phải chờ cho 1 trong 2 người ra khỏi phòng mới được vào.</p>
<p>Như vậy counting semaphore giống như người điều tiết giao thông, đảm bảo không có quá nhiều luồng cùng vào một khu vực gây nghẽn hệ thống (concurrency limit).</p>
<p>Khi các luồng đã vượt qua cổng và vào được bên trong bằng giấy phép được cấp bởi semaphore nếu cùng tác động lên 1 biến, cấu trúc dữ liệu hay tài nguyên dùng chung thì semaphore không còn bảo vệ được nữa. Khi đó chúng ta cần dùng khóa Mutex hoặc các cơ chế tương đương để đảm bảo không gây ra Race Condition làm sai lệnh dữ liệu.</p>
<p><strong>3. Ví dụ triển khai semaphore trong C++</strong></p>
<p>Binary semaphore chủ yếu được ứng dụng trong lập trình nhúng và hệ thống thời gian thực (RTOS), trong phạm vi lập trình ứng dụng ở tầng user space thì counting semaphore được sử dụng phổ biến hơn để giới hạn mức độ song song nhiều luồng.</p>
<p>C++ 20 trở đi đã cung cấp counting_semaphore trong thư viện tiêu chuẩn STL.</p>
<p>Chú ý: để biên dịch trên vscode thì cần thêm cờ biên dịch “-std=c++20” và cấu hình biên dịch là c++20 trong cấu hình json c/c++ (nhấn tổ hợp phím Ctrl + Shift + P, sau đó chọn C/C++ Edit Configulations (JSON), cấu hình cppStandard là “c++20“).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769229996771/ec7f546f-dd74-49fc-8c05-be282f5fa1c2.png" alt class="image--center mx-auto" /></p>
<pre><code class="lang-json">{
    <span class="hljs-attr">"configurations"</span>: [
        {
            <span class="hljs-attr">"name"</span>: <span class="hljs-string">"Win32"</span>,
            <span class="hljs-attr">"includePath"</span>: [
                <span class="hljs-string">"${workspaceFolder}/**"</span>
            ],
            <span class="hljs-attr">"defines"</span>: [
                <span class="hljs-string">"_DEBUG"</span>,
                <span class="hljs-string">"UNICODE"</span>,
                <span class="hljs-string">"_UNICODE"</span>
            ],
            <span class="hljs-attr">"windowsSdkVersion"</span>: <span class="hljs-string">"10.0.26100.0"</span>,
            <span class="hljs-attr">"compilerPath"</span>: <span class="hljs-string">"cl.exe"</span>,
            <span class="hljs-attr">"cStandard"</span>: <span class="hljs-string">"c17"</span>,
            <span class="hljs-attr">"cppStandard"</span>: <span class="hljs-string">"c++20"</span>,
            <span class="hljs-attr">"intelliSenseMode"</span>: <span class="hljs-string">"windows-msvc-x64"</span>
        }
    ],
    <span class="hljs-attr">"version"</span>: <span class="hljs-number">4</span>
}
</code></pre>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;semaphore&gt;            // C++ 20</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;mutex&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;thread&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;vector&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;atomic&gt;</span></span>

<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> MAX_RESOURCE  =   <span class="hljs-number">2</span>;
<span class="hljs-keyword">constexpr</span> <span class="hljs-keyword">int</span> MAX_THREAD    =   <span class="hljs-number">4</span>;

<span class="hljs-comment">// Khởi tạo semaphore với giá trị ban đầu là 2</span>
<span class="hljs-function"><span class="hljs-built_in">std</span>::counting_semaphore&lt;MAX_RESOURCE&gt; <span class="hljs-title">sem</span><span class="hljs-params">(MAX_RESOURCE)</span></span>;

<span class="hljs-comment">// Khai báo biến đếm mô phỏng số permit của semaphore</span>
<span class="hljs-comment">// Atomic là thao tác không thể chia nhỏ, đảm bảo không xảy ra race condition trong đa luồng, và được hỗ trợ bởi phần cứng CPU</span>
<span class="hljs-comment">// Lưu ý inside chỉ dùng để quan sát demo, không phải giá trị nội bộ của semaphore.</span>
<span class="hljs-built_in">std</span>::atomic&lt;<span class="hljs-keyword">int</span>&gt; inside{<span class="hljs-number">0</span>};

<span class="hljs-comment">// Định nghĩa hàm safe log để in tuần tự ra console mà không bị dính chữ do tranh chấp</span>
<span class="hljs-built_in">std</span>::mutex log_mutex;
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">safe_log</span><span class="hljs-params">(<span class="hljs-keyword">const</span> <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span>&amp; msg)</span> </span>{
    <span class="hljs-function"><span class="hljs-built_in">std</span>::lock_guard&lt;<span class="hljs-built_in">std</span>::mutex&gt; <span class="hljs-title">lock</span><span class="hljs-params">(log_mutex)</span></span>;
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; msg &lt;&lt; <span class="hljs-built_in">std</span>::<span class="hljs-built_in">endl</span>;
}

<span class="hljs-comment">// Định nghĩa hàm xử lý cho các worker thread</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">worker</span><span class="hljs-params">(<span class="hljs-keyword">int</span> id)</span> </span>{
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">string</span> msg;
    msg = <span class="hljs-string">"Worker "</span> + <span class="hljs-built_in">std</span>::to_string(id) + <span class="hljs-string">" is waiting ..."</span>;
    safe_log(msg);

    <span class="hljs-comment">// wait (lấy permit, nếu không có sẽ vào trạng thái blocked/wait không tiêu tốn CPU)</span>
    sem.acquire();

    <span class="hljs-comment">// Lấy permit thành công</span>
    <span class="hljs-keyword">int</span> current = ++inside;

    <span class="hljs-comment">// do something</span>
    msg = <span class="hljs-string">"Worker "</span> + <span class="hljs-built_in">std</span>::to_string(id) + <span class="hljs-string">" acquired resource"</span> + <span class="hljs-string">"| inside: "</span> + <span class="hljs-built_in">std</span>::to_string(current);
    safe_log(msg);
    <span class="hljs-built_in">std</span>::this_thread::sleep_for(<span class="hljs-built_in">std</span>::chrono::seconds(<span class="hljs-number">1</span>));  <span class="hljs-comment">// sleep 1 giây</span>

    current = --inside;
    msg = <span class="hljs-string">"Worker "</span> + <span class="hljs-built_in">std</span>::to_string(id) + <span class="hljs-string">" releasing resource"</span> + <span class="hljs-string">"| inside: "</span> + <span class="hljs-built_in">std</span>::to_string(current);
    safe_log(msg);

    <span class="hljs-comment">// signal (trả permit, đánh thức 1 thread đang wait nếu có)</span>
    sem.release();
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// định nghĩa vector để lưu handle của các thread</span>
    <span class="hljs-built_in">std</span>::<span class="hljs-built_in">vector</span>&lt;<span class="hljs-built_in">std</span>::thread&gt; threads;

    <span class="hljs-comment">// khởi tạo các thread và nạp các handle của thread vào vector</span>
    <span class="hljs-comment">// lưu ý không dùng push_back trong trường hợp này vì handle của thread là đối tượng không thể sao chép</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; MAX_THREAD; i++)
    {
        threads.emplace_back(worker, i);
    }

    <span class="hljs-comment">// chờ cho các thread hoàn thành</span>
    <span class="hljs-keyword">for</span>(<span class="hljs-keyword">auto</span>&amp; t: threads) {
        t.join();
    }

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Ở ví dụ trên, có 4 luồng chạy song song và có chia sẻ 2 nguồn tài nguyên.<br />Từ kết quả chúng ta có thể thấy tại một thời điểm chỉ có tối đa 2 luồng được sử dụng tài nguyên, điều này thể hiện ở biến inside.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1769235632819/20d852f4-7078-40cf-8b17-8faf3e03f621.png" alt class="image--center mx-auto" /></p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Cách đồng bộ dữ liệu bằng Mutex trong đa luồng]]></title><description><![CDATA[Mutex là gì ?
Mutex là chìa khóa để bảo vệ tài nguyên chung, tại một thời điểm chỉ cho phép một luồng truy cập. Nếu coi Tài nguyên chung là một cái phòng có một cửa thì Mutex chính là cái chì khóa phòng. Luồng nào lấy được chìa khóa và đi vào phòng t...]]></description><link>https://u40hoccode.dev/cach-dong-bo-du-lieu-bang-mutex-trong-da-luong</link><guid isPermaLink="true">https://u40hoccode.dev/cach-dong-bo-du-lieu-bang-mutex-trong-da-luong</guid><category><![CDATA[Computer Science]]></category><category><![CDATA[Windows]]></category><category><![CDATA[mutex]]></category><category><![CDATA[multithreading]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Mon, 19 Jan 2026 12:50:12 GMT</pubDate><content:encoded><![CDATA[<p><strong>Mutex là gì ?</strong></p>
<p>Mutex là chìa khóa để bảo vệ tài nguyên chung, tại một thời điểm chỉ cho phép một luồng truy cập. Nếu coi Tài nguyên chung là một cái phòng có một cửa thì Mutex chính là cái chì khóa phòng. Luồng nào lấy được chìa khóa và đi vào phòng thì các luồng còn lại phải chờ ở ngoài. Chỉ khi luồng đang giữ chìa khóa mở cửa đi ra thì luồng khác mới có thể vào.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768827787049/1cf9a81b-1293-47e5-8629-ef6842ad88d4.png" alt class="image--center mx-auto" /></p>
<p>Chúng ta cần phân biệt kernel-space mutex (trên Windows được goi bằng API CreateMutex) và user-space mutex (C++ với thư viện std::mutex, CRITICAL_SECTION của windows).</p>
<p>Kernel-space mutex là một Kernel object có phạm vi toàn hệ thống (system-wide) được dùng để đồng bộ các tiến trình trong hệ điều hành. Mỗi khi mutex này lock/unlock, CPU phải thực hiện mode switch để chuyển từ user-mode sang kernel-mode, do đó chi phí để tạo mutex ở mức kernel là lớn.</p>
<p>User-space mutex có cấu trúc và chi phí triển khai nhẹ hơn phù hợp để đồng bộ các luồng trong cùng một tiến trình. Khi gọi loại mutex này nếu như không xảy ra tranh chấp thì sẽ không cần gọi vào kernel mode. Do đó chi phí trung bình nhỏ hơn nhiều.</p>
<p>Ở phạm vi lập trình ứng dụng, đồng bộ các luồng của cùng một tiến trình ở bài viết này, chúng ta sẽ nói về user-space mutex.</p>
<p>Cùng xem xét một ví dụ sau viết bằng c++ trên windows, sử dụng win32 api để tạo thread.</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;process.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;windows.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>

<span class="hljs-comment">// Hàm xử lý của thread 1</span>
<span class="hljs-function"><span class="hljs-keyword">unsigned</span> __stdcall <span class="hljs-title">t1_worker</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* args)</span> </span>{
    <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>)
    {
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Thread "</span> &lt;&lt; <span class="hljs-string">"1 "</span> &lt;&lt; <span class="hljs-string">"is running ... \n"</span>;
        Sleep(<span class="hljs-number">1000</span>);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-comment">// Hàm xử lý của thread 2</span>
<span class="hljs-function"><span class="hljs-keyword">unsigned</span> __stdcall <span class="hljs-title">t2_worker</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* args)</span> </span>{
    <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>)
    {
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Thread "</span> &lt;&lt; <span class="hljs-string">"2 "</span> &lt;&lt; <span class="hljs-string">"is running ... \n"</span>;
        Sleep(<span class="hljs-number">1000</span>);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>; 
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Khởi tạo thread 1</span>
    <span class="hljs-keyword">uintptr_t</span> t1_Handle = _beginthreadex(<span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, t1_worker, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

    <span class="hljs-comment">// Khởi tạo thread 2</span>
    <span class="hljs-keyword">uintptr_t</span> t2_Handle = _beginthreadex(<span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, t2_worker, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);

    <span class="hljs-comment">// Chờ cho hai thread kết thúc</span>
    <span class="hljs-comment">// Thực tế 2 thread sẽ chạy mãi trong vòng lặp while(true)</span>
    WaitForSingleObject((HANDLE)t1_Handle, INFINITE);
    WaitForSingleObject((HANDLE)t2_Handle, INFINITE);

    <span class="hljs-comment">// Đóng các thread handler</span>
    CloseHandle((HANDLE)t1_Handle);
    CloseHandle((HANDLE)t2_Handle);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả: ở lần chạy thứ 6 của vòng lặp while, khi thread 2 đang sử dụng đối tượng std::cout để ghi ra console chữ “Thread 2“ thì window schedule (bộ lập lịch) đã dừng thread hiện tại và chuyển CPU sang thread 1.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768659713642/6fdef143-e33e-4665-a9ff-f581bf789d6b.png" alt class="image--center mx-auto" /></p>
<p>Std:cout là đối tượng tài nguyên dùng chung (shared resource), ở ví dụ trên đã có hiện tượng race condition xảy ra, hai thread tranh nhau sử dụng để in ra màn hình console.</p>
<p>Để không xảy ra vấn đề này chúng ta sử dụng Mutex để đảm bảo mỗi thread ghi chuỗi hoàn tất thì các thread khác mới được quyền sử dụng đối tượng std::cout.</p>
<p>Trong C++ sử dụng thư viện std::mutex để triển khai như sau.</p>
<pre><code class="lang-cpp"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;process.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;windows.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;iostream&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;mutex&gt;</span></span>

<span class="hljs-comment">// Khai báo đối tượng mutex toàn cục</span>
<span class="hljs-built_in">std</span>::mutex g_Mutex;

<span class="hljs-comment">// Hàm xử lý của thread 1</span>
<span class="hljs-function"><span class="hljs-keyword">unsigned</span> __stdcall <span class="hljs-title">t1_worker</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* args)</span> </span>{
    <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>)
    {
        {
            <span class="hljs-comment">// Sử dụng lock_guard để tự động mở khóa</span>
            <span class="hljs-function"><span class="hljs-built_in">std</span>::lock_guard&lt;<span class="hljs-built_in">std</span>::mutex&gt; <span class="hljs-title">lock</span><span class="hljs-params">(g_Mutex)</span></span>;
            <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Thread "</span> &lt;&lt; <span class="hljs-string">"1 "</span> &lt;&lt; <span class="hljs-string">"is running ... \n"</span>;
        }
        Sleep(<span class="hljs-number">1000</span>);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-comment">// Hàm xử lý của thread 2</span>
<span class="hljs-function"><span class="hljs-keyword">unsigned</span> __stdcall <span class="hljs-title">t2_worker</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* args)</span> </span>{
    <span class="hljs-keyword">while</span> (<span class="hljs-literal">true</span>)
    {
        {
            <span class="hljs-comment">// Sử dụng lock_guard để tự động mở khóa</span>
            <span class="hljs-function"><span class="hljs-built_in">std</span>::lock_guard&lt;<span class="hljs-built_in">std</span>::mutex&gt; <span class="hljs-title">lock</span><span class="hljs-params">(g_Mutex)</span></span>;
            <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Thread "</span> &lt;&lt; <span class="hljs-string">"2 "</span> &lt;&lt; <span class="hljs-string">"is running ... \n"</span>;
        }
        Sleep(<span class="hljs-number">1000</span>);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Khởi tạo thread 1</span>
    <span class="hljs-keyword">uintptr_t</span> t1_Handle = _beginthreadex(<span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, t1_worker, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    <span class="hljs-keyword">if</span>(t1_Handle == <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Lỗi khởi tạo thread 1\n"</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// Khởi tạo thread 2</span>
    <span class="hljs-keyword">uintptr_t</span> t2_Handle = _beginthreadex(<span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, t2_worker, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-number">0</span>);
    <span class="hljs-keyword">if</span>(t2_Handle == <span class="hljs-number">0</span>) {
        <span class="hljs-built_in">std</span>::<span class="hljs-built_in">cout</span> &lt;&lt; <span class="hljs-string">"Lỗi khởi tạo thread 2\n"</span>;
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// Chờ cho hai thread kết thúc</span>
    <span class="hljs-comment">// Thực tế 2 thread sẽ chạy mãi trong vòng lặp while(true)</span>
    WaitForSingleObject((HANDLE)t1_Handle, INFINITE);
    WaitForSingleObject((HANDLE)t2_Handle, INFINITE);

    <span class="hljs-comment">// Đóng các thread handler</span>
    CloseHandle((HANDLE)t1_Handle);
    CloseHandle((HANDLE)t2_Handle);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả đã không xảy ra tình trạng các chữ bị in chồng lấn như trước.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768661083050/299771e3-f048-4cdf-b165-7794dd4941c9.png" alt class="image--center mx-auto" /></p>
<p><strong>Lưu ý:</strong></p>
<p>Mặc dù Mutex giúp đảm bảo tính đúng đắn của dữ liệu hoặc tranh chấp về tài nguyên dùng chung như std::cout chúng ta vừa khảo sát, nhưng nếu tranh chấp xảy ra nhiều, việc thực hiện Lock/Unlock quá thường xuyên sẽ làm thread bị block và phát sinh Context Switch gây chi phí lớn. Do đó, cần xem xét kỹ tần suất sử dụng và giữ thời gian chiếm giữ khóa (Lock time) ngắn nhất có thể để tránh làm giảm hiệu năng tổng thể của ứng dụng đa luồng.</p>
<p>Ở bài tiếp theo, chúng ta sẽ tìm hiểu <strong>Semaphore</strong> và cách sử dụng semaphore để đồng bộ hóa trong lập trình đa luồng.</p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Race Condition, Critical Section là gì? Cách sử dụng CRITICAL_SECTION trong lập trình đa luồng (Windows API)]]></title><description><![CDATA[Trong lập trình đa luồng, các luồng thuộc cùng môt tiến trình chia sẻ vùng nhớ Heap, biến toàn cục, Files. Do đó, sẽ xảy ra vấn đề tranh chấp nguồn tài nguyên giữa các luồng.
1.　Race condition
Race condition xảy ra khi kết quả của một chương trình ph...]]></description><link>https://u40hoccode.dev/race-condition-critical-section-la-gi-cach-su-dung-criticalsection-trong-lap-trinh-da-luong-windows-api</link><guid isPermaLink="true">https://u40hoccode.dev/race-condition-critical-section-la-gi-cach-su-dung-criticalsection-trong-lap-trinh-da-luong-windows-api</guid><category><![CDATA[race-condition]]></category><category><![CDATA[Computer Science]]></category><category><![CDATA[critical section]]></category><category><![CDATA[Windows API]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Sat, 17 Jan 2026 03:04:39 GMT</pubDate><content:encoded><![CDATA[<p>Trong lập trình đa luồng, các luồng thuộc cùng môt tiến trình chia sẻ vùng nhớ Heap, biến toàn cục, Files. Do đó, sẽ xảy ra vấn đề tranh chấp nguồn tài nguyên giữa các luồng.</p>
<p><strong>1.　Race condition</strong></p>
<p>Race condition xảy ra khi kết quả của một chương trình phụ thuộc vào thứ tự thực thi không thể đoán trước của các luồng (Timing).</p>
<p>Giả sử 2 luồng cùng truy cập vào biến bất kỳ, sẽ có 3 trường hợp xảy ra như sau</p>
<p>- Hai luồng cùng đọc đồng thời (R-R): trường hợp này an toàn, không có nguy cơ gây ra lỗi dữ liệu.</p>
<p>- Một luồng đọc, một luồng ghi (R-W, W-R): trường hợp này phân thành hai trường hợp nhỏ hơn</p>
<ul>
<li><p>Đọc trước ghi sau (R-W): nguy cơ luồng đọc dữ liệu không lấy được dữ liệu mới nhất. Giả sử luồng đọc lấy được giá trị X = 10, ngay sau đó luồng ghi cập nhật X = 20. Luồng đọc vẫn tiếp tục xử lý với giá trị X = 10 mà không biết giá trị đó đã hết hạn hoặc đã cũ.</p>
</li>
<li><p>Ghi trước đọc sau (W-R): Luồng ghi đang cập nhật dữ liệu nhưng chưa hoàn chỉnh, thì luồng đọc lấy dữ liệu. Dữ liệu này không phải giá trị cũ cũng không phải giá trị mới (dữ liệu bẩn).</p>
</li>
</ul>
<p>- Hai luồng ghi đồng thời (W-W): Khi hai luồng cùng ghi vào một giá trị, nguy cơ xảy ra tình trạng giá trị của một luồng bị ghi đè bởi luồng kia. Ví dụ biến toàn cục X = 10, hai luồng cùng tăng X lên 1. Mỗi phép tăng gồm ba bước: đọc X, cộng 1, rồi cập nhật giá trị cho X. Nếu hai luồng cùng đọc được X = 10 trước khi bất kỳ luồng nào kịp ghi kết quả, khi đó cả hai đều ghi giá trị 11. Kết quả cuối cùng X = 11 thay vì 12 như mong đợi. Một ví dụ khác, nếu như hai luồng cùng ghi vào một cấu trúc dữ liệu lớn, nếu không được đồng bộ có nguy cơ xảy ra tình trạng dữ liệu sau khi được ghi có một nửa mang giá trị của luồng thứ nhất và một nửa mang giá trị của luồng thứ 2.</p>
<p>Để đồng bộ các luồng truy cập dữ liệu đọc/ghi một cách an toàn, chúng ta có một vài cơ chế phổ biến đó là Critical Section, Mutex và Semaphore.</p>
<p><strong>2. Critical Section (Vùng găng)</strong></p>
<p>Critical Section là đoạn code mà tại đó có nguy cơ xảy ra race condition. Hệ điều hành Windows cung cấp cho chúng ta một đối tượng CRITICAL_SECTION chạy chủ yế ở user-mode để bảo vệ vùng găng ở trên, đảm bảo tại mỗi thời điểm chỉ có một luồng được phép truy cập vào tài nguyên chung.</p>
<p>Khi vùng găng đang được lock bởi một luồng khác thì luồng muốn vào sẽ chuyển sang chế độ Sleep đề chờ cho tới khi vùng găng được unlock.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768618882339/496f3391-5ecd-4154-91ed-ce2e60c32263.png" alt class="image--center mx-auto" /></p>
<p>Chúng ta cùng xem xét một ví dụ sau trên windows:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;Windows.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Hàm xử lý thread 1</span>
<span class="hljs-function">DWORD WINAPI <span class="hljs-title">Worker_T1</span><span class="hljs-params">(LPVOID param)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">500000</span>; i++)
    {
        x++;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-comment">// Hàm xử lý thread 2</span>
<span class="hljs-function">DWORD WINAPI <span class="hljs-title">Worker_T2</span><span class="hljs-params">(LPVOID param)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">500000</span>; i++)
    {
        x++;
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Khởi tạo thread t1</span>
    HANDLE t1ThreadHandle = CreateThread(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, Worker_T1, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">NULL</span>);
    <span class="hljs-keyword">if</span>(!t1ThreadHandle) {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"T1 thread create failed\n"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// Khởi tạo thread t2</span>
    HANDLE t2ThreadHandle = CreateThread(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, Worker_T2, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">NULL</span>);
    <span class="hljs-keyword">if</span>(!t2ThreadHandle) {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"T2 thread create failed\n"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// Main thread chờ cho 2 luồng thực hiện xong</span>
    WaitForSingleObject(t1ThreadHandle, INFINITE);
    WaitForSingleObject(t2ThreadHandle, INFINITE);

    <span class="hljs-comment">// Đóng các handle</span>
    CloseHandle(t1ThreadHandle);
    CloseHandle(t2ThreadHandle);

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Final x = %d\n"</span>, x);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả: chúng ta có thể thấy mỗi lần chạy chương trình lai cho ra một kết quả khác nhau. Không ra được kết quả như mong đợi (1.000.000).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768617166098/79a3705f-18eb-42ae-90c3-535d42992667.png" alt class="image--center mx-auto" /></p>
<p>Để không xảy ra vấn đề này chúng ta sử dụng đối tượng CRITICAL_SECTION do hệ điều hành windows cung cấp để bảo vệ vùng găng như sau:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;Windows.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>

<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Khai báo đối tượng CRITICAL_SECTION để đồng bộ</span>
CRITICAL_SECTION g_cs;

<span class="hljs-comment">// Hàm xử lý thread 1</span>
<span class="hljs-function">DWORD WINAPI <span class="hljs-title">Worker_T1</span><span class="hljs-params">(LPVOID param)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">500000</span>; i++) {
        <span class="hljs-comment">// Khóa quyền truy cập biến x</span>
        EnterCriticalSection(&amp;g_cs);
        x++;
        LeaveCriticalSection(&amp;g_cs);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-comment">// Hàm xử lý thread 2</span>
<span class="hljs-function">DWORD WINAPI <span class="hljs-title">Worker_T2</span><span class="hljs-params">(LPVOID param)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">500000</span>; i++) {
        <span class="hljs-comment">// Chờ cho đến khi được quyền truy cập vào biến x</span>
        EnterCriticalSection(&amp;g_cs);
        x++;
        LeaveCriticalSection(&amp;g_cs);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{

    <span class="hljs-comment">// Khởi tạo đối tượng CRITICAL_SECTION</span>
    InitializeCriticalSection(&amp;g_cs);

    <span class="hljs-comment">// Khởi tạo thread t1</span>
    HANDLE t1ThreadHandle = CreateThread(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, Worker_T1, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">NULL</span>);
    <span class="hljs-keyword">if</span>(!t1ThreadHandle) {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"T1 thread create failed\n"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// Khởi tạo thread t2</span>
    HANDLE t2ThreadHandle = CreateThread(<span class="hljs-number">0</span>, <span class="hljs-number">0</span>, Worker_T2, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">NULL</span>);
    <span class="hljs-keyword">if</span>(!t2ThreadHandle) {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"T2 thread create failed\n"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// Main thread chờ cho 2 luồng thực hiện xong</span>
    WaitForSingleObject(t1ThreadHandle, INFINITE);
    WaitForSingleObject(t2ThreadHandle, INFINITE);

    <span class="hljs-comment">// Đóng các handle của luồng</span>
    CloseHandle(t1ThreadHandle);
    CloseHandle(t2ThreadHandle);

    <span class="hljs-comment">// Xóa đối tượng CRITICAL_SECTION</span>
    DeleteCriticalSection(&amp;g_cs);

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Final x = %d\n"</span>, x);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả đã không xảy ra tình trạng sai lệch giá trị của biến x</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768617844596/d58238e6-4ce7-45c8-a729-77522b6aef24.png" alt class="image--center mx-auto" /></p>
<p>Ở bài tiếp theo, chúng ta sẽ tìm hiểu về <strong>Mutex</strong> và cách sử dụng để đồng bộ hóa trong lập trình đa luồng.</p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Luồng - Thread]]></title><description><![CDATA[Luồng là một thành phần và là đơn vị thực thi nhỏ nhất của tiến trình.
Một tiến trình có thể chỉ có một luồng hoặc có nhiều luồng chạy song song để xử lý các công việc khác nhau.
Giả sử chúng ta có một bài toán như sau: tạo một phần mềm vừa tính toán...]]></description><link>https://u40hoccode.dev/luong-thread</link><guid isPermaLink="true">https://u40hoccode.dev/luong-thread</guid><category><![CDATA[Threads]]></category><category><![CDATA[Windows]]></category><category><![CDATA[Linux]]></category><category><![CDATA[Computer Science]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Thu, 15 Jan 2026 11:27:27 GMT</pubDate><content:encoded><![CDATA[<p>Luồng là một thành phần và là đơn vị thực thi nhỏ nhất của tiến trình.</p>
<p>Một tiến trình có thể chỉ có một luồng hoặc có nhiều luồng chạy song song để xử lý các công việc khác nhau.</p>
<p>Giả sử chúng ta có một bài toán như sau: tạo một phần mềm vừa tính toán xử lý dữ liệu rất lớn tốn thời gian (CPU-bound), vừa phải thực hiện truy vấn cơ sở dữ liệu để lấy thông tin (I/O-bound).</p>
<p>Trong những trường hợp như vậy có hai hướng tiếp cận.</p>
<p>Nếu như driver của Database không hỗ trợ API bất đồng bộ, ngoài luồng chính để điều phối chương trình, chúng ta cần thiết kế các luồng chạy song song, một luồng chuyên để tính toán xử lý dữ liệu và một luồng khác chuyên phục vụ truy vấn cơ sở dữ liệu.</p>
<p>Nếu như driver của Database hỗ trợ API bất đồng bộ thì luồng chính có thể gọi API bất đồng bộ, phần I/O sẽ được hệ thống hoặc thư viện xử lý ở tầng bên dưới (thread pool/ event loop), ứng dụng chỉ cần thêm luồng riêng cho tác vụ CPU-bound.</p>
<p>Trên Windows luồng có thể được tạo bằng API CreateThread, trong khi đó trên Linux sử dụng POSIX Threads để tạo và quản lý luồng.</p>
<p>Chúng ta cùng demo một chương trình tạo ra các luồng trên Windows và Linux.</p>
<p><strong>Ví dụ đa luồng trên Linux:</strong></p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;pthread.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;unistd.h&gt;</span></span>

<span class="hljs-comment">// hàm xử lý cho luồng tính toán</span>
<span class="hljs-function"><span class="hljs-keyword">void</span>* <span class="hljs-title">Compute</span><span class="hljs-params">(<span class="hljs-keyword">void</span>* args)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"[Worker thread]::Compute step ... %d\n"</span>, i);
        sleep(<span class="hljs-number">1</span>);
    }
    <span class="hljs-keyword">return</span> <span class="hljs-literal">NULL</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"[Main thread]::Start\n"</span>);

    <span class="hljs-comment">// tạo luồng tính toán</span>
    <span class="hljs-keyword">pthread_t</span> computeThread;
    <span class="hljs-keyword">if</span>(pthread_create(&amp;computeThread, <span class="hljs-literal">NULL</span>, Compute, <span class="hljs-literal">NULL</span>) != <span class="hljs-number">0</span>)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Thread create error\n"</span>);
        <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
    }

    <span class="hljs-comment">// luồng chính vẫn đang chạy song song</span>
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">10</span>; i++)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"[Main thread]::Do other something ... %d\n"</span>, i);
        sleep(<span class="hljs-number">1</span>);
    }

    <span class="hljs-comment">// chờ luồng tính toán kết thúc</span>
    pthread_join(computeThread, <span class="hljs-literal">NULL</span>);

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"[Main thread]::End\n"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả chúng ta có 1 luồng mới chạy song song với luồng chính như kết quả dưới đây.</p>
<ul>
<li><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768314391033/4745756b-f2e8-4561-91a4-c1a07da834ce.png" alt class="image--center mx-auto" /></li>
</ul>
<p><strong>Ví dụ đa luồng trên Windows sử dụng thư viện Win32 API:</strong></p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;Windows.h&gt;</span></span>

<span class="hljs-keyword">int</span> x = <span class="hljs-number">0</span>;

<span class="hljs-comment">// Hàm xử lý cho worker thread</span>
<span class="hljs-function">DWORD WINAPI <span class="hljs-title">Worker</span><span class="hljs-params">(LPVOID params)</span> </span>{
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">5</span>; i++)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Worker thread id = [%lu]: %d\n"</span>, GetCurrentThreadId(), i);

        <span class="hljs-comment">// tăng x lên 1</span>
        x = x + <span class="hljs-number">1</span>;

        Sleep(<span class="hljs-number">1000</span>);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// ở luồng chính tăng biến x lên 1</span>
    x = x + <span class="hljs-number">1</span>;

    <span class="hljs-comment">// Tạo hai luồng </span>
    HANDLE threads[<span class="hljs-number">2</span>];
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>; i &lt; <span class="hljs-number">2</span>; i++)
    {
        threads[i] = CreateThread(<span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, Worker, <span class="hljs-literal">NULL</span>, <span class="hljs-number">0</span>, <span class="hljs-literal">NULL</span>);
        <span class="hljs-keyword">if</span>(threads[i] == <span class="hljs-literal">NULL</span>) {
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Create thread failed\n"</span>);
            <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;
        }
    }

    <span class="hljs-comment">// Chờ 2 luồng kết thúc xử lý</span>
    WaitForMultipleObjects(<span class="hljs-number">2</span>, threads, TRUE, INFINITE);
    CloseHandle(threads[<span class="hljs-number">0</span>]);
    CloseHandle(threads[<span class="hljs-number">1</span>]);

    <span class="hljs-comment">// Kiểm tra giá trị biến x</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"x = %d\n"</span>, x);

    <span class="hljs-comment">// Kết thúc luồng chính</span>
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Main thread finished\n"</span>);
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả chúng ta có hai luồng chạy song song ngoài luồng chính.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768475042698/9f3bf7fb-5ec9-4943-9e46-b133d47b43f9.png" alt class="image--center mx-auto" /></p>
<p>Ở ví dụ trên, biến x trước khi kết thúc hàm main có giá trị là 11, chúng ta có thể thấy x đã được tăng lên 1 bở hàm main và tăng thêm 10 bởi 2 worker thread.</p>
<p>Các thread trên cùng một process chia sẻ chung một không gian địa chỉ (Heap, global, file handler) nhưng mỗi thread có Stack và Contex riêng (register, instruction pointer).</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768477254373/f47e45aa-02b7-46bc-9114-d2c5febe28d6.png" alt class="image--center mx-auto" /></p>
<p>Trên Windows Các thông tin riêng của Tthread như Stack và Contex được Kernel lưu ở TCB (Thread Control Block) của mỗi thread, không nằm trong PCB (Process Control Block) của tiến trình.</p>
<p>Trên linux không phân biệt PCB hay TCB mà các thread đều được biểu diễn bằng struct task_struct, tiến trình là một nhóm các thread chia sẻ tài nguyên.</p>
<p>Lưu ý ở ví dụ trên, biến x là biến dùng chung và được đọc/ghi đồng thời bởi các thread. Để không phát sinh các vấn đề gây ra sai lệch dữ liệu (giá trị x có thể không đảm bảo là 11) chúng ta sẽ cần cơ chế đồng bộ để các thread có thể đọc/ghi an toàn.<br />Vấn đề này chúng ta sẽ làm rõ ở bài tiếp theo.</p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Tiến trình (Process) - Tổ chức bộ nhớ]]></title><description><![CDATA[Tổ chức bộ nhớ của một tiến trình bao gồm các thành phần sau: Code/Text segment, Data Segment (.data và .rodata), bss, Heap và Stack và được sắp xếp trong không gian địa chỉ ảo của tiến trình theo thứ tự từ địa chỉ thấp lên địa chỉ cao.


Lưu ý: đây ...]]></description><link>https://u40hoccode.dev/tien-trinh-process-to-chuc-bo-nho</link><guid isPermaLink="true">https://u40hoccode.dev/tien-trinh-process-to-chuc-bo-nho</guid><category><![CDATA[Computer Science]]></category><category><![CDATA[embedded]]></category><category><![CDATA[process]]></category><category><![CDATA[Linux]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Tue, 13 Jan 2026 10:58:28 GMT</pubDate><content:encoded><![CDATA[<p>Tổ chức bộ nhớ của một tiến trình bao gồm các thành phần sau: Code/Text segment, Data Segment (.data và .rodata), bss, Heap và Stack và được sắp xếp trong không gian địa chỉ ảo của tiến trình theo thứ tự từ địa chỉ thấp lên địa chỉ cao.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768187958421/aac62dda-36df-4c42-9c91-16ef3222b65d.png" alt class="image--center mx-auto" /></p>
<ul>
<li>Lưu ý: đây là tổ chức bộ nhớ của tiến trình bên trong bộ nhớ ảo, không phản ánh thực tế bên trong bộ nhớ chính (RAM), các địa chỉ này sẽ được đơn vị quản lý bộ nhớ (MMU - Memory Management Unit) ánh xạ lên địa chỉ thật ở trên RAM.</li>
</ul>
<ol>
<li><strong>Code segment (Text segment)</strong></li>
</ol>
<p>Đây là phần mã lệnh (Code/Text) sau khi chương trình được biên dịch sang mã máy. Hay chính là các câu lệnh chỉ thị cho CPU thực hiện.</p>
<p>Cùng xem câu lệnh ở bên dưới:</p>
<pre><code class="lang-c"><span class="hljs-keyword">int</span> x = <span class="hljs-number">10</span>;
x = x + <span class="hljs-number">1</span>;
</code></pre>
<p><strong>“x = x + 1“</strong> khi được biên dịch sẽ sinh ra mã máy, và mã máy đó được lưu trong Code/Text segment.</p>
<p>Đặc điểm quan trọng của phần Code/Text:</p>
<ul>
<li><p>Đây là phần chỉ đọc (Read only) tránh cho chương trình tự sửa code</p>
</li>
<li><p>Nạp từ file thực thi vào RAM khi tạo process</p>
</li>
</ul>
<ol start="2">
<li><strong>Data Segment</strong></li>
</ol>
<p>Data Segment là nơi lưu trữ các biến toàn cục (global) và biến tĩnh đã được khởi tạo khác 0.</p>
<p>Data bao gồm 2 vùng là <strong>.data</strong> và <strong>.rodata</strong></p>
<p>.data là vùng chứa biến có thể thay đổi (Read/Write)</p>
<p>.rodata là vùng chứa các hằng số (Read only)</p>
<p>Chúng ta cùng xem xét một chương trình đơn giản như sau:</p>
<pre><code class="lang-c"><span class="hljs-keyword">int</span> a = <span class="hljs-number">10</span>;
<span class="hljs-keyword">int</span> b;
<span class="hljs-keyword">int</span> c = <span class="hljs-number">0</span>;
<span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* d = <span class="hljs-string">"abc"</span>;

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">int</span> e = <span class="hljs-number">100</span>;
    <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* f = <span class="hljs-string">"abc"</span>;
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> g = <span class="hljs-number">20</span>;
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> h;
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">int</span> i = <span class="hljs-number">0</span>;
    <span class="hljs-keyword">static</span> <span class="hljs-keyword">const</span> <span class="hljs-keyword">char</span>* j = <span class="hljs-string">"abc"</span>;
    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>a là biến toàn cục có khởi tạo khác 0 → a nằm trong Data Segment (vùng .data).</p>
<p>b là biến toàn cục không có khởi tạo và c là biến toàn cục khởi tạo bằng 0 → b và c không nằm trong Data Segment. (b và c nằm trong vùng .bss)</p>
<p>d là biến toàn cục có khởi tạo khác 0 → d nằm trong Data Segment (vùng .data), “abc“ là hằng số nằm trong vùng .rodata của Data Segment.</p>
<p>các biến e và f là biến cục bộ (local) của hàm main() → e và f không nằm trong Data Segment. (Nằm trong stack của hàm main() bên trong vùng Stack).</p>
<p>g là biến static có khởi tạo khác 0 → g nằm trong vùng .data của Data Segment.</p>
<p>h và i là biến static chưa được khởi tạo hoặc khởi tạo bằng 0 → h và i không nằm trong Data Segment (h và i nằm trong vùng .bss)</p>
<p>j là biến static có khởi tạo khác 0 → j nằm trong vùng .data của Data Segment, “abc“ sẽ nằm trong vùng .rodata của Data Segment.</p>
<ol start="3">
<li><strong>bss Segment</strong></li>
</ol>
<p>Bss là vùng bộ nhớ được hệ điều hành cấp phát khi khởi tạo process để lưu trữ các biến toàn cục (global) hoặc biến static chưa được khởi tạo hoặc khởi tạo bằng 0, mục đích là để giảm kích thước file thực thi.</p>
<ol start="4">
<li><strong>Heap</strong></li>
</ol>
<p>Heap là vùng bộ nhớ được cấp phát động trong quá trình thực thi tiến trình, ví dụ thực hiện hàm malloc() trong ngôn ngữ c hoặc toán tử new trong các ngôn ngữ bậc cao.</p>
<p>Chúng ta cùng xem xét ví dụ sau:</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdlib.h&gt;</span></span>

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Cấp phát một mảng có kích thước là 400 bytes</span>
    <span class="hljs-keyword">int</span>* p = <span class="hljs-built_in">malloc</span>(<span class="hljs-number">100</span> * <span class="hljs-keyword">sizeof</span>(<span class="hljs-keyword">int</span>));
    <span class="hljs-keyword">if</span>(!p) <span class="hljs-keyword">return</span> <span class="hljs-number">1</span>;

    <span class="hljs-comment">// Giải phóng vùng nhớ trên Heap sau khi sử dụng</span>
    <span class="hljs-built_in">free</span>(p);
    p = <span class="hljs-literal">NULL</span>;

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>p la biến cục bộ của hàm main() → p nằm ở trong stack của hàm main() trong vùng Stack.</p>
<p>Vùng nhớ có kích thước 100 * sizeof(int) (400 bytes) nằm trong vùng Heap.</p>
<p>Thông thường các biến cục bộ của hàm nằm trên stack của hàm sẽ được giải phóng theo phạm vi { } của hàm, nhưng khi cấp phát vùng nhớ trong vùng Heap để sử dụng, cần được giải phóng sau khi sử dụng, vì các vùng nhớ này không được tự giải phóng theo phạm vi của hàm.</p>
<ol start="5">
<li><strong>Stack</strong></li>
</ol>
<p>Stack là vùng nhớ lưu các biến cục bộ, các tham số hàm và địa chỉ trả về của hàm.</p>
<p>Địa chỉ trả về của hàm là là địa chỉ của lệnh tiếp theo ngay sau lời gọi hàm.</p>
<p>Stack hoạt động theo nguyên lý LIFO (last in first out - vào sau ra trước)</p>
<p>Chúng ta cùng xem xét ví dụ sau:</p>
<pre><code class="lang-c"><span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">total</span><span class="hljs-params">(<span class="hljs-keyword">int</span> a, <span class="hljs-keyword">int</span> b)</span> </span>{
    <span class="hljs-keyword">return</span> a + b;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">int</span> a = <span class="hljs-number">2</span>;
    <span class="hljs-keyword">int</span> b = <span class="hljs-number">3</span>;
    <span class="hljs-keyword">int</span> c = total(a, b);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Khi tiến trình được tạo, hàm main sẽ được cấp phát một vùng nhớ trong Stack (main stack frame), trong đó lưu các biến a, b, c và địa chỉ trả về của hàm main.</p>
<p>Khi hàm total được gọi, một stack frame mới cho total được tạo trong Stack (total stack frame), copy của các giá trị a và b và địa chỉ trả về của hàm total (địa chỉ của câu lệnh gán giá trị của total cho biến c trong hàm main) được đẩy vào total stack frame.</p>
<p>Sau khi hàm total thực hiện xong total stack frame sẽ được giải phóng khỏi Stack.</p>
<p>Sau khi gán giá trị của total cho biến c, hàm tiếp tục câu lệnh return 0, main stack frame sẽ được giải phóng khỏi Stack để chuẩn bị các câu lệnh kết thúc tiến trình.</p>
<p>Như vậy Stack hoạt động theo nguyên lý LIFO. Mỗi hàm được gọi sẽ tạo ra một stack frame mới và bị hủy khi hàm kết thúc.</p>
<p>Hiểu rõ tổ chức bộ nhớ của tiến trình giúp tránh lỗi memory, debug chính xác và viết chương trình hiệu quả hơn.</p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Tiến trình (Process)]]></title><description><![CDATA[Tiến trình là một chương trình đang chạy trong máy tính.
Tiến trình được tạo ra khi một chương trình được tải vào bộ nhớ để thực thi.

Các trạng thái của tiến trình

Trạng thái của tiến trình là cách hệ điều hành theo dõi quá trình hoạt động của tiến...]]></description><link>https://u40hoccode.dev/tien-trinh-process</link><guid isPermaLink="true">https://u40hoccode.dev/tien-trinh-process</guid><category><![CDATA[Computer Science]]></category><category><![CDATA[Linux]]></category><category><![CDATA[process management]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Mon, 12 Jan 2026 13:51:23 GMT</pubDate><content:encoded><![CDATA[<p>Tiến trình là một chương trình đang chạy trong máy tính.</p>
<p>Tiến trình được tạo ra khi một chương trình được tải vào bộ nhớ để thực thi.</p>
<ol>
<li><strong>Các trạng thái của tiến trình</strong></li>
</ol>
<p>Trạng thái của tiến trình là cách hệ điều hành theo dõi quá trình hoạt động của tiến trình. Mỗi tiến trình luôn nằm trong một trạng thái cụ thể tại một thời điểm. Trạng thái tiến trình thay đổi khi chạy, chờ, hay bị tạm dừng.</p>
<p>a. New</p>
<p>Tiến trình được tạo mới nhưng chưa sẵn sàng để chạy (chưa vào hàng đợi Ready).</p>
<p>b. Ready</p>
<p>Tiến trình đang ở trong bộ nhớ chờ CPU chọn để chạy.</p>
<p>c. Running</p>
<p>Tiến trình đang được CPU thực thi lệnh.</p>
<p>d. Waiting/Blocked</p>
<p>Tiến trình chuyển sang trạng thái chờ hoặc blocked khi chờ xử lý sự kiên I/O ví dụ như đọc/ghi file đồng bộ. Khi đó việc đọc/ghi file được thực hiện bằng các hàm đọc file của hệ điều hành. Tiến trình sẽ rơi vào trạng thái blocked để chờ các tác vụ này hoàn thành mới tiếp tục.</p>
<p>Tiến trình ở trạng thái blocked sẽ không được chọn để chạy dù CPU rảnh.</p>
<p>e. Terminated/Exit</p>
<p>Tiến trình đã thực hiện xong chuyển sang trạng thái kết thúc.</p>
<ol start="2">
<li><strong>Thông tin mô tả của tiến trình</strong></li>
</ol>
<p>Thông tin của tiến trình được lưu trong một cấu trúc dữ liệu là PCB (Process Control Block) gọi là khối quản lý tiến trình.</p>
<p>Trong mã nguồn của hệ điều hành Linux, thông tin của tiến trình (PCB) được lưu trong struct task_struct, định nghĩa trong file sched.h của mã nguồn.</p>
<p>Chúng ta hãy xem mã nguồn Linux phiên bản rút gọn của struct task_struct như sau:</p>
<pre><code class="lang-c"><span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">task_struct</span> {</span>
    <span class="hljs-keyword">pid_t</span> pid;             
    <span class="hljs-keyword">pid_t</span> tgid;                   
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">task_struct</span> *<span class="hljs-title">real_parent</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">task_struct</span> *<span class="hljs-title">parent</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">list_head</span> <span class="hljs-title">children</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">list_head</span> <span class="hljs-title">sibling</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">task_struct</span> *<span class="hljs-title">group_leader</span>;</span>
    <span class="hljs-keyword">char</span> comm[TASK_COMM_LEN];

    <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> __state; 

    <span class="hljs-keyword">int</span> prio;
    <span class="hljs-keyword">int</span> static_prio;
    <span class="hljs-keyword">int</span> normal_prio;
    <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">int</span> rt_priority;
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sched_entity</span> <span class="hljs-title">se</span>;</span>        
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sched_rt_entity</span> <span class="hljs-title">rt</span>;</span>     
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sched_dl_entity</span> <span class="hljs-title">dl</span>;</span>     
    <span class="hljs-keyword">const</span> <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sched_class</span> *<span class="hljs-title">sched_class</span>;</span>

    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mm_struct</span> *<span class="hljs-title">mm</span>;</span>          
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">mm_struct</span> *<span class="hljs-title">active_mm</span>;</span>   

    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">files_struct</span> *<span class="hljs-title">files</span>;</span>    
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">fs_struct</span> *<span class="hljs-title">fs</span>;</span>          

    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">signal_struct</span> *<span class="hljs-title">signal</span>;</span>
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sighand_struct</span> __<span class="hljs-title">rcu</span> *<span class="hljs-title">sighand</span>;</span>
    <span class="hljs-keyword">sigset_t</span> blocked;
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">sigpending</span> <span class="hljs-title">pending</span>;</span>

    <span class="hljs-keyword">int</span> on_cpu;                     
    <span class="hljs-keyword">int</span> on_rq;                      
    u64 utime;                      
    u64 stime;                      
    u64 start_time;                 
    <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> nvcsw;            
    <span class="hljs-keyword">unsigned</span> <span class="hljs-keyword">long</span> nivcsw;           

    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">list_head</span> <span class="hljs-title">tasks</span>;</span>         

    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">thread_struct</span> <span class="hljs-title">thread</span>;</span>
};
</code></pre>
<p>a. Nhóm định danh và mối quan hệ của tiến trình, giúp Kernel biết tiến trình là ai và thuộc về đâu.</p>
<ul>
<li><p>pid (Process ID): Trong Linux, mỗi thread cũng được coi là một process, vì vậy mỗi thread có một pid riêng.</p>
</li>
<li><p>tgid (Thread Group ID): mã số định danh duy nhất của tiến trình, Các thread thuộc cùng một tiến trình có chung tgid.</p>
</li>
</ul>
<p>b. Nhóm trạng thái và lập lịch (State, Scheduling)</p>
<ul>
<li><p>__state: là trạng thái của tiến trình như RUNNING, BLOCKED …</p>
</li>
<li><p>prio: mức độ ưu tiên của tiến trình, Kernel dùng để tính toán xem tiến trình có được ưu tiên chiếm CPU của các tiến trình khác hay không.</p>
</li>
</ul>
<p>c. Nhóm quản lý bộ nhớ</p>
<ul>
<li>mm: con trỏ trỏ đến không gian bộ nhớ của tiến trình trong RAM (Code/Text, Data, bss, Heap, Stack)</li>
</ul>
<p>d. Nhóm quản lý file và hệ thống tệp</p>
<ul>
<li><p>files: bảng mô tả file, lưu danh sách các file mà tiến trình đang mở</p>
</li>
<li><p>fs: lưu thông tin về thư mục gốc (root) và thư mục hiện hành (curent working directory) của tiến trình</p>
</li>
</ul>
<p>e. Nhóm thống kê tài nguyên</p>
<ul>
<li><p>utime/stime: thống kê thời gian tiến trình chạy ở chế độ User mode và Kernel mode</p>
</li>
<li><p>start_time: thời điểm tiến trình được tạo</p>
</li>
</ul>
<p>f. Liên kết tiến trình</p>
<ul>
<li>list_head tasks: kết nối tiến trình vào danh sách chứa toàn bộ tiến trình toàn hệ thống, giúp scheduler duyệt và quản lý toàn bộ tiến trình.</li>
</ul>
<ol start="3">
<li><strong>Xem thông tin của tiến trình đang chạy trong Linux</strong></li>
</ol>
<p>Trong linux, tiến trình đang chạy được lưu trong thư mục /proc.</p>
<p>Mỗi tiến trình có một thư mục con theo PID (process ID)</p>
<p>chúng ta có thể xem toàn bộ các tiến trình bằng câu lệnh:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> /proc
ls
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768219766483/8e6de94e-a671-4c00-bb8a-50b733e2373e.png" alt class="image--center mx-auto" /></p>
<p>Mỗi thư mục 1, 110, 114 … là số PID tương ứng của một tiến trình.</p>
<p>Chúng ta có thể truy cập vào từng thư mục để xem cụ thể thông tin của một tiến trình cụ thể (ví dụ tiến trình có mã số PID là 110) như sau:</p>
<pre><code class="lang-bash"><span class="hljs-built_in">cd</span> 110
cat status
</code></pre>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768220449485/d8454698-de03-4324-b02e-58d5ab0b1f68.png" alt class="image--center mx-auto" /></p>
<ul>
<li><p>Name: là tên tiến trình</p>
</li>
<li><p>State: là trạng thái hiện tại (Sleeping)</p>
</li>
<li><p>Pid: 110</p>
</li>
</ul>
<ol start="4">
<li><strong>Chuyển đổi giữa các tiến trình</strong></li>
</ol>
<p>Trong quá trình thực hiện, CPU có thể chuyển từ thực hiện tiến trình hiện thời sang thực hiện tiến trình khác. Trong trường hợp như vậy hệ điều hành cần lưu thông tin của tiến trình đang chạy để có thể khôi phục và thực hiện lại tiến trình tự thời điểm bị tạm dừng.</p>
<p>Thông tin về tiến trình hiện thời được gọi là Context và việc chuyển đổi giữa các tiến trình là chuyển đổi ngữ cảnh - Context Switch.</p>
<p>Việc chuyển đổi tiến trình xảy ra trong các trường hợp sau:</p>
<ul>
<li><p>Khi có ngắt:</p>
</li>
<li><p>khi tiến trình gọi lời gọi hệ thống</p>
</li>
</ul>
<p>Ngắt: có thể sinh ra do các sự kiện bên ngoài (ngắt do Time Tick khi tiến trình đã chạy hết thời gian được phân bổ, ngắt vào ra dữ liệu của DMA, ngắt khi bấm phím trên bàn phím … ) hoặc ngắt do lỗi phát sinh bên trong tiến trình.</p>
<p>Lời gọi hệ thống: ví dụ khi tiến trình gọi các hàm đọc/ghi file của hệ điều hành.</p>
<p>Ngữ cảnh của tiến trình được lưu trong cấu trúc PCB được mô tả ở trên.</p>
<p>Trước khi xảy ra Context Switch để thực hiện tiến trình khác, các thông tin có thể thay đổi như nội dung các thanh ghi, trạng thái của CPU sẽ được lưu vào PCB.</p>
<p>Sau khi kết thúc ngắt các thông tin về các thanh ghi, trạng thái của CPU được nạp lại từ PCB và tiến trình có thể tiếp tục thực hiện lại từ vị trí trước khi bị ngắt.</p>
<p>Tóm lại, để có thể chuyển đổi giữa các tiến trình thì hệ thống cần thực hiện một số bước liên quan tới việc lưu và khôi phục ngữ cảnh của tiến trình.</p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Tổ chức bộ nhớ của hệ điều hành]]></title><description><![CDATA[Bộ nhớ của máy tính bao gồm các loại sau:

Thanh ghi (nằm trong CPU) - Register
 Thanh ghi là bộ nhớ có dung lượng rất nhỏ nằm trực tiếp trong CPU và được quản lý trực tiếp bởi CPU.
 Đây là nơi lưu giữ:
 Toán hạng đang được xử lý.
 Kết quả trung gian...]]></description><link>https://u40hoccode.dev/to-chuc-bo-nho-cua-he-dieu-hanh</link><guid isPermaLink="true">https://u40hoccode.dev/to-chuc-bo-nho-cua-he-dieu-hanh</guid><category><![CDATA[Computer Science]]></category><category><![CDATA[memory]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Sun, 11 Jan 2026 14:01:43 GMT</pubDate><content:encoded><![CDATA[<p>Bộ nhớ của máy tính bao gồm các loại sau:</p>
<ol>
<li><p>Thanh ghi (nằm trong CPU) - Register</p>
<p> Thanh ghi là bộ nhớ có dung lượng rất nhỏ nằm trực tiếp trong CPU và được quản lý trực tiếp bởi CPU.</p>
<p> Đây là nơi lưu giữ:</p>
<p> Toán hạng đang được xử lý.</p>
<p> Kết quả trung gian.</p>
<p> Địa chỉ bộ nhớ, trạng thái CPU, lệnh đang được thực thi.</p>
<p> Thanh ghi có tốc độ truy xuất nhanh nhất trong bộ nhớ của CPU.</p>
</li>
<li><p>Bộ nhớ đệm (nằm giữa CPU và RAM) - Cache</p>
<p> Đây là bộ nhớ tạm thời tốc độ cao dùng để lưu dữ liệu và lệnh truy cập thường xuyên.</p>
<p> Bộ nhớ Cache nằm giữa CPU và Bộ nhớ chính (RAM).</p>
<p> Khi CPU cần dữ liệu, sẽ truy cập Cache trước, nếu có (Cache hit) sẽ dùng ngay, nếu không có (Cache miss) sẽ truy cập RAM để lấy dữ liệu, sau đó dữ liệu đó được copy vào Cache để dùng cho lần sau.</p>
<p> Kích thước của Cache thông thường từ vài KB đến 4 MB tùy thuộc loại Cache L1, L2 hay L3.</p>
</li>
<li><p>Bộ nhớ chính - RAM</p>
<p> Đây là bộ nhớ lưu giữ chương trình và dữ liệu đang được thực thi.</p>
<p> RAM nằm ngoài CPU và được kết nối với CPU thông qua bus bộ nhớ và có tốc độ chậm hơn Cache.</p>
<p> Khi chương trình chạy, hệ điều hành nạp chương trình và dữ liệu từ Disk vào RAM, CPU không truy cập trực tiếp vào Disk mà làm việc trực tiếp với dữ liệu ở trong RAM.</p>
<p> Dữ liệu trong RAM có thể được copy vào Register hoặc Cache để tăng tốc độ truy xuất dữ liệu.</p>
<p> Dữ liệu trong RAM sẽ mất khi mất điện.</p>
</li>
<li><p>Disk - SSD</p>
<p> Ổ cứng SSD là nơi lưu dữ liệu lâu dài. Dữ liệu không mất khi mất điện.</p>
<p> CPU không truy cập dữ liệu trực tiếp trong Disk mà sẽ được Load vào RAM để xử lý.</p>
</li>
</ol>
<p>Sau đây chúng ta sẽ viết một chương trình c để xem tốc độ truy xuất dữ liệu của các loại bộ nhớ trên hệ điều hành Linux.</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;time.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;string.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;fcntl.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;unistd.h&gt;</span></span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> LOOP            100000000</span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> SMALL_SIZE      (32 * 1024)             <span class="hljs-comment">// 32 KB (L1 Cache size)</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> LARGE_SIZE      (200 * 1024 * 1024)     <span class="hljs-comment">// 200 MB (vượt qua kích thước của Cache)</span></span>

<span class="hljs-meta">#<span class="hljs-meta-keyword">define</span> CACHE_LINE      64                      <span class="hljs-comment">// kích thước Cache Line là 64 byte</span></span>

<span class="hljs-comment">// Hàm lấy số giây đã trôi qua kể từ khi hệ thống khởi động</span>
<span class="hljs-function"><span class="hljs-keyword">double</span> <span class="hljs-title">now</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-class"><span class="hljs-keyword">struct</span> <span class="hljs-title">timespec</span> <span class="hljs-title">t</span>;</span>
    clock_gettime(CLOCK_BOOTTIME, &amp;t);
    <span class="hljs-keyword">return</span> t.tv_sec + t.tv_nsec*<span class="hljs-number">1e-9</span>;
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-keyword">double</span> t;

    <span class="hljs-comment">// 1. Mô phỏng thời gian CPU truy cập Cache</span>
    <span class="hljs-comment">// Mảng small được cấp phất một mảng đúng bằng 32KB vừa với kích thước của L1 Cache</span>
    <span class="hljs-comment">// do đó tỷ lệ Cache miss rất ít</span>
    <span class="hljs-keyword">char</span>* small = <span class="hljs-built_in">malloc</span>(SMALL_SIZE);
    <span class="hljs-built_in">memset</span>(small, <span class="hljs-number">0</span>, SMALL_SIZE);

    t = now();
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; LOOP; i++)
    {
        <span class="hljs-comment">// i * CACHE_LINE đảm bảo mỗi lần load dữ liệu ở một cache line mới</span>
        small[(i * CACHE_LINE) % SMALL_SIZE]++;
    }
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Thời gian truy cập Cache: %.3f ms\n"</span>, now() - t);

    <span class="hljs-comment">// 2. Mô phỏng thời gian CPU truy cập RAM</span>
    <span class="hljs-comment">// Mảng large được cấp phát là 200 MB lớn hơn rất nhiều so với kích thước của L3 Cache</span>
    <span class="hljs-comment">// do đó sẽ xuất hiện Cache miss tương đối lớn</span>
    <span class="hljs-keyword">char</span>* larger = <span class="hljs-built_in">malloc</span>(LARGE_SIZE);
    <span class="hljs-built_in">memset</span>(larger, <span class="hljs-number">0</span>, LARGE_SIZE);

    t = now();
    <span class="hljs-keyword">for</span> (<span class="hljs-keyword">size_t</span> i = <span class="hljs-number">0</span>; i &lt; LOOP; i ++)
    {
        larger[(i * CACHE_LINE) % LARGE_SIZE]++;
    }
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Thời gian truy cập RAM: %.3f ms\n"</span>, now() - t);

    <span class="hljs-comment">// 3. Mô phỏng thời gian hệ điều hành truy cập ổ cứng</span>
    <span class="hljs-comment">// Ghi mảng large ra file đồng bộ</span>
    <span class="hljs-keyword">int</span> fd = open(<span class="hljs-string">"demo.bin"</span>, O_WRONLY | O_CREAT | O_TRUNC | O_SYNC, <span class="hljs-number">0644</span>);
    t = now();
    write(fd, larger, LARGE_SIZE);
    close(fd);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Thời gian ghi 200MB ra Disk: %.3f ms\n"</span>, now() - t);

    <span class="hljs-comment">// Giải phóng bộ nhớ</span>
    <span class="hljs-built_in">free</span>(small);
    <span class="hljs-built_in">free</span>(larger);

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả:</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768139911841/54e32f8d-01b9-4d28-af24-b509bad5fbfe.png" alt class="image--center mx-auto" /></p>
<p>Kết luận:</p>
<ol>
<li><p>Mảng 32 KB có kích thước vừa với L1 Cache, tỷ lệ Cache miss thấp, thời gian truy cập rất nhanh</p>
</li>
<li><p>Mảng 200 MB vượt Cache size, nhiều Cache miss, CPU phải truy cập từ RAM, thời gian truy cập lâu hơn Cache</p>
</li>
<li><p>Ghi ra SSD là tầng thực sự chậm hơn RAM nhưng trên Linux SSD cũng có cache cho SSD trên RAM, hàm write() thực tế trả về ngay khi dữ liệu được ghi vào SSD cache nên thời gian 0.422 ms thực tế chưa phản ánh thời gian ghi file ra SSD.</p>
</li>
</ol>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Sự kiện Ctrl+C trên Linux và Windows]]></title><description><![CDATA[Trong hệ thống máy tính, khi có sự kiện xảy ra phần cứng có thể phát sinh một ngắt gửi đến CPU.
Ở tầng kernel, khi có interrupt xảy ra, CPU sẽ tạm dừng luồng đang chạy để chuyển sang xử lý sự kiện ngắt. Sau khi hoàn tất, CPU quay lại tiếp tục công vi...]]></description><link>https://u40hoccode.dev/su-kien-ctrlc-tren-linux-va-windows</link><guid isPermaLink="true">https://u40hoccode.dev/su-kien-ctrlc-tren-linux-va-windows</guid><category><![CDATA[Computer Science]]></category><category><![CDATA[Interrupt Handling]]></category><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Sat, 10 Jan 2026 17:38:56 GMT</pubDate><content:encoded><![CDATA[<p>Trong hệ thống máy tính, khi có sự kiện xảy ra phần cứng có thể phát sinh một ngắt gửi đến CPU.</p>
<p>Ở tầng kernel, khi có interrupt xảy ra, CPU sẽ tạm dừng luồng đang chạy để chuyển sang xử lý sự kiện ngắt. Sau khi hoàn tất, CPU quay lại tiếp tục công việc trước đó.<br />Tuy nhiên, ở tầng ứng dụng (user-space), một số sự kiện như tổ hợp phím Ctrl+C được hệ điều hành chuyển thành các thông báo (signal hoặc event), và cách chuyển cũng như xử lý các thông báo này khác nhau giữa Linux và Windows.</p>
<h2 id="heading-ctrlc-tren-linux">Ctrl+C trên Linux</h2>
<p>Trên Linux, khi người dùng nhấn <strong>Ctrl+C</strong></p>
<ol>
<li><p>Kernel nhận ngắt từ bàn phím</p>
</li>
<li><p>Kernel chuyển sự kiện này thành signal SIGINT</p>
</li>
<li><p>Signal handler được thực thi trong chính context của thread nhận signal</p>
</li>
</ol>
<p>Vì handler chạy trên luồng đang thực thi, nên trong thời gian xử lý signal, luồng chính bị tạm dừng.</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;signal.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdlib.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;unistd.h&gt;</span></span>

<span class="hljs-comment">// Hàm xử lý signal SIGINT (Ctrl+C) ở user-space</span>
<span class="hljs-function"><span class="hljs-keyword">void</span> <span class="hljs-title">signint_handle</span><span class="hljs-params">(<span class="hljs-keyword">int</span> sig)</span> </span>{
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Đã nhận tín hiệu Ctrl+C %d\n"</span>, sig);
    <span class="hljs-comment">// Nghỉ 5 giây</span>
    sleep(<span class="hljs-number">5</span>);
    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Đang quay lại công việc ...\n"</span>);
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Đăng kí handler xử lý signal SIGINT</span>
    signal(SIGINT, signint_handle);

    <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Chương trình đang chạy, nhấn Ctrl+C để gửi signal SIGINT ...\n"</span>);

    <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>)
    {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Đang làm việc ...\n"</span>);
        <span class="hljs-comment">// Nghỉ 1 giây</span>
        sleep(<span class="hljs-number">1</span>);
    }

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả, luồng chính bị block cho đến khi hàm xử lý SIGINT hoàn thành.</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768069270083/58949e6d-9876-4e62-8d19-71067b7c1e6e.png" alt class="image--center mx-auto" /></p>
<h2 id="heading-ctrlc-tren-windows">Ctrl+C trên Windows</h2>
<p>Trên Windows, Ctrl+C không được chuyển thành signal như Linux.<br />Thay vào đó, <strong>Console Subsystem</strong> của Windows:</p>
<ol>
<li><p>Bắt sự kiện CTRL_C_EVENT</p>
</li>
<li><p>Tạo một luồng nội bộ riêng gọi hàm đã đăng ký bằng SetConsoleCtrlHandler()</p>
</li>
</ol>
<p>Do handler chạy trên luồng riêng, chương trình chính vẫn tiếp tục chạy song song.</p>
<pre><code class="lang-c"><span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;stdio.h&gt;</span></span>
<span class="hljs-meta">#<span class="hljs-meta-keyword">include</span> <span class="hljs-meta-string">&lt;Windows.h&gt;</span></span>

<span class="hljs-comment">// Hàm xử lý sự kiện Console Ctrl+C (CTRL_C_EVENT) trên thread riêng</span>
<span class="hljs-function">BOOL WINAPI <span class="hljs-title">ConsoleHandler</span><span class="hljs-params">(DWORD dwControlType)</span> </span>{
    <span class="hljs-keyword">switch</span> (dwControlType) {
        <span class="hljs-comment">// Tổ hợp phím Ctrl+C được nhấn</span>
    <span class="hljs-keyword">case</span> CTRL_C_EVENT:
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Đã nhận tín hiệu Ctrl+C\n"</span>);
        <span class="hljs-comment">// Tạm dừng 5 giây</span>
        Sleep(<span class="hljs-number">5000</span>);
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Đang quay lại công việc ...\n"</span>);
        <span class="hljs-keyword">return</span> TRUE;
    <span class="hljs-keyword">default</span>:
        <span class="hljs-keyword">return</span> FALSE;
    }
}

<span class="hljs-function"><span class="hljs-keyword">int</span> <span class="hljs-title">main</span><span class="hljs-params">()</span> </span>{
    <span class="hljs-comment">// Ép Console sử dụng bảng mã UTF-8 (Code Page 65001) để Hiển thị tiếng việt</span>
    SetConsoleOutputCP(<span class="hljs-number">65001</span>);

    <span class="hljs-comment">// Đăng kí handler xử lý sự kiện Ctrl+C của Console (user-space)</span>
    <span class="hljs-keyword">if</span>(SetConsoleCtrlHandler(ConsoleHandler, TRUE)) {
        <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Chương trình đang chạy, nhấn Ctrl+C để gửi CTRL_C_EVENT ...\n"</span>);
        <span class="hljs-keyword">while</span> (<span class="hljs-number">1</span>)
        {
            <span class="hljs-built_in">printf</span>(<span class="hljs-string">"Đang làm việc ...\n"</span>);
            <span class="hljs-comment">// Nghỉ 1 giây</span>
            Sleep(<span class="hljs-number">1000</span>);
        }
    }

    <span class="hljs-keyword">return</span> <span class="hljs-number">0</span>;
}
</code></pre>
<p>Kết quả, luồng xử lý sự kiện Ctrl+C chạy song song với luồng chính</p>
<p><img src="https://cdn.hashnode.com/res/hashnode/image/upload/v1768069140374/a90daaed-7552-4393-9b44-f2968ba531c5.png" alt class="image--center mx-auto" /></p>
<p>The End.</p>
]]></content:encoded></item><item><title><![CDATA[Lời chào từ U40 Học Code!]]></title><description><![CDATA[Chào mọi người, đây là blog cá nhân của tôi. Đây không chỉ là nơi tôi viết cho mọi người đọc, mà quan trọng hơn, nó là nơi lưu giữ để tôi có thể tra cứu lại và hệ thống hóa kiến thức trên hành trình học code ở tuổi 40.]]></description><link>https://u40hoccode.dev/loi-chao-tu-u40-hoc-code</link><guid isPermaLink="true">https://u40hoccode.dev/loi-chao-tu-u40-hoc-code</guid><dc:creator><![CDATA[Lê Bách Khoa]]></dc:creator><pubDate>Sat, 10 Jan 2026 14:15:22 GMT</pubDate><content:encoded><![CDATA[<p>Chào mọi người, đây là blog cá nhân của tôi. Đây không chỉ là nơi tôi viết cho mọi người đọc, mà quan trọng hơn, nó là nơi lưu giữ để tôi có thể tra cứu lại và hệ thống hóa kiến thức trên hành trình học code ở tuổi 40.</p>
]]></content:encoded></item></channel></rss>