2015-05-19 15 views
11

Hãy có một cái nhìn tại chương trình này Hello WorldSự khác nhau giữa FILE * "/ dev/stdout" và stdout

#include <stdio.h> 
int main(int argc, char ** argv) { 
    printf("Hello, World!"); 

    const char* sFile = "/dev/stdout"; // or /proc/self/fd/0 
    const char* sMode = "w"; 
    FILE * output = fopen(sFile, sMode); 
    //fflush(stdout) /* forces `correct` order */ 
    putc('!', output); // Use output or stdout from stdio.h 

    return 0; 
} 

Khi biên soạn bằng cách sử dụng mô tả output tập tin đầu ra là:

!Hello, World! 

khi được biên soạn bằng cách sử dụng mô tả tệp stdout được cung cấp bởi stdio.h kết quả như mong đợi:

Hello, World!! 

Tôi hình dung khi gọi putc với sau này, nó sẽ in trực tiếp đến stdout và khi sử dụng các mô tả tập tin trên /dev/stdout nó sẽ mở một ống và in vào đó. Tôi không chắc chắn.

Hành vi này thậm chí còn thú vị hơn, vì nó không ghi đè ký tự đầu tiên của 'Hello' nhưng thay vào đó đẩy chính nó vào vị trí đầu tiên của bộ đệm dòng trước chuỗi đã được đẩy.

Từ quan điểm logic, điều này là không mong muốn yên tĩnh.

Có ai có thể giải thích chính xác những gì đang xảy ra ở đây không?


Tôi đang sử dụng cc (Ubuntu 4.8.2-19ubuntu1) 4.8.2 và một hạt nhân 3.13.0-52 linux biên soạn w/gcc 4.8.2


Sửa: Tôi đã thực hiện một strace của cả hai chương trình, và đây là phần quan trọng:

output (fopen ("/ dev/stdout", "w")) mà không cần fflush(stdout) scenar io sản xuất:

... 
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f62f21e9000 
write(3, "!", 1!)      = 1 
write(1, "Hello, World!", 13Hello, World!)   = 13 
exit_group(0)       = ? 

sử dụng fflush(stdout) sản xuất và thực thi đúng thứ tự:

... 
open("/dev/stdout", O_WRONLY|O_CREAT|O_TRUNC, 0666) = 3 
write(1, "Hello, World!", 13Hello, World!)   = 13 
fstat(3, {st_mode=S_IFCHR|0620, st_rdev=makedev(136, 1), ...}) = 0 
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f5ad4557000 
write(3, "!", 1!)      = 1 
exit_group(0)       = ? 

Các stdout (từ stdlib.h) kịch bản sản xuất:

... 
write(1, "Hello, World!!", 14Hello, World!!)   = 14 
exit_group(0)       = ? 

Vì vậy, có vẻ như các FILE * output = fopen("/dev/stdout") luồng sử dụng mô tả tệp khác với stdout Cũng có vẻ như printf sử dụng stdout Vì vậy, trong kịch bản thứ ba, chuỗi được lắp ráp trước khi nó được đẩy lên luồng.

+0

'stdout' là một tệp *, không phải là bộ mô tả tệp. Tương tự, 'output' không phải là một bộ mô tả tập tin. Mỗi tệp có một bộ mô tả tệp cơ bản và nếu bạn viết trực tiếp vào nó, bạn sẽ không thấy hành vi này. (Viết trực tiếp vào bộ mô tả tập tin bỏ qua đệm.) –

+0

Sự khác biệt lớn là mỗi 'FILE *' đang sử dụng bộ đệm riêng của nó, không liên quan đến nhau .. –

Trả lời

18

Cả hai luồng (stdoutoutput) đều được lưu vào bộ đệm. Không có gì thực sự được viết cho đến khi chúng được đỏ mặt. Vì bạn không xả chúng một cách rõ ràng, cũng như không sắp xếp để chúng tự động xả, chúng chỉ được tự động xả khi chúng được đóng lại.

Bạn cũng không đóng chúng một cách rõ ràng, vì vậy chúng đang bị đóng (và bị xóa) bởi các móc nối on_exit của thư viện chuẩn. Và như William Pursell đã chỉ ra một cách chính xác, thứ tự các luồng I/O đệm được đóng lại không được chỉ định.

Nhìn vào fflush(3), fclose(3)setbuf(3) trang thủ công để biết thêm thông tin về cách kiểm soát thời gian và cách thức đầu ra của bạn bị xóa.

+3

Điểm mấu chốt là thứ tự mà chúng đang bị đóng không xác định . –

+0

Thêm 'fflush (stdout)' trước khi 'putc (...)' trong kịch bản 'output' không thay đổi hành vi. – MrPaulch

+1

Nó làm cho tôi, khá đáng tin cậy. Tôi sẽ khuyên bạn nên chạy 'strace' trên kết quả thực thi của bạn để xem chuỗi các cuộc gọi hệ thống được tạo ra. Điều này có thể được khá chiếu sáng anyway để xem làm thế nào các chức năng đệm I/O tương tác với hệ thống. –

2

/dev/stdout không giống như /proc/self/fd/0. Trong thực tế, nếu bạn không có đủ đặc quyền, bạn sẽ không thể ghi vào /dev/stdout/dev/stdout không phải là bất kỳ thiết bị ký tự chuẩn nào trong Linux và bất kỳ nỗ lực nào để mở nó bằng tùy chọn "w" sẽ cố gắng tạo một tệp thông thường trên đó danh mục. Các thiết bị nhân vật bạn đang tìm kiếm là /dev/tty

Trong ngôn ngữ C, stdout là một biến toàn cầu khởi tạo kiểu FILE * mà chỉ vào tập tin đầu ra tiêu chuẩn, có nghĩa là, các tập tin mà mô tả là 1. stdout chỉ tồn tại trong thư mục C không gian tên và không liên quan đến bất kỳ tệp thực tế nào có tên "stdout"

+1

Tôi không biết bạn đang sử dụng distro nào nhưng '/ dev/stdout' là một liên kết tượng trưng đến'/proc/self/fd/1' mà chính nó là một liên kết tượng trưng đến '/ dev/pts/XX' trên của tôi . Vì vậy, họ có thể hoán đổi cho nhau. Cuối cùng, đó thậm chí không phải là vấn đề :) – MrPaulch

+3

Tuy nhiên, điều đáng nói là không có/dev/stdout,/proc/self/fd, hoặc/dev/fd được chuẩn hóa, và/dev/tty là. – Random832

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