本篇博客作为Memcached源码分析的开篇,这次阅读的源码版本为:1.4.15,开源软件各个版本之间差异比较大,同学们学习时,记得核对版本。
memcached的main函数位于memcached.c文件中,从main函数启动之后,会初始化一些资源和申请一些服务器资源,如下面所示:
1 Core文件大小和进程打开文件个数限制的调整。
- if (maxcore != 0)
- {
- struct rlimit rlim_new;
- //获取当前Core文件大小的配置值
- if (getrlimit(RLIMIT_CORE, &rlim) == 0)
- {
- //变量初始化为无限制
- rlim_new.rlim_cur = rlim_new.rlim_max = RLIM_INFINITY;
- if (setrlimit(RLIMIT_CORE, &rlim_new) != 0)//如果设置失败
- {
- //变量初始化为当前值的最大值
- rlim_new.rlim_cur = rlim_new.rlim_max = rlim.rlim_max;
- (void) setrlimit(RLIMIT_CORE, &rlim_new);//重新进行设置
- }
- }
- //再次确认Core文件允许的大小,如果当前的Core文件的大小为0,则不允许Core文件产生,和maxcore!=0不符,程序结束
- if ((getrlimit(RLIMIT_CORE, &rlim) != 0) || rlim.rlim_cur == 0)
- {
- fprintf(stderr, "failed to ensure corefile creation\n");
- exit(EX_OSERR);
- }
- }
- //读取进程允许打开的文件数信息,读取失败,程序退出
- if (getrlimit(RLIMIT_NOFILE, &rlim) != 0)
- {
- fprintf(stderr, "failed to getrlimit number of files\n");
- exit(EX_OSERR);
- }
- else
- { //按memcached启动时的指定的最大连接数进行设置
- rlim.rlim_cur = settings.maxconns;
- rlim.rlim_max = settings.maxconns;
- if (setrlimit(RLIMIT_NOFILE, &rlim) != 0)
- {
- fprintf(stderr,
- "failed to set rlimit for open files. Try starting as root or requesting smaller maxconns value.\n");
- exit(EX_OSERR);
- }
- }
2 启动用户的选择。
- //uid==0表示以root运行程序
- if (getuid() == 0 || geteuid() == 0)
- { //以root运行程序,同时未指定新的用户名称
- if (username == 0 || *username == '\0')
- {
- fprintf(stderr, "can't run as root without the -u switch\n");
- exit(EX_USAGE);
- }
- //判断是否存在指定的用户名称
- if ((pw = getpwnam(username)) == 0)
- {
- fprintf(stderr, "can't find the user %s to switch to\n", username);
- exit(EX_NOUSER);
- }
- //按新的用户修改memcached的执行权限位
- if (setgid(pw->pw_gid) < 0 || setuid(pw->pw_uid) < 0)
- {
- fprintf(stderr, "failed to assume identity of user %s\n", username);
- exit(EX_OSERR);
- }
- }
- int daemonize(int nochdir, int noclose)
- {
- int fd;
- //首先fork一次
- switch (fork()) {
- case -1://fork失败,程序结束
- return (-1);
- case 0://子进程执行下面的流程
- break;
- default://父进程安全退出
- _exit(EXIT_SUCCESS);
- }
- //setsid调用成功之后,返回新的会话的ID,调用setsid函数的进程成为新的会话的领头进程,并与其父进程的会话组和进程组脱离
- if (setsid() == -1)
- return (-1);
- if (nochdir == 0) {
- //进程的当前目录切换到根目录下,根目录是一直存在的,其他的目录就不保证
- if(chdir("/") != 0) {
- perror("chdir");
- return (-1);
- }
- }
- if (noclose == 0 && (fd = open("/dev/null", O_RDWR, 0)) != -1) {
- if(dup2(fd, STDIN_FILENO) < 0) {//将标准输入重定向到/dev/null下
- perror("dup2 stdin");
- return (-1);
- }
- if(dup2(fd, STDOUT_FILENO) < 0) {//将标准输出重定向到/dev/null下
- perror("dup2 stdout");
- return (-1);
- }
- if(dup2(fd, STDERR_FILENO) < 0) {//将标准错误重定向到/dev/null下
- perror("dup2 stderr");
- return (-1);
- }
- if (fd > STDERR_FILENO) {
- if(close(fd) < 0) {//大于2的描述符都可以关闭
- perror("close");
- return (-1);
- }
- }
- }
- return (0);
- }
4 锁定内存,默认分配的内存都是虚拟内存,在程序执行过程中可以按需换出,如果内存充足的话,可以锁定内存,不让系统将该进程所持有的内存换出。
- if (lock_memory)
- {
- #ifdef HAVE_MLOCKALL
- int res = mlockall(MCL_CURRENT | MCL_FUTURE);
- if (res != 0)
- {
- fprintf(stderr, "warning: -k invalid, mlockall() failed: %s\n",
- strerror(errno));
- }
- #else
- fprintf(stderr,
- "warning: -k invalid, mlockall() not supported on this platform. proceeding without.\n");
- #endif
- }
5 忽略PIPE信号,PIPE信号是当网络连接一端已经断开,这时发送数据,会进行RST的重定向,再次发送数据,会触发PIPE信号,而PIPE信号的默认动作是退出进程,所以需要忽略该信号。
- if (sigignore(SIGPIPE) == -1)
- {
- perror("failed to ignore SIGPIPE; sigaction");
- exit(EX_OSERR);
- }
- if (pid_file != NULL)
- {
- save_pid(pid_file);
- }