2011-01-31 27 views
8

Theo tài liệu, fprintf có thể thất bại và sẽ trả về số âm khi lỗi. Có rất nhiều tình huống trong đó sẽ hữu ích khi kiểm tra giá trị này.Lỗi khi kiểm tra fprintf khi in tới stderr

Tuy nhiên, tôi thường sử dụng fprintf để in thông báo lỗi thành stderr. Mã của tôi thường sẽ trông giống như sau:

rc = foo(); 
if(rc) { 
    fprintf(stderr, "An error occured\n"); 
    //Sometimes stuff will need to be cleaned up here 
    return 1; 
} 

Trong trường hợp này, vẫn có thể thất bại trong fprintf? Nếu vậy, có bất cứ điều gì có thể được thực hiện để hiển thị thông báo lỗi bằng cách nào đó hoặc là có một thay thế đáng tin cậy hơn để fprintf?

Nếu không, có cần kiểm tra fprintf khi được sử dụng theo cách này không?

Trả lời

12

Tiêu chuẩn C cho biết rằng tệp luồng stdin, stdout và stderr phải được kết nối ở đâu đó, nhưng chúng không chỉ định vị trí, tất nhiên. Hoàn toàn khả thi để chạy một chương trình với họ được chuyển hướng:

some_program_of_yours >/dev/null 2>&1 </dev/null 

Bài viết của bạn sẽ thành công - nhưng thông tin sẽ không đi đâu cả. Một cách tàn bạo hơn để chạy chương trình của bạn là:

some_program_of_yours >&- 2>&- </dev/null 

Lần này, nó đã chạy mà không có luồng tệp mở cho tiêu chuẩn và tiêu chuẩn - không đúng tiêu chuẩn. Nó vẫn đọc từ/dev/null trong ví dụ, có nghĩa là nó không nhận được bất kỳ dữ liệu đầu vào hữu ích nào từ stdin.

Nhiều chương trình không bận tâm kiểm tra xem các kênh I/O chuẩn có đang mở hay không. Nhiều chương trình không bận tâm kiểm tra xem thông báo lỗi đã được viết thành công chưa. Tạo ra một dự phòng phù hợp như phác thảo bởi Tim Postwhitey04 không phải lúc nào cũng đáng nỗ lực. Nếu bạn chạy lệnh ls với kết quả đầu ra của nó bị ức chế, nó sẽ chỉ đơn giản là làm những gì nó có thể và thoát với một tình trạng khác không:

$ ls; echo $? 
gls 
0 
$ ls >&- 2>&-; echo $? 
2 
$ 

(Tested RHEL Linux.) Có thực sự không phải là một nhu cầu cho nó làm nhiều hơn. Mặt khác, nếu chương trình của bạn được yêu cầu chạy trong nền và ghi vào tệp nhật ký, nó có thể sẽ không viết nhiều cho stderr, trừ khi nó không mở được tệp nhật ký (hoặc phát hiện lỗi trên tệp nhật ký) .

Lưu ý rằng nếu bạn rơi trở lại trên syslog(3) (hoặc POSIX), bạn không có cách nào biết liệu cuộc gọi của bạn có 'thành công' hay không; chức năng nhật ký hệ thống tất cả trở về không có thông tin trạng thái. Bạn chỉ cần giả định rằng họ đã thành công. Đó là khu nghỉ mát cuối cùng của bạn, do đó.

+0

Tôi tự hỏi tại sao điều này không nhận được nhiều phiếu bầu hơn là câu trả lời của tôi. +1. –

+0

@Tim: cảm ơn, nhưng câu trả lời của bạn cũng cung cấp thông tin tốt - và bạn đã đến đó trước. Và điểm mấu chốt của bạn - sử dụng một chức năng thực hiện báo cáo lỗi, không phải một đoạn mã mỗi lần bạn cần báo cáo lỗi - là rất quan trọng. –

4

Bạn có thể đặt lỗi trên thiết bị xuất chuẩn hoặc ở một nơi khác ... Tại một thời điểm nào đó, bạn chỉ cần báo lỗi là nỗ lực tốt nhất và sau đó bỏ cuộc.

Điều quan trọng là ứng dụng của bạn "duyên dáng" xử lý nó (ví dụ: hệ điều hành không phải giết nó vì xấu và nó cho bạn biết lý do thoát khỏi ứng dụng [nếu có thể]).

9

Thông thường, bạn sẽ sử dụng một số loại hệ thống ghi nhật ký có thể (thử) để xử lý việc này cho bạn, hoặc bạn sẽ cần sao chép logic đó trong mọi khu vực mã của bạn in ra lỗi và thoát tiêu chuẩn.

Bạn có một số tùy chọn:

  • Nếu fprintf thất bại, hãy thử syslog.
  • Nếu cả hai không thành công, hãy thử tạo tệp 'crash. {Pid} .log' chứa thông tin mà bạn muốn trong báo cáo lỗi. Kiểm tra sự tồn tại của các tệp này khi bạn khởi động, vì chúng có thể cho chương trình của bạn biết rằng nó đã bị lỗi trước đó.
  • Cho phép người dùng được kết nối mạng kiểm tra tùy chọn cấu hình cho phép chương trình của bạn gửi báo cáo lỗi.

Ngẫu nhiên, open()read()write() là bạn tốt có khi gia đình fprintf chức năng không làm việc.

whitey04 says, đôi khi bạn chỉ cần từ bỏ và cố gắng hết sức để không làm tan chảy bằng pháo hoa sẽ tắt. Nhưng hãy cố gắng tách biệt loại logic đó thành một thư viện nhỏ.

Ví dụ:

best_effort_logger(LOG_CRIT, "Heap corruption likely, bailing out!"); 

là sạch hơn nhiều so với một loạt các ifelseelse if mọi nơi mọi thứ có thể có thể đi sai.

2

Một số chương trình mà thực sự muốn đăng thông báo lỗi sẽ thiết lập một thay thế chồng tại chương trình khởi động dự trữ một số lượng bộ nhớ (xem sigaltstack(2) có thể được sử dụng bởi một bộ xử lý tín hiệu (thường SIGSEGV) để Tùy thuộc vào tầm quan trọng của việc ghi nhật ký lỗi của bạn, bạn có thể điều tra bằng cách sử dụng các ngăn xếp thay thế để phân bổ trước một số bộ nhớ. .

3

Có, tất nhiên fprintf đến stderr có thể không thành công. Ví dụ: stderr có thể là tệp bình thường và đĩa có thể hết dung lượng hoặc có thể là đường ống bị người đọc đóng, v.v.

Cho dù bạn nên kiểm tra thao tác thất bại phụ thuộc phần lớn vào việc bạn có thể đạt được hành vi chương trình tốt hơn bằng cách kiểm tra. Trong trường hợp của bạn, những điều duy nhất bạn có thể làm khi không in được thông báo lỗi là cố gắng in một thông báo lỗi khác (chắc chắn sẽ thất bại) hoặc thoát khỏi chương trình (có thể tệ hơn là không báo cáo lỗi, nhưng có lẽ không phải luôn luôn).

Các vấn đề liên quan