一、信号
发送信号的方式:
Shell
– kill命令向指定进程发送信号
系统调用
– kill系统调用向指定进程发送信号
– raise系统调用向当前进程发送信号
信号处理函数:
(1)有默认、忽略和自定义三种
(2)两种特殊信号无法被处理:SIGKILL、SIGSTOP
(3)信号处理函数执行时可以屏蔽指定的信号,使到来的信号挂起
(4)一个线程对一个锁加多次(中间没有解锁)就会死锁,这个时候就只能用递归锁
(5)信号处理函数要足够简单,很多系统调用不是异步信号安全的。像nginx中信号处理函数一般只是写管道,然后设置一个信号的标志位。
注:diydb中对信号的处理只是简单的判断要不要关闭数据库,并没有像nginx一样对信号附上相应的功能模块。
二、内核控制块KRCB,直接或间接的包含了整个数据库的所有模块信息,类似于nginx中的ngx_cycles_t。
//内核控制块
#ifndef PMD_HPP__
#define PMD_HPP__
#include "core.hpp"
#include "pmdEDUMgr.hpp"
enum DIY_DB_STATUS//当前数据库的状态
{
DIY_DB_NORMAL = 0,//正常状态
DIY_DB_SHUTDOWN,//关闭状态
DIY_DB_PANIC//非正常状态
} ;
#define DIY_IS_DB_NORMAL ( DIY_DB_NORMAL == pmdGetKRCB()->getDBStatus () )
#define DIY_IS_DB_DOWN ( DIY_DB_SHUTDOWN == pmdGetKRCB()->getDBStatus () || \
DIY_DB_PANIC == pmdGetKRCB()->getDBStatus () )
#define DIY_IS_DB_UP ( !DIY_IS_DB_DOWN )
#define DIY_SHUTDOWN_DB { pmdGetKRCB()->setDBStatus(DIY_DB_SHUTDOWN); }//将数据库设置为关闭状态
class pmdOptions ;
class DIY_KRCB//内核控制块
{
private :
// configured options
char _dataFilePath [ OSS_MAX_PATHSIZE + 1 ] ;//数据库路径
char _logFilePath [ OSS_MAX_PATHSIZE + 1 ] ;//日志文件路径
int _maxPool ;//最大的线程池数量
char _svcName [ NI_MAXSERV + 1 ] ;//监听端口的名字
DIY_DB_STATUS _dbStatus ;//当前数据库的状态
pmdEDUMgr _eduMgr ;//线程池
public :
// constructor
DIY_KRCB ()
{
_dbStatus = DIY_DB_NORMAL ;
memset ( _dataFilePath, 0, sizeof(_dataFilePath) ) ;
memset ( _logFilePath, 0, sizeof(_logFilePath) ) ;
_maxPool = 0 ;
memset ( _svcName, 0, sizeof(_svcName) ) ;
}
// destructor
~DIY_KRCB () {}
// inline function
// get diy mgr
pmdEDUMgr *getEDUMgr ()//得到线程池
{
return &_eduMgr ;
}
//rtn *getRtnMgr()
//{
// return &_rtnMgr ;
//}
// get database status
inline DIY_DB_STATUS getDBStatus ()//得到数据库的状态
{
return _dbStatus ;
}
// get data file path
inline const char *getDataFilePath ()//得到数据库数据文件的路径
{
return _dataFilePath ;
}
// get log file path
inline const char *getLogFilePath ()//得到日志文件的路径
{
return _logFilePath ;
}
// get service name
inline const char *getSvcName ()//得到服务名称
{
return _svcName ;
}
// get max thread pool
inline int getMaxPool ()//得到线程池中最大的线程数
{
return _maxPool ;
}
// setup database status
inline void setDBStatus ( DIY_DB_STATUS status )设置数据库的状态
{
_dbStatus = status ;
}
// set data file path
void setDataFilePath ( const char *pPath )//设置数据库的数据文件的路径
{
strncpy ( _dataFilePath, pPath, sizeof(_dataFilePath) ) ;
}
// set log file path
void setLogFilePath ( const char *pPath )//设置日志文件的路径
{
strncpy ( _logFilePath, pPath, sizeof(_logFilePath) ) ;
}
// set service name
void setSvcName ( const char *pName )//得到服务名
{
strncpy ( _svcName, pName, sizeof(_svcName) ) ;
}
// set max pool
void setMaxPool ( int maxPool )//设置线程池的最大线程数
{
_maxPool = maxPool ;
}
// setup from pmdOptions
int init ( pmdOptions *options ) ;//根据pmdOptions(从命令行或者配置文件读取并解析出来的配置项)来设置内核控制块的属性
} ;
extern DIY_KRCB pmd_krcb ;//对象在文件之外定义
inline DIY_KRCB *pmdGetKRCB()//全局的内联函数
{
return &pmd_krcb ;
}
#endif
pmd.cpp
#include "pmd.hpp"
#include "pmdOptions.hpp"
DIY_KRCB pmd_krcb;//全局变量
extern char _pdDiagLogPath[OSS_MAX_PATHSIZE+1];
int DIY_KRCB::init(pmdOptions *options)//根据pmdOptions(从命令行或者配置文件读取并解析出来的配置项)来设置内核控制块的属性
{
setDBStatus(DIY_DB_NORMAL);//先把数据库状态设置为正常
setDataFilePath(options->getDBPath());
setLogFilePath(options->getLogPath());
strncpy(_pdDiagLogPath,options->getLogPath(),sizeof(_pdDiagLogPath));
setSvcName(options->getServiceName());
setMaxPool(options->getMaxPool());
return DIY_OK;
}
在下面的服务器入口文件中,即数据库引擎的main()函数所在的文件中,我们会看到命令行参数和配置文件的读取和以此为依据引导内核控制块的过程。
#include "core.hpp"
#include "pmd.hpp"
#include "pmdOptions.hpp"
#include "pd.hpp"
#include "pmdEDUMgr.hpp"
static int pmdResolveArguments ( int argc, char **argv )
{
int rc = DIY_OK ;
pmdOptions options ;
rc = options.init ( argc, argv ) ;//options从命令行和配置文件读取参数,命令行参数的优先级更高
if ( rc )
{
if ( DIY_PMD_HELP_ONLY != rc )//因为配置参数有-help或者-h时,rc也不为0
PD_LOG ( PDERROR, "Failed to init options, rc = %d", rc ) ;
goto error ;
}
rc = pmdGetKRCB()->init ( &options ) ;//把options中的参数传入内核控制块
if( rc )
{
PD_LOG ( PDERROR, "Failed to init krcb, rc = %d", rc ) ;
goto error ;
}
done :
return rc ;
error :
goto done ;
}
struct _signalInfo
{
const char *name ;
int handle ;
} ;
typedef struct _signalInfo _signalInfo ;
static _signalInfo signalHandleMap [] = {//主要定义收到哪些信号(handle==1)后需要关闭数据库
{ "Unknow", 0 },
{ "SIGHUP", 1 }, //1
{ "SIGINT", 1 }, //2
{ "SIGQUIT", 1 }, //3
{ "SIGILL", 1 }, //4
{ "SIGTRAP", 1 }, //5
{ "SIGABRT", 1 }, //6
{ "SIGBUS", 1 }, //7
{ "SIGFPE", 1 }, //8
{ "SIGKILL", 1 }, //9
{ "SIGUSR1", 0 }, //10
{ "SIGSEGV", 1 }, //11
{ "SIGUSR2", 0 }, //12
{ "SIGPIPE", 1 }, //13
{ "SIGALRM", 0 }, //14
{ "SIGTERM", 1 }, //15
{ "SIGSTKFLT", 0 }, //16
{ "SIGCHLD", 0 }, //17
{ "SIGCONT", 0 }, //18
{ "SIGSTOP", 1 }, //19
{ "SIGTSTP", 0 }, //20
{ "SIGTTIN", 0 }, //21
{ "SIGTTOU", 0 }, //22
{ "SIGURG", 0 }, //23
{ "SIGXCPU", 0 }, //24
{ "SIGXFSZ", 0 }, //25
{ "SIGVTALRM", 0 }, //26
{ "SIGPROF", 0 }, //27
{ "SIGWINCH", 0 }, //28
{ "SIGIO", 0 }, //29
{ "SIGPWR", 1 }, //30
{ "SIGSYS", 1 }, //31
{ "UNKNOW", 0 }, //32
{ "UNKNOW", 0 }, //33
{ "SIGRTMIN", 0 }, //34
{ "SIGRTMIN+1", 0 }, //35
{ "SIGRTMIN+2", 0 }, //36
{ "SIGRTMIN+3", 0 }, //37
{ "SIGRTMIN+4", 0 }, //38
{ "SIGTTMIN+5", 0 }, //39
{ "SIGRTMIN+6", 0 }, //40
{ "SIGRTMIN+7", 0 }, //41
{ "SIGTTMIN+8", 0 }, //42
{ "SIGRTMIN+9", 0 }, //43
{ "SIGRTMIN+10", 0 },//44
{ "SIGRTMIN+11", 0 },//45
{ "SIGRTMIN+12", 0 },//46
{ "SIGRTMIN+13", 0 },//47
{ "SIGRTMIN+14", 0 },//48
{ "SIGRTMIN+15", 0 },//49
{ "SIGRTMAX-14", 0 },//50
{ "SIGRTMAX-13", 0 },//51
{ "SIGRTMAX-12", 0 },//52
{ "SIGRTMAX-11", 0 },//53
{ "SIGRTMAX-10", 0 },//54
{ "SIGRTMAX-9", 0 }, //55
{ "SIGRTMAX-8", 0 }, //56
{ "SIGRTMAX-7", 0 }, //57
{ "SIGRTMAX-6", 0 }, //58
{ "SIGRTMAX-5", 0 }, //59
{ "SIGRTMAX-4", 0 }, //60
{ "SIGRTMAX-3", 0 }, //61
{ "SIGRTMAX-2", 0 }, //62
{ "SIGRTMAX-1", 0 }, //63
{ "SIGRTMAX", 0 }, //64
};
#define PMD_MAX_SIGNALS 64
static void pmdSignalHandler ( int sigNum )//信号处理函数
{
if ( sigNum > 0 && sigNum <= PMD_MAX_SIGNALS )//sigNum是1才处理(关闭数据库),是0则不处理
{
if ( signalHandleMap[sigNum].handle )
{
DIY_SHUTDOWN_DB ;
}
}
}
int pmdTcpListenerEntryPoint () ;//监听线程的入口函数
static int pmdSetupSignalHandle()//设置信号处理函数
{
int rc=DIY_OK;
struct sigaction newact;
memset(&newact,0,sizeof(newact));
sigemptyset(&newact.sa_mask);
newact.sa_flags=0;
newact.sa_handler=(__sighandler_t)pmdSignalHandler;//真正的处理函数
for(int i=0;i<PMD_MAX_SIGNALS;i++)
{
sigaction(i+1,&newact,NULL);//把所有信号的处理函数设置为pmdSignalHandler
}
return rc;
}
int pmdMasterThreadMain(int argc, char **argv)//主线程
{
int rc=DIY_OK;
DIY_KRCB *krcb=pmdGetKRCB();//得到全局的内核控制块
pmdEDUMgr *eduMgr=krcb->getEDUMgr();//得到内核控制块中的线程池
EDUID agentEDU =PMD_INVALID_EDUID;//初始化代理线程的EDUID
//处理信号
rc=pmdSetupSignalHandle();
PD_RC_CHECK(rc,PDERROR,"failed to setup signal handler,rc = %d",rc);
rc=pmdResolveArguments(argc,argv);//获取命令行和配置文件中的所有参数,并用这些参数初始化内核控制块
if(DIY_PMD_HELP_ONLY==rc)
{
goto done;
}
PD_RC_CHECK(rc,PDERROR,"Failed to resolve argument, rc=%d",rc);
rc = eduMgr->startEDU(EDU_TYPE_TCPLISTENER,NULL,&agentEDU);//起一个监听线程
while(DIY_IS_DB_UP)
{
sleep(1);
}
done:
return rc;
error:
goto done;
}
int main ( int argc, char **argv )
{
pmdMasterThreadMain(argc,argv);
return 0 ;
}
总结:
1.到此,我们的数据库引擎和客户端的交互雏形已经建立起来。
2.数据库引擎服务器启动的大致过程为:读取命令行参数并解析,如果命令行参数中指定了配置文件路径则将解析配置文件。如果配置文件和命令行中的配置项参数不同,则以命令行的输入为准;如果一个配置项在配置文件和命令行都没有设置,则自动启用默认参数。然后以解析得到的参数来初始化内核控制块(也就是一个存放数据库所有信息的全局对象),然后启动监听线程。(当然,你可以猜到,后续当一个客户端的连接请求到来时,监听线程会从线程池去拿一个线程来执行代理线程的代码,并且处理这个客户端的请求)。
3.下一个博文将完成diydb的线程池的实现,届时,一个完整的服务器框架就搭建起来了(虽然具体的数据功能还没有实现,但可以运行服务器和客户端,并调试代码,看到成果的时候到了)。