Tôi không thể cung cấp cho bạn câu trả lời chính xác cho câu hỏi này và câu trả lời có thể khác với hệ thống, nhưng tôi mong đợi một ổ cắm không chặn không bao giờ bị lỗi với EINTR
. Nếu bạn xem các trang hướng dẫn của các hệ thống khác nhau cho các chức năng ổ cắm sau đây bind()
, connect()
, send()
và , hoặc xem chúng trong tiêu chuẩn POSIX, bạn sẽ thấy một điều thú vị: Tất cả các chức năng này ngoại trừ một hàm có thể trả về -1
và đặt errno
thành EINTR
. Một chức năng không được ghi lại là không thành công với EINTR
là bind()
. Và bind()
cũng là chức năng duy nhất của danh sách đó sẽ không bao giờ chặn theo mặc định. Vì vậy, có vẻ như chỉ có chức năng chặn có thể không thành công vì EINTR
, bao gồm read()
và write()
, nhưng nếu các chức năng này không bao giờ chặn, chúng cũng sẽ không bao giờ bị lỗi với EINTR
và nếu bạn sử dụng O_NONBLOCK
, các chức năng đó sẽ không bao giờ chặn.
Nó cũng sẽ không có ý nghĩa từ góc nhìn logic. Ví dụ. xem xét bạn đang sử dụng chặn I/O và bạn gọi read()
và cuộc gọi này phải chặn, nhưng trong khi nó đang chặn, một tín hiệu được gửi đến quá trình của bạn và do đó yêu cầu đọc được ublocked. Hệ thống xử lý tình huống này như thế nào? Tuyên bố rằng read()
đã thành công? Đó sẽ là một lời nói dối, nó không thành công vì không có dữ liệu nào được đọc. Tuyên bố nó đã thành công, nhưng dữ liệu byte không được đọc? Điều này sẽ không chính xác, bởi vì "kết quả đọc không" được sử dụng để biểu thị kết thúc luồng (hoặc kết thúc luồng), do đó quy trình của bạn sẽ giả định rằng không có dữ liệu nào được đọc, vì kết thúc của một tập tin đã đạt được (hoặc một ổ cắm/ống đã được đóng ở đầu kia), mà chỉ đơn giản là không phải là trường hợp. Kết thúc tập tin (hoặc cuối luồng) chưa đạt được, nếu bạn gọi lại read()
, nó sẽ có thể trả lại nhiều dữ liệu hơn. Vì vậy, đó cũng sẽ là một lời nói dối. Bạn kỳ vọng là cuộc gọi đọc này thành công và đọc dữ liệu hoặc không thành công với lỗi. Vì vậy, cuộc gọi đọc phải thất bại và trả lại -1
trong trường hợp đó, nhưng giá trị errno
nào sẽ được hệ thống đặt? Tất cả các giá trị lỗi khác chỉ ra một lỗi nghiêm trọng với bộ mô tả tập tin, nhưng không có lỗi nghiêm trọng và chỉ ra một lỗi như vậy cũng sẽ là một lời nói dối. Đó là lý do tại sao errno
được đặt thành EINTR
, có nghĩa là: "Không có gì sai với luồng. Cuộc gọi đọc của bạn không thành công, vì nó bị gián đoạn bởi tín hiệu. Nếu nó không bị gián đoạn, nó vẫn có thể thành công, vì vậy nếu bạn vẫn quan tâm đến dữ liệu, vui lòng thử lại. "
Nếu bây giờ bạn chuyển sang I/O không chặn, tình huống trên không bao giờ phát sinh. Cuộc gọi đọc sẽ không bao giờ chặn và nếu nó không thể đọc dữ liệu ngay lập tức, nó sẽ không thành công với lỗi EAGAIN
(POSIX) hoặc EWOULDBLOCK
(không chính thức, trên Linux đều là lỗi tương tự, chỉ thay thế tên cho nó), có nghĩa là: "Có không có sẵn dữ liệu ngay bây giờ và do đó cuộc gọi đọc của bạn sẽ phải chặn và chờ dữ liệu đến, nhưng việc chặn không được phép, do đó, nó không thành công. " Vì vậy, có một lỗi cho mọi tình huống có thể phát sinh.
Tất nhiên, ngay cả với I/O không chặn, cuộc gọi đọc có thể tạm thời bị gián đoạn bởi tín hiệu nhưng tại sao hệ thống phải chỉ ra điều đó? Mỗi chức năng gọi, cho dù đây là một chức năng hệ thống hoặc một bằng văn bản của người sử dụng, có thể tạm thời bị gián đoạn bởi một tín hiệu, thực sự mọi người duy nhất, không có ngoại lệ. Nếu hệ thống sẽ phải thông báo cho người dùng bất cứ khi nào điều đó xảy ra, tất cả các chức năng hệ thống có thể bị lỗi vì EINTR
. Tuy nhiên, ngay cả khi có sự gián đoạn tín hiệu, các chức năng thường thực hiện nhiệm vụ của mình từ đầu đến cuối, đó là lý do tại sao gián đoạn này không liên quan. Lỗi EINTR
được sử dụng để thông báo cho người gọi rằng hành động mà anh ta yêu cầu không được thực hiện do gián đoạn tín hiệu, nhưng trong trường hợp I/O không chặn, không có lý do nào khiến chức năng không thực hiện việc đọc hoặc ghi yêu cầu, trừ khi nó không thể được thực hiện ngay bây giờ, nhưng sau đó điều này có thể được chỉ định bởi một lỗi thích hợp.
Để xác nhận lý thuyết của mình, tôi đã xem hạt nhân của MacOS (10.8), phần lớn vẫn dựa trên hạt nhân FreeBSD và dường như xác nhận sự nghi ngờ. Nếu một cuộc gọi đọc hiện không khả thi, vì không có sẵn dữ liệu, hạt nhân sẽ kiểm tra cờ O_NONBLOCK
trong cờ mô tả tệp. Nếu cờ này được đặt, nó sẽ thất bại ngay lập tức với EAGAIN
. Nếu nó không được thiết lập, nó sẽ đặt luồng hiện hành vào chế độ ngủ bằng cách gọi một hàm có tên là msleep()
. Chức năng là documented here (như tôi đã nói, OS X sử dụng nhiều mã FreeBSD trong hạt nhân của nó). Chức năng này làm cho luồng hiện tại ngủ cho đến khi nó được đánh thức rõ ràng (trường hợp nếu dữ liệu sẵn sàng để đọc) hoặc thời gian chờ đã được nhấn (ví dụ: bạn có thể đặt thời gian chờ nhận trên ổ cắm). Tuy nhiên, thread cũng được đánh thức, nếu một tín hiệu được gửi đi, trong trường hợp đó, msleep()
sẽ trả về EINTR
và lớp cao hơn kế tiếp chỉ vượt qua lỗi này. Vì vậy, nó là msleep()
sản xuất lỗi EINTR
, nhưng nếu cờ O_NONBLOCK
được đặt, msleep()
không bao giờ được gọi ở vị trí đầu tiên, do đó lỗi này không thể được trả lại. Tất nhiên đó là MacOS/FreeBSD, các hệ thống khác có thể khác nhau, nhưng vì hầu hết các hệ thống cố gắng giữ ít nhất một mức nhất quán nhất quán giữa các API này, nếu hệ thống phá vỡ giả định, I/O không bị chặn cuộc gọi có thể không bao giờ thất bại vì EINTR
, điều này có thể không phải theo ý định và thậm chí có thể được khắc phục nếu báo cáo của bạn.
Cảm ơn câu trả lời chi tiết của bạn. – haohaolee
Giải thích hay. Cảm ơn bạn. –