进程间文件描述符的实现
#include <unistd.h>
#include <sys/types.h>
#include <functional>
#include <memory.h>
#include <sys/socket.h>
class CFunctionBase
{
public:
virtual ~CFunctionBase() {}
virtual int operator()() = 0;
};
template<typename _FUNCTION_, typename...
_ARGS_>
class CFunction :public CFunctionBase
{
public:
CFunction(_FUNCTION_ func, _ARGS_... args)
:m_binder(std::forward<_FUNCTION_>
(func), std::forward<_ARGS_>(args)...)
{}
virtual ~CFunction() {}
virtual int operator()() {
return m_binder();
}
typename std::_Bindres_helper<int,
_FUNCTION_, _ARGS_...>::type m_binder;
};
class CProcess
{
public:
CProcess() {
m_func = NULL;
memset(pipes, 0, sizeof(pipes));
}
~CProcess() {
if (m_func != NULL) {
delete m_func;
m_func = NULL;
}
}
template<typename _FUNCTION_, typename...
_ARGS_>
int SetEntryFunction(_FUNCTION_ func,
_ARGS_... args)
{
m_func = new CFunction<_FUNCTION_,
_ARGS_...>(func, args...);
return 0;
}
int CreateSubProcess() {
if (m_func == NULL)return -1;
int ret = socketpair(AF_LOCAL,
SOCK_STREAM, 0, pipes);
if (ret == -1)return -2;
pid_t pid = fork();
if (pid == -1)return -3;
if (pid == 0) {
//子进程
close(pipes[1]);//关闭掉写
pipes[1] = 0;
return (*m_func)();
}
//主进程
close(pipes[0]);
pipes[0] = 0;
m_pid = pid;
return 0;
}
int SendFD(int fd) {//主进程完成
struct msghdr msg;
iovec iov[2];
iov[0].iov_base = (char*)"edoyun";
iov[0].iov_len = 7;
iov[1].iov_base = (char*)"jueding";
iov[1].iov_len = 8;
msg.msg_iov = iov;
msg.msg_iovlen = 2;
// 下面的数据,才是我们需要传递的。
cmsghdr* cmsg = (cmsghdr*)calloc(1,
CMSG_LEN(sizeof(int)));
if (cmsg == NULL)return -1;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
*(int*)CMSG_DATA(cmsg) = fd;
msg.msg_control = cmsg;
msg.msg_controllen = cmsg->cmsg_len;
ssize_t ret = sendmsg(pipes[1], &msg,
0);
free(cmsg);
if (ret == -1) {
return -2;
}
return 0;
}
int RecvFD(int& fd)
{
msghdr msg;
iovec iov[2];
char buf[][10] = { "","" };
iov[0].iov_base = buf[0];
iov[0].iov_len = sizeof(buf[0]);
iov[1].iov_base = buf[1];
iov[1].iov_len = sizeof(buf[1]);
msg.msg_iov = iov;
msg.msg_iovlen = 2;
cmsghdr* cmsg = (cmsghdr*)calloc(1,
CMSG_LEN(sizeof(int)));
if (cmsg == NULL)return -1;
cmsg->cmsg_len = CMSG_LEN(sizeof(int));
cmsg->cmsg_level = SOL_SOCKET;
cmsg->cmsg_type = SCM_RIGHTS;
msg.msg_control = cmsg;
msg.msg_controllen =
CMSG_LEN(sizeof(int));
ssize_t ret = recvmsg(pipes[0], &msg,
0);
if (ret == -1) {
free(cmsg);
return -2;
}
fd = *(int*)CMSG_DATA(cmsg);
return 0;
}
private:
CFunctionBase* m_func;
pid_t m_pid;
int pipes[2];
};
守护进程的实现
守护进程的流程
守护进程实现代码如下:
static int SwitchDeamon() {
pid_t ret = fork();
if (ret == -1)return -1;
if (ret > 0)exit(0);//主进程到此为止
//子进程内容如下
ret = setsid();
if (ret == -1)return -2;//失败,则返回
ret = fork();
if (ret == -1)return -3;
if (ret > 0)exit(0);//子进程到此为止
//孙进程的内容如下,进入守护状态
for (int i = 0; i < 3; i++) close(i);
umask(0);
signal(SIGCHLD, SIG_IGN);
return 0;
}
日志模块的设计
现在我们一开始就是多进程模式了,所以直接就可以上进程间通
信。
进程间通信,最方便最快速的就是本地套接字通信了。
文件通信磁盘速度慢
管道在多线程环境下不太方便(可能会出现内容插入)而且是单
向的。
信号量信息量太少
内存共享需要反复加锁同步,否则可能出现问题
消息函数(sendmsg、recvmsg)需要创建时确定
网络套接字通信,需要额外的IP和端口
所以本地套接字是最佳选择
无需IP和端口,不影响服务器对外的资源
信息无需加锁,可以多线程并发写
数据传输量巨大,传输速率高(纯内存读写)
日志模块的设计图
进程间通信的实现
本地套接字的封装
日志模块的实现
#pragma once
#include "Thread.h"
#include "Socket.h"
#include <list>
#include <sys/timeb.h>
#include <stdarg.h>
#include <sstream>
#include <sys/stat.h>
enum LogLevel {
LOG_INFO,
LOG_DEBUG,
LOG_WARNING,
LOG_ERROR,
LOG_FATAL
};
class LogInfo {
public:
LogInfo(
const char* file, int line, const char*
func,
pid_t pid, pthread_t tid, int level,
const char* fmt, ...);
LogInfo(
const char* file, int line, const char*
func,
pid_t pid, pthread_t tid, int level);
LogInfo(const char* file, int line, const
char* func,
pid_t pid, pthread_t tid, int level,
void* pData, size_t nSize);
~LogInfo();
operator Buffer()const {
return m_buf;
}
template<typename T>
LogInfo& operator<<(const T& data) {
std::stringstream stream;
stream << data;
m_buf += stream.str();
return *this;
}
private:
bool bAuto;//默认是false 流式日志,则为true
Buffer m_buf;
};
class CLoggerServer
{
public:
CLoggerServer() :
m_thread(&CLoggerServer::ThreadFunc,
this)
{
m_server = NULL;
m_path = "./log/" + GetTimeStr() +
".log";
printf("%s(%d):[%s]path=%s\n", __FILE__,
__LINE__, __FUNCTION__, (char*)m_path);
}
~CLoggerServer() {
Close();
}
public:
CLoggerServer(const CLoggerServer&) =
delete;
CLoggerServer& operator=(const
CLoggerServer&) = delete;
public:
//日志服务器的启动
int Start() {
if (m_server != NULL)return -1;
if (access("log", W_OK | R_OK) != 0) {
mkdir("log", S_IRUSR | S_IWUSR |
S_IRGRP | S_IWGRP | S_IROTH);
}
m_file = fopen(m_path, "w+");
if (m_file == NULL)return -2;
int ret = m_epoll.Create(1);
if (ret != 0)return -3;
m_server = new CLocalSocket();
if (m_server == NULL) {
Close();
return -4;
}
ret = m_server-
>Init(CSockParam("./log/server.sock",
(int)SOCK_ISSERVER));
if (ret != 0) {
Close();
return -5;
}
ret = m_thread.Start();
if (ret != 0) {
Close();
return -6;
}
return 0;
}
int ThreadFunc() {
EPEvents events;
std::map<int, CSocketBase*> mapClients;
while (m_thread.isValid() && (m_epoll !=
-1) && (m_server != NULL)) {
ssize_t ret =
m_epoll.WaitEvents(events, 1);
if (ret < 0)break;
if (ret > 0) {
ssize_t i = 0;
for (; i < ret; i++) {
if (events[i].events &
EPOLLERR) {
break;
}
else if (events[i].events &
EPOLLIN) {
if (events[i].data.ptr
== m_server) {
CSocketBase* pClient
= NULL;
int r = m_server-
>Link(&pClient);
if (r < 0) continue;
r =
m_epoll.Add(*pClient, EpollData((void*)pClient),
EPOLLIN | EPOLLERR);
if (r < 0) {
delete pClient;
continue;
}
auto it =
mapClients.find(*pClient);
if (it->second !=
NULL) {
delete it-
>second;
}
mapClients[*pClient]
= pClient;
}
else {
CSocketBase* pClient
= (CSocketBase*)events[i].data.ptr;
if (pClient != NULL)
{
Buffer data(1024
* 1024);
* int r = pClient-
>Recv(data);
if (r <= 0) {
delete
pClient;
mapClients[*pClient] = NULL;
}
else {
WriteLog(data);
}
}
}
}
}
if (i != ret) {
break;
}
}
}
for (auto it = mapClients.begin(); it !=
mapClients.end(); it++) {
if (it->second) {
delete it->second;
}
}
mapClients.clear();
return 0;
}
int Close() {
if (m_server != NULL) {
CSocketBase* p = m_server;
m_server = NULL;
delete p;
}
m_epoll.Close();
m_thread.Stop();
return 0;
}
//给其他非日志进程的进程和线程使用的
static void Trace(const LogInfo& info) {
static thread_local CLocalSocket client;
if (client == -1) {
int ret = 0;
ret =
client.Init(CSockParam("./log/server.sock", 0));
if (ret != 0) {
#ifdef _DEBUG
printf("%s(%d):[%s]ret=%d\n",
__FILE__, __LINE__, __FUNCTION__, ret);
#endif
return;
}
}
client.Send(info);
}
static Buffer GetTimeStr() {
Buffer result(128);
timeb tmb;
ftime(&tmb);
tm* pTm = localtime(&tmb.time);
int nSize = snprintf(result,
result.size(),
"%04d-%02d-%02d %02d-%02d-%02d
%03d",
pTm->tm_year + 1900, pTm->tm_mon +
1, pTm->tm_mday,
pTm->tm_hour, pTm->tm_min, pTm-
>tm_sec,
tmb.millitm
);
result.resize(nSize);
return result;
}
private:
void WriteLog(const Buffer& data) {
if (m_file != NULL) {
FILE* pFile = m_file;
fwrite((char*)data, 1, data.size(),
pFile);
fflush(pFile);
#ifdef _DEBUG
printf("%s", (char*)data);
#endif
}
}
private:
CThread m_thread;
CEpoll m_epoll;
CSocketBase* m_server;
Buffer m_path;
FILE* m_file;
};