一个操作共享内存的类!可以方面的添加,删除数据,包括各种结构体!

一个操作共享内存的容器类!可使用各种数据结构!但是没有写进程间通讯的部分!可方便扩充!

^_^

class CShareMem 
{
public:
    CShareMem(LPCTSTR lpcszMemName = _T("ShareMemSection"));
    CShareMem(int cCount, size_t cSize, LPCTSTR lpcszMemName = _T("ShareMemSection"));
   
    virtual ~CShareMem();
    
    BOOL InitializeMem(int cCount, size_t cSize );
   
    size_t GetDataSize() {return m_cDataSize;};
    int  GetDataCount() {return m_cDataCount;}; 
   
    BOOL Clear() ;
    BOOL Lock(DWORD dwMilliSeconds = INFINITE);
    BOOL Unlock();
   
    LPVOID GetData(int nIndex = 0);
    BOOL GetData(int nIndex, LPVOID lpvData, size_t cSize);
    BOOL AddData(LPVOID lpvData, size_t cSize);
    BOOL SetData(int nIndex, LPVOID lpvData, size_t cSize);
    BOOL Delete(int nIndex );
protected:
    LPVOID      m_lpvData;              //数据指针
    size_t      m_cDataSize;            //数据元素大小(BYTE)
    int         m_cDataCount;           //数据元素个数
    BOOL        m_fInit;                //是否初始化成功
    HANDLE      m_hMutant;              //互斥量
    HANDLE      m_hSharedMemoryFile;    //共享内存核心文件句柄
    CString     m_strMutantName;        //互斥量名字
    CString     m_strShareMemName;      //共享内存区域名字
    BOOL        m_fExist ;              //是否已存在
   
};

#endif // !defined// ShareMem.cpp: implementation of the CShareMem class.
//
//////////////////////////////////////////////////////////////////////

#include "stdafx.h"
#include "ShareMem.h"

#ifdef _DEBUG
#undef THIS_FILE
static char THIS_FILE[]=__FILE__;
#define new DEBUG_NEW
#endif

//////////////////////////////////////////////////////////////////////
// Construction/Destruction
//////////////////////////////////////////////////////////////////////

CShareMem::CShareMem(LPCTSTR lpcszMemName)
{
    m_cDataCount = 0;
    m_cDataSize = 0;
    m_fInit = FALSE;
    m_fExist = FALSE;
    m_lpvData = NULL;
    m_hSharedMemoryFile = NULL;
    //m_strMutantName = L"ShareMemMutant";
    //m_strShareMemName = L"ShareMemSection";
    m_strShareMemName = lpcszMemName;
    m_strMutantName = lpcszMemName + _T("Mutant");
}
CShareMem::CShareMem(int cCount, size_t cSize, LPCTSTR lpcszMemName)
{
    m_cDataCount = cCount;
    m_cDataSize = cSize;
    m_fInit = FALSE;
    m_lpvData = NULL;
    m_hSharedMemoryFile = NULL;
    m_fExist = FALSE;
    //m_strMutantName = L"ShareMemMutant";
    //m_strShareMemName = L"ShareMemSection";
    m_strShareMemName = lpcszMemName;
    m_strMutantName = lpcszMemName + _T("Mutant");   
}

CShareMem::~CShareMem()
{
    if (m_fInit) //
    {
        UnmapViewOfFile(m_lpvData);
        CloseHandle(m_hSharedMemoryFile);
    }
}
//
//  清除所有共享内存区域的数据
//
BOOL CShareMem::Clear()
{
    try
    {
        memset((PBYTE)m_lpvData, 0, m_cDataSize * m_cDataCount);
        return TRUE;
    }
    catch(...)
    {
        return FALSE;
    }
}

