2013-01-20 42 views
8

Tôi đang gặp vấn đề trong chương trình C trên Linux.Làm thế nào tôi có thể vượt qua một ổ cắm từ quá trình cha mẹ đến con

Tôi biết rằng khi một quy trình được chia nhỏ, quy trình con sẽ thừa kế một số thứ từ cha mẹ, bao gồm mở mô tả tệp.

Vấn đề là tôi đang viết một ứng dụng máy chủ đa quy trình với quy trình chính chấp nhận các kết nối mới và đặt các bộ mô tả vào bộ nhớ dùng chung.

Khi quá trình con tìm cách đọc từ một trong các bộ mô tả này từ bộ nhớ dùng chung, trên select() tôi gặp lỗi EBADF!

Quy trình con đọc và sử dụng ổ cắm (hoặc bất kỳ bộ mô tả tệp nào nói chung) được tạo bởi quy trình gốc sau nó đã được chia hai?

Trả lời

9

Khi bạn gọi ngã ba, quá trình con được thừa hưởng bản của tất cả các mô tả tệp mở. Cách điển hình để làm điều này là cho quá trình cha mẹ mở một ổ cắm nghe, hãy gọi chấp nhận khối nào cho đến khi kết nối đến và sau đó gọi fork sau khi nhận kết nối. Sau đó, cha mẹ đóng bản sao của bộ mô tả tệp, trong khi tiến trình con mới có thể tiếp tục sử dụng bộ mô tả tệp và thực hiện bất kỳ việc xử lý nào cần thiết. Khi trẻ đã hoàn thành, cũng sẽ đóng ổ cắm. Điều quan trọng cần nhớ hai điều sau đây: 1. Bộ mô tả/socket là một tài nguyên trong hệ điều hành và sau mỗi nhánh cha và con có một tay cầm với tài nguyên đó, nó giống như một con trỏ thông minh. Tôi giải thích điều này in more detail here. Điều thứ hai là chỉ có các bộ mô tả tập tin được mở trước khi chia sẻ số gọi là vì sau khi giả mạo cha mẹ và con là các quy trình hoàn toàn riêng biệt, mặc dù chúng có thể chia sẻ một số tài nguyên như mô tả tệp tồn tại trước ngã ba. Nếu bạn đang sử dụng một mô hình mà bạn muốn có một phụ huynh bàn giao công việc cho các quy trình công nhân, bạn nên cân nhắc việc sử dụng các chủ đề và a thread pool.

Nhân tiện, bạn có thể tải xuống tất cả các ví dụ hay về máy chủ và khách hàng từ trang web Unix Network Programming.

11

Bạn không thể truyền một ổ cắm (hoặc bất kỳ bộ mô tả tệp nào khác) từ quy trình này sang quy trình khác thông qua bộ nhớ dùng chung. Trình mô tả tệp chỉ là một số nguyên nhỏ. Đặt số nguyên này trong bộ nhớ chia sẻ và truy cập nó từ một tiến trình khác không tự động làm cho cùng một số nguyên thành một bộ mô tả tệp hợp lệ từ quan điểm của quá trình khác.

Cách chính xác để gửi bộ mô tả tệp từ quy trình này sang quy trình khác là gửi dữ liệu phụ như SCM_RIGHTS thông qua một kênh truyền thông socket hiện có giữa hai quy trình.

Trước tiên, hãy tạo kênh giao tiếp của bạn với socketpair() trước khi bạn fork(). Bây giờ, trong phụ huynh, đóng một đầu của cặp ổ cắm, và, trong con, đóng đầu kia. Bây giờ bạn có thể sendmsg() từ cha mẹ ở một đầu của ổ cắm này và nhận được với recvmsg() ở trẻ em bằng cách sử dụng đầu kia.

Gửi một tin nhắn với SCM_RIGHTS trông giống như sau:

struct msghdr m; 
struct cmsghdr *cm; 
struct iovec iov; 
char buf[CMSG_SPACE(sizeof(int))]; 
char dummy[2];  

memset(&m, 0, sizeof(m)); 
m.msg_controllen = CMSG_SPACE(sizeof(int)); 
m.msg_control = &buf; 
memset(m.msg_control, 0, m.msg_controllen); 
cm = CMSG_FIRSTHDR(&m); 
cm->cmsg_level = SOL_SOCKET; 
cm->cmsg_type = SCM_RIGHTS; 
cm->cmsg_len = CMSG_LEN(sizeof(int)); 
*((int *)CMSG_DATA(cm)) = your_file_descriptor_to_send; 
m.msg_iov = &iov; 
m.msg_iovlen = 1; 
iov.iov_base = dummy; 
iov.iov_len = 1; 
dummy[0] = 0; /* doesn't matter what data we send */ 
sendmsg(fd, &m, 0); 

Tiếp nhận một tin nhắn với SCM_RIGHTS trong nó đi một cái gì đó như thế này:

struct msghdr m; 
struct cmsghdr *cm; 
struct iovec iov; 
struct dummy[100]; 
char buf[CMSG_SPACE(sizeof(int))]; 
ssize_t readlen; 
int *fdlist; 

iov.iov_base = dummy; 
iov.iov_len = sizeof(dummy); 
memset(&m, 0, sizeof(m)); 
m.msg_iov = &iov; 
m.msg_iovlen = 1; 
m.msg_controllen = CMSG_SPACE(sizeof(int)); 
m.msg_control = buf; 
readlen = recvmsg(fd, &m, 0); 
/* Do your error handling here in case recvmsg fails */ 
received_file_descriptor = -1; /* Default: none was received */ 
for (cm = CMSG_FIRSTHDR(&m); cm; cm = CMSG_NXTHDR(&m, cm)) { 
    if (cm->cmsg_level == SOL_SOCKET && cm->cmsg_type == SCM_RIGHTS) { 
     nfds = (cm->cmsg_len - CMSG_LEN(0))/sizeof(int); 
     fdlist = (int *)CMSG_DATA(cm); 
     received_file_descriptor = *fdlist; 
     break; 
    } 
} 
+0

tôi đã nói về quá trình cha/con, vì vậy câu hỏi là: Làm cách nào để truy cập con vào tệp mô tả do phụ huynh tạo sau khi ngã ba? – user1995143

+0

+1 Để có câu trả lời hay, nhưng nghe có vẻ như tôi muốn sử dụng đề tài tốt hơn. –

+0

@ user1995143 Đó chính là câu hỏi tôi đã trả lời. Không quan trọng mối quan hệ cha/con của 2 quy trình là gì. Miễn là bạn có một ổ cắm miền UNIX như một kênh truyền thông, bạn có thể truyền tải các bộ mô tả tập tin bằng cách sử dụng kỹ thuật này. Hoặc bạn có thể làm theo đề xuất của [Robert S. Barnes] (http://stackoverflow.com/users/71074/robert-s-barnes) và sử dụng nhiều chủ đề thay vì nhiều quy trình. Sau đó, bạn không phải lo lắng về nó bởi vì các bộ mô tả tập tin được chia sẻ giữa tất cả các luồng của một tiến trình đơn lẻ. – Celada