zoukankan      html  css  js  c++  java
  • SRS之信号的管理:SrsSignalManager

    1. 综述

    SRS 中使用了 State Threads 协程库,该库对信号的处理是将信号事件转换为 I/O 事件。主要做法是:对关注的信号设置同样地信号处理函数 sig_catcher(),该函数捕获信号,并将信号写入管道,然后由创建的信号线程 signal 不断循环读取该管道,读取到事件并做相应的处理。

    2. 信号管理器之创建

    信号管理器的定义是在 SrsServer 类下的:

    /**
    * SRS RTMP server, initialize and listen, 
    * start connection service thread, destroy client.
    */
    class SrsServer : virtual public ISrsReloadHandler
        , virtual public ISrsSourceHandler
        , virtual public IConnectionManager
    {
    ...
    private:
        ...
        /**
         * signal manager which convert signal to io message.
         */
        SrsSignalManager* signal_manager;
        ...
    };
    

    2.1 SrsSignalManager 类

    位于 srs_app_server.hpp:

    /**
     * convert signal to io,
     * @see: st-1.9/docs/notes.html
     */
    class SrsSignalManager : public ISrsEndlessThreadHandler
    {
    private:
        /* Per-process pipe which is used as a signal queue. */
        /* Up to PIPE_BUF/sizeof(int) signals can be queued up. */
        int sig_pipe[2];
        st_netfd_t signal_read_stfd;
    private:
        SrsServer* _server;
        SrsEndlessThread* pthread;
    public:
        SrsSignalManager(SrsServer* server);
        virtual ~SrsSignalManager();
    public:
        virtual int initialize();
        virtual int start();
    // interface ISrsEndlessThreadHandler.
    public:
        virtual int cycle();
    private:
        // global singleton instance
        static SrsSignalManager* instance;
        /* Signal catching function. */
        /* Converts signal event to I/O event. */
        static void sig_catcher(int signo);
    };
    

    SrsSignalManager 类继承自 ISrsEndlessThreadHandler 类。

    SrsServer 与 SrsSignalManager 之间的关系图 1(双向关联)

    2.2 ISrsEndlessThreadHandler

    位于 srs_app_thread.hpp:

    /**
     * the endless thread is a loop thread never quit.
     *     user can create thread always running util server terminate.
     *     the step to create a thread never stop:
     *     1. create SrsEndlessThread field.
     *     for example:
     *         class SrsStreamCache: public ISrsEndlessThreadHandler {
     *             public: SrsStreamCache() { 
     *                 pthread = new SrsEndlessThread("http-stream", this); 
     *             }
     *             public: virtual int cycle() {
     *                 // do some work never end.
     *             }
     }
     *         }
     * @remark user must use block method in cycle method, for example, sleep or socket io.
     */
    class ISrsEndlessThreadHandler 
    {
    public:
        ISrsEndlessThreadHandler();
        virtual ~ISrsEndlessThreadHandler();
    public:
        /**
         * the cycle method for the common thread.
         * @remark user must use block method in cycle method, for example, sleep or socket io.
         */
        virtual int cycle() = 0;
    public:
        /**
         * other callback for handler.
         * @remark all callback is optional, handler can ignore it.
         */
        virtual void on_thread_start();
        virtual int on_before_cycle();
        virtual int on_end_cycle();
        virtual void on_thread_stop();
    }
    

    该类是用于创建一个无限循环、从不退出的线程,直到 server 终止。

    SrsSignalManager 与 ISrsEndlessThreadHandler 之间的关系图 2(继承)

    2.3 SrsSignalManager 构造函数

    SrsSignalManager* SrsSignalManager::instance = NULL;
    
    SrsSignalManager::SrsSignalManager(SrsServer* server)
    {
        SrsSignalManager::instance = this;
        
        _server = server;
        sig_pipe[0] = sig_pipe[1] = -1;
        /* 创建一个无线循环且永不退出的线程 */
        pthread = new SrsEndlessThread("signal", this);
        signal_read_stfd = NULL;
    }
    

    2.4 SrsEndlessThread

    2.4.1 SrsEndlessThread 类定义

    class SrsEndlessThread : public internal::ISrsThreadHandler
    {
    private:
        internal::SrsThread* pthread;
        ISrsEndlessThreadHandler* handler;
    public:
        SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h);
        virtual ~SrsEndlessThread();
    public:
        /**
         * for the endless thread, never quit.
         */
        virtual int start();
    // interface internal::ISrsThreadHandler
    public:
        virtual int cycle();
        virtual void on_thread_start();
        virtual int on_before_cycle();
        virtual int on_end_cycle();
        virtual void on_thread_stop();
    };
    

    该 SrsEndlessThread 类继承自命名空间 internal 下的 ISrsThreadHandler 类。

    SrsEndlessThread 与 SrsSignalManager 之间的关系图 3(单向关联)

    image

    SrsEndlessThread 与 ISrsEndlessThreadHandler 之间的关系 4(单向关联)

    2.4.2 ISrsThreadHandler 类定义

    // the internal classes, user should never use it.
    // user should use the public classes at the bellow:
    // @see SrsEndlessThread, SrsOneCycleThread, SrsReusableThread
    namestacp internal {
        /**
         * the handler for the thread, callback interface.
         * the thread model defines as:
         *     handler->on_thread_start()
         *     while loop:
         *         handler->on_before_cycle()
         *         handler->cycle()
         *         handler->on_end_cycle()
         *         if !loop then break for user stop thread.
         *         sleep(CycleIntervalMilliseconds)
         *     handler->on_thread_stop()
         * when stop, the thread will interrupt the st_thread,
         * which will cause the socket to return error and
         * terminater the cycle thread.
         *
         * @remark why should check can_loop() in cycle method?
         *       when thread interrupt, the socket maybe not got EINT,
         *       espectially on st_usleep(), so the cycle must check the loop,
         *       when handler->cycle() has loop itself, for example:
         *           while (true):
         *               if (read_from_socket(skt) < 0) break;
         *       if thread stop when read_from_socket, it's ok, the loop will break,
         *       but when thread stop interrupt the st_usleep(0), then the loop is 
         *       death loop.
         *       in a word, the handler->cycle() must:
         *           while (pthread->can_loop()):
         *               if (read_from_socket(skt) < 0) break;
         *       check the loop, then it works.
         *
         * @remark why should use stop_loop() to terminate thread in itself?
         *       in the thread itself, that is the cycle method,
         *       if itself want to terminater the thread, should never use stop(),
         *       but use stop_loop() to set the loop to false and terminater normally.
         *
         * @remark when should set the interval_us, and when not?
         *       the cycle will invoke util cannot loop, eventhough the return code of cycle 
         *       is error, so the interval_us used to sleep for each cycle.
         *       
         */
        class ISrsThreadHandler
        {
        public:
            ISrsThreadHandler();
            virtual ~ISrsThreadHandler();
        public:
            virtual void on_thread_start();
            virtual int on_before_cycle();
            virtual int cycle() = 0;
            virtual int on_end_cycle();
            virtual void on_thread_stop();
        };
    }
    

    SrsEndlessThread 与 internal::ISrsThreadHandler 之间的关系图 5(继承)

    2.4.3 SrsEndlessThread 构造函数

    SrsEndlessThread::SrsEndlessThread(const char* n, ISrsEndlessThreadHandler* h)
    {
        handler = h;
        /*
         * @n: 线程的名字,由前面可知为 signal
         * @this: 线程循环的处理程序
         * @0: 线程循环后休眠的时间,这里为 0,即不休眠
         * @false: 该线程是否可 joinable,这里为 false,即表示不可以被其他线程终止
         */
        pthread = new internal::SrsThread(n, this, 0, false);
    }
    

    在该构造函数中,创建了一个 SrsThread 类的线程,这里为 signal,该线程是一个无限循环且永不退出的线程。

    2.4.4 SrsThread 类定义

    namespace internal {
        ...
        /**
         * provides services from st_thread_t,
         * for common thread usage.
         */
        class SrsThread
        {
        private:
            st_thread_t tid;
            int _cid;
            bool loop;
            bool can_run;
            bool really_terminated;
            bool _joinable;
            const char* _name;
            bool disposed;
        private:
            ISrsThreadHandler* handler;
            int64_t cycle_interval_us;
        public:
            /**
             * initialize the thread.
             * @param name, human readable name for st debug.
             * @param thread_handler, the cycle handler for the thread.
             * @param interval_us, the sleep inteval when cycle finished.
             * @param joinable, if joinable, other thread must stop the thread.
             * @remark if joinable, thread never quit itself, or memory leak.
             * @see: https://github.com/ossrs/srs/issues/78
             * #remark about st debug, see st-1.9/README, _st_iterate_threads_flag
             */
            /**
             * TODO: FIXME: maybe all thread must be reap by others threads,
             * @see: https://github.com/ossrs/srs/issues/77
             */
            SrsThread(const char* name, ISrsThreadHandler* thread_handler, 
                      int64_t interval_us, bool joinable);
            virtual ~SrsThread();
        public:
            /**
             * get the context id. @see: ISrsThreadContext.get_id().
             * used for parent thread to get thd id.
             * @remark when start thread, parent thread will block and wait for this id ready.
             */
            virtual int cid();
            /**
             * start the thread, invoke the cycle of handler util
             * user stop the thread.
             * @remark ignore any error of cycle of handler.
             * @remark user can start multiple times, ignore if already started.
             * @remark wait for the cid is set by thread pfn.
             */
            virtual int start();
            /**
             * stop the thread, wait for the thread to terminate.
             * @remark user can stop multiple times, ignore if already stopped.
             */
            virtual void stop();
        public:
            /**
             * whether the thread should loop,
             * used for handler->cycle() which has a loop method,
             * to check this method, break if false.
             */
            virtual bool can_loop();
            /**
             * for the loop thread to stop the loop.
             * other thread can directly use stop() to stop loop and wait for quit.
             * this stop loop method only set loop to false.
             */
            virtual void stop_loop();
        private:
            virtual void dispose();
            virtual void thread_cycle();
            static void* thread_fun(void* arg);
        };
    }
    

    该类定义了一个线程,用于提供来自 st_thread_t 的服务,便于通用的线程使用。

    SrsEndlessThread 与 SrsThread 之间的关系图 6(单向关联)

    2.4.5 SrsThread 构造函数

    SrsThread::SrsThread(const char* name, ISrsThreadHandler* thread_handler, 
            int64_t interval_us, bool joinable)
    {
        _name = name;
        handler = thread_handler;
        cycle_interval_us = interval_us;
        
        tid = NULL;
        loop = false;
        really_terminated = true;
        _cid = -1;
        _joinable = joinable;
        disposed = false;
        
        /*
         * in start(), the thread cycle method maybe stop the remove the thread itself,
         * and the thread start(0 is waiting for the _cid, and sement fault then.
         * @see https://github.com/ossrs/srs/issues/110
         * thread will set _cid, callback on_thread_start(), then wait for the can_run signal.
         */
        can_run = false;
    }
    

    以上即为 SrsSignalManager 信号管理器的构造过程:主要就是创建一个无线循环且永不退出的线程:signal。

    3. 信号管理器之初始化

    信号管理器的初始化是在 run_master 函数中进行的。

    int run_master()
    {
        ...
        
        /* 初始化信号管理器 */
        if ((ret = _srs_server->initialize_signal()) != ERROR_SUCCESS) {
            return ret;
        }
        
        ...
    }
    

    3.1 SrsServer::initialize_signal

    int SrsServer::initialize_signal()
    {
        return signal_manager->initialize();
    }
    

    该函数接着又调用信号管理器 SrsSignalManager 类的初始化函数 initialize.

    3.2 SrsSignalManager::initialize

    int SrsSignalManager::initialize()
    {
        int ret = ERROR_SUCCESS;
        
        /* Create signal pipe */
        if (pipe(sig_pipe) < 0) {
            ret = ERROR_SYSTEM_CREATE_PIPE;
            srs_error("create signal manager pipe failed. ret=%d", ret);
            return ret;
        }
        
        /* 根据给定的文件描述符sig_pipe[0](读管道)创建一个 _st_netfd_t 类型的结构体 */
        if ((signal_read_stfd = st_netfd_open(sig_pipe[0])) == NULL) {
            ret = ERROR_SYSTEM_CREATE_PIPE;
            srs_error("create signal manage st pipe failed. ret=%d", ret);
            return ret;
        }
        
        return ret;
    }
    

    3.2.1 st_netfd_open

    _st_netfd_t *st_netfd_open(int osfd)
    {
        /* 这里构建一个 _st_netfd_t 的结构体,同时设置 osfd 为非阻塞 */
        return _st_netfd_new(osfd, 1, 0);
    }
    

    3.2.2 _st_netfd_new

    static _st_netfd_t *_st_netfd_new(int osfd, int nonblock, int is_socket)
    {
        _st_netfd_t *fd;
        int flags = 1;
        
        /* 这里调用到 epoll 的 _st_epoll_fd_new 函数,
         * 该函数中主要是检测 osfd 文件描述符的大小是否超过
         * 当前允许打开的文件描述符最大值,若是,则扩大当前支持
         * 的文件描述符个数,否则直接返回 0 */
        if ((*_st_eventsys->fd_new)(osfd) < 0) {
            return NULL;
        }
        
        /* 若 _st_netfd_freelist 列表中有空闲的 _st_netfd_t 结构体,
         * 则从其中取出一个使用 */
        if (_st_netfd_freelist) {
            fd = _st_netfd_freelist;
            _st_netfd_freelist = _st_netfd_freelist->next;
        } else {
            /* 否则,新分配一个 _st_netfd_t  */
            fd = calloc(1, sizeof(_st_netfd_t));
            if (!fd) {
                return NULL;
            }
        }
        
        fd->osfd = osfd;
        fd->inuse = 1;
        fd->next = NULL;
        
        if (nonblock) {
            /* Use just one system call */
            if (is_socket && ioctl(osfd, FIONBIO, &flags) != -1) {
                return fd;
            }
            /* Do it the Posix way */
            if ((flags = fcntl(osfd, F_GETFL, 0)) < 0 || 
                 fcntl(osfd, F_SETFL, flags | O_NONBLOCK) < 0) {
                st_netfd_free(fd);
                return NULL;
            }
        }
        
        return fd;
    }
    

    3.2.3 _st_epoll_fd_new

    ST_HIDDEN int _st_epoll_fd_new(int osfd)
    {
        if (osfd >= _st_epoll_data->fd_data_size &&
            _st_epoll_fd_data_expand(osfd) < 0)
            return -1;
    
        return 0;   
    }
    

    该函数中仅检测当前打开的文件描述符 osfd 是否已超过当前 ST 所支持的文件描述符个数的最大值,若是,则扩大为原先的两倍.

    3.2.4 _st_epoll_fd_data_expand

    ST_HIDDEN int _st_epoll_fd_data_expand(int msxfd)
    {
        _epoll_fd_data_t *ptr;
        int n = _st_epoll_data->fd_data_size;
        
        /* 若大于当前 ST 中 epoll 所分配好的监听个数最大值,
         * 则扩大两倍 */
        while (maxfd >= n)
            n <<= 1;
        
        /* 重新分配 */
        ptr = (_epoll_fd_data_t *)realloc(_st_epoll_data->fd_data, 
                                          n * sizeof(_epoll_fd_data_t));
        if (!ptr)
            return -1;
        
        /* 将新增的内存置为 0 */
        memset(ptr + _st_epoll_data->fd_data_size, 0, 
               (n - _st_epoll_data->fd_data_size) * sizeof(_epoll_fd_data_t));
        
        _st_epoll_data->fd_data = ptr;
        _st_epoll_data->fd_data_size = n;
    
        return 0;
    }
    

    4. 信号管理器之注册信号

    初始化好信号管理器后,接着向该信号管理器中注册信号。

    int run_master()
    {
        ... 
        
        if ((ret = _srs_server->register_signal()) != ERROR_SUCCESS) {
            return ret;
        }
        
        ...
    }
    

    4.1 SrsServer::register_signal

    int SrsServer::register_signal()
    {
        // start signal process thread.
        return signal_manager->start();
    }
    

    该函数中直接调用信号管理器的启动函数。

    4.2 SrsSignalManager::start

    // signal defines.
    #define SIGNAL_RELOAD SIGHUP
    
    int SrsSignalManager::start()
    {
        /**
         * Note that if multiple processes are used (see below),
         * the signal pipe should be initialized after the fork(2) call
         * so that each process has its own private pipe.
         */
        struct sigaction sa;
        
        /* Install sig_catcher() as a signal handler */
        sa.sa_handler = SrsSignalManager::sig_catcher;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGNAL_RELOAD, &sa, NULL);
        
        sa.sa_handler = SrsSignalManager::sig_catcher;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGTERM, &sa, NULL);
        
        sa.sa_handler = SrsSignalManager::sig_catcher;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGINT, &sa, NULL);
        
        sa.sa_handler = SrsSignalManager::sig_catcher;
        sigemptyset(&sa.sa_mask);
        sa.sa_flags = 0;
        sigaction(SIGUSR2, &sa, NULL);
        
        srs_trace("signal installed");
        
        /* 这里一层层调用下去,最后会调用 st_thread_create 函数创建一个新的线程 */
        return pthread->start();
    }
    

    该函数中,为信号 SIGHUP、SIGTERM、SIGINT、SIGUSR2 等信号注册了同一个信号处理函数:sig_catcher。最后调用 pthread->start() 函数创建一个线程。从前面可知,该线程名为 signal,是一个无线循环且永不退出的线程。

    4.2.1 SrsEndlessThread::start

    int SrsEndlessThread::start()
    {
        return pthread->start();
    }
    

    4.2.2 SrsThread::start

    该函数位于 srs_app_thread.cpp 文件中。

    int SrsThread::start()
    {
        int ret = ERROR_SUCCESS;
        
        if (tid) {
            srs_info("thread %s already running.", _name);
            return ret;
        }
        
        /* 创建一个线程,线程函数为 thread_fun,由前面可知创建的线程是
         * 一个无线循环且从不退出的线程,因此 joinable 为 0,即不可被其他线程终止 */
        if ((tid = st_thread_create(thread_fun, this, (_joinable ? 1 : 0), 0)) == NULL) {
            ret = ERROR_ST_CREATE_CYCLE_THREAD;
            srs_error("st_thread_create failed. ret=%d", ret);
            return ret;
        }
        
        disposed = false;
        // we set to loop to true for thread to run.
        loop = true;
        
        // wait for cid to ready, for parent thread to get the cid.
        while (_cid < 0) {
            /* 当 _cid 小于 0 时,调用 st_usleep 函数将当前线程添加到 sleep 队列中,
             * 即表示当前线程进入休眠,然后保存当前线程的上下文环境,以便当前线程的
             * 休眠时间到达时,从休眠状态中唤醒过来,即再次回到当前位置继续往下执行,
             * 直到 _cid 不为 0. 当调度其他线程运行时,有可能会调度到上面的新建的线程
             * signal,该线程的线程函数为 thread_fun,在该函数中会构建一个 context id,
             * 即 _cid */
            st_usleep(10 * 1000);
        }
        
        /* 当该线程从休眠中唤醒,且 _cid 准备好时,会设置 can_run 标志位,
         * 表示 thread_fun 线程函数中的循环可以开始了 */
        // now, cycle thread can run.
        can_run = true;
        
        return ret;
    }
    

    4.2.3 st_usleep

    int st_usleep(st_utime_ usecs)
    {
        _st_thread_t *me = _ST_CURRENT_THREAD();
        
        if (me->flags & _ST_FL_INTERRUPT) {
            me->flags &= ~_ST_FL_INTERRUPT;
            errno = EINTR;
            return -1;
        }
        
        if (usecs != _ST_UTIME_NO_TIMEOUT) {
            /* 将该线程添加到 sleep 队列中 */
            me->state = _ST_ST_SLEEPING;
            _ST_ADD_SLEEPQ(me, usecs);
        } else {
            me->state = _ST_ST_SUSPENDED;
        }
        
        /* 保存上下文环境,然后切换上下文环境,即调度其他线程执行 */
        _ST_SWITCH_CONTEXT(me);
        
        if (me->flags & _ST_FL_INTERRUPT) {
            me->flags &= ~_ST_FL_INTERRUPT;
            errno = EINTR;
            return -1;
        }
        
        return 0;
    }
    

    该函数将该当前线程添加到 sleep 队列中,然后切换线程上下文。

    注:线程超时的管理是在 idle 线程中进行的,在 idle 线程中会检测 sleep 队列中是否有线程的超时时间已经到达,若有,则唤醒该线程,即重新调度该线程从原先休眠的位置继续往下执行。

    4.2.4 _ST_ADD_SLEEPQ

    #define _ST_ADD_SLEEPQ(_thr, _timeout)  _st_add_sleep_q(_thr, _timeout)
    

    4.2.5 _st_add_sleep_q

    void _st_add_sleep_q(_st_thread_t *trd, st_utime_t timeout)
    {
        /* 计算该线程超时的绝对时间 */
        trd->due = _ST_LAST_CLOCK + timeout;
        trd->flags |= _ST_FL_ON_SLEEPQ;
        trd->heap_index = ++_ST_SLEEPQ_SIZE;
        /* 将该线程插入到平衡二叉树中 */
        heap_insert(trd);
    }
    

    ST 的超时管理是使用平衡二叉树实现的。

    5. 信号的捕获

    SRS 对于 SIGNAL_RELOAD(即 SIGHUP)、SIGTERM、SIGINT、SIGUSR2 等信号设置了同一个信号处理函数:sig_catcher。该信号处理函数主要是将发生的信号事件写入管道,转换为 I/O 事件。

    void SrsSignalManager::sig_catcher(int signo)
    {
        int err;
        
        /* Save errno to restore it after the write() */
        err = errno;
        
        /* write() is reentrant/async-safe */
        int fd = SrsSignalManager::instance->sig_pipe[1];
        write(fd, &signo, sizeof(int));
        
        errno = err;
    }
    

    6. signal 线程

    该 signal 线程是一个无限循环且永不退出的线程。该线程函数为 SrsThread::thread_fun,如下:

    vid* SrsThread::thread_fun(void* arg)
    {
        SrsThread* obj = (SrsThread*)arg;
        srs_assert(obj);
        
        /* 进入线程循环 */
        obj->thread_cycle();
        
        // for valgrind to detect.
        SrsThreadContext* ctx = dynamic_cast<SrsThreadContext*>(_srs_context);
        if (ctx) {
            ctx->clear_cid();
        }
        
        st_thread_exit(NULL);
        
        return NULL;
    }
    

    6.1 线程循环:SrsThread::thread_cycle

    void SrsThread::thread_cycle()
    {
        int ret = ERROR_SUCCESS;
        
        _srs_context->generage_id();
        srs_info("thread %s cycle start", _name);
        
        _cid = _srs_context->get_id();
        
        srs_assert(handler);
        /* 多态:调用的是子类 SrsEndlessThread 的成员函数 on_thread_start */
        handler->on_thread_start();
        
        // thread is running now.
        really_terminated = false;
        
        /* 当 can_run 为 0 时,该线程进入休眠,将控制权让出去,调度其他线程运行,直到
         * 其他线程将 can_run 置为 1,才会在唤醒后跳出该循环继续往下执行 */
        // wait for cid to ready, for parent thread to get the cid.
        while (!can_run && loop) {
            st_usleep(10 * 1000);
        }
        
        /* 检测是否可以进入循环,该 loop 在 signal 线程创建后就已经置为 1 了 */
        while (loop) {
            /* 多态:调用子类 SrsEndlessThread 的成员函数 on_before_cycle */
            if ((ret = handler->on_before_cycle()) != ERROR_SUCCESS) {
                srs_warn("thread %s on before cycle failed, ignored and retry, ret=%d", 
                         _name, ret);
                    goto failed;
            }
            srs_info("thread %s on before cycle success", _name);
            
            if ((ret = handler->cycle()) != ERROR_SUCCESS) {
                if (!srs_is_client_gracefully_close(ret) && !srs_is_system_control_error(ret)) 
                {
                    srs_warn("thread %s cycle failed, ignored and retry, ret=%d", _name, ret);
                }
                goto failed;
            }
            srs_info("thread %s cycle success", _name);
            
            if ((ret = handler->on_end_cycle()) != ERROR_SUCCESS) {
                srs_warn("thread %s on end cycle failed, ignored and retry, ret=%d", 
                         _name, ret);
                goto failed;
            }
            srs_info("thread %s on end cycle success", _name);
            
        failed:
            if (!loop) {
                break;
            }
            
            // to improve performance, donot sleep when interval is zero.
            // @see: https://github.com/ossrs/srs/issues/237
            if (cycle_interval_us != 0) {
                st_usleep(cycle_interval_us);
            }
        }
        
        // ready terminated now.
        really_terminated = true;
        
        handler->on_thread_stop();
        srs_info("thread %s cycle finished", _name);
    }
    
    SrsThread、SrsEndlessThread 和 internal::ISrsThreadHandler 三者之间的关系

    6.2 SrsEndlessThread::on_thread_start

    位于 srs_app_thread.cpp:

    void SrsEndlessThread::on_thread_start()
    {
        handler->on_thread_start();
    }
    

    由于 ISrsEndlessThreadHandler 的子类 SrsSignalManager 没有实现 on_thread_start 函数,因此还是调用父类的 on_thread_start 函数。

    SrsEndlessThread、SrsSignalManager 和 ISrsEndlessThreadHandler 三者之间的关系

    image

    6.2.1 ISrsEndlessThreadHandler::on_thread_start

    void ISrsEndlessThreadHandler::on_thread_start()
    {
    }
    

    该函数为空。

    6.3 SrsEndlessThread::on_before_cycle

    int SrsEndlessThread::on_before_cycle()
    {
        return handler->on_before_cycle();
    }
    

    同理,参考上图,可知,ISrsEndlessThreadHandler 的子类 SrsSignalManager 没有实现 on_before_cycle 函数,因此还是调用父类的 on_before_cycle 函数。

    int ISrsEndlessThreadHandler::on_before_cycle()
    {
        return ERROR_SUCCESS;
    }
    

    6.4 SrsEndlessThread::cycle

    int SrsEndlessThread::cycle()
    {
        return handler->cycle();
    }
    

    这里,发生多态:ISrsEndlessThreadHandler 的子类 SrsSignalManager 实现了 cycle() 函数,因此调用子类的 cycle() 函数。

    6.4.1 SrsSignalManager::cycle

    位于 srs_app_server.cpp:

    int SrsSignalManager::cycle()
    {
        int ret = ERROR_SUCCESS;
        
        int signo;
        
        /* Read the next signal from the pipe */
        st_read(signal_read_stfd, &signo, sizeof(int), ST_UTIME_NO_TIMEOUT);
        
        /* Process signal synchronously */
        _server->on_signal(signo);
        
        return ret;
    }
    

    该函数主要做两件事:

    1. 从管道中读取由信号事件状态而来的 I/O 事件,若本次没有读取到,则会对该管道进行监听,直到监听到事件才会将再次调度该线程
    2. 处理信号同步

    6.4.2 st_read

    这里调用 st_read 从管道中读取 I/O 事件,该 I/O 事件其实就是由 sig_catcher 函数将信号事件转换后的 I/O 事件。

    ssize_t st_read(_st_netfd_t *fd, void *buf, size_t nbyte, st_utime_t timeout)
    {
        ssize_t n;
        
        while ((n = read(fd->osfd, buf, nbyte)) < 0) {
            if (errno == EINTR) 
                continue;
            if (!_IO_NOT_READY_ERROR)
                return -1;
            /* Wait until the socket becomes readable */
            if (st_netfd_poll(fd, POLLIN, timeout) < 0)
                reurn -1;
        }
        
        return n;
    }
    

    该函数中直接调用 read 函数从管道中读取 nbyte 直接的数据到 buf 中,读取成功,直接返回;若失败,则调用 st_netfd_poll 函数轮询,超时时间为 timeout (由前面知,传入的超时时间为 -1)。

    6.4.3 st_netfd_poll

    /*
     * Wait for I/O on a single descriptor.
     */
    int st_netfd_poll(_st_netfd_t *fd, int how, st_utime_t timeou)
    {
        struct pollfd pd;
        int n;
        
        pd.fd = fd->osfd;
        pd.events = (short)how;
        pd.revents = 0;
        
        /* 监听指定的 IO 事件 */
        if ((n = st_poll(&pd, 1, timeout)) < 0)
            return -1;
        if (n == 0) {
            /* Timed out */
            errno = ETIME;
            return -1;
        }
        if (pd.revents & POLLNVAL) {
            errno = EBADF;
            return -1;
        }
        
        return 0;
    }
    

    该函数中,又调用 st_poll 函数。

    6.4.4 st_poll

    int st_poll(struct pollfd *pds, int npds, st_utime_t timeout)
    {
        struct pollfd *pd;
        struct pollfd *epd = pds + npds;
        _st_pollq_t pq;
        /*这里当前线程应为 signal 线程 */
        _st_thread_t *me = _ST_CURRENT_THREAD();
        int n;
        
        /* 检测当前线程是否已被中断了 */
        if (me->flags & _ST_FL_INTERRUPT) {
            me->flags &= ~_ST_FL_INTERRUPT;
            errno = EINTR;
            return -1;
        }
        
        /* 这里调用的是 _st_epoll_pollset_add 函数向 epoll 中
         * 添加或修改感兴趣的事件 */
        if ((*_st_eventsys->pollset_add)(pds, npds) < 0) 
            return -1;
        
        pq.pds = pds;
        pq.npds = npds;
        pq.thread = me;
        pq.on_ioq = 1;
        /* 向 io 队列中添加一个指向 _st_pollq_t 结构体的指针,该结构体代表正在监听的事件 */
        _ST_ADD_IOQ(pq);
        /* 若传入的超时时间 timeout 不为 -1,则将该线程休眠 timeout */
        if (timeout != ST_UTIME_NO_TIMEOUT)
            _ST_ADD_SLEEPQ(me, timeout);
        /* 否则,该线程等待 I/O 事件的到达后才会再次调度,从下面继续运行 */
        me->state = _ST_ST_IO_WAIT;
        
        _ST_SWITCH_CONTEXT(me);
        
        /* 若监听到所监听的 I/O 事件后,该线程再次被调度,从这里开始继续执行 */
        
        n = 0;
        /* 正常情况下,再次获得调度,pq.on_ioq 应为 0,若为 1,表示超时了,这里将其从 io 中删除 */
        if (pq.on_ioq) {
            /* If we timed out, the pollq might still be on the ioq. Remove it */
            _ST_DEL_IOQ(pq);
            /* 将该事件从 epoll 中移除 */
            (*st_eventsys->pollset_del)(pds, npds);
        } else {
            /* Count the number of ready descriptors */
            for (pd = pds; pd < epd; pd++) {
                if (pd->revents)
                    n++;
            }
        }
        
        if (me->flags & _ST_FL_INTERRUPT) {
            me->flags &= ~_ST_FL_INTERRUPT;
            errno = EINTR;
            return -1;
        }
        
        /* 成功监听到事件,返回事件的个数 */
        return n;
    }
    

    在该函数中:

    • 首先是向 epoll中 添加或修改感兴趣的事件
    • 构建一个 _st_pollq_t 结构体的指针变量,并将其添加到 io 队列中,表示正在监听该结构体上的事件
    • 将当前线程的控制权让出,调度其他线程运行
    • 当监听到 _st_pollq_t 中所要监听的事件后,该线程会再次获得调度,从切换线程处再次往下继续执行,最后也就是计算监听到的事件个数,然后返回该值

    6.4.5 _st_epoll_pollset_add

    位于 event.c:

    #define _ST_EPOLL_READ_CNT(fd)   (_st_epoll_data->fd_data[fd].rd_ref_cnt)
    #define _ST_EPOLL_WRITE_CNT(fd)  (_st_epoll_data->fd_data[fd].wr_ref_cnt)
    #define _ST_EPOLL_EXCEP_CNT(fd)  (_st_epoll_data->fd_data[fd].ex_ref_cnt)
    
    #define _ST_EPOLL_READ_BIT(fd)   (_ST_EPOLL_READ_CNT(fd) ? EPOLLIN : 0)
    #define _ST_EPOLL_WRITE_BIT(fd)  (_ST_EPOLL_WRITE_CNT(fd) ? EPOLLOUT : 0)
    #define _ST_EPOLL_EXCEP_BIT(fd)  (_ST_EPOLL_EXCEP_CNT(fd) ? EPOLLPRI : 0)
    #define _ST_EPOLL_EVENTS(fd) 
        (_ST_EPOLL_READ_BIT(fd)|_ST_EPOLL_WRITE_BIT(fd)|_ST_EPOLL_EXCEP_BIT(fd))
    
    
    ST_HIDDEN int _st_epoll_pollset_add(struct pollfd *pds, int npds)
    {
        struct epoll_event ev;
        int i, fd;
        int old_events, events, op;
    
        /* Do as many checks as possible up front */
        for (i = 0; i < npds; i++) {
            fd = pds[i].fd;
            if (fd < 0 || !pds[i].events ||
                (pds[i].events & ~(POLLIN | POLLOUT | POLLPRI))) {
                errno = EINVAL;
                return -1;
            }
            if (fd >= _st_epoll_data->fd_data_size &&
                _st_epoll_fd_data_expand(fd) < 0)
                return -1;
        }
    
        for (i = 0; i < npds; i++) {
            fd = pds[i].fd;
            /* 先保存该描述符旧的监听的事件 */
            old_events = _ST_EPOLL_EVENTS(fd);
    
            /* 根据本次该 fd 要监听的事件,将相应事件的引用值加 1 */
            if (pds[i].events & POLLIN)
                _ST_EPOLL_READ_CNT(fd)++;
            if (pds[i].events & POLLOUT)
                _ST_EPOLL_WRITE_CNT(fd)++;
            if (pds[i].events & POLLPRI)
                _ST_EPOLL_EXCEP_CNT(fd)++;
    
            /* 再次获取该 fd 新的要监听的事件 */
            events = _ST_EPOLL_EVENTS(fd);
            /* 若旧监听事件与新的监听事件不等 */
            if (events != old_events) {
                /* 若旧监听事件不为0,则本次操作为修改该 fd 要监听的事件,
                 * 否则该 fd 新添加要监听的事件 */
                op = old_events ? EPOLL_CTL_MOD : EPOLL_CTL_ADD;
                ev.events = events;
                ev.data.fd = fd;
                /* 向 epoll 对象中添加或修改感兴趣的事件,返回 0 表示成功 */
                if (epoll_ctl(_st_epoll_data->epfd, op, fd, &ev) < 0 &&
                    (op != EPOLL_CTL_ADD || errno != EEXIST))
                    break;
                /* 若是向 epoll 中添加感兴趣的事件 */
                if (op == EPOLL_CTL_ADD) {
                    /* epoll 的 epoll_event 类的数组元素个数加 1 */
                    _st_epoll_data->evtlist_cnt++;
                    if (_st_epoll_data->evtlist_cnt > _st_epoll_data->evtlist_size)
                        _st_epoll_evtlist_expand();
                }
            }
        }
    
        if (i < npds) {
            /* Error */
            int err = errno;
            /* Unroll the state */
            _st_epoll_pollset_del(pds, i + 1);
            errno = err;
            return -1;
        }
    
        return 0;
    }
    

    该函数主要是向 epoll 中添加或修改感兴趣的事件。

    6.4.6 SrsServer::on_signal

    void SrsServer::on_signal(int signo)
    {
        if (signo == SIGNAL_RELOAD) {
            signal_reload = true;
            return;
        }
        
        if (signo == SIGINT || signo == SIGUSR2) {
    #ifdef SRS_AUTO_GPERF_MC
            rs_trace("gmc is on, main cycle will terminate normally.");
            signal_gmc_stop = true;
    #else
            srs_trace("user terminate program");
    #ifdef SRS_AUTO_MEM_WATCH
            srs_memory_report();
    #endif
            exit(0);
    #endif
            return;
        }
        
        if (signo == SIGTERM && !signal_gracefully_quit) {
            srs_trace("user terminate program, gracefully quit.");
            signal_gracefully_quit = true;
            return;
        }
    }
    

    在该函数中对捕获的信号进行相应的处理。

    致此,以上就是 SRS 对信号的管理。

  • 相关阅读:
    Python字典推导式将cookie字符串转化为字典
    爬取百度贴吧前1000页内容(requests库面向对象思想实现)
    牛客网:连续子数组的最大和
    在字符串中找出第一个只出现一次的字符,Python实现
    关于时间日期的程序,主要datetime模块
    [读书笔记] Python数据分析 (五) pandas入门
    [学习笔记] CS131 Computer Vision: Foundations and Applications:Lecture 3 线性代数初步
    [读书笔记] Python数据分析 (四) 数组和矢量计算
    [读书笔记] Python数据分析 (三) IPython
    [读书笔记] R语言实战 (六) 基本图形方法
  • 原文地址:https://www.cnblogs.com/jimodetiantang/p/9036579.html
Copyright © 2011-2022 走看看