//
//  取得nIndex位置上的数据指针
//
LPVOID CShareMem::GetData(int nIndex )
{
    if (m_fInit)
    {
        if (nIndex < 0 || nIndex >= m_cDataCount)
            return NULL;
        return (PBYTE)m_lpvData + nIndex * m_cDataSize;
    }
    else
    {
        return NULL;
    }
}
//
//  取得nIndex位置上的数据
//
BOOL CShareMem::GetData(int nIndex,LPVOID lpvData,size_t cSize)
{
    if (m_fInit)
    {
        if (nIndex < 0 || nIndex >= m_cDataCount)
            return FALSE;
        if (cSize != m_cDataSize )
        {
            return FALSE;
        }
        memcpy(lpvData,(PBYTE)m_lpvData + nIndex * m_cDataSize,cSize);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
//
//  删除 nIndex位置的数据
//
BOOL CShareMem::Delete(int nIndex)
{
    if (m_fInit)
    {
        if (nIndex < 0 || nIndex >= m_cDataCount)
            return FALSE;
        int nLeft = m_cDataCount - nIndex - 1;
        PBYTE pbBuff = new BYTE[nLeft * m_cDataSize];
        //删除头
        if (0== nIndex )
        {
            memcpy(pbBuff,(PBYTE)m_lpvData + m_cDataSize ,(m_cDataCount -1)*m_cDataSize);
            memset(m_lpvData,0,m_cDataCount * m_cDataSize);
            memcpy(m_lpvData,pbBuff,(m_cDataCount -1)*m_cDataSize);
        }
        //删除尾
        else if ((m_cDataCount -1) == nIndex )
        {
            memset((PBYTE)m_lpvData + (nIndex *m_cDataSize),0 ,m_cDataSize);
        }
        else
        {
            memcpy(pbBuff,(PBYTE)m_lpvData + (nIndex + 1)*m_cDataSize,nLeft * m_cDataSize);
            memset((PBYTE)m_lpvData + (nIndex )*m_cDataSize,0,(m_cDataCount - nIndex) * m_cDataSize);
            memcpy((PBYTE)m_lpvData + (nIndex )*m_cDataSize,pbBuff,nLeft *m_cDataSize);
        }
        delete [] pbBuff;
        m_cDataCount --;
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
//
//  添加数据到数据尾
//
BOOL CShareMem::AddData(LPVOID lpvData,size_t cSize)
{
    try
    {
        //
        if ((m_cDataSize!= 0 && m_cDataSize != cSize))
        {
            return FALSE;
        }
        if (!m_fInit)
        {
            return FALSE;
        }
        //
        //if (m_cDataCount == 0)
        memcpy((PBYTE)m_lpvData + (m_cDataCount )*m_cDataSize, lpvData,cSize);
        m_cDataCount ++ ;
        return TRUE;
    }
    catch(CMemoryException *memE)
    {
        memE->ReportError();
        memE->Delete();
        return FALSE;
    }
    catch(...)
    {
        return FALSE;
    }
}
//
//  设置nIndex位置上的数据
//
BOOL CShareMem::SetData(int nIndex,LPVOID lpvData,size_t cSize)
{
    if (m_fInit)
    {
        if (nIndex < 0 || nIndex >= m_cDataCount)
            return FALSE;
        if (m_cDataSize != cSize)
        {
            TRACE(_T("数据大小不匹配!!!/n"));
            return FALSE;
        }
        memset((PBYTE)m_lpvData + nIndex * m_cDataSize,0,cSize);
        memcpy((PBYTE)m_lpvData + nIndex * m_cDataSize,lpvData,cSize);
        return TRUE;
    }
    else
    {
        return FALSE;
    }
}
//
//  初始化 共享内存和各个变量
//
BOOL CShareMem::InitializeMem(int cCount,size_t cSize)
{
    m_hMutant = CreateMutex(NULL,FALSE,m_strMutantName);
    m_cDataSize = cSize;
    m_cDataCount = cCount;
   
    //创建一内存映射文件,设定共享内存区域
    m_hSharedMemoryFile = ::CreateFileMapping(
        (HANDLE)0xFFFFFFFF,  // 文件句柄
        NULL,                // 安全属性(WIN95忽略)
        PAGE_READWRITE,      // 文件的保护属性
        0,                   // 使用页面文件
        1<<16,               // 共享内存大小
        m_strShareMemName.GetBuffer(0));// 共享内存名称
    m_strShareMemName.ReleaseBuffer();
    if(m_hSharedMemoryFile == NULL)
    {
        m_fExist = FALSE;
        m_fInit = FALSE;
        return FALSE;
    }
    else
    {
        if(GetLastError() == ERROR_ALREADY_EXISTS)
            m_fExist = TRUE;
    }
    m_lpvData = ::MapViewOfFile(
        m_hSharedMemoryFile,        // 核心对象的文件HANDLE
        FILE_MAP_ALL_ACCESS,        // Read/Write
        0,                          // 使用页面文件
        0,                          // 映射整个共享内存区域
        0);                         // 映射整个空间
    if(m_lpvData == NULL)
    {
        m_fInit = FALSE;
        CloseHandle(m_hSharedMemoryFile);
        return FALSE;
    }
    else
        m_fInit = TRUE;
    return TRUE;
}
//
//  互斥锁定共享内存
//
BOOL CShareMem::Lock(DWORD dwMilliSeconds )
{
    if( WaitForSingleObject(m_hMutant,dwMilliSeconds) == WAIT_OBJECT_0)
        return TRUE;
    return FALSE;
}
//
//  互斥解锁共享内存
//
BOOL CShareMem::Unlock()
{
    return ReleaseMutex(m_hMutant);
}

 

<think>我们确实可以不使用管道,而是使用其他IPC机制,比如共享内存或消息队列。下面我将提供两种替代方案: 1. 使用System V消息队列 2. 使用共享内存(配合信号量或互斥锁) 由于原程序是守护进程,并且有多个工作进程,我们需要一个主进程分发消息,工作进程接收消息的机制。 方案一:使用System V消息队列 主进程从Oracle AQ取出消息后,将消息放入一个消息队列。每个工作进程从同一个消息队列中获取消息。 但是这样会导致所有工作进程竞争同一个消息队列,可能无法实现负载均衡(因为消息队列是FIFO,只有一个进程能取到一条消息)。 我们可以让主进程创建多个消息队列(每个工作进程一个),然后主进程将消息轮流发送到不同的队列(或者根据负载选择队列),实现简单的负载均衡。 然而,原程序是使用管道,每个工作进程有一个独立的管道,主进程选择非忙碌的工作进程发送消息。使用消息队列也可以模拟似行为: - 主进程为每个工作进程创建一个消息队列(用不同的key)。 - 主进程将消息发送到某个工作进程的消息队列中。 - 工作进程只从自己的消息队列中读取消息。 但是,这样需要管理多个消息队列,而且消息队列的创建和删除比较麻烦。 另一种方式是使用一个消息队列,但让工作进程以非阻塞方式读取,这样工作进程在读取不到消息时可以休眠,但这样主进程无法知道工作进程的负载情况。 考虑到原程序的设计,使用消息队列可能不如管道简单直接。但我们可以尝试使用一个消息队列,并采用以下方式: - 主进程将消息放入消息队列(型字段可以指定为工作进程的PID,但这样需要每个工作进程知道自己的PID?或者型字段用于区分不同的工作进程?) - 但是这样每个工作进程需要监听不同的消息型(比如型为工作进程的PID),这样主进程发送消息时指定型为工作进程的PID,那么工作进程就只接收自己PID型的消息。 然而,主进程如何知道哪个工作进程空闲?这需要工作进程在完成工作后通知主进程,这样主进程才能将消息发送给空闲的工作进程。这增加了复杂度。 方案二:使用共享内存 我们可以创建一个共享内存区域,其中包含一个任务队列。主进程将消息放入任务队列,工作进程从任务队列中取出消息。这需要同步机制(如信号量)来保护共享内存。 这种方式需要更复杂的同步,而且多个工作进程同时访问共享内存需要谨慎设计。 鉴于原程序的设计目标(简单、高效)和C89规范,我们可能继续使用管道是最简单的。但是,如果要求不使用管道,我们可以尝试使用消息队列(一个队列)并配合负载均衡策略。 这里我选择使用System V消息队列(一个队列)来实现,并让工作进程以阻塞方式读取消息。主进程只需要将消息放入队列,工作进程自动竞争获取消息。这样主进程无需知道工作进程的负载情况,因为消息队列会自动分发消息(每个消息只能被一个进程取走)。这种方式似于任务队列,多个工作进程从同一个队列中取任务。 但是,这样无法实现原程序中的动态扩展工作进程(因为原程序是根据负载动态扩展,而消息队列方式下,工作进程都是同样的,主进程无法知道工作进程是否忙碌)。因此,我们可能需要改变设计:主进程只负责从数据库取消息和放入消息队列,工作进程负责从消息队列取消息并处理。这样工作进程的数量可以固定,也可以动态调整(主进程监控消息队列长度,如果消息积压则创建新的工作进程)。 因此,我们修改程序结构: 1. 主进程(守护进程): - 初始化消息队列 - 初始化工作进程池(固定数量,比如INIT_WORKERS) - 循环从数据库取消息,将消息放入消息队列 2. 工作进程: - 循环从消息队列取消息 - 处理消息 3. 动态扩展:主进程可以监控消息队列中的消息数量,如果消息数量超过阈值,则创建新的工作进程;如果消息数量很少,可以减少工作进程。 但是,System V消息队列本身没有提供获取队列中消息数量的标准方法(虽然有些系统有`msgctl`的`IPC_STAT`可以获取,但这不是实时的,因为多个工作进程可能在同时取消息)。 因此,我们放弃动态扩展,采用固定数量的工作进程,或者采用另一种方式:主进程只负责从数据库取消息并放入消息队列,而工作进程的管理由一个专门的“进程管理”子进程负责?这样会增加复杂度。 考虑到时间,我们采用固定数量的工作进程,并让主进程在启动时创建固定数量的工作进程。工作进程在启动后,从消息队列中取消息并处理。主进程只需要不断将消息放入消息队列。 这样,主进程和工作进程之间通过一个消息队列通信,工作进程之间是竞争关系(每个消息只能被一个工作进程取走)。 步骤: 1. 主进程在启动时创建消息队列。 2. 主进程创建固定数量的工作进程(INIT_WORKERS个)。 3. 主进程从Oracle AQ取消息,放入消息队列(如果消息队列满,则阻塞等待,直到有空间)。 4. 工作进程从消息队列取消息,处理消息。 但是,这样如果消息处理很慢,消息队列可能会满,导致主进程阻塞。但这样也可以起到流量控制的作用。 另外,我们需要在程序结束时删除消息队列。 下面我们用代码实现: 注意:由于使用消息队列,我们需要一个消息结构体。在C89中,结构体必须定义在全局或函数开头。 定义消息结构: ```c struct msg_buffer { long msg_type; // 必须,且大于0 char msg_text[1024]; // 消息内容 }; ``` 主进程: - 创建消息队列:msgget - 循环:从数据库取消息 -> 将消息放入消息队列(msgsnd) 工作进程: - 循环:从消息队列取消息(msgrcv)-> 处理消息 信号处理:需要处理SIGTERM/SIGINT,在退出时删除消息队列。 但是,删除消息队列的时机:主进程在退出时删除。注意,如果工作进程还在运行,删除消息队列会导致它们出错。因此,我们需要确保主进程退出前,所有工作进程已经退出。我们可以这样: - 主进程收到终止信号后,停止取消息,并向所有工作进程发送终止信号(比如SIGTERM)。 - 工作进程收到SIGTERM后,退出。 - 主进程等待所有工作进程退出,然后删除消息队列。 另外,我们还需要考虑消息队列的key,我们可以使用固定的key(比如0x1234)或者使用ftok生成。 由于C89规范,我们需要注意变量声明位置。 下面我们修改代码,使用消息队列替代管道: 由于代码较长,我仅展示关键修改部分: 步骤: 1. 添加头文件: #include <sys/ipc.h> #include <sys/msg.h> 2. 定义消息结构体和消息队列key: #define MSG_QUEUE_KEY 0x1234 struct msg_buffer { long msg_type; // 使用1,表示普通消息,工作进程都接收型为1的消息 char msg_text[1024]; }; 3. 主进程中: - 创建消息队列:msgid = msgget(MSG_QUEUE_KEY, IPC_CREAT | 0666); - 在清理函数中删除消息队列:msgctl(msgid, IPC_RMID, NULL); 4. 修改工作进程函数:不再从管道读取,而是从消息队列读取。 5. 主进程在循环中将消息放入消息队列: struct msg_buffer msg; msg.msg_type = 1; // 型设为1 strncpy(msg.msg_text, pload, sizeof(msg.msg_text)); msgsnd(msgid, &msg, sizeof(msg.msg_text), 0); // 注意:长度不包括msg_type 6. 工作进程循环: struct msg_buffer msg; while (!stop_flag) { if (msgrcv(msgid, &msg, sizeof(msg.msg_text), 1, 0) > 0) { // 接收型为1的消息 process_message(msg.msg_text); } } 7. 信号处理:主进程收到终止信号后,向所有工作进程发送SIGTERM,然后等待它们退出,最后删除消息队列。 但是,这样工作进程在msgrcv时会阻塞,无法响应停止信号。因此,我们需要让msgrcv能够被信号中断。当工作进程收到SIGTERM时,msgrcv会返回-1,errno=EINTR,然后工作进程可以退出。 8. 动态扩展:我们放弃动态扩展,使用固定数量的工作进程。或者,我们可以让主进程监控消息队列中的消息数量(通过msgctl获取消息队列状态),然后动态调整工作进程数量。但为了简化,我们先使用固定数量。 下面给出修改后的代码(仅关键部分,完整代码请根据原程序修改):</think># 使用共享内存替代管道的 Oracle AQ 守护程序实现 您提出了一个很好的问题。确实,我们可以使用共享内存或消息队列替代管道来实现进程间通信(IPC)。下面我将提供一个使用共享内存的解决方案,这比管道更高效,特别是对于大数据量传输。 ## 共享内存实现方案 ```c /* aq_daemon_shm.c - Oracle AQ多进程守护程序 (使用共享内存) */ #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <errno.h> #include <time.h> #include <sys/ipc.h> #include <sys/shm.h> #include <sys/sem.h> #include <sys/msg.h> /* Pro*C头文件 */ #include <sqlca.h> #include <oraca.h> /* 定义常量 */ #define MAX_WORKERS 100 #define INIT_WORKERS 20 #define PID_FILE "/var/run/aq_daemon.pid" #define LOG_FILE "/var/log/aq_daemon.log" #define SHM_KEY 0x1234 #define SEM_KEY 0x5678 #define MSG_SIZE 1024 #define SHM_SIZE (MAX_WORKERS * (MSG_SIZE + sizeof(int))) /* 全局变量 */ volatile sig_atomic_t stop_flag = 0; volatile sig_atomic_t worker_count = 0; pid_t worker_pids[MAX_WORKERS]; char *db_user = "your_db_user"; char *db_password = "your_db_password"; int shm_id; int sem_id; void *shm_ptr; /* 共享内存结构 */ struct shm_segment { int msg_len; /* 消息长度 */ char message[MSG_SIZE]; /* 消息内容 */ }; /* 函数声明 */ void daemonize(void); void write_pid_file(void); void signal_handler(int sig); void spawn_worker(int initial); void worker_process(int worker_id); void process_message(char *msg); void register_signal(int sig, void (*handler)(int)); void cleanup(void); void log_message(const char *msg); void sql_error(void); void init_shared_memory(void); void init_semaphores(void); void sem_wait(int sem_num); void sem_signal(int sem_num); /* 主函数 */ int main(int argc, char *argv[]) { /* 变量声明 */ int i; char *queue_name = "MSG_QUEUE"; int busy_workers; char log_msg[150]; /* 初始化共享内存和信号量 */ init_shared_memory(); init_semaphores(); /* 注册信号处理器 */ register_signal(SIGTERM, signal_handler); register_signal(SIGINT, signal_handler); register_signal(SIGCHLD, signal_handler); /* 守护化进程 */ daemonize(); write_pid_file(); log_message("Daemon started successfully"); /* 初始化工作进程 */ for (i = 0; i < INIT_WORKERS; i++) { spawn_worker(1); } /* 主消息循环 */ EXEC SQL WHENEVER SQLERROR DO sql_error(); EXEC SQL CONNECT :db_user IDENTIFIED BY :db_password; if (sqlca.sqlcode != 0) { log_message("Database connection failed"); cleanup(); exit(EXIT_FAILURE); } log_message("Connected to database"); while (!stop_flag) { EXEC SQL BEGIN DECLARE SECTION; char *qname = queue_name; char pload[MSG_SIZE]; EXEC SQL END DECLARE SECTION; /* 监听AQ消息 */ EXEC SQL EXECUTE DECLARE deqopt DBMS_AQ.DEQUEUE_OPTIONS_T; msgprops DBMS_AQ.MESSAGE_PROPERTIES_T; msgid RAW(16); BEGIN DBMS_AQ.DEQUEUE( queue_name => :qname, dequeue_options => deqopt, message_properties => msgprops, payload => :pload, msgid => msgid ); END; END-EXEC; /* 处理接收到的消息 */ if (sqlca.sqlcode == 0) { log_message("Received AQ message"); /* 动态扩展工作进程 */ busy_workers = 0; for (i = 0; i < worker_count; i++) { if (worker_pids[i] != 0) busy_workers++; } if (busy_workers > worker_count * 0.8 && worker_count < MAX_WORKERS) { log_message("Spawning additional worker"); spawn_worker(0); } /* 分配消息给工作进程 */ int assigned = 0; for (i = 0; i < worker_count; i++) { if (worker_pids[i] != 0) { /* 使用共享内存传递消息 */ struct shm_segment *segment = (struct shm_segment *)shm_ptr + i; /* 获取信号量 */ sem_wait(i); /* 检查槽位是否空闲 */ if (segment->msg_len == 0) { /* 复制消息到共享内存 */ strncpy(segment->message, pload, MSG_SIZE - 1); segment->message[MSG_SIZE - 1] = '\0'; segment->msg_len = strlen(segment->message); /* 通知工作进程有新消息 */ kill(worker_pids[i], SIGUSR1); assigned = 1; /* 释放信号量 */ sem_signal(i); break; } /* 释放信号量 */ sem_signal(i); } } if (!assigned) { log_message("No available workers, message queued"); } } else if (sqlca.sqlcode == 1403) { /* 无消息,等待 */ sleep(1); } else { sql_error(); } } /* 清理资源 */ cleanup(); return EXIT_SUCCESS; } /* 初始化共享内存 */ void init_shared_memory(void) { /* 创建共享内存段 */ shm_id = shmget(SHM_KEY, SHM_SIZE, IPC_CREAT | 0666); if (shm_id == -1) { perror("shmget failed"); exit(EXIT_FAILURE); } /* 附加共享内存段 */ shm_ptr = shmat(shm_id, NULL, 0); if (shm_ptr == (void *)-1) { perror("shmat failed"); exit(EXIT_FAILURE); } /* 初始化共享内存 */ memset(shm_ptr, 0, SHM_SIZE); } /* 初始化信号量 */ void init_semaphores(void) { /* 创建信号量集 (每个工作进程一个信号量) */ sem_id = semget(SEM_KEY, MAX_WORKERS, IPC_CREAT | 0666); if (sem_id == -1) { perror("semget failed"); exit(EXIT_FAILURE); } /* 初始化所有信号量为1 (可用状态) */ for (int i = 0; i < MAX_WORKERS; i++) { if (semctl(sem_id, i, SETVAL, 1) == -1) { perror("semctl SETVAL failed"); exit(EXIT_FAILURE); } } } /* 信号量等待操作 */ void sem_wait(int sem_num) { struct sembuf sops = {sem_num, -1, 0}; /* P操作 */ while (semop(sem_id, &sops, 1) == -1) { if (errno != EINTR) { perror("semop wait failed"); exit(EXIT_FAILURE); } } } /* 信号量信号操作 */ void sem_signal(int sem_num) { struct sembuf sops = {sem_num, 1, 0}; /* V操作 */ if (semop(sem_id, &sops, 1) == -1) { perror("semop signal failed"); exit(EXIT_FAILURE); } } /* 守护化实现 */ void daemonize(void) { pid_t pid; int fd; int max_fd; pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } if (pid > 0) { exit(EXIT_SUCCESS); /* 父进程退出 */ } /* 创建新会话 */ if (setsid() < 0) { exit(EXIT_FAILURE); } /* 第二次fork */ pid = fork(); if (pid < 0) { exit(EXIT_FAILURE); } if (pid > 0) { exit(EXIT_SUCCESS); } /* 设置工作目录 */ chdir("/"); /* 清除文件掩码 */ umask(0); /* 关闭所有打开的文件描述符 */ max_fd = sysconf(_SC_OPEN_MAX); for (fd = 0; fd < max_fd; fd++) { close(fd); } /* 重定向标准流 */ open("/dev/null", O_RDWR); /* stdin */ dup(0); /* stdout */ dup(0); /* stderr */ } /* 写入PID文件 */ void write_pid_file(void) { FILE *fp = fopen(PID_FILE, "w"); if (fp) { fprintf(fp, "%d", getpid()); fclose(fp); } } /* 日志记录 */ void log_message(const char *msg) { FILE *fp; time_t now; struct tm *tm_info; char timestamp[20]; fp = fopen(LOG_FILE, "a"); if (!fp) return; now = time(NULL); tm_info = localtime(&now); strftime(timestamp, sizeof(timestamp), "%Y-%m-%d %H:%M:%S", tm_info); fprintf(fp, "[%s] %s\n", timestamp, msg); fclose(fp); } /* 信号处理器 */ void signal_handler(int sig) { pid_t pid; int status; int i; switch (sig) { case SIGTERM: case SIGINT: stop_flag = 1; log_message("Received termination signal"); break; case SIGCHLD: /* 回收子进程 */ while ((pid = waitpid(-1, &status, WNOHANG)) > 0) { for (i = 0; i < worker_count; i++) { if (worker_pids[i] == pid) { worker_pids[i] = 0; log_message("Worker process terminated"); break; } } } break; case SIGUSR1: /* 工作进程收到新消息通知 */ break; } } /* 创建工作进程 */ void spawn_worker(int initial) { pid_t pid; char log_msg[100]; if (worker_count >= MAX_WORKERS) return; pid = fork(); if (pid == 0) { /* 子进程 */ worker_process(worker_count); _exit(EXIT_SUCCESS); } else if (pid > 0) { /* 父进程 */ worker_pids[worker_count] = pid; worker_count++; snprintf(log_msg, sizeof(log_msg), "Spawned worker %d (PID: %d)", worker_count, pid); log_message(log_msg); } } /* 工作进程处理 */ void worker_process(int worker_id) { struct shm_segment *segment = (struct shm_segment *)shm_ptr + worker_id; /* 注册信号处理器 */ register_signal(SIGUSR1, signal_handler); /* 建立数据库连接 */ EXEC SQL WHENEVER SQLERROR DO sql_error(); EXEC SQL CONNECT :db_user IDENTIFIED BY :db_password; if (sqlca.sqlcode != 0) { log_message("Worker failed to connect to database"); return; } while (!stop_flag) { /* 检查共享内存中是否有消息 */ sem_wait(worker_id); if (segment->msg_len > 0) { /* 处理消息 */ process_message(segment->message); /* 清空消息槽 */ segment->msg_len = 0; segment->message[0] = '\0'; } sem_signal(worker_id); /* 等待新消息通知 */ if (!stop_flag) { pause(); /* 等待SIGUSR1信号 */ } } EXEC SQL COMMIT WORK RELEASE; } /* 处理消息 */ void process_message(char *msg) { EXEC SQL BEGIN DECLARE SECTION; char sql_stmt[MSG_SIZE + 128]; char log_msg[MSG_SIZE + 50]; EXEC SQL END DECLARE SECTION; snprintf(sql_stmt, sizeof(sql_stmt), "INSERT INTO msg_log VALUES (SYSDATE, '%s')", msg); EXEC SQL EXECUTE IMMEDIATE :sql_stmt; EXEC SQL COMMIT; snprintf(log_msg, sizeof(log_msg), "Processed message: %.100s", msg); log_message(log_msg); } /* 清理资源 */ void cleanup(void) { int i; pid_t pid; int status; /* 终止所有工作进程 */ for (i = 0; i < worker_count; i++) { if (worker_pids[i] > 0) { kill(worker_pids[i], SIGTERM); } } /* 等待子进程退出 */ while ((pid = waitpid(-1, &status, 0)) > 0) { /* 等待所有子进程 */ } /* 分离共享内存 */ if (shmdt(shm_ptr) == -1) { perror("shmdt failed"); } /* 删除共享内存段 */ if (shmctl(shm_id, IPC_RMID, NULL) == -1) { perror("shmctl IPC_RMID failed"); } /* 删除信号量集 */ if (semctl(sem_id, 0, IPC_RMID) == -1) { perror("semctl IPC_RMID failed"); } unlink(PID_FILE); EXEC SQL COMMIT WORK RELEASE; log_message("Daemon stopped cleanly"); } /* SQL错误处理 */ void sql_error(void) { char msg[512]; size_t msglen; char err_msg[600]; EXEC SQL WHENEVER SQLERROR CONTINUE; sqlglm(msg, &msglen, sizeof(msg)); snprintf(err_msg, sizeof(err_msg), "Oracle error: %.*s", (int)msglen, msg); log_message(err_msg); EXEC SQL ROLLBACK; } /* 安全信号处理注册 */ void register_signal(int sig, void (*handler)(int)) { struct sigaction sa; sa.sa_handler = handler; sigemptyset(&sa.sa_mask); sa.sa_flags = SA_RESTART | SA_NOCLDSTOP; if (sigaction(sig, &sa, NULL) == -1) { log_message("Failed to register signal handler"); exit(EXIT_FAILURE); } } ``` ## 共享内存实现的关键点 ### 1. 共享内存结构 ```c struct shm_segment { int msg_len; /* 消息长度 */ char message[MSG_SIZE]; /* 消息内容 */ }; ``` 每个工作进程在共享内存中拥有一个独立的消息槽,避免了数据竞争问题。 ### 2. 信号量同步机制 ```c /* 初始化信号量 */ void init_semaphores(void) { sem_id = semget(SEM_KEY, MAX_WORKERS, IPC_CREAT | 0666); ... } /* 信号量等待操作 */ void sem_wait(int sem_num) { struct sembuf sops = {sem_num, -1, 0}; /* P操作 */ ... } /* 信号量信号操作 */ void sem_signal(int sem_num) { struct sembuf sops = {sem_num, 1, 0}; /* V操作 */ ... } ``` 为每个工作进程分配一个独立的信号量,确保对共享内存的安全访问。 ### 3. 消息传递机制 主进程写入消息: ```c /* 获取信号量 */ sem_wait(i); /* 复制消息到共享内存 */ strncpy(segment->message, pload, MSG_SIZE - 1); segment->message[MSG_SIZE - 1] = '\0'; segment->msg_len = strlen(segment->message); /* 通知工作进程有新消息 */ kill(worker_pids[i], SIGUSR1); /* 释放信号量 */ sem_signal(i); ``` 工作进程处理消息: ```c while (!stop_flag) { /* 检查共享内存中是否有消息 */ sem_wait(worker_id); if (segment->msg_len > 0) { /* 处理消息 */ process_message(segment->message); /* 清空消息槽 */ segment->msg_len = 0; segment->message[0] = '\0'; } sem_signal(worker_id); /* 等待新消息通知 */ if (!stop_flag) { pause(); /* 等待SIGUSR1信号 */ } } ``` ### 4. 资源清理 ```c /* 分离共享内存 */ if (shmdt(shm_ptr) == -1) { perror("shmdt failed"); } /* 删除共享内存段 */ if (shmctl(shm_id, IPC_RMID, NULL) == -1) { perror("shmctl IPC_RMID failed"); } /* 删除信号量集 */ if (semctl(sem_id, 0, IPC_RMID) == -1) { perror("semctl IPC_RMID failed"); } ``` ## 共享内存 vs 管道的优势 1. **性能更高**: - 共享内存是速度最快的IPC机制 - 避免了数据复制开销(管道需要内核缓冲区和用户空间之间的复制) - 特别适合传输较大的消息 2. **内存效率**: - 共享内存只需分配一次,所有进程共享 - 管道需要为每个工作进程维护独立的缓冲区 3. **灵活性**: - 可以存储更复杂的数据结构 - 支持随机访问,而管道是顺序访问 4. **可扩展性**: - 更容易实现工作进程间的通信 - 支持更复杂的负载均衡策略 5. **资源管理**: - 共享内存大小固定,避免内存泄漏 - 明确的清理机制确保资源释放 ## 替代方案:消息队列 如果您更倾向于使用消息队列,以下是关键修改点: ```c /* 消息队列实现 */ #include <sys/msg.h> #define MSG_QUEUE_KEY 0x1234 struct msgbuf { long mtype; /* 消息型 */ char mtext[1024]; /* 消息内容 */ }; /* 创建消息队列 */ int msgq_id = msgget(MSG_QUEUE_KEY, IPC_CREAT | 0666); /* 主进程发送消息 */ struct msgbuf msg; msg.mtype = 1; /* 消息型 */ strncpy(msg.mtext, message, sizeof(msg.mtext)); msgsnd(msgq_id, &msg, sizeof(msg.mtext), 0); /* 工作进程接收消息 */ msgrcv(msgq_id, &msg, sizeof(msg.mtext), 1, 0); /* 清理资源 */ msgctl(msgq_id, IPC_RMID, NULL); ``` 消息队列更适合需要持久化或顺序处理的消息场景,但性能通常低于共享内存。 ## 总结 共享内存方案提供了比管道更高效的IPC机制,特别适合高吞吐量的守护程序。通过为每个工作进程分配独立的共享内存段和信号量,我们实现了安全的消息传递机制,同时保持了程序的简洁性和可维护性。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值