您好,欢迎访问一九零五行业门户网

memcached探索之thread model(2)

6. 下面我们看看主线程dispatch_thread的事件处理设置。在memcached.c中的main函数中 /* create unix mode sockets after dropping privileges */ if ( settings . socketpath ! = null ) { errno = 0 ; if ( server_socket_unix ( settings . socketpath ,
6. 下面我们看看主线程dispatch_thread的事件处理设置。在memcached.c中的main函数中
/* create unix mode sockets after dropping privileges */
    if (settings.socketpath != null) {
        errno = 0;
        if (server_socket_unix(settings.socketpath,settings.access)) {
            vperror(failed to listen on unix socket: %s, settings.socketpath);
            exit(ex_oserr);
        }
    }
/* create the listening socket, bind it, and init */
    if (settings.socketpath == null) {
        int udp_port;
const char *portnumber_filename = getenv(memcached_port_filename);
        char temp_portnumber_filename[path_max];
        file *portnumber_file = null;
if (portnumber_filename != null) {
            snprintf(temp_portnumber_filename,
                     sizeof(temp_portnumber_filename),
                     %s.lck, portnumber_filename);
portnumber_file = fopen(temp_portnumber_filename, a);
            if (portnumber_file == null) {
                fprintf(stderr, failed to open \%s\: %s\n,
                        temp_portnumber_filename, strerror(errno));
            }
        }
errno = 0;
        if (settings.port && server_socket(settings.port, tcp_transport,
                                           portnumber_file)) {
            vperror(failed to listen on tcp port %d, settings.port);
            exit(ex_oserr);
        }
/*
         * initialization order: first create the listening sockets
         * (may need root on low ports), then drop root if needed,
         * then daemonise if needed, then init libevent (in some cases
         * descriptors created by libevent wouldn't survive forking).
         */
        udp_port = settings.udpport ? settings.udpport : settings.port;
/* create the udp listening socket and bind it */
        errno = 0;
        if (settings.udpport && server_socket(settings.udpport, udp_transport,
                                              portnumber_file)) {
            vperror(failed to listen on udp port %d, settings.udpport);
            exit(ex_oserr);
        }
if (portnumber_file) {
            fclose(portnumber_file);
            rename(temp_portnumber_filename, portnumber_filename);
        }
    }
    /* drop privileges no longer needed */
    drop_privileges();
    /* enter the event loop */
    event_base_loop(main_base, 0); //主线程(dispatcher_thread)的事件监听循环。。。
7. 继续跟踪server_socket函数(memcached.c中)
/**
 * create a socket and bind it to a specific port number
 * @param port the port number to bind to
 * @param transport the transport protocol (tcp / udp)
 * @param portnumber_file a filepointer to write the port numbers to
 * when they are successfully added to the list of ports we
 * listen on.
 */
static int server_socket(int port, enum network_transport transport,
                         file *portnumber_file) {
    int sfd;
    struct linger ling = {0, 0};
    struct addrinfo *ai;
    struct addrinfo *next;
    struct addrinfo hints = { .ai_flags = ai_passive,
                              .ai_family = af_unspec };
    char port_buf[ni_maxserv];
    int error;
    int success = 0;
    int flags =1;
hints.ai_socktype = is_udp(transport) ? sock_dgram : sock_stream;
if (port == -1) {
        port = 0;
    }
    snprintf(port_buf, sizeof(port_buf), %d, port);
    error= getaddrinfo(settings.inter, port_buf, &hints, &ai);
    if (error != 0) {
        if (error != eai_system)
          fprintf(stderr, getaddrinfo(): %s\n, gai_strerror(error));
        else
          perror(getaddrinfo());
        return 1;
    }
for (next= ai; next; next= next->ai_next) {
        conn *listen_conn_add;
        if ((sfd = new_socket(next)) == -1) { //creat socket
            /* getaddrinfo can return junk addresses,
             * we make sure at least one works before erroring.
             */
            continue;
        }
#ifdef ipv6_v6only
        if (next->ai_family == af_inet6) {
            error = setsockopt(sfd, ipproto_ipv6, ipv6_v6only, (char *) &flags, sizeof(flags));
            if (error != 0) {
                perror(setsockopt);
                close(sfd);
                continue;
            }
        }
#endif
        //setsockopt
        setsockopt(sfd, sol_socket, so_reuseaddr, (void *)&flags, sizeof(flags));
        if (is_udp(transport)) {
            maximize_sndbuf(sfd);
        } else {
            error = setsockopt(sfd, sol_socket, so_keepalive, (void *)&flags, sizeof(flags));
            if (error != 0)
                perror(setsockopt);
error = setsockopt(sfd, sol_socket, so_linger, (void *)&ling, sizeof(ling));
            if (error != 0)
                perror(setsockopt);
error = setsockopt(sfd, ipproto_tcp, tcp_nodelay, (void *)&flags, sizeof(flags));
            if (error != 0)
                perror(setsockopt);
        }
        //bind
        if (bind(sfd, next->ai_addr, next->ai_addrlen) == -1) {
            if (errno != eaddrinuse) {
                perror(bind());
                close(sfd);
                freeaddrinfo(ai);
                return 1;
            }
            close(sfd);
            continue;
        } else {
            success++;
            //tcp listen
            if (!is_udp(transport) && listen(sfd, settings.backlog) == -1) {
                perror(listen());
                close(sfd);
                freeaddrinfo(ai);
                return 1;
            }
            if (portnumber_file != null &&
                (next->ai_addr->sa_family == af_inet ||
                 next->ai_addr->sa_family == af_inet6)) {
                union {
                    struct sockaddr_in in;
                    struct sockaddr_in6 in6;
                } my_sockaddr;
                socklen_t len = sizeof(my_sockaddr);
                if (getsockname(sfd, (struct sockaddr*)&my_sockaddr, &len)==0) {
                    if (next->ai_addr->sa_family == af_inet) {
                        fprintf(portnumber_file, %s inet: %u\n,
                                is_udp(transport) ? udp : tcp,
                                ntohs(my_sockaddr.in.sin_port));
                    } else {
                        fprintf(portnumber_file, %s inet6: %u\n,
                                is_udp(transport) ? udp : tcp,
                                ntohs(my_sockaddr.in6.sin6_port));
                    }
                }
            }
        }
if (is_udp(transport)) {
//udp 的处理中不需要accept,所以直接派发connection到工作线程。
            int c;
for (c = 0; c settings.num_threads; c++) {
                /* this is guaranteed to hit all threads because we round-robin */
                dispatch_conn_new(sfd, conn_read, ev_read | ev_persist,
                                  udp_read_buffer_size, transport);
            }
        } else {
//tcp的处理(注意,这里dispatcher_thread同样调用了conn_new来绑定conn_event到其main_base. 并且此时conn的初始状态为conn_listening, 事件为持久可读, 而在conn_new中注册了conn_event的回调函数为event_handler,所以,dispatche_thread在当前listen的socket可读时就会调用event_handler,进而调用driver_machine(c) 进入状态机。而在driver_machine中如果是主线程(dispatcher_thread)则会在accept socket后调用dispatch_new_conn函数来给各worker_thread派发connection...)
            if (!(listen_conn_add = conn_new(sfd, conn_listening,
                                             ev_read | ev_persist, 1,
                                             transport, main_base))) {
                fprintf(stderr, failed to create listening connection\n);
                exit(exit_failure);
            }
            listen_conn_add->next = listen_conn;
            listen_conn = listen_conn_add;
        }
    }
freeaddrinfo(ai);
/* return zero iff we detected no errors in starting up connections */
    return success == 0;
}
8. 看看udp和tcp模式下dispatcher_thread都会调用的dispatch_new_conn函数(在thread.c中)
/*
 * dispatches a new connection to another thread. this is only ever called
 * from the main thread, either during initialization (for udp) or because
 * of an incoming connection.
 */
void dispatch_conn_new(int sfd, enum conn_states init_state, int event_flags,
                       int read_buffer_size, enum network_transport transport) {
    cq_item *item = cqi_new();
    int tid = (last_thread + 1) % settings.num_threads; //轮询的方式找worker_thread
libevent_thread *thread = threads + tid;
last_thread = tid;
item->sfd = sfd;
    item->init_state = init_state;
    item->event_flags = event_flags;
    item->read_buffer_size = read_buffer_size;
    item->transport = transport;
    //push conn到worker_thread的cq中
    cq_push(thread->new_conn_queue, item);
memcached_conn_dispatch(sfd, thread->thread_id);
    if (write(thread->notify_send_fd, , 1) != 1) {
        perror(writing to thread notify pipe);
    }
}
9. 先看看unix domain socket模式下主线程的事件处理设置。(在上面的6中调用的 server_socket_unix函数)
static int server_socket_unix(const char *path, int access_mask) {
    int sfd;
    struct linger ling = {0, 0};
    struct sockaddr_un addr;
    struct stat tstat;
    int flags =1;
    int old_umask;
if (!path) {
        return 1;
    }
if ((sfd = new_socket_unix()) == -1) {
        return 1;
    }
/*
     * clean up a previous socket file if we left it around
     */
    if (lstat(path, &tstat) == 0) {
        if (s_issock(tstat.st_mode))
            unlink(path);
    }
setsockopt(sfd, sol_socket, so_reuseaddr, (void *)&flags, sizeof(flags));
    setsockopt(sfd, sol_socket, so_keepalive, (void *)&flags, sizeof(flags));
    setsockopt(sfd, sol_socket, so_linger, (void *)&ling, sizeof(ling));
/*
     * the memset call clears nonstandard fields in some impementations
     * that otherwise mess things up.
     */
    memset(&addr, 0, sizeof(addr));
addr.sun_family = af_unix;
    strncpy(addr.sun_path, path, sizeof(addr.sun_path) - 1);
    assert(strcmp(addr.sun_path, path) == 0);
    old_umask = umask( ~(access_mask&0777));
    if (bind(sfd, (struct sockaddr *)&addr, sizeof(addr)) == -1) {
        perror(bind());
        close(sfd);
        umask(old_umask);
        return 1;
    }
    umask(old_umask);
    if (listen(sfd, settings.backlog) == -1) {
        perror(listen());
        close(sfd);
        return 1;
    }
    if (!(listen_conn = conn_new(sfd, conn_listening, //同样是调用conn_new
                                 ev_read | ev_persist, 1,
                                 local_transport, main_base))) {
        fprintf(stderr, failed to create listening connection\n);
        exit(exit_failure);
    }
return 0;
}
其它类似信息

推荐信息