2010-10-18 11 views
8

मैं सी/लिनक्स में एक बहुप्रचारित यूडीपी सर्वर विकसित करना चाहता हूं। यह सेवा एक बंदरगाह एक्स पर चल रही है, इस प्रकार केवल एक ही यूडीपी सॉकेट को बांधने की संभावना है। उच्च लोड के तहत काम करने के लिए, मेरे पास एन थ्रेड (स्थिर रूप से परिभाषित) हैं, प्रति सीपीयू 1 धागा कहें। Epoll_wait का उपयोग करके धागे को काम पर पहुंचाया जा सकता है, इसलिए थ्रेड को मांग पर जागृत हो गया 'EPOLLET | EPOLLONESHOT '। मैंने एक कोड उदाहरण संलग्न किया है:एपोल के साथ मल्टीथ्रेडिंग यूडीपी सर्वर?

static int epfd; 
static sig_atomic_t sigint = 0; 

... 

/* Thread routine with epoll_wait */ 
static void *process_clients(void *pevents) 
{ 
    int rc, i, sock, nfds; 
    struct epoll_event ep, *events = (struct epoll_event *) pevents; 

    while (!sigint) { 
     nfds = epoll_wait(epfd, events, MAX_EVENT_NUM, 500); 

     for (i = 0; i < nfds; ++i) { 
      if (events[i].data.fd < 0) 
       continue; 

      sock = events[i].data.fd; 

      if((events[i].events & EPOLLIN) == EPOLLIN) { 
       printf("Event dispatch!\n"); 
       handle_request(sock); // do a recvfrom 
      } else 
       whine("Unknown poll event!\n"); 

      memset(&ep, 0, sizeof(ep)); 
      ep.events = EPOLLIN | EPOLLET | EPOLLONESHOT; 
      ep.data.fd = sock; 

      rc = epoll_ctl(epfd, EPOLL_CTL_MOD, sock, &ep); 
      if(rc < 0) 
       error_and_die(EXIT_FAILURE, "Cannot add socket to epoll!\n"); 
     } 
    } 

    pthread_exit(NULL); 
} 

int main(int argc, char **argv) 
{ 
    int rc, i, cpu, sock, opts; 
    struct sockaddr_in sin; 
    struct epoll_event ep, *events; 
    char *local_addr = "192.168.1.108"; 
    void *status; 
    pthread_t *threads = NULL; 
    cpu_set_t cpuset; 

    threads = xzmalloc(sizeof(*threads) * MAX_THRD_NUM); 
    events = xzmalloc(sizeof(*events) * MAX_EVENT_NUM); 

    sock = socket(PF_INET, SOCK_DGRAM, 0); 
    if (sock < 0) 
     error_and_die(EXIT_FAILURE, "Cannot create socket!\n"); 

    /* Non-blocking */ 
    opts = fcntl(sock, F_GETFL); 
    if(opts < 0) 
     error_and_die(EXIT_FAILURE, "Cannot fetch sock opts!\n"); 
    opts |= O_NONBLOCK; 
    rc = fcntl(sock, F_SETFL, opts); 
    if(rc < 0) 
     error_and_die(EXIT_FAILURE, "Cannot set sock opts!\n"); 

    /* Initial epoll setup */ 
    epfd = epoll_create(MAX_EVENT_NUM); 
    if(epfd < 0) 
     error_and_die(EXIT_FAILURE, "Error fetching an epoll descriptor!\n"); 

    memset(&ep, 0, sizeof(ep)); 
    ep.events = EPOLLIN | EPOLLET | EPOLLONESHOT; 
    ep.data.fd = sock; 

    rc = epoll_ctl(epfd, EPOLL_CTL_ADD, sock, &ep); 
    if(rc < 0) 
     error_and_die(EXIT_FAILURE, "Cannot add socket to epoll!\n"); 

    /* Socket binding */ 
    sin.sin_family = AF_INET; 
    sin.sin_addr.s_addr = inet_addr(local_addr); 
    sin.sin_port = htons(port_xy); 

    rc = bind(sock, (struct sockaddr *) &sin, sizeof(sin)); 
    if (rc < 0) 
     error_and_die(EXIT_FAILURE, "Problem binding to port! " 
         "Already in use?\n"); 

    register_signal(SIGINT, &signal_handler); 

    /* Thread initialization */ 
    for (i = 0, cpu = 0; i < MAX_THRD_NUM; ++i) { 
     rc = pthread_create(&threads[i], NULL, process_clients, events); 
     if (rc != 0) 
      error_and_die(EXIT_FAILURE, "Cannot create pthread!\n"); 

     CPU_ZERO(&cpuset); 
     CPU_SET(cpu, &cpuset); 

     rc = pthread_setaffinity_np(threads[i], sizeof(cpuset), &cpuset); 
     if (rc != 0) 
      error_and_die(EXIT_FAILURE, "Cannot create pthread!\n"); 

     cpu = (cpu + 1) % NR_CPUS_ON; 
    } 

    printf("up and running!\n"); 

    /* Thread joining */ 
    for (i = 0; i < MAX_THRD_NUM; ++i) { 
     rc = pthread_join(threads[i], &status); 
     if (rc != 0) 
      error_and_die(EXIT_FAILURE, "Error on thread exit!\n"); 
    } 

    close(sock); 
    xfree(threads); 
    xfree(events); 

    printf("shut down!\n"); 

    return 0; 
} 

