2012-02-20 37 views
56

Tôi có một chương trình đơn giản:fork() và đầu ra

int main() 
{ 
    std::cout << " Hello World"; 
    fork(); 
} 

Sau khi chương trình thực hiện đầu ra của tôi là: Hello World Hello World. Tại sao điều này xảy ra thay vì một đơn Hello world? Tôi đoán rằng quá trình con là chạy lại đằng sau hậu trường và bộ đệm đầu ra được chia sẻ giữa các quá trình hoặc một cái gì đó dọc theo những dòng, nhưng đó là trường hợp hoặc là cái gì khác xảy ra?

+2

Đây là những gì ngã ba hiện nó sinh ra quá trình với bộ nhớ của phụ huynh. http://linux.die.net/man/2/fork – v01d

+2

Chắc chắn đã có rất nhiều câu hỏi 'fork()' gần đây ... hmm ... – Mysticial

+4

Tôi ngửi thấy các cuộc phỏng vấn: p –

Trả lời

91

Đây không phải là điều bạn nghĩ ban đầu. Bộ đệm đầu ra không được chia sẻ - khi bạn thực hiện ngã ba, cả hai quá trình sẽ nhận được một bản sao của cùng một bộ đệm. Vì vậy, sau khi bạn ngã ba, cả hai quá trình cuối cùng tuôn ra bộ đệm và in nội dung để màn hình riêng biệt.

Điều này chỉ xảy ra vì cout được đệm IO. Nếu bạn sử dụng cerr, mà không được đệm, bạn chỉ nên xem tin nhắn một lần, trước ngã ba.

+6

Điều này chỉ xảy ra vì cout là vùng đệm do người dùng đệm.^_ ^ –

9

Nếu bạn sử dụng:

std::cout << " Hello World" << std::flush; 

Bạn chỉ nhìn thấy một. Tôi đoán fork() sao chép bất kỳ bộ đệm đầu ra nào std::cout ghi vào.

6

Chuỗi không được ghi ngay lập tức vào màn hình; thay vào đó, nó được ghi vào bộ đệm bên trong. Quá trình con kế thừa một bản sao của bộ đệm đầu ra, vì vậy khi cout của trẻ được tự động xóa, Hello World được in trên màn hình. Phụ huynh cũng in Hello World.

Nếu bạn xả cout trước fork(), sự cố gần như chắc chắn sẽ biến mất.

+1

Tôi sẽ không nói phân tích là chính xác. Quá trình con không phải là "chạy lại đằng sau hậu trường". –

+0

@MichaelMior: Bạn nói đúng. Tôi đã bỏ lỡ bit "chạy lại". Tôi đã chỉnh sửa ngôn ngữ. – NPE

43

đầu ra tiêu chuẩn sử dụng bộ đệm IO. Khi fork() được gọi là đầu ra tiêu chuẩn không được flushed và nội dung đệm được nhân rộng trong quá trình con. Các bộ đệm này được xóa khi thoát khỏi quá trình, dẫn đến hai kết quả đầu ra mà bạn nhìn thấy.

Nếu bạn thay đổi chương trình để:

std::cout << " Hello World;" << std::endl; 

bạn sẽ thấy chỉ có một.

+2

câu trả lời này làm rõ cho tôi mọi thứ. cảm ơn lớn hai lần – Tebe

17

Vì bạn gọi là fork() mà không xả tất cả bộ đệm trước.

cout.flush(); 
fork(); 
2

Điều bạn có thể thấy ở đây là ảnh hưởng của việc lưu vào bộ đệm. Trong đầu ra chung được đệm cho đến khi nó được xóa hoàn toàn hoặc ngầm được thực hiện với một hành động như xuất ra một dòng mới. Bởi vì đầu ra được đệm cả hai bản sao của quá trình chia đôi có đầu ra được bufferred và do đó cả hai hiển thị nó khi quá trình chấm dứt và xóa bộ đệm

10

Mã vào đầu ra "Hello World" chỉ được thực hiện một lần. Vấn đề là bộ đệm đầu ra không bị xóa. Vì vậy, khi bạn ngã ba quá trình, "Hello World" vẫn còn ngồi trong bộ đệm đầu ra. Khi cả hai chương trình thoát, bộ đệm đầu ra của chúng sẽ bị xóa và bạn sẽ thấy đầu ra hai lần.

Cách dễ nhất để chứng minh điều này là bằng cách thêm một dòng mới ở cuối chuỗi của bạn, điều này sẽ gây ra hiện tượng tuôn ra ngầm, hoặc tuôn ra một cách rõ ràng với std::cout.flush();. Sau đó, bạn sẽ chỉ thấy đầu ra một lần.

3

Lý do là khi bạn gọi std::cout<< nó không thực sự thực hiện đầu ra, nhưng dữ liệu còn lại trong bộ đệm trong hệ thống. Khi bạn làm ngã ba, cả hai mã và dữ liệu được sao chép, cũng như tất cả các bộ đệm liên quan. Cuối cùng, cả hai cha và con trai tuôn ra chúng để sản lượng tiêu chuẩn và do đó bạn thấy đầu ra nhân đôi.