2015-04-28 42 views
0

我有一項任務,要求我編寫一個多處理程序,該程序可與包含字符串的內存映射文件一起使用。父進程將文件映射到內存後,它會生成2個子進程來修改該文件。子1輸出文件的內容,將文件的內容轉換爲大寫字母,然後輸出文件的新內容。孩子2等待1秒讓孩子1完成,輸出文件的內容,刪除任何連字符「 - 」字符,然後輸出文件的新內容。我的兩個子進程的問題是,在第一次顯示文件的內容之後,進程嘗試修改文件的內容,但子進程都不輸出文件的新內容。在運行或編譯時我沒有遇到任何錯誤,所以我無法找出問題所在。當然,我是內存映射的新手,所以請隨時讓我知道我做錯了什麼。下面是我的源代碼:C - 使用多個進程的內存映射

#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:~$** 

任何幫助是極大的讚賞。

+0

因爲您從不編輯內存映射文件。你只需要改變'string'變量,這是完全不同的。 –

+0

我的導師給了我一些我們程序中需要的代碼片段。我認爲msync函數是將字符串變量重新映射到內存的東西。我是否錯誤地使用了功能,或者我只是不理解它的使用? – Anthony

+0

'msync()'會同步內存映射的文件內容。您設置爲所需原始字符串的'string'變量,並且它始終指向那裏。您只需將其內容複製到文件中,但指針不會更改爲指向內存映射區域。 –

回答

1

這裏有一些錯誤。首先,你寫入文件,然後將其映射到內存中。映射是正確的,但寫作不是。如果字符串有n個字符,則必須編寫n + 1個字符,因爲C中的字符串以空字符結尾。現在你只有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所述的數據。在這種情況下,似乎你被允許。

還要記住,文件的長度比字符串的長度大一個字節,所以請正確使用這些變量。目前你這樣做,因爲你用文件長度同步文件並且處理字符串長度的字符串。

+0

啊非常感謝你,我認爲這是一個簡單的錯誤。整天盯着這段代碼剛剛殺死了我的大腦。另外我忘了在msync()調用中更改內存地址,所以我很高興你指出了這一點。我會upvote你的帖子,但我想我需要更多的聲譽:( – Anthony

+0

你可以只接受它作爲答案與刻度線標記 –

+0

好吧很酷的作品了太對不起第一次實際登錄到這個網站 – Anthony

0

你已經讓mem map阻擋了邏輯。

爲了得到這個工作評論出所有mem地圖的東西,只是在文件上工作。 這會告訴你,任何一個孩子都不會從輸入文件中讀取數據,也不會介意給它寫入新的內容。 在某些操作系統下,如果您正在混合讀取和寫入操作,那麼Linux就是一個操作系統,您需要在兩者之間進行尋找,以使寫指針和讀指針保持在同一位置。這可能必須是fseek(stream,0,SEEK_CUR);

兒童應該是這樣的

// 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 

因爲你必須採取行動,你必須執行寫鎖(排他鎖)在同一對象上的多個進程。 閱讀手冊頁flock(2),fcntl(2)和lockf(3)。

這些合作鎖可以實現爲信號量。 不用鎖定兩個孩子可能會嘗試同時寫入同一個字符,在這個例子中,它應該不重要,因爲一個孩子做連字符和其他字母。

現在它的工作取消你的mem地圖的東西。

+0

我喜歡鎖的方法,我已經學會了信號量,所以我可以使用這些信號。另外,對內存映射的評論也有幫助。謝謝! – Anthony