क्या इस परिदृश्य को एपोल के साथ संभालने का यह सही तरीका है? क्या कार्य _handle_request_ जितनी जल्दी हो सके वापस लौटना चाहिए, क्योंकि इस समय सॉकेट के लिए इवेंट्यू अवरुद्ध है ?!

उत्तर के लिए धन्यवाद!

उत्तर

9

जैसा कि आप केवल एक ही यूडीपी सॉकेट का उपयोग कर रहे हैं, एपोल का उपयोग करने का कोई मतलब नहीं है - बस इसके बजाय अवरुद्ध रिकॉर्फ़ का उपयोग करें।

अब, प्रोटोकॉल के आधार पर आपको संभालने की आवश्यकता है - यदि आप प्रत्येक यूडीपी पैकेट को व्यक्तिगत रूप से संसाधित कर सकते हैं - तो आप वास्तव में एकाधिक धागे (थ्रेड पूल में) से समवर्ती रूप से रिकॉर्फ़ कॉल कर सकते हैं। ओएस ध्यान रखेगा कि वास्तव में एक धागा यूडीपी पैकेट प्राप्त करेगा। यह थ्रेड तब भी कर सकता है जो इसे handle_request में करने की ज़रूरत है।

हालांकि, अगर आप एक विशेष क्रम में यूडीपी पैकेट पर कार्रवाई करने की आवश्यकता है, तो आप शायद यह है कि कई अवसरों अपने कार्यक्रम parallalise करने की जरूरत नहीं होगी ...

+0

बिल्कुल मैं क्या कहने जा रहा था :) – MarkR

-1

नहीं, यह आपके इच्छित तरीके से काम नहीं करेगा। एपोल इंटरफ़ेस के माध्यम से आने वाली घटनाओं को कार्य करने के लिए, आपको एक अलग वास्तुकला की आवश्यकता होती है। SysV/POSIX संकेतबाहु:

उदाहरण डिजाइन का उपयोग करता है (वहाँ यह करने के लिए कई तरीके हैं)।

  • फिर अपने सॉकेट epolling (या जो भी) ब्लॉक मास्टर धागा अंडे n subthreads और एक सेमाफोर है,।

  • सेमफोर को कम करने पर प्रत्येक सबथ्रेड ब्लॉक है।

  • जब मास्टर थ्रेड अनब्लॉक करता है, तो यह घटनाओं को एक वैश्विक संरचना में संग्रहीत करता है और प्रति घटना एक बार सैमफोर को ऊपर रखता है।

  • subthreads अनवरोधित, प्रक्रिया की घटनाओं, ब्लॉक फिर जब 0.

को सेमाफोर रिटर्न आप सेमाफोर की है कि बहुत इसी तरह की सुविधा प्राप्त करने के लिए एक पाइप सभी धागे के बीच साझा कर सकते हैं। यह आपको सैमफोर के बजाय select() पर अवरुद्ध करने देगा, जिसका उपयोग आप किसी अन्य घटना (टाइमआउट, अन्य पाइप इत्यादि) पर धागे को उठाने के लिए कर सकते हैं

आप इस नियंत्रण को भी उलट सकते हैं, और मास्टर थ्रेड उठो जब उसके श्रमिक कार्यों की मांग करते हैं। मुझे लगता है कि उपरोक्त दृष्टिकोण आपके मामले के लिए बेहतर है, हालांकि।

+1

क्या epoll आंतरिक रूप से करता है को यह समान नहीं है? इसमें एक इवेंट कतार और एक इवेंट डिस्पैचर है। epoll_wait के साथ मांग पर धागे जाग गए ?! मैंने एलकेएमएल पर इस थैड को पढ़ा जहां पावर डीएनएस डेवलपर के पास एक समान प्रश्न था (http://www.gossamer-threads.com/lists/linux/kernel/1197050) ... – Daniel

+0

आपने मुझे संदेह किया है, ...हालांकि, मुझे काफी यकीन है कि, एक ही एपोल डिस्क्रिप्टर पर प्रतीक्षा करने वाले कई धागे होने से थंडरिंग हर्ड समस्या का कारण बनता है। रुको, मैं अब पूरी तरह से यकीन नहीं कर रहा हूँ। Dammit। – slezica

+0

यदि आप EPOLLET (एज-ट्रिगर) का उपयोग नहीं कर रहे हैं तो आपको केवल एक थंडरिंग झुंड मिलता है। बीटीडब्ल्यू, आप हैंडल_रेक्वेस्ट में जो कर रहे हैं उसके आधार पर आप शायद EPOLLONESHOT का उपयोग किए बिना दूर हो सकते हैं। लेकिन फिर भी, अगर आपके पास केवल एक सॉकेट है तो एपोल ज्यादा समझ में नहीं आता है। – cmeerw

संबंधित मुद्दे