Beanstalk源码分析--系统初始化

概述

本文接上文,继续分析和探讨beanstalk的实现原理。本文对beanstalk的启动过程做一个分析,这样可以对其运行框架有一个总体上的把握。
beanstalkd是使用epoll作为事件处理框架,为什么epoll如此高效,可以参考我以前写的一篇对其实现代码分析的文章

源码分析

软件入口

beanstalkd的初始化流程代码非常简洁。主要完成各种数据结构的初始化,创建服务器监听socket,然后启动epoll的事件驱动模型,监听客户端连接请求。

int
main(int argc, char **argv)
{
    int r;
    struct job list = {}; //job空头节点

    progname = argv[0];
    setlinebuf(stdout);
    optparse(&srv, argv+1); //分解启动参数

    if (verbose) {
        printf("pid %d\n", getpid());
    }

    r = make_server_socket(srv.addr, srv.port); //创建服务器监听socket fd
    if (r == -1) twarnx("make_server_socket()"), exit(111);
    srv.sock.fd = r;

    prot_init();

    if (srv.user) su(srv.user); //转换成srv.user用户来运行
    set_sig_handlers();    // 安装信号处理函数

    if (srv.wal.use) {
        // We want to make sure that only one beanstalkd tries
        // to use the wal directory at a time. So acquire a lock
        // now and never release it.
        if (!waldirlock(&srv.wal)) {
            twarnx("failed to lock wal dir %s", srv.wal.dir);
            exit(10);
        }

        // 初始化job链表头
        list.prev = list.next = &list;
        walinit(&srv.wal, &list);
        r = prot_replay(&srv, &list);
        if (!r) {
            twarnx("failed to replay log");
            return 1;
        }
    }

    // 启动服务器事件监听
    srvserve(&srv);
    return 0;
}

初始化服务

这一步会完成一个服务器的基本框架。大多数的后台服务这一段代码都相似。
这里是通过epoll的方式来实现事件处理框架。其实很多软件都是用epoll。

// 服务器监听初始化过程
void srvserve(Server *s)
{
    int r;
    Socket *sock;
    int64 period;

    if (sockinit() == -1) { //socket初始化,其实就是调用epoll_create得到一个fd。
        twarnx("sockinit");
        exit(1);
    }

    s->sock.x = s; // 这里Socket结构中的x字段保存的是Server结构的指针
    s->sock.f = (Handle)srvaccept;  // 当有连接请求到来时,调用Socket结构中的f回调函数
    s->conns.less = (Less)connless;    // 
    s->conns.rec = (Record)connrec;  // 

    r = listen(s->sock.fd, 1024);  // 创建服务器监听socket
    if (r == -1) {
        twarn("listen");
        return;
    }

    r = sockwant(&s->sock, 'r');  // 为s->sock.fd添加读事件到epoll_ctl中,用来相应客户端的连接请求
    if (r == -1) {
        twarn("sockwant");
        exit(2);
    }

    for (;;) {    // 进入死循环进行事件处理
        period = prottick(s);  // 处理超时的job

        int rw = socknext(&sock, period);  // 某个fd有读或写事件发生
        if (rw == -1) {
            twarnx("socknext");
            exit(1);
        }

        if (rw) {  // 调用对应的函数处理该事件
            sock->f(sock->x, rw);  //第一次是调用srvaccept函数来接受客户端的连接请求
        }
    }
}

总结

本文描述了beanstalk的启动过程,并对其启动代码进行了一些分析。通过本文,可以对beanstalk的启动过程和处理框架有一个比较清楚的认识了。

猜你喜欢

转载自blog.csdn.net/zg_hover/article/details/81295094