英文:
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)) < 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); // make sure child process get lock firstly, just test use the new file
log_spinlock(log_mutex_ptr, 1, 2048);
write_log("from parent\n");
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
> 但如果一个进程修改了共享地址为一个新文件,其他进程无法感知到这一点。
另一种解决方法:
-
让另一个进程使用类似于
mv
命令行实用程序或rename()
POSIX函数更改日志文件名。例如,mv /log/file.log /log/file.log.1
。正如您注意到的,这将意味着所有记录到/log/file.log
的进程现在将记录到/log/file.log.1
,这不是您想要的。 -
使用您的日志功能的每个进程都将具有一个信号处理程序(在历史上,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);
} -
您可以使用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:
-
Let another process change the log file name with something like the
mv
command-line utility or therename()
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. -
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 simpleopen()
call. If your log functionality is fully formatting each log message internally and using a singlewrite()
call with the log file opened with theO_APPEND
flag set, even in a multithreaded process you can replace the active log file descriptor atomically withopen( "/log/file.log", O_WRITE | O_CREAT | O_APPEND, ...)
to a temporary file descriptor and then you replace the log file descriptor with a call todup2()
:dup2( tempLogFD, logFileFD );
And since bothopen()
anddup2()
are async-signal-safe, you can do both calls in yourSIGHUP
signal handler:// needs headers and error-checking int logFileFD; void sighupHandler( int sig ) { int tempLogFD = open( "/log/file.log", O_WRONLY | O_CREAT | O_APPEND, 0644 ); dup2( tempLogFD, logFileFD ); close( tempLogFD ); }
-
You can use the Linux command
fuser -SIGHUP /log/file.log.1
(the renamed original log file) to sendSIGHUP
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.
通过集体智慧和协作来改善编程学习和解决问题的方式。致力于成为全球开发者共同参与的知识库,让每个人都能够通过互相帮助和分享经验来进步。
评论