- Khi biên dịch và chạy lặp lại chương trình C bằng
entr,hellokết thúc bất thường, nhưng sau phần output biên dịch không xuất hiện thông báoSegmentation fault - Thông báo này không do chương trình bị crash in ra, mà do shell cha in ra sau khi thu hồi tiến trình con và xác nhận nó đã kết thúc vì
SIGSEGV - Ngay cả khi chỉ định
bash -c, nếu về thực chất chỉ còn một lệnh cần chạy, Bash có thể tự thay thế chính nó bằngexecvethay vì tạo tiến trình mới, khiến shell cha chịu trách nhiệm in thông báo biến mất - Trong một script riêng, Bash được khởi động bằng shebang sẽ chạy
./hellonhư tiến trình con và chờ nó, nên khi xác nhậnSIGSEGVsẽ in raSegmentation fault (core dumped) - Có thể thấy thông báo lỗi bằng cách chạy
./hellotrong subshell(./hello)hoặc thêm; truephía sau để còn việc tiếp theo, qua đó ngăn tối ưu hóaexec
Luồng thực thi khiến thông báo biến mất
-
Dùng lệnh
entrsau để lặp lại việc biên dịch và chạy mỗi khihello.cthay đổils hello.c | entr -s "gcc -o hello hello.c && ./hello" -
Dù
hellokết thúc vì lỗi segmentation fault, không để lại output nào, không có thông báoSegmentation faulthay trạng thái thoát khác 0 nào nhìn thấy được -
Kể cả bọc lệnh bằng
bash -c, kết quả vẫn như vậyls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello'" -
Ngược lại, nếu chuyển cùng lệnh vào
run.shrồi chạy thì lỗi được hiển thị#!/bin/bash gcc -o hello hello.c && ./hellols hello.c | entr -s ./run.sh ./run.sh: line 2: 104465 Segmentation fault (core dumped) ./hello -
Chủ thể in ra
Segmentation fault- Chương trình bị crash lúc đó đã kết thúc; shell cha thu hồi tiến trình con, xác nhận nó chết vì
SIGSEGV, rồi in thông báo - Vì vậy nếu không còn shell cha để in thông báo, sẽ không có gì xuất hiện
- Trong
bash -c "some_command", nếu việc cần chạy về thực chất chỉ là lệnh đó, Bash có thể không fork tiến trình mới mà tự thay thế chính nó bằngexecve - Tối ưu hóa này không lộ ra bên ngoài trong phần lớn các lần thực thi
- Với cách dùng script,
entrchạy./run.shnhư tiến trình con, và shebang khởi động một Bash mới - Bash mới sau khi chạy
gccsẽ fork và chờ./hello, nên có thể xác nhận tiến trình con kết thúc vìSIGSEGVvà để lại thông báo lỗi
- Chương trình bị crash lúc đó đã kết thúc; shell cha thu hồi tiến trình con, xác nhận nó chết vì
Hai cách giữ lại shell cha
- Nếu chạy lệnh gây crash trong subshell, thông báo sẽ xuất hiện trở lại
ls hello.c | entr -s "gcc -o hello hello.c && (./hello)" hello.c: line 1: 106595 Segmentation fault (core dumped) ( ./hello )- Dấu ngoặc buộc
./hellođược chạy trong một subshell đã được fork - Vì Bash vẫn giữ vai trò cha, nó có thể thu hồi kết thúc
SIGSEGVcủa subshell và in thông báo
- Dấu ngoặc buộc
- Nếu để lại một việc cần chạy sau lệnh gây crash, cũng có thể ngăn tối ưu hóa
execls hello.c | entr -s "bash -c 'gcc -o hello hello.c && ./hello; true'" bash: line 1: 109516 Segmentation fault (core dumped) ./hello- Do có
; true, Bash vẫn còn việc phải làm sau khi./hellokết thúc, nên không tự thay thế chính nó bằng chương trình đó - Có thể thấy thông báo lỗi segmentation fault ngay cả khi không có script wrapper riêng
- Do có
1 bình luận
Ý kiến trên Lobste.rs
Các bản phân phối Linux mới nhất mặc định vô hiệu hóa core dump, nên nhiều lúc khá phiền
Có thể bật lại bằng
ulimit -c unlimited, nhưng có vẻ file core cũng không được tạo trong cùng thư mục với tệp thực thi, và hình như systemd chuyển nó đi đâu đóSẽ hay hơn nếu giống OpenBSD, bật core dump theo mặc định và đặt nó cùng thư mục với tệp thực thi, nhưng hiện giờ khi xảy ra crash thì phần lớn phải chạy lại tệp thực thi trong gdb
coredumpctlđể liệt kê và lấy các core dump gần đâyNgười ta đâu có nghĩ đến chuyện tìm core dump trong những nơi như
/usr/bin, hơn nữa các vị trí đó thường nằm trên hệ thống tệp dung lượng nhỏ hoặc chỉ đọcCó lẽ thư mục làm việc sẽ phù hợp hơn nhiều làm vị trí mặc định
Thậm chí ngay cả khi người dùng bật core file, dường như hệ thống vẫn cho rằng họ không biết mình đang làm gì, nên nó bị giấu ở nơi mà ngay cả root cũng khó tìm
Nếu viết chương trình bằng C, D hoặc ngôn ngữ tương tự và muốn biết nguyên nhân kết thúc, thì nên kiểm tra giá trị trả về của lời gọi hệ thống
wait, từ đó có thể biết chương trình có bị kết thúc bởi tín hiệu hay khôngNếu hệ điều hành chuyển core dump sang nơi khác thì cũng có lợi thế là có thể gỡ lỗi ngay khi cần hoặc gửi báo cáo lỗi cho bản phân phối
Khi tạo core dump, nhân Linux sẽ đọc tệp đặc biệt
/proc/sys/kernel/core_patternđể quyết định nơi lưuThay vì ghi ra file, nó cũng có thể chuyển qua pipe cho một chương trình, nhờ đó có thể hiện hộp thoại GUI, chạy debugger ngay lập tức hoặc gửi email báo cáo cho nhà phát triển
systemd đặt
core_patterncủa kernel thành giá trị do nó quản lý, nhưng ngay cả khi không dùng systemd thì vẫn có thể dùng cùng tính năng này của kernelCũng có câu chuyện rằng trong thời phát triển Windows XP, việc gửi core dump về Microsoft đã giúp sửa nhiều lỗi mà nếu không thì họ còn chẳng biết tồn tại; và nếu cài công cụ phát triển trên Windows thì debugger theo yêu cầu cũng khá hữu ích khi phát triển chương trình của riêng mình
Tính năng này được đưa vào Linux từ năm 2003, và có thể xem chi tiết trong
man 5 coreCore dump rất tuyệt cho việc gỡ lỗi, nhưng nếu nằm cạnh những file có vẻ không liên quan thì sẽ khiến người dùng phổ thông bối rối, và nếu dùng như một hệ thống báo cáo chẩn đoán thì có thể rất tệ
Tôi đã sốc khi lần đầu thấy lượng dữ liệu được đưa vào các báo cáo crash gửi từ Windows
Nếu giả định có một hệ thống báo cáo crash bảo vệ quyền riêng tư, thì cần học cách suy ra nguyên nhân chỉ từ thông tin stack và thanh ghi để có thể chẩn đoán các crash đã được gửi lên
Tôi hoàn toàn không biết rằng Bash đôi khi trực tiếp
execcác lệnh trong script, và đây là một tính năng khá bất ngờBản thân tiến trình Bash cũng không tạo ra tải lớn đến thế, và ở những chỗ cần thiết tôi vốn đã có thói quen dùng
exec, nên tính năng này có cảm giác hơi được thiết kế saiTuy vậy, nếu cộng tổng số lần thực thi lại thì có lẽ nó vẫn có thể tạo ra khác biệt lớn hơn dự đoán