memcached源码阅读笔记(二)

来源:互联网 发布:华为算法工程师 编辑:程序博客网 时间:2024/05/16 10:08

    之前的文章,主要分析过main()函数.那本文从分析主线程的执行流程开始,分析一下memcached的核心函数drive_machine()函数.\

    在main()函数中,会调用server_sockets()函数初始化监听的连接.在server_sockets()函数中,会调用server_socket()函数.server_socket()函数中,对于UDP会调用dispacth_conn_new()函数;对于TCP,会调用conn_new()函数.对于UDP,是直接将连接轮询到子线程中处理,主线程不做任何处理,对于TCP,会设置listen的fd的event回调函数为event_handler()函数,该函数中调用的是核心函数drive_machine()函数,该函数内部实现了一个connection的状态机。

     drive_machine()函数的源代码分析:

static void drive_machine(conn *c) {    bool stop = false;    int sfd, flags = 1;    socklen_t addrlen;    struct sockaddr_storage addr;    int nreqs = settings.reqs_per_event;    int res;    const char *str;    assert(c != NULL);    while (!stop) {        switch(c->state) {        case conn_listening:            addrlen = sizeof(addr);            if ((sfd = accept(c->sfd, (struct sockaddr *)&addr, &addrlen)) == -1) {                if (errno == EAGAIN || errno == EWOULDBLOCK) {                    /* these are transient, so don't log anything */                    stop = true;                } else if (errno == EMFILE) {                    if (settings.verbose > 0)                        fprintf(stderr, "Too many open connections\n");                    accept_new_conns(false);                    stop = true;                } else {                    perror("accept()");                    stop = true;                }                break;            }            if ((flags = fcntl(sfd, F_GETFL, 0)) < 0 ||                fcntl(sfd, F_SETFL, flags | O_NONBLOCK) < 0) {                perror("setting O_NONBLOCK");                close(sfd);                break;            }            if (settings.maxconns_fast &&                stats.curr_conns + stats.reserved_fds >= settings.maxconns - 1) {                str = "ERROR Too many open connections\r\n";                res = write(sfd, str, strlen(str));                close(sfd);                STATS_LOCK();                stats.rejected_conns++;                STATS_UNLOCK();            } else {                dispatch_conn_new(sfd, conn_new_cmd, EV_READ | EV_PERSIST,                                     DATA_BUFFER_SIZE, tcp_transport);            }            stop = true;            break;        case conn_waiting:            if (!update_event(c, EV_READ | EV_PERSIST)) {                if (settings.verbose > 0)                    fprintf(stderr, "Couldn't update event\n");                conn_set_state(c, conn_closing);                break;            }            conn_set_state(c, conn_read);            stop = true;            break;        case conn_read:            res = IS_UDP(c->transport) ? try_read_udp(c) : try_read_network(c);            switch (res) {            case READ_NO_DATA_RECEIVED:                conn_set_state(c, conn_waiting);                break;            case READ_DATA_RECEIVED:                conn_set_state(c, conn_parse_cmd);                break;            case READ_ERROR:                conn_set_state(c, conn_closing);                break;            case READ_MEMORY_ERROR: /* Failed to allocate more memory */                /* State already set by try_read_network */                break;            }            break;        case conn_parse_cmd :            if (try_read_command(c) == 0) {                /* wee need more data! */                conn_set_state(c, conn_waiting);            }            break;        case conn_new_cmd:            /* Only process nreqs at a time to avoid starving other               connections */            --nreqs;            if (nreqs >= 0) {                reset_cmd_handler(c);            } else {                pthread_mutex_lock(&c->thread->stats.mutex);                c->thread->stats.conn_yields++;                pthread_mutex_unlock(&c->thread->stats.mutex);                if (c->rbytes > 0) {                    /* We have already read in data into the input buffer,                       so libevent will most likely not signal read events                       on the socket (unless more data is available. As a                       hack we should just put in a request to write data,                       because that should be possible ;-)                    */                    if (!update_event(c, EV_WRITE | EV_PERSIST)) {                        if (settings.verbose > 0)                            fprintf(stderr, "Couldn't update event\n");                        conn_set_state(c, conn_closing);                    }                }                stop = true;            }            break;        case conn_nread:            if (c->rlbytes == 0) {                complete_nread(c);                break;            }            /* first check if we have leftovers in the conn_read buffer */            if (c->rbytes > 0) {                int tocopy = c->rbytes > c->rlbytes ? c->rlbytes : c->rbytes;                if (c->ritem != c->rcurr) {                    memmove(c->ritem, c->rcurr, tocopy);                }                c->ritem += tocopy;                c->rlbytes -= tocopy;                c->rcurr += tocopy;                c->rbytes -= tocopy;                if (c->rlbytes == 0) {                    break;                }            }            /*  now try reading from the socket */            res = read(c->sfd, c->ritem, c->rlbytes);            if (res > 0) {                pthread_mutex_lock(&c->thread->stats.mutex);                c->thread->stats.bytes_read += res;                pthread_mutex_unlock(&c->thread->stats.mutex);                if (c->rcurr == c->ritem) {                    c->rcurr += res;                }                c->ritem += res;                c->rlbytes -= res;                break;            }            if (res == 0) { /* end of stream */                conn_set_state(c, conn_closing);                break;            }            if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {                if (!update_event(c, EV_READ | EV_PERSIST)) {                    if (settings.verbose > 0)                        fprintf(stderr, "Couldn't update event\n");                    conn_set_state(c, conn_closing);                    break;                }                stop = true;                break;            }            /* otherwise we have a real error, on which we close the connection */            if (settings.verbose > 0) {                fprintf(stderr, "Failed to read, and not due to blocking:\n"                        "errno: %d %s \n"                        "rcurr=%lx ritem=%lx rbuf=%lx rlbytes=%d rsize=%d\n",                        errno, strerror(errno),                        (long)c->rcurr, (long)c->ritem, (long)c->rbuf,                        (int)c->rlbytes, (int)c->rsize);            }            conn_set_state(c, conn_closing);            break;        case conn_swallow:            /* we are reading sbytes and throwing them away */            if (c->sbytes == 0) {                conn_set_state(c, conn_new_cmd);                break;            }            /* first check if we have leftovers in the conn_read buffer */            if (c->rbytes > 0) {                int tocopy = c->rbytes > c->sbytes ? c->sbytes : c->rbytes;                c->sbytes -= tocopy;                c->rcurr += tocopy;                c->rbytes -= tocopy;                break;            }            /*  now try reading from the socket */            res = read(c->sfd, c->rbuf, c->rsize > c->sbytes ? c->sbytes : c->rsize);            if (res > 0) {                pthread_mutex_lock(&c->thread->stats.mutex);                c->thread->stats.bytes_read += res;                pthread_mutex_unlock(&c->thread->stats.mutex);                c->sbytes -= res;                break;            }            if (res == 0) { /* end of stream */                conn_set_state(c, conn_closing);                break;            }            if (res == -1 && (errno == EAGAIN || errno == EWOULDBLOCK)) {                if (!update_event(c, EV_READ | EV_PERSIST)) {                    if (settings.verbose > 0)                        fprintf(stderr, "Couldn't update event\n");                    conn_set_state(c, conn_closing);                    break;                }                stop = true;                break;            }            /* otherwise we have a real error, on which we close the connection */            if (settings.verbose > 0)                fprintf(stderr, "Failed to read, and not due to blocking\n");            conn_set_state(c, conn_closing);            break;        case conn_write:            /*             * We want to write out a simple response. If we haven't already,             * assemble it into a msgbuf list (this will be a single-entry             * list for TCP or a two-entry list for UDP).             */            if (c->iovused == 0 || (IS_UDP(c->transport) && c->iovused == 1)) {                if (add_iov(c, c->wcurr, c->wbytes) != 0) {                    if (settings.verbose > 0)                        fprintf(stderr, "Couldn't build response\n");                    conn_set_state(c, conn_closing);                    break;                }            }            /* fall through... */        case conn_mwrite:          if (IS_UDP(c->transport) && c->msgcurr == 0 && build_udp_headers(c) != 0) {            if (settings.verbose > 0)              fprintf(stderr, "Failed to build UDP headers\n");            conn_set_state(c, conn_closing);            break;          }            switch (transmit(c)) {            case TRANSMIT_COMPLETE:                if (c->state == conn_mwrite) {                    while (c->ileft > 0) {                        item *it = *(c->icurr);                        assert((it->it_flags & ITEM_SLABBED) == 0);                        item_remove(it);                        c->icurr++;                        c->ileft--;                    }                    while (c->suffixleft > 0) {                        char *suffix = *(c->suffixcurr);                        cache_free(c->thread->suffix_cache, suffix);                        c->suffixcurr++;                        c->suffixleft--;                    }                    /* XXX:  I don't know why this wasn't the general case */                    if(c->protocol == binary_prot) {                        conn_set_state(c, c->write_and_go);                    } else {                        conn_set_state(c, conn_new_cmd);                    }                } else if (c->state == conn_write) {                    if (c->write_and_free) {                        free(c->write_and_free);                        c->write_and_free = 0;                    }                    conn_set_state(c, c->write_and_go);                } else {                    if (settings.verbose > 0)                        fprintf(stderr, "Unexpected state %d\n", c->state);                    conn_set_state(c, conn_closing);                }                break;            case TRANSMIT_INCOMPLETE:            case TRANSMIT_HARD_ERROR:                break;                   /* Continue in state machine. */            case TRANSMIT_SOFT_ERROR:                stop = true;                break;            }            break;        case conn_closing:            if (IS_UDP(c->transport))                conn_cleanup(c);            else                conn_close(c);            stop = true;            break;        case conn_max_state:            assert(false);            break;        }    }    return;}



    

    

原创粉丝点击