CppGuide社区 CppGuide社区
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
首页
  • 🔥最新谷歌C++风格指南(含C++17/20)
  • 🔥C++17详解
  • 🔥C++20完全指南
  • 🔥C++23快速入门
  • C++语言面试问题集锦
  • 🔥交易系统开发岗位求职与面试指南 (opens new window)
  • 第1章 高频C++11重难点知识解析
  • 第2章 Linux GDB高级调试指南
  • 第3章 C++多线程编程从入门到进阶
  • 第4章 C++网络编程重难点解析
  • 第5章 网络通信故障排查常用命令
  • 第6章 高性能网络通信协议设计精要
  • 第7章 高性能服务结构设计
  • 第8章 Redis网络通信模块源码分析
  • 第9章 后端服务重要模块设计探索
  • 🚀 全部章节.pdf 下载 (opens new window)
  • 源码分析系列

    • leveldb源码分析
    • libevent源码分析
    • Memcached源码分析
    • TeamTalk源码分析
    • 优质源码分享 (opens new window)
    • 🔥远程控制软件gh0st源码分析
  • 从零手写C++项目系列

    • 🔥C++游戏编程入门(零基础学C++)
    • 🔥使用C++17从零开发一个调试器 (opens new window)
    • 🔥使用C++20从零构建一个完整的低延迟交易系统 (opens new window)
    • 🔥使用C++从零写一个C语言编译器 (opens new window)
    • 🔥从零用C语言写一个Redis
  • 🔥Windows 10系统编程
  • 🔥Linux 5.x内核开发与调试 完全指南 (opens new window)
  • TCP源码实现超详细注释版.pdf (opens new window)
  • Go语言特性

    • Go系统接口编程
    • 高效Go并发编程
    • Go性能调优
    • Go项目架构设计
  • Go项目实战

    • 🔥使用Go从零开发一个数据库
    • 🔥使用Go从零开发一个编译器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥使用Go从零开发一个解释器 (opens new window)
    • 🔥用Go从零写一个编排器(类Kubernetes) (opens new window)
Rust编程指南
  • SQL零基础指南
  • MySQL开发与调试指南
GitHub (opens new window)
  • 服务器资源调整
  • 初始化参数解析
  • 网络监听的建立
  • 网络连接建立
  • 内存初始化
  • 资源初始化
  • get过程
  • cas属性
  • 内存池
  • 连接队列
  • Hash表操作
  • LRU操作
  • set操作
  • do_item_alloc操作
  • item结构
  • Hash表扩容
  • 线程交互
  • 状态机
  • Memcached源码分析
zhangxf
2023-04-02

服务器资源调整

# Memcached源码阅读序 服务器资源调整

本篇作为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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40

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);
    }
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21

3 以daemon的方式启动,daemon的实现如下,该daemon没有进行2次fork,APUE上面也有说第二次fork不是必须的。

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);
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47

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
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14

5 忽略PIPE信号,PIPE信号是当网络连接一端已经断开,这时发送数据,会进行RST的重定向,再次发送数据,会触发PIPE信号,而PIPE信号的默认动作是退出进程,所以需要忽略该信号。

if (sigignore(SIGPIPE) == -1)
{
    perror("failed to ignore SIGPIPE; sigaction");
    exit(EX_OSERR);
}
1
2
3
4
5

6 保存daemon进程的进程id到文件中,这样便于控制程序,读取文件内容,即可得到进程ID。

if (pid_file != NULL)
{
    save_pid(pid_file);
}
1
2
3
4
编辑 (opens new window)
上次更新: 2023/12/11, 22:32:09
初始化参数解析

初始化参数解析→

最近更新
01
第二章 关键字static及其不同用法
03-27
02
第一章 auto与类型推导
03-27
03
C++语言面试问题集锦 目录与说明
03-27
更多文章>
Copyright © 2024-2025 沪ICP备2023015129号 张小方 版权所有
  • 跟随系统
  • 浅色模式
  • 深色模式
  • 阅读模式