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(单向关联)

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 三者之间的关系

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 对信号的管理。

SRS之信号的管理:SrsSignalManager的更多相关文章

  1. SRS微信号和QQ组

    联系方式:https://github.com/winlinvip/simple-rtmp-server/wiki/v1_CN_Contact $(function () { $('pre.prett ...

  2. Linux - 进程查看与管理

    标签(空格分隔): Linux 进程的静态查看 查看系统所有进程 ps -ef -- 输出来好乱,看不懂..: ps aux -- a表示所有与终端相关的进程,u表示所有以用户组织的进程状态的信息,x ...

  3. Linux信号基础

    Linux信号基础   作者:Vamei 出处:http://www.cnblogs.com/vamei 欢迎转载,也请保留这段声明.谢谢! Linux进程基础一文中已经提到,Linux以进程为单位来 ...

  4. 浅论Javascript在汽车信号测试中的应用

    起因 上周老板又给了我这个车辆工程毕业的码农一份工作: 要我写一个测试台架出来. 我先简单的分析了测试台架的几种典型的工况: 1.发送一个CAN信号,测试能否查到. 2.发送一个信号,是否能在规定时间 ...

  5. python学习笔记——多进程间通信——Linux信号基础

    1 信号的基本描述 Signal信号(其全程为软中断信号)是Linux系统编程中非常重要的概念,信号是异步进程中通信的一种方式. 作用是通知进程发生了异步事件.进程之间可以调用系统来传递信号, 本身内 ...

  6. linux信号------探步

    前言 Linux以进程为单位来执行程序.我们可以 将计算机看作一个大楼,内核(kernel)是大楼的管理员,进程是大楼的房客.每个进程拥有一个独立的房间(属于进程的内存空间),而每个房间都是不允 许该 ...

  7. Linux就该这么学 20181004(第六章磁盘管理)

    参考链接https://www.linuxprobe.com/ /boot 开机锁需要文件-内核.开机菜单以及所需配置文件 /dev 以文件形式存放的任何设备与接口 /etc 配置文件 /home 用 ...

  8. Linux基础篇,系统服务(service)的管理

    一.服务是什么? 用白话文说,服务就是"常驻在内存中的进程",用来提供一些系统或网络功能. 二.service和daemon的区别与联系 因为服务(service)本质上来说也是程 ...

  9. Nginx的进程管理与重载原理

    目录 进程结构图 信号量管理 Linux的信号量管理机制 利用信号量管理Nginx进程 配置文件重载原理 进程结构图 Nginx是多进程结构,多进程结构设计是为了保证Nginx的高可用高可靠,包含: ...

随机推荐

  1. vue-loading图

    父组件给子组件src地址: columns(){ return [ {'title': '图片', 'key': 'img', render(h, {row}){ return h(LoadingIm ...

  2. init是一个自定义方法名

    init是一个自定义方法名,用于初始化页面变量.上面的代码表示初始化方法是在当前网页加载后执行的(当浏览器打开网页时,触发窗口对象的onload方法,用上面的代码执行名为init的初始化方法).事实上 ...

  3. JS 中的跨域请求

    跨域请求并不仅仅只是 Ajax 的跨域请求,而是对于一个页面来说,只要它请求了其他域名的资源了,那么这个过程就属于跨域请求了. 比如,一个带有其他域名的 src 的 <img> 标签,以及 ...

  4. scrapy命令:scrapy genspider详解 转

    当我们使用: scrapy startproject taobao 命令创建好scrapy蜘蛛后,你是否注意到,下面还有这么一行内容: F:\scrapyTest> scrapy startpr ...

  5. linux 删除文件空间未释放问题

    现象:我们测试环境上,导出数据文件时,由于作业报错,重复导出,空间使用到达100%,按理说,导出的文件时在相同的路径下,文件名也是一致的,会自动替换. 那么之前导出的文件会被删除,问题就出现在删除这一 ...

  6. 获得npm server 上 package 的版本信息

    通过这个命令可以获取package 的历史版本信息 npm view packagename   versions

  7. 从一道Hard学习滑动窗口

    滑动窗口 滑动窗口(sliding windows algorithm)这种方法,专门用于解决区间解的问题.它在运算的时候,将解集放在窗口中,结束的时候比对是否符合预期.在运算的过程中,会对窗口的左右 ...

  8. Oracle笔记(五) 单行函数

    虽然各个数据库都是支持SQL语句的,但是每一个数据库也有每一个数据库自己所支持的操作函数,这些就是单行函数,而如果要想进行数据库开发的话,除了要会使用SQL之外 ,就是要多学习函数. 单行函数主要分为 ...

  9. NOIP2016 Day1 T2 天天爱跑步(树上差分,LCA)

    原文链接 原题链接 题目描述 小c同学认为跑步非常有趣,于是决定制作一款叫做<天天爱跑步>的游戏.<天天爱跑步>是一个养成类游戏,需要玩家每天按时上线,完成打卡任务. 这个游戏 ...

  10. URLConnection类的使用

    URLConnection类概述 URLConnection是个抽象类,它有两个直接子类分别是HttpURLConnection和JarURLConnection,它是基于Http协议的.另外一个重要 ...