⭐ 欢迎来到虫虫下载站! | 📦 资源下载 📁 资源专辑 ℹ️ 关于我们
⭐ 虫虫下载站

📄 memcached.c

📁 memcached是一个高性能的分布式的内存对象缓存系统
💻 C
📖 第 1 页 / 共 5 页
字号:
/* -*- Mode: C; tab-width: 4; c-basic-offset: 4; indent-tabs-mode: nil -*- *//* *  memcached - memory caching daemon * *       http://www.danga.com/memcached/ * *  Copyright 2003 Danga Interactive, Inc.  All rights reserved. * *  Use and distribution licensed under the BSD license.  See *  the LICENSE file for full text. * *  Authors: *      Anatoly Vorobey <mellon@pobox.com> *      Brad Fitzpatrick <brad@danga.com>std * *  $Id: memcached.c 521 2007-04-18 12:21:27Z plindner $ */#include "memcached.h"#include <sys/stat.h>#include <sys/socket.h>#include <sys/un.h>#include <sys/signal.h>#include <sys/resource.h>#include <sys/uio.h>/* some POSIX systems need the following definition * to get mlockall flags out of sys/mman.h.  */#ifndef _P1003_1B_VISIBLE#define _P1003_1B_VISIBLE#endif/* need this to get IOV_MAX on some platforms. */#ifndef __need_IOV_MAX#define __need_IOV_MAX#endif#include <pwd.h>#include <sys/mman.h>#include <fcntl.h>#include <unistd.h>#include <netinet/tcp.h>#include <arpa/inet.h>#include <errno.h>#include <stdlib.h>#include <stdio.h>#include <string.h>#include <time.h>#include <assert.h>#include <limits.h>#ifdef HAVE_MALLOC_H/* OpenBSD has a malloc.h, but warns to use stdlib.h instead */#ifndef __OpenBSD__#include <malloc.h>#endif#endif/* FreeBSD 4.x doesn't have IOV_MAX exposed. */#ifndef IOV_MAX#if defined(__FreeBSD__)# define IOV_MAX 1024#endif#endif/* * forward declarations */static void drive_machine(conn *c);static int new_socket(const bool is_udp);static int server_socket(const int port, const bool is_udp);static int try_read_command(conn *c);static int try_read_network(conn *c);static int try_read_udp(conn *c);/* stats */static void stats_reset(void);static void stats_init(void);/* defaults */static void settings_init(void);/* event handling, network IO */static void event_handler(const int fd, const short which, void *arg);static void conn_close(conn *c);static void conn_init(void);static void accept_new_conns(const bool do_accept);static bool update_event(conn *c, const int new_flags);static void complete_nread(conn *c);static void process_command(conn *c, char *command);static int transmit(conn *c);static int ensure_iov_space(conn *c);static int add_iov(conn *c, const void *buf, int len);static int add_msghdr(conn *c);/* time handling */static void set_current_time(void);  /* update the global variable holding                              global 32-bit seconds-since-start time                              (to avoid 64 bit time_t) */void pre_gdb(void);static void conn_free(conn *c);/** exported globals **/struct stats stats;struct settings settings;/** file scope variables **/static item **todelete = 0;static int delcurr;static int deltotal;static conn *listen_conn;static struct event_base *main_base;#define TRANSMIT_COMPLETE   0#define TRANSMIT_INCOMPLETE 1#define TRANSMIT_SOFT_ERROR 2#define TRANSMIT_HARD_ERROR 3static int *buckets = 0; /* bucket->generation array for a managed instance */#define REALTIME_MAXDELTA 60*60*24*30/* * given time value that's either unix time or delta from current unix time, return * unix time. Use the fact that delta can't exceed one month (and real time value can't * be that low). */static rel_time_t realtime(const time_t exptime) {    /* no. of seconds in 30 days - largest possible delta exptime */    if (exptime == 0) return 0; /* 0 means never expire */    if (exptime > REALTIME_MAXDELTA) {        /* if item expiration is at/before the server started, give it an           expiration time of 1 second after the server started.           (because 0 means don't expire).  without this, we'd           underflow and wrap around to some large value way in the           future, effectively making items expiring in the past           really expiring never */        if (exptime <= stats.started)            return (rel_time_t)1;        return (rel_time_t)(exptime - stats.started);    } else {        return (rel_time_t)(exptime + current_time);    }}static void stats_init(void) {    stats.curr_items = stats.total_items = stats.curr_conns = stats.total_conns = stats.conn_structs = 0;    stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = stats.evictions = 0;    stats.curr_bytes = stats.bytes_read = stats.bytes_written = 0;    /* make the time we started always be 2 seconds before we really       did, so time(0) - time.started is never zero.  if so, things       like 'settings.oldest_live' which act as booleans as well as       values are now false in boolean context... */    stats.started = time(0) - 2;    stats_prefix_init();}static void stats_reset(void) {    STATS_LOCK();    stats.total_items = stats.total_conns = 0;    stats.get_cmds = stats.set_cmds = stats.get_hits = stats.get_misses = stats.evictions = 0;    stats.bytes_read = stats.bytes_written = 0;    stats_prefix_clear();    STATS_UNLOCK();}static void settings_init(void) {    settings.port = 11211;    settings.udpport = 0;    settings.interf.s_addr = htonl(INADDR_ANY);    settings.maxbytes = 67108864; /* default is 64MB: (64 * 1024 * 1024) */    settings.maxconns = 1024;         /* to limit connections-related memory to about 5MB */    settings.verbose = 0;    settings.oldest_live = 0;    settings.evict_to_free = 1;       /* push old items out of cache when memory runs out */    settings.socketpath = NULL;       /* by default, not using a unix socket */    settings.managed = false;    settings.factor = 1.25;    settings.chunk_size = 48;         /* space for a modest key and value */#ifdef USE_THREADS    settings.num_threads = 4;#else    settings.num_threads = 1;#endif    settings.prefix_delimiter = ':';    settings.detail_enabled = 0;}/* returns true if a deleted item's delete-locked-time is over, and it   should be removed from the namespace */static bool item_delete_lock_over (item *it) {    assert(it->it_flags & ITEM_DELETED);    return (current_time >= it->exptime);}/* * Adds a message header to a connection. * * Returns 0 on success, -1 on out-of-memory. */static int add_msghdr(conn *c){    struct msghdr *msg;    assert(c != NULL);    if (c->msgsize == c->msgused) {        msg = realloc(c->msglist, c->msgsize * 2 * sizeof(struct msghdr));        if (! msg)            return -1;        c->msglist = msg;        c->msgsize *= 2;    }    msg = c->msglist + c->msgused;    /* this wipes msg_iovlen, msg_control, msg_controllen, and       msg_flags, the last 3 of which aren't defined on solaris: */    memset(msg, 0, sizeof(struct msghdr));    msg->msg_iov = &c->iov[c->iovused];    msg->msg_name = &c->request_addr;    msg->msg_namelen = c->request_addr_size;    c->msgbytes = 0;    c->msgused++;    if (c->udp) {        /* Leave room for the UDP header, which we'll fill in later. */        return add_iov(c, NULL, UDP_HEADER_SIZE);    }    return 0;}/* * Free list management for connections. */static conn **freeconns;static int freetotal;static int freecurr;static void conn_init(void) {    freetotal = 200;    freecurr = 0;    if (!(freeconns = (conn **)malloc(sizeof(conn *) * freetotal))) {        perror("malloc()");    }    return;}/* * Returns a connection from the freelist, if any. Should call this using * conn_from_freelist() for thread safety. */conn *do_conn_from_freelist() {    conn *c;    if (freecurr > 0) {        c = freeconns[--freecurr];    } else {        c = NULL;    }    return c;}/* * Adds a connection to the freelist. 0 = success. Should call this using * conn_add_to_freelist() for thread safety. */int do_conn_add_to_freelist(conn *c) {    if (freecurr < freetotal) {        freeconns[freecurr++] = c;        return 0;    } else {        /* try to enlarge free connections array */        conn **new_freeconns = realloc(freeconns, sizeof(conn *) * freetotal * 2);        if (new_freeconns) {            freetotal *= 2;            freeconns = new_freeconns;            freeconns[freecurr++] = c;            return 0;        }    }    return 1;}conn *conn_new(const int sfd, const int init_state, const int event_flags,                const int read_buffer_size, const bool is_udp, struct event_base *base) {    conn *c = conn_from_freelist();    if (NULL == c) {        if (!(c = (conn *)malloc(sizeof(conn)))) {            perror("malloc()");            return NULL;        }        c->rbuf = c->wbuf = 0;        c->ilist = 0;        c->iov = 0;        c->msglist = 0;        c->hdrbuf = 0;        c->rsize = read_buffer_size;        c->wsize = DATA_BUFFER_SIZE;        c->isize = ITEM_LIST_INITIAL;        c->iovsize = IOV_LIST_INITIAL;        c->msgsize = MSG_LIST_INITIAL;        c->hdrsize = 0;        c->rbuf = (char *)malloc((size_t)c->rsize);        c->wbuf = (char *)malloc((size_t)c->wsize);        c->ilist = (item **)malloc(sizeof(item *) * c->isize);        c->iov = (struct iovec *)malloc(sizeof(struct iovec) * c->iovsize);        c->msglist = (struct msghdr *)malloc(sizeof(struct msghdr) * c->msgsize);        if (c->rbuf == 0 || c->wbuf == 0 || c->ilist == 0 || c->iov == 0 ||                c->msglist == 0) {            if (c->rbuf != 0) free(c->rbuf);            if (c->wbuf != 0) free(c->wbuf);            if (c->ilist !=0) free(c->ilist);            if (c->iov != 0) free(c->iov);            if (c->msglist != 0) free(c->msglist);            free(c);            perror("malloc()");            return NULL;        }        STATS_LOCK();        stats.conn_structs++;        STATS_UNLOCK();    }    if (settings.verbose > 1) {        if (init_state == conn_listening)            fprintf(stderr, "<%d server listening\n", sfd);        else if (is_udp)            fprintf(stderr, "<%d server listening (udp)\n", sfd);        else            fprintf(stderr, "<%d new client connection\n", sfd);    }    c->sfd = sfd;    c->udp = is_udp;    c->state = init_state;    c->rlbytes = 0;    c->rbytes = c->wbytes = 0;    c->wcurr = c->wbuf;    c->rcurr = c->rbuf;    c->ritem = 0;    c->icurr = c->ilist;    c->ileft = 0;    c->iovused = 0;    c->msgcurr = 0;    c->msgused = 0;    c->write_and_go = conn_read;    c->write_and_free = 0;    c->item = 0;    c->bucket = -1;    c->gen = 0;    event_set(&c->event, sfd, event_flags, event_handler, (void *)c);    event_base_set(base, &c->event);    c->ev_flags = event_flags;    if (event_add(&c->event, 0) == -1) {        if (conn_add_to_freelist(c)) {            conn_free(c);        }        return NULL;    }    STATS_LOCK();    stats.curr_conns++;    stats.total_conns++;    STATS_UNLOCK();    return c;}static void conn_cleanup(conn *c) {    assert(c != NULL);    if (c->item) {        item_remove(c->item);        c->item = 0;    }    if (c->ileft != 0) {        for (; c->ileft > 0; c->ileft--,c->icurr++) {            item_remove(*(c->icurr));        }    }    if (c->write_and_free) {        free(c->write_and_free);        c->write_and_free = 0;    }}/* * Frees a connection. */void conn_free(conn *c) {    if (c) {        if (c->hdrbuf)            free(c->hdrbuf);        if (c->msglist)            free(c->msglist);        if (c->rbuf)            free(c->rbuf);        if (c->wbuf)            free(c->wbuf);        if (c->ilist)            free(c->ilist);        if (c->iov)            free(c->iov);        free(c);    }}static void conn_close(conn *c) {    assert(c != NULL);    /* delete the event, the socket and the conn */    event_del(&c->event);    if (settings.verbose > 1)        fprintf(stderr, "<%d connection closed.\n", c->sfd);    close(c->sfd);    accept_new_conns(true);    conn_cleanup(c);    /* if the connection has big buffers, just free it */    if (c->rsize > READ_BUFFER_HIGHWAT || conn_add_to_freelist(c)) {        conn_free(c);    }    STATS_LOCK();    stats.curr_conns--;    STATS_UNLOCK();    return;}/* * Shrinks a connection's buffers if they're too big.  This prevents

⌨️ 快捷键说明

复制代码 Ctrl + C
搜索代码 Ctrl + F
全屏模式 F11
切换主题 Ctrl + Shift + D
显示快捷键 ?
增大字号 Ctrl + =
减小字号 Ctrl + -