************************************************************************************************************
#ifndef __CONFIG_H
#define __CONFIG_H
#ifdef __APPLE__
#include <AvailabilityMacros.h>
#endif
#ifdef __linux__
#include <linux/version.h>
#include <features.h>
#endif
/* Define redis_fstat to fstat or fstat64() */
定义redis_fstat 为fstat 或 fstat64()
检查文件的属性
#if defined(__APPLE__) && !defined(MAC_OS_X_VERSION_10_6)
#define redis_fstat fstat64
#define redis_stat stat64
#else
#define redis_fstat fstat
#define redis_stat stat
#endif
/* Test for proc filesystem */ 对进程文件系统的测试
#ifdef __linux__
#define HAVE_PROC_STAT 1
#define HAVE_PROC_MAPS 1
#define HAVE_PROC_SMAPS 1
#define HAVE_PROC_SOMAXCONN 1
#endif
/* Test for task_info() */ 任务信息的测试
#if defined(__APPLE__)
#define HAVE_TASKINFO 1
#endif
/* Test for backtrace() */ 堆栈跟踪的测试
#if defined(__APPLE__) || (defined(__linux__) && defined(__GLIBC__)) || \
defined(__FreeBSD__) || (defined(__OpenBSD__) && defined(USE_BACKTRACE))\
|| defined(__DragonFly__)
#define HAVE_BACKTRACE 1
#endif
/* MSG_NOSIGNAL. */
禁止 send() 函数向系统发送常消息
#ifdef __linux__
#define HAVE_MSG_NOSIGNAL 1
#endif
/* Test for polling API */ 为epoll的API提供测试
#ifdef __linux__
#define HAVE_EPOLL 1
#endif
常见的IO复用技术有select, poll, epoll以及kqueue等等。
其中epoll为Linux独占,而kqueue则在许多UNIX系统上存在,如下所示
#if (defined(__APPLE__) && defined(MAC_OS_X_VERSION_10_6)) || defined(__FreeBSD__) || defined(__OpenBSD__) || defined (__NetBSD__)
#define HAVE_KQUEUE 1 这些系统默认有KQUEUE
#endif
#ifdef __sun
#include <sys/feature_tests.h>
#ifdef _DTRACE_VERSION
#define HAVE_EVPORT 1 使用evport
#endif
#endif
/* Define redis_fsync to fdatasync() in Linux and fsync() for all the rest */
在Linux系统中定义redis的文件同步函数为fdatasync,其它系统中为fsync
#ifdef __linux__
#define redis_fsync fdatasync
#else
#define redis_fsync fsync
#endif
/* Define rdb_fsync_range to sync_file_range() on Linux, otherwise we use
* the plain fsync() call. */
在Linux系统上定义rdb_fsync_range为sync_file_range, 其它系统我们使用一般的fsync()
将数据flush到持久设备,sync_file_range性能更好
#ifdef __linux__
#if defined(__GLIBC__) && defined(__GLIBC_PREREQ)
if (LINUX_VERSION_CODE >= 0x020611 && __GLIBC_PREREQ(2, 6)) Linux版本高于2.6.17 并且 Glibc 版本至少为2.6
#define HAVE_SYNC_FILE_RANGE 1 才可以使用sync_file_range
#endif
#else
#if (LINUX_VERSION_CODE >= 0x020611) Linux版本高于2.6.17 注意11为16进制
#define HAVE_SYNC_FILE_RANGE 1
#endif
#endif
#endif
#ifdef HAVE_SYNC_FILE_RANGE
#define rdb_fsync_range(fd,off,size) sync_file_range(fd,off,size,SYNC_FILE_RANGE_WAIT_BEFORE|SYNC_FILE_RANGE_WRITE)
#else
#define rdb_fsync_range(fd,off,size) fsync(fd)
#endif
/* Check if we can use setproctitle().
* BSD systems have support for it, we provide an implementation for
* Linux and osx. */
检查我们是否可以使用函数setproctitle(用于进程修改名字)
BSD系统支持这个函数,我们提供了一个Linux和osx的实现
#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__)
#define USE_SETPROCTITLE
#endif
#if ((defined __linux && defined(__GLIBC__)) || defined __APPLE__) 自己实现的setproctitle
#define USE_SETPROCTITLE
#define INIT_SETPROCTITLE_REPLACEMENT
void spt_init(int argc, char *argv[]); 这两个函数请参见文件 setproctitle.c
void setproctitle(const char *fmt, ...);
#endif
/* Byte ordering detection */检测字节序
#include <sys/types.h> /* This will likely define BYTE_ORDER */ 这个头文件可能会定义字节序
#ifndef BYTE_ORDER
#if (BSD >= 199103) BSD在这个版本之后 定义了字节序
# include <machine/endian.h>
#else
#if defined(linux) || defined(__linux__) Linux系统定义了字节序
# include <endian.h>
#else
#define LITTLE_ENDIAN 1234 /* least-significant byte first (vax, pc) */ 小端
#define BIG_ENDIAN 4321 /* most-significant byte first (IBM, net) */ 大端
#define PDP_ENDIAN 3412 /* LSB first in word, MSW first in long (pdp)*/ PDP中 在字中是小端,长整型为大端
以上三个表示的都是 0x4321
#if defined(__i386__) || defined(__x86_64__) || defined(__amd64__) || \
defined(vax) || defined(ns32000) || defined(sun386) || \
defined(MIPSEL) || defined(_MIPSEL) || defined(BIT_ZERO_ON_RIGHT) || \
defined(__alpha__) || defined(__alpha)
#define BYTE_ORDER LITTLE_ENDIAN 小端
#endif
#if defined(sel) || defined(pyr) || defined(mc68000) || defined(sparc) || \
defined(is68k) || defined(tahoe) || defined(ibm032) || defined(ibm370) || \
defined(MIPSEB) || defined(_MIPSEB) || defined(_IBMR2) || defined(DGUX) ||\
defined(apollo) || defined(__convex__) || defined(_CRAY) || \
defined(__hppa) || defined(__hp9000) || \
defined(__hp9000s300) || defined(__hp9000s700) || \
defined (BIT_ZERO_ON_LEFT) || defined(m68k) || defined(__sparc)
#define BYTE_ORDER BIG_ENDIAN 大端
#endif
#endif /* linux */
#endif /* BSD */
#endif /* BYTE_ORDER */
/* Sometimes after including an OS-specific header that defines the
* endianess we end with __BYTE_ORDER but not with BYTE_ORDER that is what
* the Redis code uses. In this case let's define everything without the
* underscores. */
有时,在包含一个特定于操作系统的头文件(定义了endianess)之后,我们会以字节顺序结束,
但不会以Redis代码使用的字节顺序结束。在这种情况下,让我们定义没有下划线的所有内容
#ifndef BYTE_ORDER
#ifdef __BYTE_ORDER
#if defined(__LITTLE_ENDIAN) && defined(__BIG_ENDIAN)
#ifndef LITTLE_ENDIAN
#define LITTLE_ENDIAN __LITTLE_ENDIAN
#endif
#ifndef BIG_ENDIAN
#define BIG_ENDIAN __BIG_ENDIAN
#endif
#if (__BYTE_ORDER == __LITTLE_ENDIAN)
#define BYTE_ORDER LITTLE_ENDIAN
#else
#define BYTE_ORDER BIG_ENDIAN
#endif
#endif
#endif
#endif
#if !defined(BYTE_ORDER) || \
(BYTE_ORDER != BIG_ENDIAN && BYTE_ORDER != LITTLE_ENDIAN)
/* you must determine what the correct bit order is for
* your compiler - the next line is an intentional error
* which will force your compiles to bomb until you fix
* the above macros.
*/
必须确定编译器的正确字节序-下一行是提示错误信息,这将使编译器出错,直到给出上面的宏定义
#error "Undefined or invalid BYTE_ORDER"
#endif
#if (__i386 || __amd64 || __powerpc__) && __GNUC__
#define GNUC_VERSION (__GNUC__ * 10000 + __GNUC_MINOR__ * 100 + __GNUC_PATCHLEVEL__)
#if defined(__clang__) 如果是Clang编译器,那么定义了原子操作
#define HAVE_ATOMIC
#endif
#if (defined(__GLIBC__) && defined(__GLIBC_PREREQ))
#if (GNUC_VERSION >= 40100 && __GLIBC_PREREQ(2, 6)) 如果GNUC_VERSION的版本大于4.1 GLIBC版本高于2.6 也定义了原子操作
#define HAVE_ATOMIC
#endif
#endif
#endif
/* Make sure we can test for ARM just checking for __arm__, since sometimes
* __arm is defined but __arm__ is not. */
确保我们能够测试ARM,只需要检查__arm__即可,因为有些时候__arm被定义但是__arm__没有被定义
#if defined(__arm) && !defined(__arm__)
#define __arm__
#endif
#if defined (__aarch64__) && !defined(__arm64__)
#define __arm64__
#endif
/* Make sure we can test for SPARC just checking for __sparc__. */
确保我们测试SPARC,只需要检查__sparc__的定义
#if defined(__sparc) && !defined(__sparc__)
#define __sparc__
#endif
#if defined(__sparc__) || defined(__arm__) 处理器是 sparc 或 arm,字节对齐
#define USE_ALIGNED_ACCESS
#endif
/* Define for redis_set_thread_title */ 定义redis_set_thread_title,设置线程名字
#ifdef __linux__
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name)
#else
#if (defined __FreeBSD__ || defined __OpenBSD__)
#include <pthread_np.h> FreeBSD系统使用
#define redis_set_thread_title(name) pthread_set_name_np(pthread_self(), name)
#elif defined __NetBSD__ NetBSD使用
include <pthread.h>
#define redis_set_thread_title(name) pthread_setname_np(pthread_self(), name, NULL)
#else
#if (defined __APPLE__ && defined(MAC_OS_X_VERSION_10_7)) 苹果系统高于一定版本使用
int pthread_setname_np(const char *name);
#include <pthread.h>
#define redis_set_thread_title(name) pthread_setname_np(name)
#else
#define redis_set_thread_title(name) 默认情况没有这个函数,替代为空
#endif
#endif
#endif
/* Check if we can use setcpuaffinity(). */
检查我们是否可以使用CPU亲和(将进程绑定到特定的cpu)
#if (defined __linux || defined __NetBSD__ || defined __FreeBSD__)
#define USE_SETCPUAFFINITY
void setcpuaffinity(const char *cpulist); 具体参见文件setcpuaffinity.c
#endif
#endif
***********************************文件setproctitle.c*************************************************************************
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stddef.h> /* NULL size_t */ 空 和 size_t 的定义
#include <stdarg.h> /* va_list va_start va_end */ 可变输入参数定义
#include <stdlib.h> /* malloc(3) setenv(3) clearenv(3) setproctitle(3) getprogname(3) */
环境参数获取和修改 进程明细获取和修改
#include <stdio.h> /* vsnprintf(3) snprintf(3) */ 可变参数打印
#include <string.h> /* strlen(3) strchr(3) strdup(3) memset(3) memcpy(3) */ 字符串相关函数定义
#include <errno.h> /* errno program_invocation_name program_invocation_short_name */ 错误信息定义
#if !defined(HAVE_SETPROCTITLE)
没有定义HAVE_SETPROCTITLE,但是定义了如下的操作系统还可以使用系统的SETPROCTITLE函数
#if (defined __NetBSD__ || defined __FreeBSD__ || defined __OpenBSD__ || defined __DragonFly__)
#define HAVE_SETPROCTITLE 1
#else
#define HAVE_SETPROCTITLE 0
#endif
#endif
************************************************************************************************************
#if !HAVE_SETPROCTITLE
#if (defined __linux || defined __APPLE__)
extern char **environ; 保存进程的环境变量
static struct {
/* original value */ 原始字符串
const char *arg0;
/* title space available */ 总的可用空间
char *base, *end;
/* pointer to original nul character within base */ 指向基于开始位置的原始nul字符串(第一个参数)指针
char *nul;
_Bool reset; _Bool是C99新增加的关键字,长度是1个字节
int error;
} SPT;
#ifndef SPT_MIN
#define SPT_MIN(a, b) (((a) < (b))? (a) : (b))
#endif
定义内联函数spt_min,用的是上面的宏定义,求小值
static inline size_t spt_min(size_t a, size_t b) {
return SPT_MIN(a, b);
} /* spt_min() */
/*
* For discussion on the portability of the various methods, see
有关各种方法的可移植性的讨论,请参见
* http://lists.freebsd.org/pipermail/freebsd-stable/2008-June/043136.html
上面的文件时说明为了适应各种不同操作系统版本而采取如下办法来清除环境变量
*/
清理环境变量的函数
static int spt_clearenv(void) {
#if __GLIBC__
clearenv();
printf("******__GLIBC__ clearenv**** \n");
return 0;
#else
extern char **environ;
static char **tmp;
printf("******else __GLIBC__ clearenv**** \n");
if (!(tmp = malloc(sizeof *tmp)))
return errno;
tmp[0] = NULL;
environ = tmp;
return 0;
#endif
} /* spt_clearenv() */
************************************************************************************************************
拷贝环境变量
static int spt_copyenv(char *oldenv[]) {
extern char **environ;
char *eq;
int i, error;
if (environ != oldenv) 新值和旧值不一样,不是同样的进程环境变量地址
return 0;
if ((error = spt_clearenv())) 清理环境变量
goto error;
for (i = 0; oldenv[i]; i++) { 遍历旧的环境变量
if (!(eq = strchr(oldenv[i], '='))) 查找参数是否有等号
continue;
*eq = '\0';
error = (0 != setenv(oldenv[i], eq + 1, 1))? errno : 0;
设置环境变量(覆盖已经存在的变量),从等号后面开始设置
*eq = '=';
if (error)
goto error;
}
return 0;
error:
environ = oldenv;
return error;
} /* spt_copyenv() */
************************************************************************************************************
拷贝传入参数
static int spt_copyargs(int argc, char *argv[]) {
char *tmp;
int i;
for (i = 1; i < argc || (i >= argc && argv[i]); i++) {
if (!argv[i]) 参数为空 就下一个
continue;
if (!(tmp = strdup(argv[i]))) 赋值传入的参数
return errno;
argv[i] = tmp; 指向新分配的空间
}
return 0;
} /* spt_copyargs() */
************************************************************************************************************
初始化
void spt_init(int argc, char *argv[]) {
char **envp = environ;
char *base, *end, *nul, *tmp;
int i, error;
if (!(base = argv[0])) 将第一个输入参数赋值给开始地址,程序执行的命令名
return;
nul = &base[strlen(base)]; 指向程序的第一个参数
end = nul + 1; 指向一个参数
for (i = 0; i < argc || (i >= argc && argv[i]); i++) { 获取输入参数 从第一个参数开始 我们这里为./redis.conf
if (!argv[i] || argv[i] < end)
continue;
end = argv[i] + strlen(argv[i]) + 1;
}
for (i = 0; envp[i]; i++) { 进程环境变量的值
if (envp[i] < end)
continue;
end = envp[i] + strlen(envp[i]) + 1;
}
if (!(SPT.arg0 = strdup(argv[0]))) 拷贝进程名
goto syerr;
#if __GLIBC__
if (!(tmp = strdup(program_invocation_name))) 全名 /sourcecode/redis/./src/redis-server
goto syerr;
program_invocation_name = tmp;
if (!(tmp = strdup(program_invocation_short_name))) 简称 redis-server
goto syerr;
program_invocation_short_name = tmp;
#elif __APPLE__
if (!(tmp = strdup(getprogname())))
goto syerr;
setprogname(tmp);
#endif
if ((error = spt_copyenv(envp))) 拷贝原进程环境变量成功
goto error;
if ((error = spt_copyargs(argc, argv))) 拷贝输入参数成功
goto error;
SPT.nul = nul;
SPT.base = base; 指向开始地址
SPT.end = end; 指向结束地址
return;
syerr:
error = errno;
error:
SPT.error = error;
} /* spt_init() */
************************************************************************************************************
#ifndef SPT_MAXTITLE
#define SPT_MAXTITLE 255 最长的名字为255个字节
#endif
void setproctitle(const char *fmt, ...) {
char buf[SPT_MAXTITLE + 1]; /* use buffer in case argv[0] is passed */如果传入了argv[0] ,那么使用缓存
va_list ap;
char *nul;
int len, error;
if (!SPT.base)
return;
if (fmt) { 传入参数非空
va_start(ap, fmt); 指向第一个参数
len = vsnprintf(buf, sizeof buf, fmt, ap);
va_end(ap); 失效
} else {
len = snprintf(buf, sizeof buf, "%s", SPT.arg0);
}
if (len <= 0) 写入不成功
{ error = errno; goto error; }
if (!SPT.reset) { 没有重设
memset(SPT.base, 0, SPT.end - SPT.base); 全部重设
SPT.reset = 1;
} else { 只设置进程名
memset(SPT.base, 0, spt_min(sizeof buf, SPT.end - SPT.base));
}
len = spt_min(len, spt_min(sizeof buf, SPT.end - SPT.base) - 1); 获取实际的参数长度
memcpy(SPT.base, buf, len);
nul = &SPT.base[len];指向第一个参数
if (nul < SPT.nul) { 实际执行参数比输入参数短(程序可能会自己增加一些默认参数)
*SPT.nul = '.';
} else if (nul == SPT.nul && &nul[1] < SPT.end) { 实际执行参数 和 输入参数一致 并且 还没有到最后
*SPT.nul = ' ';
*++nul = '\0';
}
return;
error:
SPT.error = error;
} /* setproctitle() */
#endif /* __linux || __APPLE__ */
#endif /* !HAVE_SETPROCTITLE */
************************************************************************************************************
************************************************************************************************************
********************************文件setcpuaffinity.c********************************************************
#ifndef _GNU_SOURCE
#define _GNU_SOURCE
#endif
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#ifdef __linux__
#include <sched.h>
#endif
#ifdef __FreeBSD__
#include <sys/param.h>
#include <sys/cpuset.h>
#endif
#ifdef __NetBSD__
#include <pthread.h>
#include <sched.h>
#endif
#include "config.h"
************************************************************************************************************
#ifdef USE_SETCPUAFFINITY
static const char *next_token(const char *q, int sep) {
if (q) 字符串q非空
q = strchr(q, sep); 在q中是否存在字符sep
if (q) 存在字符sep
q++; 指向下一个位置的字符
return q;
}
static int next_num(const char *str, char **end, int *result) {
if (!str || *str == '\0' || !isdigit(*str)) 非空 或者 不是结尾 或者 不是数字
return -1;
*result = strtoul(str, end, 10);
参数nptr字符串根据参数base来转换成无符号的长整型数,这里的base为10,就是10进制
if (str == *end) 没有转化,或者说转化失败,非数字
return -1;
return 0; 成功的情况
}
/* set current thread cpu affinity to cpu list, this function works like
* taskset command (actually cpulist parsing logic reference to util-linux).
* example of this function: "0,2,3", "0,2-3", "0-20:2". */
将当前线程cpu亲和力设置为cpu列表,此函数的工作方式类似于taskset命令(实际上是对util linux的cpulist解析逻辑引用)。
此函数的输入示例:“0,2,3”,“0,2-3”,“0-20:2”
void setcpuaffinity(const char *cpulist) {
const char *p, *q;
char *end = NULL;
#ifdef __linux__
cpu_set_t cpuset;
#endif
#ifdef __FreeBSD__
cpuset_t cpuset;
#endif
#ifdef __NetBSD__
cpuset_t *cpuset;
#endif
if (!cpulist)
return;
#ifndef __NetBSD__
CPU_ZERO(&cpuset); 清空一个cpu集合
#else
cpuset = cpuset_create(); 创建一个cpu集合
#endif
q = cpulist;
while (p = q, q = next_token(q, ','), p) {
int a, b, s;
const char *c1, *c2;
if (next_num(p, &end, &a) != 0) 将p转化为数字a
return;
b = a;
s = 1;
p = end; 指向剩下的字符串
c1 = next_token(p, '-'); 下一个-
c2 = next_token(p, ','); 下一个,
if (c1 != NULL && (c2 == NULL || c1 < c2)) { 如上所示 0-20
if (next_num(c1, &end, &b) != 0) 获取"-"符号后面的数字
return;
c1 = end && *end ? next_token(end, ':') : NULL; end指针和end指向的内容都不为空
if (c1 != NULL && (c2 == NULL || c1 < c2)) {
if (next_num(c1, &end, &s) != 0) 获取":"后面的参数 0-20:2
return;
if (s == 0)
return;
}
}
if ((a > b)) 要按照顺序
return;
while (a <= b) { 将一些列cpu添加到集合
#ifndef __NetBSD__
CPU_SET(a, &cpuset); 将一个给定的CPU号加到一个集合
#else
cpuset_set(a, cpuset);
#endif
a += s;
}
}
if (end && *end)
return;
#ifdef __linux__
sched_setaffinity(0, sizeof(cpuset), &cpuset);
将当前进程限制在 cpuset这个集合中,0表示当前进程,sizeof(cpuset)表示mask所指定的数的长度 cpuset cpu集合
#endif
#ifdef __FreeBSD__
cpuset_setaffinity(CPU_LEVEL_WHICH, CPU_WHICH_TID, -1, sizeof(cpuset), &cpuset);
#endif
#ifdef __NetBSD__
pthread_setaffinity_np(pthread_self(), cpuset_size(cpuset), cpuset);
cpuset_destroy(cpuset);
#endif
}
#endif /* USE_SETCPUAFFINITY */
************************************************************************************************************