首页
学习
活动
专区
圈层
工具
发布
社区首页 >问答首页 >具有多进程的C-内存映射

具有多进程的C-内存映射
EN

Stack Overflow用户
提问于 2015-04-28 05:18:46
回答 2查看 1.7K关注 0票数 0

我有一个任务,要求我写一个多处理的程序,它与一个包含一个字符串的内存映射文件一起工作。父进程将文件映射到内存后,它会生成2个子进程来修改该文件。子1输出文件的内容,将文件的内容转换为大写等效的内容,然后输出文件的新内容。子2等待1秒让子1完成,输出文件的内容,删除任何连字符“-”字符,然后输出文件的新内容。两个子进程的问题是,在第一次显示文件的内容之后,进程试图修改文件的内容,但是两个子进程都没有输出文件的新内容。我在运行或编译时没有错误,所以我无法找出问题所在。当然,我对内存映射还不熟悉,所以可以让我知道我做错了什么。这是我的源代码:

代码语言:javascript
复制
#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;
}

,那么我的输出如下:

代码语言:javascript
复制
**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:~$**

,但是我想要的结果是:

代码语言:javascript
复制
**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:~$**

任何帮助都是非常感谢的。

EN

回答 2

Stack Overflow用户

回答已采纳

发布于 2015-04-28 05:34:24

这里有一些错误。首先,您写入文件,然后将其映射到内存中。映射是正确的,但书写不正确。如果字符串有n个字符,则必须编写n+1字符,因为C中的字符串以空结尾。现在您只有n,所以所有C字符串函数都将尝试访问至少一个字节,这是不好的。如果额外的一个字节不是null (零),函数就会更进一步。在调试更多时,它们可能是零的,但在优化的代码中通常不会。所以你必须用

代码语言:javascript
复制
write(fd, string, strlen(string)+1); //Writes the string to be modified to the file

然后你就这么做:

代码语言:javascript
复制
    for (i = 0; i < length; i++) {
        string[i] = toupper(string[i]);
    }

这只会更改指针string引用的数据,该指针与内存映射文件无关。你应该:

代码语言:javascript
复制
    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引用的数据。在这种情况下,你似乎被允许。

还请记住,文件的长度比字符串的长度大一个字节,所以正确使用变量。当前需要这样做,因为您用文件长度同步文件,用字符串长度处理字符串。

票数 1
EN

Stack Overflow用户

发布于 2015-04-28 06:17:02

你让我的地图妨碍了逻辑。

为了让这个有用的注释出来,所有的mem地图的东西,只是工作的文件。这将告诉您,两个子文件都不是从输入文件中读取的,更不用说向它写入新的内容了。在某些操作系统下,Linux就是其中之一--如果将读和写混合在一起,则需要在两者之间寻找,以使写指针和读指针保持在同一位置。这可能需要寻找(流,0,SEEK_CUR);

儿童一号应该像

代码语言:javascript
复制
// 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

因为在同一个对象上有多个进程,所以必须实现写锁(排他锁)。阅读手册页群(2)、fcntl (2)和lockf(3)。

这些协作锁可以作为信号量来实现。如果没有锁定,两个孩子可能会尝试同时写到同一个字符,在这个例子中,这不应该像一个孩子做连字符和其他字母一样重要。

现在它起作用了取消你的mem地图的东西。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/29910878

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档