2010-09-07 47 views
16

Tôi tự hỏi nếu có một cách dễ dàng để lặp qua fd_set? Lý do tôi muốn làm điều này là không phải lặp qua tất cả các ổ cắm được kết nối, vì select() thay đổi các fd_sets này để chỉ bao gồm những cái mà tôi quan tâm. Tôi cũng biết rằng việc sử dụng loại triển khai không có nghĩa là được truy cập trực tiếp thường là một ý tưởng tồi vì nó có thể khác nhau trên các hệ thống khác nhau. Tuy nhiên, tôi cần một số cách để làm điều này, và tôi đang hết ý tưởng. Vì vậy, câu hỏi của tôi là:Làm thế nào để lặp qua một fd_set

Làm cách nào để lặp qua fd_set? Nếu đây là một thực tế tồi tệ, có cách nào khác để giải quyết "vấn đề" của tôi ngoại trừ từ looping thông qua tất cả các ổ cắm được kết nối?

Cảm ơn

+0

Để nhấn mạnh ý của tôi. Tôi không muốn sử dụng phương pháp FD_ISSET vì nó yêu cầu tôi lặp qua tất cả các ổ cắm được kết nối. Nhưng vì, theo định nghĩa, select() loại bỏ các mô tả tệp không liên quan khỏi tập hợp, tôi muốn lặp qua bộ này. – Andreas

+6

Nó không nhất thiết có nghĩa là "tất cả các kết nối". Bạn có thể vượt qua một tập hợp con của các ổ cắm được kết nối của bạn để chọn và sau đó sử dụng FD_ISSET chỉ trên tập hợp con đó sau khi chọn trả về. Ngoài ra, có một vấn đề thực tế với looping trên tất cả chúng? Trừ khi bạn đang đối phó với hàng ngàn ổ cắm được kết nối, vòng lặp sẽ có thể mất một khoảng thời gian không quan trọng. – Rakis

+1

Đồng ý với Rakis. Đây là một trong những điều mà dường như không hiệu quả nhưng trong hầu hết các trường hợp thực sự không phải là Thời gian để đi qua vòng lặp sẽ được dwarfed bởi thời gian cần để phục vụ chỉ là một trong những FDs thiết lập. – Duck

Trả lời

5

Chọn đặt bit tương ứng với bộ mô tả tập tin trong tập hợp, vì vậy, bạn không cần phải lặp qua tất cả các fds nếu bạn chỉ quan tâm đến một vài (và có thể bỏ qua những người khác) chỉ kiểm tra những bộ mô tả tập tin mà bạn quan tâm.

if (select(fdmax+1, &read_fds, NULL, NULL, NULL) == -1) { 
    perror("select"); 
    exit(4); 
} 

if(FD_ISSET(fd0, &read_fds)) 
{ 
    //do things 
} 

if(FD_ISSET(fd1, &read_fds)) 
{ 
    //do more things 
} 

EDIT
Đây là struct fd_set:

typedef struct fd_set { 
     u_int fd_count;    /* how many are SET? */ 
     SOCKET fd_array[FD_SETSIZE]; /* an array of SOCKETs */ 
} fd_set; 

đâu, fd_count là số ổ cắm thiết lập (như vậy, bạn có thể thêm tối ưu hóa sử dụng này) và fd_array là một bit-vector (kích thước FD_SETSIZE * sizeof (int) phụ thuộc vào máy). Trong máy của tôi, nó là 64 * 64 = 4096.

Vì vậy, câu hỏi của bạn chủ yếu là: cách hiệu quả nhất để tìm vị trí bit của 1s trong bit-bit (kích thước khoảng 4096 bit) là gì?

Tôi muốn xóa một điều ở đây:
"lặp qua tất cả các ổ cắm được kết nối" không có nghĩa là bạn đang thực sự đọc/thực hiện công cụ với kết nối. FD_ISSET() chỉ kiểm tra thời tiết bit trong fd_set được đặt ở số file_descriptor được gán của kết nối được thiết lập hay không. Nếu hiệu quả là mục tiêu của bạn, thì đây không phải là hiệu quả nhất? sử dụng heuristics?

Hãy cho chúng tôi biết điều gì sai với phương pháp này và bạn đang cố gắng đạt được gì khi sử dụng phương pháp thay thế.

+0

Cảm ơn bạn. Nhưng xin vui lòng xem bình luận của tôi, có lẽ tôi đã không giải thích rõ ràng đây là cách tiếp cận mà tôi không muốn thực hiện. – Andreas

+3

Nếu đây không phải là câu trả lời [đúng/bạn muốn], tại sao câu trả lời được đánh dấu là câu trả lời? –

+0

Vì hai lý do. a) bản chỉnh sửa cung cấp thông tin tôi đang tìm kiếm b) Tôi đã thay đổi quyết định và do đó câu trả lời trở nên có liên quan. – Andreas

1

Xem phần này 7.2 của Beej Hướng dẫn về mạng - '7.2. select() - Ghép kênh I/O đồng bộ 'bằng cách sử dụng FD_ISSET.

trong ngắn hạn, bạn phải lặp thông qua một fd_set để xác định xem mô tả tập tin đã sẵn sàng cho việc đọc/viết ...

+0

Cảm ơn câu trả lời. Tôi biết đây là cách tiếp cận tiêu chuẩn, tuy nhiên tôi đang tìm cách để vượt qua nó, xin vui lòng xem bình luận của tôi trên bài viết của riêng tôi. – Andreas

