Segmentation fault là gì?
Segmentation fault, hay segfault, là lỗi khiến chương trình bị dừng đột ngột khi nó cố đọc hoặc ghi vào vùng nhớ không được phép truy cập, như địa chỉ 0 qua một con trỏ null. Hệ điều hành dừng chương trình bằng tín hiệu SIGSEGV.
Cập nhật 24 tháng 9, 2026
Một chương trình C in ra dòng đầu tiên rồi dừng lại với một thông báo không ai viết ra:
#include <stdio.h>
int main(void) {
int *score = NULL;
printf("About to read the score\n");
printf("Score: %d\n", *score);
return 0;
}
About to read the score
bash: line 1: 64030 Segmentation fault: 11 ./seg
Kết quả trên là từ bash trên macOS, trong đó 64030 là ID tiến trình và 11 là số hiệu tín hiệu. Trên Linux, cùng lỗi này thường hiện là Segmentation fault (core dumped). Dù thế nào, chương trình cũng không bao giờ in ra điểm số. score chứa địa chỉ 0, và *score yêu cầu bộ xử lý đọc vùng nhớ tại địa chỉ đó, điều mà không chương trình nào được phép làm.
Segmentation fault xảy ra thế nào
Mỗi chương trình chạy trong không gian địa chỉ ảo riêng, một dải địa chỉ khổng lồ mà hệ điều hành lấp đầy từng phần. Nó ánh xạ code của chương trình, các biến toàn cục, stack và heap vào dải đó, theo từng khối gọi là trang (page), 4 KB trên phần lớn hệ thống Linux x86, 16 KB trên máy Mac dùng chip Apple. Phần lớn địa chỉ không được ánh xạ, và địa chỉ 0 luôn nằm trong số đó để bắt được các bug con trỏ null.
- Chương trình thực thi một lệnh đọc hoặc ghi vào một địa chỉ. Ở đây là lệnh đọc
*score, địa chỉ 0. - Bộ quản lý bộ nhớ (MMU) của bộ xử lý tra địa chỉ trong bảng trang. Trang đó không được ánh xạ, hoặc chương trình đang cố ghi vào một trang chỉ đọc.
- Bộ xử lý dừng lệnh lại và chuyển quyền điều khiển cho kernel bằng một page fault.
- Kernel kiểm tra xem truy cập này có thể hợp lệ không, ví dụ stack cần được mở rộng. Không phải vậy, nên kernel gửi cho tiến trình tín hiệu 11,
SIGSEGV. - Hành động mặc định với
SIGSEGVlà kết thúc tiến trình và, khi hệ thống cho phép, lưu một core dump. Shell sau đó in ra thông báo và đặt mã thoát là 139, tức 128 cộng số hiệu tín hiệu.
Vì vậy segmentation fault không do trình biên dịch báo ra, và cũng không phải ngoại lệ mà ngôn ngữ đưa ra. Đó là phần cứng và kernel đang bảo vệ bộ nhớ, khiến nó trở thành loại lỗi runtime đột ngột nhất. Windows xử lý cùng sự kiện này dưới tên access violation, mã ngoại lệ 0xC0000005.
Nguyên nhân thường gặp của segmentation fault
C và C++ cho phép chương trình tính ra bất kỳ địa chỉ nào và dùng nó, nên mọi nguyên nhân đều quy về việc dùng một địa chỉ không hợp lệ.
- Truy xuất con trỏ null.
int *p = NULL; *p = 5;Một hàm trả vềNULLkhi thất bại, nhưmallochayfopen, sẽ dẫn đến lỗi này nếu kết quả không được kiểm tra. - Chỉ số vượt xa cuối mảng. C không kiểm tra giới hạn, nên
arr[1000000]đơn giản là một địa chỉ xa thêm một triệu phần tử. - Dùng vùng nhớ sau khi
free. Con trỏ vẫn giữ địa chỉ cũ, nhưng vùng nhớ đó không còn thuộc về bạn. - Con trỏ chưa được khởi tạo.
int *p; *p = 5;ghi vào bất kỳ giá trị rác nào màpđang chứa. - Tràn stack. Một hàm đệ quy không có điểm dừng cứ thêm stack frame cho đến khi vượt quá cuối stack. Ví dụ bên dưới đã bị dừng trên macOS với
Segmentation fault: 11và mã thoát 139. - Ghi vào chuỗi hằng.
char *name = "coddy"; name[0] = 'C';cố thay đổi vùng nhớ chỉ đọc. Trên Linux, đó là segfault. Trên macOS, cùng chương trình này dừng vớiBus error: 10, một tín hiệu có liên quan.
#include <stdio.h>
int depth(int n) {
return depth(n + 1) + 1; /* never stops calling itself */
}
int main(void) {
printf("%d\n", depth(0));
return 0;
}
Một sai sót nhỏ thường không làm chương trình dừng, và điều đó còn nguy hiểm hơn. Vòng lặp này đọc dư một phần tử sau cuối mảng ba phần tử:
#include <stdio.h>
int main(void) {
int scores[3] = {72, 88, 95};
int total = 0;
for (int i = 0; i <= 3; i++) { /* <= reads scores[3] */
total += scores[i];
}
printf("Total: %d\n", total);
return 0;
}
Khi biên dịch bằng Clang trên Mac, chương trình in ra Total: 256. Tổng đúng là 255. scores[3] là 4 byte tiếp theo trên stack, vẫn thuộc về chương trình, nên không có lỗi nào xảy ra và giá trị rác bị cộng vào một cách âm thầm. Hệ điều hành chỉ chặn các truy cập vào vùng nhớ hoàn toàn không thuộc về chương trình.
Cách sửa cho cả hai sai sót là cùng một thói quen: biết có bao nhiêu phần tử, và kiểm tra con trỏ trước khi đi theo nó.
Best: 95
No scores, nothing to read
"Core dumped" nghĩa là gì
Core dump là một tệp chứa bản sao bộ nhớ của chương trình tại thời điểm nó bị dừng. Debugger có thể mở tệp này sau đó và cho thấy chính xác chương trình đang ở đâu và các biến đang chứa gì: gdb ./app core. Trên nhiều bản phân phối Linux, systemd-coredump thu thập các tệp này, và coredumpctl list liệt kê chúng. Khi core dump bị tắt, ví dụ bằng ulimit -c 0, thông báo chỉ là Segmentation fault, không có phần trong ngoặc.
Cách tìm dòng gây lỗi
Dòng mà chương trình bị dừng thường không phải dòng chứa bug. Một con trỏ có thể trở nên không hợp lệ trong một hàm và được dùng trong một hàm khác rất lâu sau đó. Những công cụ sau cho thấy cả hai.
Debugger. Biên dịch kèm thông tin gỡ lỗi và chạy chương trình bên trong debugger. Khi chương trình dừng, bt (backtrace) in ra chuỗi lời gọi hàm kèm tên tệp và số dòng. Trên Linux, debugger thường là gdb; trên macOS là lldb, nơi bt hoạt động y như vậy.
gcc -g app.c -o app
gdb ./app
(gdb) run
(gdb) bt
AddressSanitizer. Biên dịch với -fsanitize=address (cả GCC và Clang đều hỗ trợ) và chạy chương trình như bình thường. Thay vì một segfault trơn, nó in ra báo cáo nêu rõ loại sai sót, như heap-use-after-free hay stack-buffer-overflow, kèm dòng đã truy cập và dòng đã cấp phát vùng nhớ. Nó cũng bắt được lỗi đọc dư một phần tử âm thầm ở trên, loại lỗi tự nó không bao giờ làm chương trình dừng.
Valgrind. Trên Linux, valgrind ./app chạy chương trình không cần sửa đổi và báo mọi lần đọc hoặc ghi không hợp lệ, như Invalid read of size 4.
Segmentation fault trong các ngôn ngữ khác
Python, Java và JavaScript kiểm tra mọi chỉ số và mọi tham chiếu trước khi dùng, nên cùng những sai sót đó trở thành ngoại lệ với thông báo rõ ràng: IndexError hoặc AttributeError trong Python, ArrayIndexOutOfBoundsException hoặc NullPointerException trong Java, TypeError trong JavaScript. Những ngoại lệ này có thể bắt được bằng xử lý ngoại lệ. Segfault thì không xử lý được theo cách đó: nó là một tín hiệu, và khối catch của C++ không nhìn thấy nó.
Chương trình Python vẫn có thể segfault khi code C bên dưới gặp lỗi. Dòng này yêu cầu ctypes đọc địa chỉ 0:
import ctypes
ctypes.string_at(0)
Khi chạy bằng python3 -X faulthandler, Python 3.12 in ra Fatal Python error: Segmentation fault, theo sau là các dòng Python đang chạy. Lỗi tương tự xảy ra khi một module mở rộng viết bằng C hoặc một thư viện native có bug bộ nhớ. Rust chọn cách khác: trình biên dịch của nó từ chối phần lớn code có thể truy cập vùng nhớ không hợp lệ ngay trước khi chương trình được build.
Đọc tiếp
Hướng dẫn C về segmentation fault đi qua từng nguyên nhân với một chương trình tối giản và cách sửa. Để tránh các sai sót này ngay từ đầu, hãy đọc về con trỏ, con trỏ null và stack và heap, rồi luyện tập trong khóa học C. Để xem lỗi được báo ra thế nào trong các ngôn ngữ tự kiểm tra bộ nhớ giúp bạn, hãy xem trang lỗi runtime.
Câu hỏi thường gặp
Làm sao sửa lỗi segmentation fault?
-g và chạy chương trình trong gdb hoặc lldb, rồi gõ bt sau khi chương trình dừng, hoặc biên dịch với -fsanitize=address để có báo cáo chi tiết. Sau đó sửa con trỏ hoặc chỉ số mà dòng đó dùng: kiểm tra con trỏ có phải NULL không, giữ chỉ số mảng nhỏ hơn độ dài, ngừng dùng vùng nhớ sau khi free, và đảm bảo đệ quy có điểm dừng.Segmentation fault có phải là rò rỉ bộ nhớ không?
free, như dùng con trỏ sau khi đã giải phóng, có thể gây segfault, còn quên free thì gây rò rỉ.Vì sao lại gọi là segmentation fault?
SIGSEGV vẫn được giữ lại. Windows gọi cùng sự kiện này là access violation.Python có bị segmentation fault không?
ctypes. Chạy python -X faulthandler script.py sẽ in ra các dòng Python đang chạy khi chương trình bị dừng.Exit code 139 nghĩa là gì?
SIGSEGV, một segmentation fault. Shell báo việc tiến trình bị kết thúc bởi tín hiệu bằng 128 cộng số hiệu tín hiệu, và 128 + 11 = 139. Trong Docker và Kubernetes, một container thoát với mã 139 nghĩa là tiến trình chính của nó bị segfault.