C - 多进程内存映射
C - Memory Mapping with Multiple Processes
我有一项作业要求我编写一个 multi-processed 程序,该程序处理包含一串字符的 memory-mapped 文件。 parent 进程将文件映射到内存后,它会派生 2 个 children 进程来修改文件。 Child 1 输出文件内容,将文件内容转换为大写形式,然后输出文件的新内容。 Child 2 等待 1 秒让 child 1 完成,输出文件的内容,删除任何连字符“-”字符,然后输出文件的新内容。我对这两个 child 进程的问题是,在第一次显示文件内容后,进程试图修改文件的内容,但是 child 都没有输出文件的新内容。 运行 或编译时我没有收到任何错误,所以我无法找出问题所在。当然,我是内存映射的新手,所以请随时告诉我我做错了什么。这是我的源代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
int main (int argc, char *argv[]) {
struct stat buf;
int fd, length, status, i, j, k;
char *mm_file;
char *string = "this is a lowercase-sentence.";
length = strlen(string);
fd = open(argv[1], O_CREAT | O_RDWR, 0666); //Creates file with name given at command line
write(fd, string, strlen(string)); //Writes the string to be modified to the file
fstat(fd, &buf); //used to determine the size of the file
//Establishes the mapping
if ((mm_file = mmap(0, (size_t) buf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == (caddr_t) - 1) {
fprintf(stderr, "mmap call fails\n");
}
//Initializes child processes
pid_t MC0;
pid_t MC1;
//Creates series of child processes which share the same parent ID
if((MC0 = fork()) == 0) {
printf("Child 1 %d reads: \n %s\n", getpid(), mm_file);
//{convert file content to uppercase string};
for (i = 0; i < length; i++) {
string[i] = toupper(string[i]);
}
//sync the new contents to the file
msync(0, (size_t) buf.st_size, MS_SYNC);
printf("Child 1 %d reads again: \n %s\n", getpid(), mm_file);
exit(EXIT_SUCCESS); //Exits process
} else if ((MC1 = fork()) == 0) {
sleep(1); //so that child 2 will perform its task after child 1 finishes
("Child 2 %d reads: \n %s\n", getpid(), mm_file);
//{remove hyphens}
for (j = 0; j < length; i++) {
if (string[i] == '-') {
string[i] = ' ';
}
}
//sync the new contents to the file
msync(0, (size_t) buf.st_size, MS_SYNC);
printf("Child 2 %d reads again: \n %s\n", getpid(), mm_file);
exit(EXIT_SUCCESS); //Exits process
}
// Waits for all child processes to finish before continuing.
waitpid(MC0, &status, 0);
waitpid(MC1, &status, 0);
return 0;
}
那么我的输出结果如下:
**virtual-machine:~$** ./testt file
Child 1 3404 reads:
this is a lowercase-sentence.
Child 2 3405 reads:
this is a lowercase-sentence.
All child processes have finished. Now exiting program.
**virtual-machine:~$**
但我想要的结果是:
**virtual-machine:~$** ./testt file
Child 1 3404 reads:
this is a lowercase-sentence.
Child 1 3404 reads again:
THIS IS A LOWERCASE-SENTENCE.
Child 2 3405 reads:
THIS IS A LOWERCASE-SENTENCE.
Child 2 3405 reads:
THIS IS A LOWERCASE SENTENCE.
All child processes have finished. Now exiting program.
**virtual-machine:~$**
非常感谢任何帮助。
这里有一些错误。首先,您写入文件,然后将其映射到内存中。映射正确,但书写不正确。如果字符串有 n 个字符,则必须编写 n+1 个字符,因为 C 中的字符串以 null 结尾。现在你只有 n,所以所有的 C 字符串函数都会尝试访问至少一个字节,这是不好的。如果那个额外的字节不为空(零),函数将走得更远。在更多的调试中,它们可能会被归零,但在优化代码中通常不会。所以你必须使用
write(fd, string, strlen(string)+1); //Writes the string to be modified to the file
然后你这样做:
for (i = 0; i < length; i++) {
string[i] = toupper(string[i]);
}
这只会改变指针string
引用的数据,与内存映射文件无关。你应该有:
for (i = 0; i < length; i++) {
mm_file[i] = toupper(mm_file[i]);
}
第二个子进程也是如此。
您的 msync()
电话也有点可疑。您将内存地址指定为 0,这不在您的内存映射文件中,因此它不会同步内容。您需要致电 msync(mm_file, (size_t) buf.st_size, MS_SYNC);
此外,许多编译器会将常量字符串放入只读内存中,因此您甚至可能不允许更改 string
引用的数据。在这种情况下,您似乎被允许了。
还请记住,文件的长度比字符串的长度大一个字节,因此请正确使用变量。目前您这样做,因为您将文件与文件长度同步并使用字符串长度处理字符串。
你让内存映射妨碍了逻辑。
要获得此工作,请注释掉所有内存映射内容并只处理文件。
这将向您显示 child 都不会从输入文件中读取,更不用说向其中写入新内容了。
在某些操作系统下,Linux 是一个,如果您混合读取和写入,则需要在两者之间进行查找以将写入和读取指针保持在同一位置。这可能必须是 fseek(stream, 0,SEEK_CUR);
Child 应该是这样的
// Lock file here
rewind(file);
printf ("child 1 reads ");
int ch;
while(1){
ch = fgetc(file);
if(ch == EOF) break;
fputc (Ch,stdout );
}
fputc('\n',stdout );
Rewind(file);
while(1){
ch=fgetc (file);
if(Ch == EOF) break;
fseek(file,-1,SEEK_CUR);
fputc (toupper (Ch),file);
fseek(file,0,SEEK_CUR);
}
Rewind ( file);
Printf (" child 1 reads ");
while(1){
ch=fgetc(file);
if(Ch == EOF) break;
fputc (Ch,file) ;
}
// Unlock file here
因为您有多个进程作用于同一个 object,所以您必须实施写锁定(独占锁)。
阅读手册页 flock(2)、fcntl (2) 和 lockf(3)。
这些协作锁可以作为信号量实现。
如果没有锁定,两个 children 可能会尝试同时写入同一个字符,在这个例子中它应该无关紧要,因为一个 child 做连字符和其他字母。
现在可以取消对内存映射内容的注释。
我有一项作业要求我编写一个 multi-processed 程序,该程序处理包含一串字符的 memory-mapped 文件。 parent 进程将文件映射到内存后,它会派生 2 个 children 进程来修改文件。 Child 1 输出文件内容,将文件内容转换为大写形式,然后输出文件的新内容。 Child 2 等待 1 秒让 child 1 完成,输出文件的内容,删除任何连字符“-”字符,然后输出文件的新内容。我对这两个 child 进程的问题是,在第一次显示文件内容后,进程试图修改文件的内容,但是 child 都没有输出文件的新内容。 运行 或编译时我没有收到任何错误,所以我无法找出问题所在。当然,我是内存映射的新手,所以请随时告诉我我做错了什么。这是我的源代码:
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/mman.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <signal.h>
#include <string.h>
int main (int argc, char *argv[]) {
struct stat buf;
int fd, length, status, i, j, k;
char *mm_file;
char *string = "this is a lowercase-sentence.";
length = strlen(string);
fd = open(argv[1], O_CREAT | O_RDWR, 0666); //Creates file with name given at command line
write(fd, string, strlen(string)); //Writes the string to be modified to the file
fstat(fd, &buf); //used to determine the size of the file
//Establishes the mapping
if ((mm_file = mmap(0, (size_t) buf.st_size, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0)) == (caddr_t) - 1) {
fprintf(stderr, "mmap call fails\n");
}
//Initializes child processes
pid_t MC0;
pid_t MC1;
//Creates series of child processes which share the same parent ID
if((MC0 = fork()) == 0) {
printf("Child 1 %d reads: \n %s\n", getpid(), mm_file);
//{convert file content to uppercase string};
for (i = 0; i < length; i++) {
string[i] = toupper(string[i]);
}
//sync the new contents to the file
msync(0, (size_t) buf.st_size, MS_SYNC);
printf("Child 1 %d reads again: \n %s\n", getpid(), mm_file);
exit(EXIT_SUCCESS); //Exits process
} else if ((MC1 = fork()) == 0) {
sleep(1); //so that child 2 will perform its task after child 1 finishes
("Child 2 %d reads: \n %s\n", getpid(), mm_file);
//{remove hyphens}
for (j = 0; j < length; i++) {
if (string[i] == '-') {
string[i] = ' ';
}
}
//sync the new contents to the file
msync(0, (size_t) buf.st_size, MS_SYNC);
printf("Child 2 %d reads again: \n %s\n", getpid(), mm_file);
exit(EXIT_SUCCESS); //Exits process
}
// Waits for all child processes to finish before continuing.
waitpid(MC0, &status, 0);
waitpid(MC1, &status, 0);
return 0;
}
那么我的输出结果如下:
**virtual-machine:~$** ./testt file
Child 1 3404 reads:
this is a lowercase-sentence.
Child 2 3405 reads:
this is a lowercase-sentence.
All child processes have finished. Now exiting program.
**virtual-machine:~$**
但我想要的结果是:
**virtual-machine:~$** ./testt file
Child 1 3404 reads:
this is a lowercase-sentence.
Child 1 3404 reads again:
THIS IS A LOWERCASE-SENTENCE.
Child 2 3405 reads:
THIS IS A LOWERCASE-SENTENCE.
Child 2 3405 reads:
THIS IS A LOWERCASE SENTENCE.
All child processes have finished. Now exiting program.
**virtual-machine:~$**
非常感谢任何帮助。
这里有一些错误。首先,您写入文件,然后将其映射到内存中。映射正确,但书写不正确。如果字符串有 n 个字符,则必须编写 n+1 个字符,因为 C 中的字符串以 null 结尾。现在你只有 n,所以所有的 C 字符串函数都会尝试访问至少一个字节,这是不好的。如果那个额外的字节不为空(零),函数将走得更远。在更多的调试中,它们可能会被归零,但在优化代码中通常不会。所以你必须使用
write(fd, string, strlen(string)+1); //Writes the string to be modified to the file
然后你这样做:
for (i = 0; i < length; i++) {
string[i] = toupper(string[i]);
}
这只会改变指针string
引用的数据,与内存映射文件无关。你应该有:
for (i = 0; i < length; i++) {
mm_file[i] = toupper(mm_file[i]);
}
第二个子进程也是如此。
您的 msync()
电话也有点可疑。您将内存地址指定为 0,这不在您的内存映射文件中,因此它不会同步内容。您需要致电 msync(mm_file, (size_t) buf.st_size, MS_SYNC);
此外,许多编译器会将常量字符串放入只读内存中,因此您甚至可能不允许更改 string
引用的数据。在这种情况下,您似乎被允许了。
还请记住,文件的长度比字符串的长度大一个字节,因此请正确使用变量。目前您这样做,因为您将文件与文件长度同步并使用字符串长度处理字符串。
你让内存映射妨碍了逻辑。
要获得此工作,请注释掉所有内存映射内容并只处理文件。 这将向您显示 child 都不会从输入文件中读取,更不用说向其中写入新内容了。 在某些操作系统下,Linux 是一个,如果您混合读取和写入,则需要在两者之间进行查找以将写入和读取指针保持在同一位置。这可能必须是 fseek(stream, 0,SEEK_CUR);
Child 应该是这样的
// Lock file here
rewind(file);
printf ("child 1 reads ");
int ch;
while(1){
ch = fgetc(file);
if(ch == EOF) break;
fputc (Ch,stdout );
}
fputc('\n',stdout );
Rewind(file);
while(1){
ch=fgetc (file);
if(Ch == EOF) break;
fseek(file,-1,SEEK_CUR);
fputc (toupper (Ch),file);
fseek(file,0,SEEK_CUR);
}
Rewind ( file);
Printf (" child 1 reads ");
while(1){
ch=fgetc(file);
if(Ch == EOF) break;
fputc (Ch,file) ;
}
// Unlock file here
因为您有多个进程作用于同一个 object,所以您必须实施写锁定(独占锁)。 阅读手册页 flock(2)、fcntl (2) 和 lockf(3)。
这些协作锁可以作为信号量实现。 如果没有锁定,两个 children 可能会尝试同时写入同一个字符,在这个例子中它应该无关紧要,因为一个 child 做连字符和其他字母。
现在可以取消对内存映射内容的注释。