The parent process 如何感知共享内存的修改

huangapple go评论73阅读模式
英文:

How can the parent process perceive the modification of the shared memory

问题

您想在多进程服务器中使用mmap来写日志文件,但是如果一个进程修改了共享地址以指向新文件,其他进程无法感知这一修改。您希望父进程能够写入新的日志文件,而不是旧文件,以便感知子进程的修改。您的问题是:如何使父进程(或其他子进程)能够感知共享内存的修改,并写入新文件?

以下是您提供的代码的翻译部分:

typedef struct shared_mgs_s {
    log_atomic_t lock;
    int offset;
} shared_msg_t;
char *write_addr;

shared_msg_t *sm;
log_atomic_t *log_mutex_ptr;

#define FILE_SIZE 1 * 1024 * 1024

int map_log_file(char *file_name)
{
    // 创建日志文件并将其映射到共享内存
    int fd = -1;
    if ((fd = open(file_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) < 0)
    {
        printf("create %s failed %d\n", file_name, errno);
        return -1;
    }

    if (ftruncate(fd, FILE_SIZE) < 0)
    {
        printf("ftruncate %d failed", errno);
        return -1;
    }
    write_addr = (char *)mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    close(fd);
    sm->offset = 0;

    return 0;
}

void write_log(char *msg)
{
    memcpy(write_addr + sm->offset, msg, strlen(msg));
    sm->offset += strlen(msg);
}

int create_shared_info()
{
    sm = mmap(NULL, 64, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
    if (!sm)
        return -1;

    sm->offset = 0;
    sm->lock = 0;
    log_mutex_ptr = &(sm->lock);
}

int main()
{
    if (create_shared_info() < 0)
    {
        printf("create shared info failed\n");
        return -1;
    }

    if (map_log_file("./old.log") != 0)
    {
        printf("map old log failed\n");
        return -1;
    }
    if (0 == fork())
    {
        log_spinlock(log_mutex_ptr, 1, 2048);
        munmap(write_addr, FILE_SIZE);
        if (map_log_file("./new.log") != 0)
        {
            printf("create new log failed\n");
            return -1;
        }
        write_log("from child\n");
        log_unlock(log_mutex_ptr);
        exit(0);
    }
    else
    {
        usleep(10); // 确保子进程首先获取锁,只是用于测试使用新文件
        log_spinlock(log_mutex_ptr, 1, 2048);
        write_log("from parent\n");
        log_unlock(log_mutex_ptr);
    }

    return 0;
}

在这种情况下,父进程写入了old.log,这不是您想要的,您希望它写入新的日志文件。解决此问题的一种方法是在父进程中定期检查共享内存中的标志或状态,以判断是否需要切换到新文件,然后再进行写入。这需要一种协调机制来通知父进程发生了切换。

英文:

I want to write log files using mmap in multi-process server, the processes can write to the initial log file, but if one process modify the shared address to a new file, other processes can not perceive this. The codes are like this:

firstly: open a file and map it to shared memory;

secondly: child process writes something to the file

thirdly: child process open a new log and modify the shared memory to the new file

fourthly: parent process writes something to the log file. I want the parent process write to the new log, but it writes to the old file. This means the parent process does not know the modification of child process.

my question is : How can the parent(or other children) process perceive the modification of the shared memory, and write to the new file?

typedef struct shared_mgs_s {
log_atomic_t lock;
int          offset;    
} shared_msg_t;
char         *write_addr;
shared_msg_t    *sm;
log_atomic_t    *log_mutex_ptr;
#define FILE_SIZE 1 * 1024 * 1024
int map_log_file(char *file_name)
{
// create a log file and map it to shared memory
int fd = -1;
if ((fd = open(file_name, O_RDWR | O_CREAT | O_EXCL, S_IRUSR | S_IWUSR)) &lt; 0)
{
printf(&quot;create %s failed %d\n&quot;,file_name, errno);
return -1;
}
if (ftruncate(fd, FILE_SIZE) &lt; 0)
{
printf(&quot;ftruncate %d failed&quot;, errno);
return -1;
}
write_addr = (char*)mmap(NULL, FILE_SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
close(fd);
sm-&gt;offset = 0;
return 0;
}
void write_log(char *msg)
{    
memcpy(write_addr + sm-&gt;offset, msg, strlen(msg));
sm-&gt;offset += strlen(msg);
}
int create_shared_info()
{
sm = mmap(NULL, 64, PROT_READ | PROT_WRITE, MAP_SHARED | MAP_ANONYMOUS, -1, 0);
if (!sm) 
return -1;
sm-&gt;offset = 0;
sm-&gt;lock = 0;
log_mutex_ptr = &amp;(sm-&gt;lock);
}
int main()
{
if(create_shared_info() &lt; 0)
{
printf(&quot;create shared info failed\n&quot;);
return -1;
}
if(map_log_file(&quot;./old.log&quot;) != 0)
{
printf(&quot;map old log failed\n&quot;);
return -1;
}
if(0 == fork())
{
log_spinlock(log_mutex_ptr, 1, 2048);
munmap(write_addr, FILE_SIZE);
if(map_log_file(&quot;./new.log&quot;) != 0)
{
printf(&quot;create new log failed\n&quot;);
return -1;
}
write_log(&quot;from child\n&quot;);
log_unlock(log_mutex_ptr);
exit(0);
} else {
usleep(10); // make sure child process get lock firstly, just test use the new file
log_spinlock(log_mutex_ptr, 1, 2048);
write_log(&quot;from parent\n&quot;);
log_unlock(log_mutex_ptr);
}
return 0;
}

in the way, the parent writes to old.log, this is not what I want, I want it write to the new one.

答案1

得分: 0

> 但如果一个进程修改了共享地址为一个新文件,其他进程无法感知到这一点。

另一种解决方法:

  1. 让另一个进程使用类似于mv命令行实用程序或rename() POSIX函数更改日志文件名。例如,mv /log/file.log /log/file.log.1。正如您注意到的,这将意味着所有记录到/log/file.log的进程现在将记录到/log/file.log.1,这不是您想要的。

  2. 使用您的日志功能的每个进程都将具有一个信号处理程序(在历史上,Unix进程使用SIGHUP)。这将导致它重新打开其日志文件,从而通过简单的open()调用来获取新的日志文件。如果您的日志功能在内部完全格式化每条日志消息并使用带有设置了O_APPEND标志的日志文件的单个write()调用,即使在多线程进程中,您也可以使用open("/log/file.log", O_WRITE | O_CREAT | O_APPEND, ...)原子地替换活动日志文件描述符为临时文件描述符,然后使用dup2()替换日志文件描述符:dup2(tempLogFD, logFileFD);由于open()dup2()都是异步信号安全的,您可以在您的SIGHUP信号处理程序中执行这两个调用:

    // 需要包含头文件和错误检查
    int logFileFD;

    void sighupHandler(int sig)
    {
    int tempLogFD = open("/log/file.log", O_WRONLY | O_CREAT | O_APPEND, 0644);
    dup2(tempLogFD, logFileFD);
    close(tempLogFD);
    }

  3. 您可以使用Linux命令fuser -SIGHUP /log/file.log.1重命名的原始日志文件)向仍在写入/log/file.log.1的所有进程发送SIGHUP,它们将切换将其日志从/log/file.log.1写入到新的/log/file.log

请注意,您的进程之间不需要复杂的同步或任何共享资源。

英文:

> but if one process modify the shared address to a new file, other processes can not perceive this.

Another way to address this:

  1. Let another process change the log file name with something like the mv command-line utility or the rename() POSIX function. For example, mv /log/file.log /log/file.log.1. As you noticed, this will mean all the processes that were logging to /log/file.log will now be logging to /log/file.log.1, which you don't want.

  2. Each process using your logging functionality will have a signal handler (historically, Unix processes have used SIGHUP) that will cause it to reopen its log file, thus picking up the new log file with a simple open() call. If your log functionality is fully formatting each log message internally and using a single write() call with the log file opened with the O_APPEND flag set, even in a multithreaded process you can replace the active log file descriptor atomically with open( &quot;/log/file.log&quot;, O_WRITE | O_CREAT | O_APPEND, ...) to a temporary file descriptor and then you replace the log file descriptor with a call to dup2(): dup2( tempLogFD, logFileFD ); And since both open() and dup2() are async-signal-safe, you can do both calls in your SIGHUP signal handler:

    // needs headers and error-checking
    int logFileFD;
    void sighupHandler( int sig )
    {
    int tempLogFD = open( &quot;/log/file.log&quot;, O_WRONLY | O_CREAT | O_APPEND,
    0644 );
    dup2( tempLogFD, logFileFD );
    close( tempLogFD );
    }
    
  3. You can use the Linux command fuser -SIGHUP /log/file.log.1 (the renamed original log file) to send SIGHUP to all the processes that are still writing to /log/file.log.1 and they will swap writing their logs from /log/file.log.1 to the new /log/file.log.

Note that there's no complex synchronization nor any shared resources needed between your processes.

huangapple
  • 本文由 发表于 2023年5月11日 13:54:05
  • 转载请务必保留本文链接:https://go.coder-hub.com/76224520.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定