IPC — pipe, shared memory và socket giữa hai tiến trình
Khi hai tiến trình cần nói chuyện: pipe, shared memory (nhanh nhất nhưng cần đồng bộ), socket (linh hoạt nhất) — chi phí và mô hình chia sẻ của từng cách.
TL;DR: Hai tiến trình (process) không chia sẻ biến được vì mỗi tiến trình có không gian địa chỉ riêng — chúng cần IPC (inter-process communication) để nói chuyện. Ba cơ chế nền tảng: pipe — dòng byte một chiều, đơn giản, chính là dấu | trong shell; shared memory — cho hai tiến trình ánh xạ cùng vùng nhớ vật lý, nhanh nhất (không sao chép qua kernel) nhưng bạn phải tự đồng bộ (vòng lại race/mutex bài 01–02); socket — linh hoạt nhất, cùng một API chạy cho cả tiến trình trên máy local (Unix domain socket) lẫn qua mạng (TCP/UDP). Chọn theo hai trục: chi phí sao chép dữ liệu và mô hình chia sẻ. Điểm mấu chốt: pipe và socket là message passing — mỗi bên có dữ liệu riêng, gửi bản sao cho nhau — nên tránh được lock và deadlock; shared memory nhanh hơn nhưng kéo lại mọi bài toán đồng bộ.
Ba bài trước xoay quanh nhiều luồng trong một tiến trình, chia sẻ chung heap. Nhưng thực tế phần mềm hiếm khi là một tiến trình: shell nối nhiều lệnh (cat file | grep error | wc -l), web server nói chuyện với database qua tiến trình riêng, một app tách worker ra process con để cách ly lỗi. Khi ranh giới là tiến trình, biến chung biến mất — mỗi process có bộ nhớ riêng, không thấy được của nhau. Bài này trả lời: vậy chúng phối hợp bằng cách nào, và ba cách chính khác nhau ở đâu. Học xong, bạn chọn được cơ chế IPC đúng cho một tình huống — pipe, shared memory hay socket — và giải thích được vì sao message passing (pipe/socket) tránh được cả race lẫn deadlock.
1. Vì sao hai tiến trình không chia sẻ biến được?
Ở Module 2 — Tiến trình bạn đã thấy: OS cho mỗi tiến trình một không gian địa chỉ ảo riêng. Địa chỉ 0x1000 trong tiến trình A và 0x1000 trong tiến trình B ánh xạ tới hai vùng nhớ vật lý khác nhau — đây là cách ly bảo vệ, để một tiến trình lỗi không ghi đè bộ nhớ tiến trình khác.
Hệ quả: một biến count trong process A hoàn toàn vô hình với process B. Không có "biến chung" như giữa các thread cùng process. Muốn trao đổi dữ liệu, chúng phải nhờ OS làm trung gian — đó chính là IPC. OS cung cấp vài "kênh" được cả hai tiến trình cùng nhìn thấy, và ba kênh nền tảng nhất là pipe, shared memory, socket.
flowchart LR
subgraph PA["Tien trinh A"]
VA["count = 5<br/>(khong gian rieng)"]
end
subgraph PB["Tien trinh B"]
VB["count = ? <br/>(khong thay cua A)"]
end
PA -->|"IPC: pipe / shm / socket"| PB2. Analogy — ba cách gửi cho đồng nghiệp bàn bên
Bạn cần đưa dữ liệu cho đồng nghiệp ngồi bàn bên (tiến trình khác):
- Pipe — ống chuyển tài liệu một chiều. Như ống khí nén ở nhà băng: bạn nhét giấy vào đầu này, nó ra đầu kia. Một chiều, đơn giản, ai nhét trước ra trước (FIFO). Muốn hai chiều thì cần hai ống.
- Shared memory — cùng viết lên một tấm bảng trắng. Cả hai cùng nhìn và cùng ghi lên một tấm bảng đặt giữa hai bàn. Nhanh nhất — không phải chép đi chép lại, thấy ngay. Nhưng nếu cả hai cùng viết một lúc, chữ đè lên nhau (race!) — phải có luật "ai đang cầm bút" (đồng bộ).
- Socket — gọi điện thoại. Có thể gọi người bàn bên (local) hoặc gọi người ở thành phố khác (network) bằng cùng một cái điện thoại. Hai chiều, có mở/đóng cuộc gọi rõ ràng. Linh hoạt nhất, đổi lại thiết lập cầu kỳ hơn ống chuyển giấy.
| Đời thường | Cơ chế IPC | Đặc trưng |
|---|---|---|
| Ống chuyển giấy một chiều | Pipe | Đơn giản, một chiều, FIFO byte |
| Cùng viết lên bảng trắng | Shared memory | Nhanh nhất, nhưng cần luật tránh đè |
| Điện thoại (gọi gần hoặc xa) | Socket | Hai chiều, local + network, linh hoạt |
Chọn IPC theo hai câu hỏi: (1) Có cần cực nhanh và dữ liệu lớn không? → shared memory (đổi lại tự đồng bộ). (2) Có thể cần chạy qua mạng không? → socket. Còn lại, cho luồng dữ liệu một chiều đơn giản giữa tiến trình cha-con → pipe.
3. Pipe — dòng byte một chiều
Pipe là kênh IPC lâu đời và đơn giản nhất: một dòng byte một chiều (unidirectional byte stream). Một tiến trình ghi vào đầu ghi, tiến trình kia đọc ở đầu đọc. Bạn dùng nó mỗi ngày trong shell — dấu | chính là một pipe:
# stdout cua 'cat' noi vao stdin cua 'grep' qua mot pipe kernel
cat access.log | grep "ERROR" | wc -l
Ba tiến trình cat, grep, wc chạy song song; kernel nối chúng bằng hai pipe. Dữ liệu chảy trái sang phải như nước qua ống.
Đặc điểm cốt lõi (theo pipe(7)):
- Byte stream, không có ranh giới message. Pipe không biết đâu là "một tin nhắn" — nó chỉ là dãy byte liên tục. Nếu A ghi hai lần 10 byte, B có thể đọc về một cục 20 byte, hoặc 5 rồi 15. Muốn tách bản ghi, bạn tự thêm dấu phân cách (như
\n). - Bộ đệm kernel giới hạn. Từ Linux 2.6.11, pipe có sức chứa 65.536 byte (64 KiB). Ghi khi đệm đầy thì bị block cho tới khi bên kia đọc bớt — đây là flow control (điều tiết dòng chảy) tự động, miễn phí: bên nhanh tự chờ bên chậm.
- Ghi nhỏ là nguyên tử. POSIX đảm bảo ghi không vượt
PIPE_BUFbyte (trên Linux là 4096, tức ≤ 4096 byte) không bị xen kẽ với dữ liệu của tiến trình ghi khác — hữu ích khi nhiều tiến trình cùng ghi vào một pipe. - Một chiều. Muốn giao tiếp hai chiều cần hai pipe. Pipe ẩn danh (
pipe()) nối tiến trình có quan hệ (cha-con quafork); muốn nối tiến trình bất kỳ dùng named pipe / FIFO (có tên trên filesystem).
Pipe cho phép ghép các chương trình nhỏ làm-một-việc thành pipeline mạnh mà không cái nào biết cái kia tồn tại — mỗi lệnh chỉ đọc stdin, ghi stdout. Đây là nền của triết lý "làm một việc thật tốt rồi nối lại". Flow control tự động (bên ghi block khi đệm đầy) khiến pipeline tự cân bằng tốc độ mà không cần lập trình gì thêm.
4. Shared memory — nhanh nhất, nhưng bạn tự trả giá đồng bộ
Shared memory cho hai (hay nhiều) tiến trình ánh xạ cùng một vùng nhớ vật lý vào không gian địa chỉ của mỗi bên. Sau khi thiết lập, tiến trình A ghi vào vùng đó và tiến trình B thấy ngay — không qua kernel, không sao chép. Đây là lý do nó nhanh nhất trong các cơ chế IPC: dữ liệu không phải copy user → kernel → user như pipe/socket.
Luồng dùng POSIX shared memory (theo shm_overview(7)):
// Tien trinh A: tao va ghi
int fd = shm_open("/mydata", O_CREAT | O_RDWR, 0666); // tao doi tuong shm
ftruncate(fd, 4096); // dat kich thuoc
char *ptr = mmap(NULL, 4096, PROT_READ | PROT_WRITE,
MAP_SHARED, fd, 0); // anh xa vao dia chi
strcpy(ptr, "hello from A"); // ghi -> B thay ngay
Tiến trình B shm_open cùng tên /mydata, mmap, và đọc thẳng ptr. Trên Linux các đối tượng này nằm ở /dev/shm (một tmpfs — filesystem trên RAM).
Nhưng "thấy ngay" đi kèm cái bẫy: vì cả hai cùng đọc-ghi cùng bộ nhớ, bạn quay lại đúng bài toán race của bài 01 — chỉ khác là giữa hai tiến trình thay vì hai luồng. shm_overview(7) lưu ý: "Typically, processes must synchronize their access to a shared memory object, using, for example, POSIX semaphores." Không có đồng bộ, hai tiến trình ghi đè lên nhau y như hai thread giẫm chân trên count++.
flowchart TB
subgraph K["Bo nho vat ly"]
SH["Vung shared memory<br/>/dev/shm/mydata"]
end
PA["Tien trinh A<br/>mmap"] <--> SH
PB["Tien trinh B<br/>mmap"] <--> SH
SEM["Semaphore / mutex lien tien trinh<br/>(BAT BUOC de tranh race)"] -. "bao ve" .-> SHTức là shared memory không loại bỏ các bài toán đồng bộ — nó chuyển chúng sang cấp liên-tiến-trình. Bạn cần một khoá mà cả hai tiến trình cùng thấy (POSIX semaphore, hoặc mutex đặt trong chính vùng shared memory với thuộc tính PTHREAD_PROCESS_SHARED). Đây là đánh đổi trung tâm: bạn mua tốc độ tối đa bằng cách nhận lại toàn bộ độ phức tạp của race/mutex/deadlock.
5. Socket — một API cho cả local lẫn mạng
Socket là kênh hai chiều linh hoạt nhất. Sức mạnh của nó là một API duy nhất phủ hai tình huống rất khác:
- Unix domain socket (
AF_UNIX, theounix(7)): hai tiến trình trên cùng máy, giao tiếp qua kernel, nhanh và không đụng tới network stack. Man page mô tả nó "used to communicate between processes on the same machine efficiently". - Network socket (
AF_INETTCP/UDP): hai tiến trình trên hai máy khác nhau, qua mạng.
Điều đẹp: code gần như giống nhau cho cả hai — bạn viết logic gửi/nhận một lần, đổi loại socket là chạy được local hay qua mạng. Đây là lý do socket thống trị kiến trúc client-server và microservice.
Socket còn cho chọn kiểu giao tiếp (theo unix(7)):
SOCK_STREAM— dòng byte tin cậy, có thứ tự (như TCP). Giống pipe nhưng hai chiều.SOCK_DGRAM— datagram, giữ ranh giới message (mỗi lần gửi là một gói riêng).SOCK_SEQPACKET— vừa tin cậy vừa giữ ranh giới message.
Unix domain socket còn làm được thứ pipe không làm được: truyền file descriptor giữa các tiến trình (passing fd qua ancillary data). File descriptor là một số nguyên đại diện cho một tài nguyên I/O đang mở của tiến trình — file, socket, hay pipe (khoá tier 4 sẽ đào sâu). Nhờ vậy, một tiến trình có thể mở file/socket rồi chuyển "quyền truy cập" đó cho tiến trình khác.
Socket (như pipe) sao chép dữ liệu qua bộ đệm kernel, nên chậm hơn shared memory zero-copy. Nhưng nó thắng ở message passing sạch: mỗi bên có dữ liệu riêng, gửi bản sao — không chia sẻ trạng thái nên không race, không cần khoá, không deadlock. Cộng với việc chạy được qua mạng mà không đổi code, socket là mặc định hợp lý cho phần lớn giao tiếp liên-tiến-trình; chỉ đổi sang shared memory khi đo được rằng chi phí sao chép là bottleneck thật.
6. So sánh — chọn theo chi phí và mô hình chia sẻ
| Tiêu chí | Pipe | Shared memory | Socket |
|---|---|---|---|
| Hướng | Một chiều | Hai chiều (cùng vùng) | Hai chiều |
| Ranh giới message | Không (byte stream) | Không (byte thô) | Có (DGRAM/SEQPACKET) hoặc không (STREAM) |
| Chi phí sao chép | Copy qua kernel (2 lần) | Không copy (nhanh nhất) | Copy qua kernel |
| Cần tự đồng bộ? | Không (kernel lo flow control) | Có (semaphore/mutex) | Không |
| Qua được mạng? | Không | Không | Có (network socket) |
| Mô hình | Message passing | Chia sẻ trạng thái | Message passing |
| Hợp cho | Pipeline cha-con đơn giản | Dữ liệu lớn, tốc độ tối đa, cùng máy | Client-server, microservice, local + xa |
Hai trục quyết định:
Chi phí sao chép. Shared memory không sao chép nên nhanh nhất cho dữ liệu lớn / trao đổi dày đặc. Pipe và socket sao chép qua kernel — chậm hơn nhưng thường không đáng kể trừ khi bạn ở hot path.
Mô hình chia sẻ. Pipe và socket theo message passing: mỗi tiến trình giữ dữ liệu riêng, chỉ gửi bản sao. Vì không có trạng thái chung, chúng tránh được toàn bộ race/lock/deadlock — đúng "lựa chọn thứ ba" của bài 02, giờ áp cho tiến trình. Shared memory theo chia sẻ trạng thái: nhanh hơn nhưng kéo lại mọi bài toán đồng bộ. Đây là cùng một đánh đổi xuyên suốt module — nhanh-mà-phải-khoá so với chậm-hơn-mà-sạch.
Thử chọn trước khi xem đáp án
Ba tình huống dưới, mỗi cái hợp một cơ chế IPC khác nhau. Tự quyết định trước khi đọc tiếp:
- Nối
cat access.log | grep ERROR | wc -ltrong shell — dữ liệu chảy một chiều qua vài lệnh nhỏ. - Hai tiến trình trên cùng một máy trao đổi khối dữ liệu lớn, cần độ trễ thấp nhất có thể.
- Một service gọi một service khác chạy trên máy khác qua mạng.
Với mỗi tình huống, hỏi hai câu ở mục "Cách nhớ": (1) có cần cực nhanh và dữ liệu lớn không? (2) có thể phải chạy qua mạng không? Ghi lựa chọn của bạn ra giấy trước khi đọc bảng dưới.
Đáp án:
| Tình huống | Cơ chế | Vì sao |
|---|---|---|
| 1. Pipeline shell một chiều | Pipe | Luồng byte một chiều giữa tiến trình cha-con, kernel lo flow control — chính là thứ dấu gạch đứng trong shell tạo ra. |
| 2. Cùng máy, dữ liệu lớn, latency thấp nhất | Shared memory | Không sao chép qua kernel nên nhanh nhất; đổi lại phải tự đồng bộ bằng semaphore. |
| 3. Qua mạng, hai máy khác nhau | Socket | Chỉ socket chạy được qua mạng; lại là message passing sạch nên không cần khoá. |
Điểm cần thấm: không có cơ chế "tốt nhất" tuyệt đối — mỗi cái thắng ở một trục (đơn giản / tốc độ / linh hoạt và chạy được qua mạng). Chọn theo ràng buộc thật của tình huống.
7. Áp dụng vào code của bạn
- Mặc định chọn message passing (socket/pipe). Trừ khi đo được chi phí sao chép là bottleneck, ưu tiên socket/pipe: chúng loại bỏ cả một lớp bug (race, deadlock) mà bạn không phải nghĩ tới. Kiến trúc "mỗi tiến trình một chủ dữ liệu, nói chuyện qua message" là con đường ít lỗi nhất.
- Chỉ dùng shared memory khi tốc độ bắt buộc. Trao đổi hàng trăm MB/s, video frame, dữ liệu khoa học — chỗ mà một lần copy là quá đắt. Và khi dùng, đầu tư nghiêm túc vào đồng bộ: semaphore liên tiến trình, cẩn thận đúng như với mutex ở bài 02.
- Dùng socket khi có thể cần scale ra mạng. Nếu hôm nay hai tiến trình trên một máy nhưng ngày mai có thể tách sang hai máy, viết bằng socket từ đầu — đổi từ Unix domain sang TCP gần như không phải sửa logic.
- Nhớ pipe không có ranh giới message. Nếu gửi các bản ghi rời qua pipe/SOCK_STREAM, tự thêm framing (dấu phân cách hoặc độ dài prefix) — đừng giả định "một lần ghi = một lần đọc".
8. 📚 Deep Dive — man pages IPC
Nguồn chính (Linux man pages):
- pipe(7) — dòng byte một chiều, sức chứa 65.536 byte (từ Linux 2.6.11), đảm bảo nguyên tử cho ghi không vượt
PIPE_BUF(≤ 4096 byte). - shm_overview(7) — POSIX shared memory:
shm_open+mmap, đối tượng ở/dev/shm, và câu then chốt về việc phải đồng bộ bằng POSIX semaphore. - unix(7) — Unix domain socket
AF_UNIX: các kiểuSOCK_STREAM/SOCK_DGRAM/SOCK_SEQPACKET, giao tiếp local hiệu quả, và truyền file descriptor qua ancillary data.
Ghi chú: Ba man page này là nguồn chuẩn khi bạn thật sự viết code IPC. Điểm chung đáng nhớ: pipe/socket được kernel quản lý bộ đệm và flow control (an toàn sẵn), còn shared memory giao toàn bộ trách nhiệm đồng bộ cho bạn — đọc kỹ đoạn semaphore trong shm_overview(7) trước khi dùng.
9. Liên hệ các bài khác
- Bài 01 — Race condition: shared memory giữa hai tiến trình tái tạo đúng race của hai luồng — cùng bài toán, khác cấp.
- Bài 02 — Mutex & atomic: "đừng chia sẻ / message passing" ở bài 02 chính là lý do pipe/socket tránh được lock; shared memory thì cần semaphore liên tiến trình như mutex.
- Bài 03 — Deadlock: message passing (pipe/socket) không dùng khoá chia sẻ nên tránh được deadlock do tranh khoá — dù vẫn có thể kẹt nếu hai bên cùng block chờ nhận của nhau, hoặc cùng block ghi vào kênh đã đầy (pipe 64 KiB); một lý do kiến trúc nữa để cân nhắc nó.
- Module 2 — Tiến trình và PCB: không gian địa chỉ riêng của mỗi tiến trình là lý do gốc vì sao cần IPC.
10. Tóm tắt
- Mỗi tiến trình có không gian địa chỉ riêng nên không chia sẻ biến — cần IPC để trao đổi dữ liệu.
- Pipe: dòng byte một chiều, không ranh giới message, bộ đệm kernel 64 KiB với flow control tự động — chính là dấu
|trong shell. - Shared memory: hai tiến trình ánh xạ cùng vùng nhớ vật lý, nhanh nhất (không sao chép) nhưng phải tự đồng bộ (POSIX semaphore) — kéo lại toàn bộ bài toán race/mutex.
- Socket: linh hoạt nhất — cùng API cho local (Unix domain
AF_UNIX) và mạng (TCP/UDP), hai chiều, chọn được kiểu stream/datagram, truyền được file descriptor. - Hai trục chọn: chi phí sao chép (shm không copy → nhanh nhất) và mô hình chia sẻ (pipe/socket là message passing → tránh race/deadlock; shm là chia sẻ trạng thái → cần khoá).
- Mặc định message passing (socket/pipe) vì nó loại bỏ cả lớp bug đồng bộ; chỉ đổi sang shared memory khi đo được chi phí copy là bottleneck thật.
- Pipe/SOCK_STREAM không có ranh giới message — tự thêm framing khi gửi bản ghi rời.
11. Tự kiểm tra
Q1Vì sao hai tiến trình không dùng chung một biến như hai luồng được, và điều đó dẫn tới nhu cầu gì?▸
OS cho mỗi tiến trình một không gian địa chỉ ảo riêng để cách ly bảo vệ: cùng địa chỉ 0x1000 ở hai tiến trình ánh xạ tới hai vùng nhớ vật lý khác nhau. Nên một biến trong tiến trình A hoàn toàn vô hình với tiến trình B — không có "biến chung" như giữa các luồng cùng tiến trình (vốn chia sẻ heap).
Vì thế hai tiến trình phải nhờ OS làm trung gian để trao đổi dữ liệu — đó là IPC (inter-process communication). OS cung cấp các kênh cả hai cùng thấy: pipe, shared memory, socket. Đây là khác biệt nền tảng: đồng bộ giữa luồng dùng biến chung + khoá; giao tiếp giữa tiến trình phải qua kênh IPC.
Q2Vì sao shared memory nhanh nhất trong các cơ chế IPC, nhưng lại là cái nguy hiểm nhất về đồng bộ?▸
Nhanh nhất vì hai tiến trình ánh xạ cùng vùng nhớ vật lý: A ghi, B thấy ngay, không phải sao chép dữ liệu user → kernel → user như pipe/socket. Với dữ liệu lớn, tránh được copy là thắng lớn.
Nguy hiểm nhất vì "cùng bộ nhớ" nghĩa là quay lại đúng bài toán race của bài 01 — chỉ khác giữa hai tiến trình thay vì hai luồng. Nếu cả hai cùng đọc-ghi mà không đồng bộ, chúng đè lên nhau y như count++ mất update. shm_overview(7) nói rõ phải dùng POSIX semaphore để đồng bộ. Tức shared memory không loại bỏ race/mutex/deadlock — nó chuyển chúng sang cấp liên-tiến-trình, và bạn phải tự xây khoá cả hai tiến trình cùng thấy.
Q3Điều gì khiến socket "linh hoạt nhất", và vì sao nó vẫn được ưa dùng dù chậm hơn shared memory?▸
Linh hoạt vì một API duy nhất phủ cả hai tình huống: Unix domain socket (AF_UNIX) cho hai tiến trình cùng máy, và network socket (TCP/UDP) cho hai máy qua mạng — code gần như không đổi khi chuyển local sang mạng. Nó còn hai chiều, chọn được kiểu stream/datagram, và truyền được file descriptor.
Chậm hơn shared memory vì sao chép qua bộ đệm kernel, nhưng thắng ở message passing sạch: mỗi bên giữ dữ liệu riêng, chỉ gửi bản sao — không chia sẻ trạng thái nên không race, không khoá, không deadlock. Cộng với khả năng scale ra mạng miễn phí, socket là mặc định hợp lý; chỉ đổi sang shared memory khi đo được chi phí copy là bottleneck thật.
Q4"Pipe không có ranh giới message" nghĩa là gì, và nó buộc bạn làm gì khi gửi nhiều bản ghi rời?▸
Pipe (và SOCK_STREAM) là dòng byte liên tục — kernel không biết đâu là "một tin nhắn", chỉ thấy dãy byte. Nếu bên gửi ghi hai lần 10 byte, bên nhận có thể đọc về một cục 20 byte, hoặc 5 rồi 15 — số lần đọc không tương ứng số lần ghi.
Vì thế khi gửi các bản ghi rời, bạn phải tự thêm framing: một dấu phân cách (như \n) để bên nhận biết chỗ cắt, hoặc một prefix độ dài (gửi "12" rồi 12 byte dữ liệu) để đọc đúng số byte. Đừng bao giờ giả định "một lần ghi = một lần đọc". Muốn kernel giữ ranh giới sẵn thì dùng SOCK_DGRAM/SOCK_SEQPACKET thay vì stream.
Q5Hai trục "chi phí sao chép" và "mô hình chia sẻ" giúp bạn chọn IPC thế nào? Xếp pipe, shared memory, socket theo mỗi trục.▸
Chi phí sao chép: shared memory không sao chép (nhanh nhất) vì hai bên dùng chung vùng nhớ; pipe và socket sao chép qua kernel (chậm hơn nhưng thường không đáng kể trừ hot path). Trục này quan trọng khi dữ liệu lớn / trao đổi dày.
Mô hình chia sẻ: pipe và socket theo message passing — mỗi bên giữ dữ liệu riêng, gửi bản sao, nên tránh được race/lock/deadlock. Shared memory theo chia sẻ trạng thái — nhanh hơn nhưng kéo lại mọi bài toán đồng bộ. Quy tắc chọn: mặc định message passing (socket/pipe) cho an toàn; đổi sang shared memory chỉ khi đo được chi phí copy là bottleneck và bạn sẵn sàng tự đồng bộ. Cần chạy qua mạng → socket.
Q6Vì sao có thể nói pipe/socket là hiện thân của "lựa chọn thứ ba" (đừng chia sẻ) ở bài 02, còn shared memory thì không?▸
Ở bài 02, "lựa chọn thứ ba" là tránh chia sẻ dữ liệu (immutable, confinement, message passing) để khỏi cần khoá. Pipe và socket đúng là message passing: mỗi tiến trình giữ dữ liệu riêng, chỉ gửi bản sao qua kênh. Không có trạng thái chung nên không có race, không cần mutex, không thể deadlock vì chia sẻ khoá — cả một lớp bug biến mất theo thiết kế.
Shared memory thì ngược lại: nó cố ý tạo trạng thái chung (cùng vùng nhớ vật lý) để đạt tốc độ. Vì có chia sẻ, nó kéo lại toàn bộ race/mutex/deadlock ở cấp liên-tiến-trình. Nói cách khác, pipe/socket mua sự an toàn bằng cách không chia sẻ; shared memory mua tốc độ bằng cách chia sẻ và chấp nhận cái giá đồng bộ.
Bài tiếp theo: Mini-challenge — săn race và deadlock
Bài này có giúp bạn hiểu bản chất không?
Hỏi đáp về bài này
Chưa có câu hỏi
Có gì chưa rõ trong bài? Đặt câu hỏi đầu tiên — câu trả lời từ cộng đồng giúp bạn (và người sau).
Đặt câu hỏi đầu tiên