📄 memcached.c
字号:
/* -*- 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 + -