4

Nó khá thẳng về phía trước:

for(int fd = 0; fd < max_fd; fd++) 
    if (FD_ISSET(fd, &my_fd_set)) 
     do_socket_operation(fd); 
+0

Cảm ơn câu trả lời. Xin vui lòng xem bình luận của tôi để làm rõ những gì tôi muốn làm. – Andreas

0

Tôi không nghĩ rằng những gì bạn đang cố gắng làm là một ý tưởng tốt.

Trước hết phụ thuộc vào hệ thống của nó, nhưng tôi tin rằng bạn đã biết điều đó.

Thứ hai, ở cấp độ nội bộ, các bộ này được lưu trữ dưới dạng một mảng các số nguyên và fds được lưu trữ dưới dạng các bit thiết lập. Bây giờ theo các trang của người đàn ông chọn FD_SETSIZE là 1024. Thậm chí nếu bạn muốn lặp lại và nhận được fd quan tâm của bạn, bạn phải lặp lại con số đó cùng với sự lộn xộn của thao tác bit. Vì vậy, trừ khi bạn đang chờ đợi hơn FD_SETSIZE fd trên lựa chọn mà tôi không nghĩ như vậy là có thể, nó không phải là một ý tưởng tốt.

Oh wait !!. Trong mọi trường hợp nó không phải là một ý tưởng tốt.

10

Bạn phải điền vào cấu trúc fd_set trước khi gọi select(), bạn không thể chuyển trực tiếp phần gốc của bạn :: bộ ổ cắm trực tiếp. select() sau đó sửa đổi fd_set cho phù hợp, loại bỏ bất kỳ ổ cắm nào không được "đặt" và trả về số lượng ổ cắm còn lại. Bạn phải lặp qua fd_set kết quả, không phải std :: set của bạn. Không cần phải gọi FD_ISSET() vì fd_set kết quả chỉ có chứa "thiết lập" ổ cắm đã sẵn sàng, ví dụ:

fd_set read_fds; 
FD_ZERO(&read_fds); 

int max_fd = 0; 

read_fds.fd_count = connected_sockets.size(); 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    read_fds.fd_array[i] = connected_sockets[i]; 
    if (read_fds.fd_array[i] > max_fd) 
     max_fd = read_fds.fd_array[i]; 
} 

if (select(max_fd+1, &read_fds, NULL, NULL, NULL) > 0) 
{ 
    for(int i = 0; i < read_fds.fd_count; ++i) 
     do_socket_operation(read_fds.fd_array[i]); 
} 

đâu FD_ISSET() đến chơi thường xuyên hơn là khi sử dụng kiểm tra lỗi với lựa chọn() , ví dụ:

fd_set read_fds; 
FD_ZERO(&read_fds); 

fd_set error_fds; 
FD_ZERO(&error_fds); 

int max_fd = 0; 

read_fds.fd_count = connected_sockets.size(); 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    read_fds.fd_array[i] = connected_sockets[i]; 
    if (read_fds.fd_array[i] > max_fd) 
     max_fd = read_fds.fd_array[i]; 
} 

error_fds.fd_count = read_fds.fd_count; 
for(int i = 0; i < read_fds.fd_count; ++i) 
{ 
    error_fds.fd_array[i] = read_fds.fd_array[i]; 
} 

if (select(max_fd+1, &read_fds, NULL, &error_fds, NULL) > 0) 
{ 
    for(int i = 0; i < read_fds.fd_count; ++i) 
    { 
     if(!FD_ISSET(read_fds.fd_array[i], &error_fds)) 
      do_socket_operation(read_fds.fd_array[i]); 
    } 

    for(int i = 0; i < error_fds.fd_count; ++i) 
    { 
     do_socket_error(error_fds.fd_array[i]); 
    } 
} 
+0

+1, mặc dù, tôi tìm thấy một số 'aray' trong mã của bạn :) – Default

+0

Tôi đã sửa lỗi chính tả –

+0

[chọn manpage] (http://linux.die.net/man/2/select) nói:' nfds là bộ mô tả tệp được đánh số cao nhất trong bất kỳ bộ nào trong số ba bộ, cộng với 1. 'Sử dụng * số cao nhất *, không phải là * số *! – MaPePeR

3

Vòng lặp này là giới hạn của giao diện select(). Việc triển khai cơ bản của fd_set thường là một bộ bit, điều này rõ ràng có nghĩa là tìm kiếm một ổ cắm đòi hỏi phải quét qua các bit.

Chính vì lý do này mà một số giao diện thay thế đã được tạo - thật không may, tất cả đều là hệ điều hành cụ thể. Ví dụ, Linux cung cấp epoll, trả về một danh sách chỉ các bộ mô tả tập tin đang hoạt động. FreeBSD và Mac OS X đều cung cấp kqueue, kết quả này cũng đạt được kết quả tương tự.

+0

Đây phải là câu trả lời được chấp nhận. – Agis

0

Tôi không nghĩ rằng bạn có thể làm được nhiều việc bằng cách sử dụng cuộc gọi select() hiệu quả. Thông tin tại "The C10K problem" vẫn hợp lệ.

Bạn sẽ cần một số nền tảng giải pháp cụ thể:

Hoặc bạn có thể sử dụng một thư viện trường để ẩn các chi tiết nền tảng cho bạn libev

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