I / O không chặn UNIX: O_NONBLOCK so với FIONBIO


92

Trong mọi ví dụ và cuộc thảo luận mà tôi chạy qua trong ngữ cảnh lập trình BSD socket, có vẻ như cách được khuyến nghị để đặt bộ mô tả tệp thành chế độ I / O không chặn là sử dụng O_NONBLOCKcờ fcntl(), ví dụ:

int flags = fcntl(fd, F_GETFL, 0);
fcntl(fd, F_SETFL, flags | O_NONBLOCK);

Tôi đã lập trình mạng trong UNIX hơn mười năm và luôn sử dụng lệnh FIONBIO ioctl()gọi để làm điều này:

int opt = 1;
ioctl(fd, FIONBIO, &opt);

Không bao giờ thực sự suy nghĩ nhiều về lý do tại sao. Chỉ cần học nó theo cách đó.

Có ai có bất kỳ bình luận về giá trị tương ứng có thể có của người này hay người khác không? Tôi tưởng tượng locus khả năng di chuyển hơi khác nhau, nhưng không biết ở mức độ nào vì ioctl_list(2)không nói về khía cạnh đó của các ioctlphương pháp riêng lẻ .

Câu trả lời:


135

Trước khi tiêu chuẩn hóa có ioctl(... FIONBIO... )fcntl(... O_NDELAY... ), nhưng chúng hoạt động không nhất quán giữa các hệ thống, và thậm chí trong cùng một hệ thống. Ví dụ: thông thường FIONBIOkhi làm việc trên các ổ cắm và trên Solaris gây ra việc đọc không có dữ liệu để trả về 0 trên tty hoặc pipe, hoặc -1 với errno EAGAIN trên ổ cắm. Tuy nhiên 0 là không rõ ràng vì nó cũng được trả về cho EOF.O_NDELAY làm việc trên các tty, với rất nhiều điểm không nhất quán đối với những thứ như đường ống, máy tính bảng và thiết bị. Và nếu bạn không biết mình có loại bộ mô tả tệp nào, bạn phải đặt cả hai để chắc chắn. Nhưng ngoài ra, việc đọc không chặn không có sẵn dữ liệu cũng được chỉ ra không nhất quán; tùy thuộc vào hệ điều hành và loại bộ mô tả tệp, việc đọc có thể trả về 0 hoặc -1 với errno EAGAIN hoặc -1 với errno EWOVERBLOCK. Ngay cả ngày hôm nay, thiết lập FIONBIOhoặcO_NDELAY

POSIX đã giải quyết vấn đề này bằng cách giới thiệu O_NONBLOCK, có hành vi tiêu chuẩn hóa trên các hệ thống khác nhau và các loại trình mô tả tệp. Vì các hệ thống hiện có thường muốn tránh bất kỳ thay đổi nào đối với hành vi có thể phá vỡ khả năng tương thích ngược, nên POSIX đã xác định một cờ mới thay vì ủy thác hành vi cụ thể cho một trong những hành vi khác. Một số hệ thống như Linux coi cả 3 như nhau và cũng xác định EAGAIN và EWOVERBLOCK thành cùng một giá trị, nhưng các hệ thống muốn duy trì một số hành vi cũ khác để tương thích ngược có thể làm như vậy khi các cơ chế cũ hơn được sử dụng.

Các chương trình mới nên sử dụng fcntl(... O_NONBLOCK... ), theo tiêu chuẩn của POSIX.


6
Tôi có xu hướng sử dụng ioctl () cho việc này vì tôi chỉ tốn một cuộc gọi tổng hợp để bật chế độ không chặn thay vì hai cuộc gọi cho fcntl (). Ngoài ra, Windows ioctlsocket () API tương đương với ioctl () cho các mục đích của chức năng này.
Wez Furlong

nginx thực hiện việc này nếu có thể và đánh dấu bằng nhận xét "ioctl (FIONBIO) đặt chế độ không chặn với một cuộc gọi tổng hợp." Bây giờ có accept2 cho phép bạn chấp nhận kết nối và đặt nó vào chế độ không chặn trong cùng một cuộc gọi tổng hợp.
Eloff

6

Như @Sean đã nói, fcntl()phần lớn được tiêu chuẩn hóa và do đó có sẵn trên các nền tảng. Các ioctl()chức năng có trước fcntl()trong Unix, nhưng không được chuẩn hóa ở tất cả. Điều đó ioctl()làm việc cho bạn trên tất cả các nền tảng phù hợp với bạn là may mắn, nhưng không được đảm bảo. Đặc biệt, các tên được sử dụng cho đối số thứ hai là phức tạp và không đáng tin cậy trên các nền tảng. Thật vậy, chúng thường là duy nhất đối với trình điều khiển thiết bị cụ thể mà trình mô tả tệp tham chiếu. (Ví dụ: ioctl()các lệnh gọi được sử dụng cho một thiết bị đồ họa được ánh xạ bit chạy trên ICL Perq chạy PNX (Perq Unix) của hai mươi năm trước không bao giờ được dịch sang bất kỳ thứ gì khác ở bất kỳ nơi nào khác.)


6

Tôi tin rằng đó fcntl()là một chức năng POSIX. Ở đâu cũng như ioctl()một thứ UNIX tiêu chuẩn. Đây là danh sách của POSIX io . ioctl()là một thứ rất cụ thể về nhân / trình điều khiển / hệ điều hành, nhưng tôi chắc chắn rằng những gì bạn sử dụng hoạt động trên hầu hết các phiên bản Unix. một số ioctl()thứ khác có thể chỉ hoạt động trên một số hệ điều hành nhất định hoặc thậm chí một số vòng quay nhất định của hạt nhân của nó.


Tôi đã sử dụng FIONBIO trên AIX, Solaris, Linux, * BSD và IRIX mà không gặp vấn đề gì. Nhưng đúng vậy, tôi hiểu rằng nó sẽ không hoạt động trên Windows, chẳng hạn - đó là một giao diện cấp thấp để triển khai kernel rất cụ thể. Tuy nhiên, tôi tự hỏi liệu có bất kỳ yếu tố khác biệt nào khác không.
Alex Balashov
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.