父级和子级访问相同的缓冲区内存,但接收不同的数据

Parent and Child access the same buffer memory but receive different data

提问人:Kenny Ynnek 提问时间:4/4/2023 最后编辑:Kenny Ynnek 更新时间:4/4/2023 访问量:83

问:

代码在做什么:(底部提供完整的代码) Parent(Producer) 进程将数组中的文件逐个读取,3 个字节 x 3 个字节并投入使用 , Child(Consumer) 进程从 中读取。 通过与 3 个互斥锁同步来确保读取和写入之间的相互排斥:和 。shared_buffer(3 bytes of memory size)fread()shared_buffer_full__empty__mutex_

但是,即使确保父级和子级访问位于同一地址的共享内存,它们接收的数据也不同。

数组中的第一个文本文件:

This is a test file of 8 words.

我的代码:(提取)

主要:

#include <stdio.h>
//a lot of other libraries
#include "wordCount.h" //wordCount function
#define BUFFER_SIZE 3 
char **file_paths;
int file_count = 0, word_count = 0;
int main(int argc, char **argv)
{
    int process_id; 
    file_paths = malloc(100 * sizeof(char *)); //supposed that we already have files inside
    int shmid, f,e,m;
    char *shared_buffer;
    shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666 | IPC_CREAT);
    shared_buffer = (char *)shmat(shmid, 0, 0);
    
    sem_t *_full_, *_empty_, *_mutex_;
    f = shmget(IPC_PRIVATE, sizeof(sem_t), 0666 | IPC_CREAT);
    e = shmget(IPC_PRIVATE, sizeof(sem_t), 0666 | IPC_CREAT); 
    m = shmget(IPC_PRIVATE, sizeof(sem_t), 0666 | IPC_CREAT);
    _full_ = (sem_t *)shmat(f, 0, 0);
    _empty_ = (sem_t *)shmat(e, 0, 0);
    _mutex_ = (sem_t *)shmat(m, 0, 0);
    sem_init(_full_, 1, 0);
    sem_init(_empty_, 1, 1);
    sem_init(_mutex_, 1, 1); 
    int *read_file_count;
    int rKey = shmget(IPC_PRIVATE, sizeof(int), 0666 | IPC_CREAT);
    read_file_count = (int *)shmat(rKey, 0, 0);
    *read_file_count = 0;
    switch (process_id = fork())
    {

父母:

    default:
        printf("Parent process: My ID is %jd\n", (intmax_t)getpid());
        printf("address of the shared buffer in parent: %p\n", (void *)shared_buffer);
        printf("parent: address of full: %p; empty: %p; mutex: %p\n", (void *)_full_, (void *)_empty_, (void *)_mutex_);
        int value; sem_getvalue(_full_, &value);
        printf("Value of _full_ semaphore: %d\n", value);
        sem_getvalue(_empty_, &value);
        printf("Value of _empty_ semaphore: %d\n", value);
        sem_getvalue(_mutex_, &value);
        printf("Value of _mutex_ semaphore: %d\n", value);

        while(*read_file_count < file_count){ //LOOPING all the files in the array
        FILE *fp = fopen(file_paths[*read_file_count],"r");     
        if( fp == NULL ){
            printf("Fail to open file!\n");
            exit(0);
        }
            size_t total_read = 0;
            size_t num_read = 0;
            printf("++++++++++Reading FILE %d++++++++++\n", *read_file_count);
            while((num_read = fread(shared_buffer, sizeof(char), BUFFER_SIZE, fp)) > 0) { //LOOPing the same file
                sem_wait(_empty_); printf("Parent process got empty. ");
                sem_wait(_mutex_); printf("Parent process got mutex.\n");
                for (size_t i = 0; i < num_read; i++) {
                    printf("address of shared_buffer[%zu]: %p, ", i, (void *)(shared_buffer+i));
                    printf("shared_buffer[%zu] = [%c]\n", i, shared_buffer[i]);
                }
                printf("------------------------------\n");
                sem_post(_mutex_); printf("Parent process released mutex. "); 
                sem_post(_full_); printf("Parent process released full.\n");
            }
            (*read_file_count)++;               
        }
    printf("Parent process: Finished.\n");
    printf("Parent process: The total number of words is %d.\n", word_count);
    saveResult("p2_result.txt", word_count);
    break;

孩子:


    case 0: //child process
        printf("Child process: My ID is %jd\n", (intmax_t)getpid());
        printf("address of the shared buffer in child: %p\n", (void *)shared_buffer);
        printf("child: address of full: %p; empty: %p; mutex: %p\n", (void *)_full_, (void *)_empty_, (void *)_mutex_);
        sem_getvalue(_full_, &value); printf("Value of _full_ semaphore: %d\n", value);
        sem_getvalue(_empty_, &value); printf("Value of _empty_ semaphore: %d\n", value);
        sem_getvalue(_mutex_, &value); printf("Value of _mutex_ semaphore: %d\n", value);
        while(*read_file_count < file_count){
        sem_wait(_full_); printf("Child process got full. ");
        sem_wait(_mutex_); printf("Child process got mutex.\n");
        printf("Child:\n");
        for (size_t i = 0; i < 3; i++) {
            printf("address of shared_buffer[%zu]: %p, ", i, (void *)(shared_buffer+i));
            printf("shared_buffer[%zu] = [%c]\n", i, shared_buffer[i]);
        }
        printf("previous word count: %d\n", word_count);
        word_count += wordCount(shared_buffer);
        printf("child counted words : %d\n", word_count);
        printf("------------------------------\n");
        sem_post(_mutex_); printf("Child process released mutex. ");
        sem_post(_empty_); printf("Child process released empty.\n");
        }
        printf("Child process: Finished.\n");
        exit(0);

输出:

address of the shared buffer in parent: 0x7fc37dd11000
Child process: My ID is 3231821
parent: address of full: 0x7fc37dcd4000; empty: 0x7fc37dcd3000; mutex: 0x7fc37dcd2000
address of the shared buffer in child: 0x7fc37dd11000
child: address of full: 0x7fc37dcd4000; empty: 0x7fc37dcd3000; mutex: 0x7fc37dcd2000
Value of _full_ semaphore: 0
Value of _full_ semaphore: 0
Value of _empty_ semaphore: 1
Value of _empty_ semaphore: 1
Value of _mutex_ semaphore: 1
Value of _mutex_ semaphore: 1
++++++++++Reading FILE 0++++++++++
Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [T]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [h]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [i]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [s]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [i]
previous word count: 0
address of text[0]: 0x7fc37dd11000, text[0] = [s]
address of text[1]: 0x7fc37dd11001, text[1] = [ ]
address of text[2]: 0x7fc37dd11002, text[2] = [i]

child counted words : 2
------------------------------
Child process released mutex. Child process released empty.
Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [s]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [i]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [s]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [a]
previous word count: 2
address of text[0]: 0x7fc37dd11000, text[0] = [s]
address of text[1]: 0x7fc37dd11001, text[1] = [ ]
address of text[2]: 0x7fc37dd11002, text[2] = [a]

child counted words : 4
------------------------------
Child process released mutex. Child process released empty.
Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [s]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [a]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [ ]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [t]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [e]
previous word count: 4
address of text[0]: 0x7fc37dd11000, text[0] = [ ]
address of text[1]: 0x7fc37dd11001, text[1] = [t]
address of text[2]: 0x7fc37dd11002, text[2] = [e]

child counted words : 6
------------------------------
Child process released mutex. Child process released empty.
Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [ ]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [t]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [e]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [s]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [t]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [ ]
previous word count: 6
address of text[0]: 0x7fc37dd11000, text[0] = [s]
address of text[1]: 0x7fc37dd11001, text[1] = [t]
address of text[2]: 0x7fc37dd11002, text[2] = [ ]

child counted words : 8
------------------------------
Child process released mutex. Child process released empty.
Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [s]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [t]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [ ]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [f]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [i]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [l]
previous word count: 8
address of text[0]: 0x7fc37dd11000, text[0] = [f]
address of text[1]: 0x7fc37dd11001, text[1] = [i]
address of text[2]: 0x7fc37dd11002, text[2] = [l]

child counted words : 9
------------------------------
Child process released mutex. Child process released empty.
Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [f]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [i]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [l]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [e]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [o]
previous word count: 9
address of text[0]: 0x7fc37dd11000, text[0] = [e]
address of text[1]: 0x7fc37dd11001, text[1] = [ ]
address of text[2]: 0x7fc37dd11002, text[2] = [o]

child counted words : 11
------------------------------
Child process released mutex. Child process released empty.
Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [e]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [o]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [f]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [8]
previous word count: 11
address of text[0]: 0x7fc37dd11000, text[0] = [f]
address of text[1]: 0x7fc37dd11001, text[1] = [ ]
address of text[2]: 0x7fc37dd11002, text[2] = [8]
...

从输出中可以看出:

Parent process got empty. Parent process got mutex.
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [T]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [h]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [i]
------------------------------
Parent process released mutex. Parent process released full.
Child process got full. Child process got mutex.
Child:
address of shared_buffer[0]: 0x7fc37dd11000, shared_buffer[0] = [s]
address of shared_buffer[1]: 0x7fc37dd11001, shared_buffer[1] = [ ]
address of shared_buffer[2]: 0x7fc37dd11002, shared_buffer[2] = [i]

他们从字面上访问相同的内存,为什么输出不同,正如我所观察到的,孩子阅读的实际上是接下来的 3 个字符后面的 3 个字符,父母阅读的 3 个字符,为什么会发生这种情况?如何解决?

不要介意字数统计功能和打印出来的功能。这里的主要问题是为什么子项从父项读取不同的缓冲区数据。text[]

完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/wait.h>
#include <string.h>
#include <stdint.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
#include <pthread.h>
#include <sys/shm.h>
#include <errno.h>
#include <assert.h>
#include <dirent.h>

#include "helpers.h"
#define BUFFER_SIZE 3 

/**
 * @brief This function recursively traverse the source directory.
 *
 * @param dir_name : The source directory name.
 */

char **file_paths;
int file_count = 0;
int word_count = 0;

void traverseDir(char *dir_name);

int main(int argc, char **argv)
{
    int process_id; 
    file_paths = malloc(100 * sizeof(char *));

    
    
    char *dir_name = argv[1];

    if (argc < 2){
        printf("Main process: Please enter a source directory name.\nUsage: ./main <dir_name>\n");
        exit(-1);
    }

    traverseDir(dir_name);
    if (file_count == 0)
    {
        printf("Main process: No txt files found in the source directory.\n");
        exit(-1);
    }
    if (file_count > 100)
    {
        printf("Main process: The number of txt files is greater than 100.\n");
        exit(-1);
    }
    
    printf("Found %d files in the source directory.\n", file_count);
    for (int i = 0; i < file_count; i++)
    {
        printf("%s\n", file_paths[i]);
    }

    
    
    

    
    int shmid;
    int f,e,m;
    
    char *shared_buffer;
    shmid = shmget(IPC_PRIVATE, BUFFER_SIZE, 0666 | IPC_CREAT);
    shared_buffer = (char *)shmat(shmid, 0, 0);
    
    sem_t *_full_, *_empty_, *_mutex_;
    f = shmget(IPC_PRIVATE, sizeof(sem_t), 0666 | IPC_CREAT);
    e = shmget(IPC_PRIVATE, sizeof(sem_t), 0666 | IPC_CREAT); 
    m = shmget(IPC_PRIVATE, sizeof(sem_t), 0666 | IPC_CREAT);
    _full_ = (sem_t *)shmat(f, 0, 0);
    _empty_ = (sem_t *)shmat(e, 0, 0);
    _mutex_ = (sem_t *)shmat(m, 0, 0);
    sem_init(_full_, 1, 0); 
    sem_init(_empty_, 1, 1); 
    sem_init(_mutex_, 1, 1); 

    int *read_file_count;
    int rKey = shmget(IPC_PRIVATE, sizeof(int), 0666 | IPC_CREAT);
    read_file_count = (int *)shmat(rKey, 0, 0);
    *read_file_count = 0;
    switch (process_id = fork())
    {

    default:
    
        printf("Parent process: My ID is %jd\n", (intmax_t)getpid());
        
        
        
        
            printf("address of the shared buffer in parent: %p\n", (void *)shared_buffer);
            printf("parent: address of full: %p; empty: %p; mutex: %p\n", (void *)_full_, (void *)_empty_, (void *)_mutex_);
            int value;
            sem_getvalue(_full_, &value);
            printf("Value of _full_ semaphore: %d\n", value);

            sem_getvalue(_empty_, &value);
            printf("Value of _empty_ semaphore: %d\n", value);

            sem_getvalue(_mutex_, &value);
            printf("Value of _mutex_ semaphore: %d\n", value);

            while(*read_file_count < file_count){
            FILE *fp = fopen(file_paths[*read_file_count],"r");     
            if( fp == NULL ){
                printf("Fail to open file!\n");
                exit(0);
            }
                size_t total_read = 0;
                size_t num_read = 0;
                printf("++++++++++Reading FILE %d++++++++++\n", *read_file_count);
                while((num_read = fread(shared_buffer, sizeof(char), BUFFER_SIZE, fp)) > 0) {
                    sem_wait(_empty_); printf("Parent process got empty. ");
                    sem_wait(_mutex_); printf("Parent process got mutex.\n");
                    if(shared_buffer[0] == ' '){
                        word_count--;
                    } else if (shared_buffer[num_read] == ' '){
                        word_count--;
                    }
                    for (size_t i = 0; i < num_read; i++) {
                        printf("address of shared_buffer[%zu]: %p, ", i, (void *)(shared_buffer+i));
                        printf("shared_buffer[%zu] = [%c]\n", i, shared_buffer[i]);
                    }
                    printf("------------------------------\n");
                    sem_post(_mutex_); printf("Parent process released mutex. "); 
                    sem_post(_full_); printf("Parent process released full.\n");
                }
                (*read_file_count)++;               
            }
        printf("Parent process: Finished.\n");
        printf("Parent process: The total number of words is %d.\n", word_count);
        saveResult("p2_result.txt", word_count);
        break;

    case 0:
    

        printf("Child process: My ID is %jd\n", (intmax_t)getpid());
        printf("address of the shared buffer in child: %p\n", (void *)shared_buffer);
        printf("child: address of full: %p; empty: %p; mutex: %p\n", (void *)_full_, (void *)_empty_, (void *)_mutex_);
        sem_getvalue(_full_, &value);
        printf("Value of _full_ semaphore: %d\n", value);

        sem_getvalue(_empty_, &value);
        printf("Value of _empty_ semaphore: %d\n", value);

        sem_getvalue(_mutex_, &value);
        printf("Value of _mutex_ semaphore: %d\n", value);

        
        
        
        sleep(3);
        while(*read_file_count < file_count){
        sem_wait(_full_); printf("Child process got full. ");
        sem_wait(_mutex_); printf("Child process got mutex.\n");
        printf("Child:\n");
        for (size_t i = 0; i < 3; i++) {
            printf("address of shared_buffer[%zu]: %p, ", i, (void *)(shared_buffer+i));
            printf("shared_buffer[%zu] = [%c]\n", i, shared_buffer[i]);
        }
        printf("previous word count: %d\n", word_count);
        word_count += wordCount(shared_buffer);
        printf("child counted words : %d\n", word_count);
        printf("------------------------------\n");
        sem_post(_mutex_); printf("Child process released mutex. ");
        sem_post(_empty_); printf("Child process released empty.\n");
        }
        printf("Child process: Finished.\n");
        exit(0);

    case -1:
    
        printf("Fork failed!\n");
        exit(-1);
    }

    exit(0);
}

/**
 * @brief This function recursively traverse the source directory.
 *
 * @param dir_name : The source directory name.
 */
void traverseDir(char *dir_name)
{
    
    
    DIR *dir;
    struct dirent *ent;
    if ((dir = opendir(dir_name)) != NULL)
    {
    
        while ((ent = readdir(dir)) != NULL)
        {
            if (ent->d_type == DT_DIR)
            {
                
                if (strcmp(ent->d_name, ".") == 0 || strcmp(ent->d_name, "..") == 0)
                {
                    continue;
                }
                char *new_dir_name = malloc(strlen(dir_name) + strlen(ent->d_name) + 2);
                strcpy(new_dir_name, dir_name);
                strcat(new_dir_name, "/");
                strcat(new_dir_name, ent->d_name);
                traverseDir(new_dir_name);
            }
            else
            {
                
                if (strstr(ent->d_name, ".txt") != NULL)
                {
                    char *new_file_path = malloc(strlen(dir_name) + strlen(ent->d_name) + 2);
                    strcpy(new_file_path, dir_name);
                    strcat(new_file_path, "/");
                    strcat(new_file_path, ent->d_name);
                    
                    
                    file_paths[file_count] = new_file_path;
                    file_count++;
                }
            }
        }
        
        closedir(dir);
    }
    else
    {
    
        perror("");
        return;
    }
    }

助手:

int wordCount(char *text) {
    int counter = 0;
    int i=0;
    while(text[i] != '\0'){ 
        printf("address of text[%d]: %p, ", i, &text[i]);
        printf("text[%d] = [%c]\n", i, text[i]);
        if (text[i] == ' ' || text[i] == '\n'){
            counter++;
        }
        i++;
    }
    printf("\n");
    return counter + 1;
}

helper.h 只提供 wordCount 函数,可以忽略

C 文件 内存管理 进程 同步

评论

1赞 Jonathan Leffler 4/4/2023
你听说过“功能”吗?它们是值得写的好东西。您可以通过编写和使用自己的函数来使代码更易于阅读。
0赞 Jonathan Leffler 4/4/2023
您似乎没有错误地检查您调用的许多(任何)系统函数。这会导致人们担心是否报告了您忽略的错误。如果出现问题,添加检查是确保您知道哪里出了问题的第一步。(旁注:错误消息应写入 ,而不是 。这就是目的!stderrstdoutstderr
0赞 Kenny Ynnek 4/4/2023
很抱歉,我将尝试尽可能多地消除代码片段中不相关的内容,以使代码更易于查看。

答:

1赞 Sylvain Chiron 4/4/2023 #1

如果你想在两个程序之间完全共享内存,你可能应该使用线程而不是分支;请参阅:fork 和 thread 有什么区别?我认为没有理由在您的代码中使用分叉。

他们从字面上访问相同的内存,为什么输出不同

这是无关紧要的——是的,由于您的“共享内存操作”,它们访问相同的内存,但即使您只是使用(或自动数组)也会显示相同的地址。因此,显示的是两个进程(父进程和子进程)中的每一个都对这些值使用相同的内部地址,但实际上每个进程都有自己的内存映射,其中地址可能与其他内存映射中的地址相同,但这些值特定于进程。最重要的是,操作系统知道如何将进程的内部地址转换为内存中的物理地址。显然,现代操作系统会检查地址;否则,他们无法检测到分段故障,例如,任何进程都可以轻松更改另一个进程的数据。经过这样的检查后,操作系统会将进程的内部地址转换为物理地址(使用特定于进程的公式)。malloc

因此,您的进程可能会显示它们具有相同的内部地址,而与它们关联的值不同。您可以使用以下简单的程序来查看它:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>

int main()
{
   int myVar = 1, * myPtr = (int *)malloc(sizeof(*myPtr));
   *myPtr = 4;
   if (fork() == 0) {
      myVar = 3;
   } else {
      *myPtr = 2;
   }
   sleep(2);
   printf(" myVar: %p %d\n", &myVar, myVar);
   printf("*myPtr: %p %d\n", myPtr, *myPtr);
}

虽然每个进程通常会在两个进程中的任何一个访问指令之前更改其中一个值,但您会看到相同的地址显示不同的值;例如:printf

 myVar: 0x7fff663a0f5c 1
*myPtr: 0x55e1b084a2a0 2
 myVar: 0x7fff663a0f5c 3
*myPtr: 0x55e1b084a2a0 4

据我所知,孩子读的实际上是接下来的 3 个字符和父母读的 3 个字符,为什么会这样?

这是因为在你实际锁定(使用)互斥锁之前,父级会跳回包含 的循环的头部。因此,缓冲区获取接下来的 3 个字符,父级不使用它们,但这是子级进入其 for 循环时所拥有的。实际上,您的系统是不确定的,子系统在父系统读取接下来的 3 个字符之前打印缓冲区内容的可能性很小。fread(shared_buffer, sizeof(char), BUFFER_SIZE, fp)sem_wait

如何解决?

您应该小心地将互斥锁锁定和解锁在连续块中,中间没有其他内容(例如循环的条件部分):

  • 父母:
    while (*read_file_count < file_count) {
        FILE * fp = fopen(file_paths[*read_file_count], "r");
        if (fp == NULL) {
            fprintf(stderr, "Failed to open file %s!\n", file_paths[*read_file_count]);
        } else {
            while ((*num_read = fread(shared_buffer, sizeof(*shared_buffer), BUFFER_SIZE, fp)) > 0) {
                printf("[Parent] Buffer of size %d from %p contains: %.*s\n",
                    *num_read, shared_buffer, *num_read, shared_buffer);
                sem_post(_full_);
                sem_wait(_empty_);
            }
        }
        ++*read_file_count;
    }
    sem_post(_full_);
    
  • 孩子:
    sem_wait(_full_);
    while (*read_file_count < file_count) {
        printf("[Child]  Buffer of size %d from %p contains: %.*s\n",
            *num_read, shared_buffer, *num_read, shared_buffer);
        sem_post(_empty_);
        sem_wait(_full_);
    }
    

父级在开始工作之前没有理由等待或发布,因此在到达循环结束之前,它不应该接触任何互斥锁。事实上,由于孩子不会更改任何共享数据,因此父母可以在打印之前发布 - 在我看来,它应该这样做(如我下面的代码所示),但如果您不是特别想查看细节,请记住,您应该发布并等待中间没有任何内容。while

相反,孩子必须从等待开始。最后,父级必须释放子级,以便它看到所有文件都已解析。

这是对我有用的完整代码:

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <semaphore.h>
#include <sys/shm.h>
#include <dirent.h>

#define BUFFER_SIZE 3ul

#define Declare_Shared(type, var_name, size) \
    type * var_name = (type *)shmat(shmget(IPC_PRIVATE, size * sizeof(type), 0666 | IPC_CREAT), 0, 0)
#define Declare_Shared2(type, var_name) Declare_Shared(type, var_name, 1)

char * file_paths[100];
int file_count = 0;

void traverseDir(char * dir_name);

int main(int argc, char * * argv)
{
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <dir_name>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    traverseDir(argv[1]);
    if (file_count <= 0 || 100 < file_count) {
        fprintf(stderr, "%d .txt files found in the directory; must be more than 0 and no more than 100.\n", file_count);
        exit(EXIT_FAILURE);
    }

    printf("Found %d files in the source directory.\n", file_count);
    for (int i = 0; i < file_count; ++i) {
        printf("%s\n", file_paths[i]);
    }

    Declare_Shared(char, shared_buffer, BUFFER_SIZE);

    Declare_Shared2(sem_t, _full_);
    Declare_Shared2(sem_t, _empty_);
    sem_init(_full_, 1, 0);
    sem_init(_empty_, 1, 0);

    Declare_Shared2(int, read_file_count);
    Declare_Shared2(int, num_read);
    *read_file_count = 0;

    switch (fork()) {
    default:
        while (*read_file_count < file_count) {
            FILE * fp = fopen(file_paths[*read_file_count], "r");
            if (fp == NULL) {
                fprintf(stderr, "Failed to open file %s!\n", file_paths[*read_file_count]);
            } else {
                while ((*num_read = fread(shared_buffer, sizeof(*shared_buffer), BUFFER_SIZE, fp)) > 0) {
                    sem_post(_full_);
                    printf("[Parent] Buffer of size %d from %p contains: %.*s\n",
                        *num_read, shared_buffer, *num_read, shared_buffer);
                    sem_wait(_empty_);
                }
            }
            ++*read_file_count;
        }
        sem_post(_full_);
        break;

    case 0:
        sem_wait(_full_);
        while (*read_file_count < file_count) {
            printf("[Child]  Buffer of size %d from %p contains: %.*s\n",
                *num_read, shared_buffer, *num_read, shared_buffer);
            sem_post(_empty_);
            sem_wait(_full_);
        }
        break;

    case -1:
        fprintf(stderr, "Fork failed!\n");
        exit(EXIT_FAILURE);
    }

    for (int i = 0; i < file_count; ++i) {
        free(file_paths[i]);
    }
}

void traverseDir(char * dir_name)
{
    DIR * dir;
    if ((dir = opendir(dir_name)) != NULL) {
        struct dirent *ent;
        while ((ent = readdir(dir)) != NULL) {
            size_t ent_name_len = strlen(ent->d_name);
            static char const accepted_ext[] = ".txt";
            size_t accepted_ext_len = sizeof(accepted_ext) - 1;
            if (ent->d_type == DT_DIR) {
                if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0) {
                    char new_dir_name[strlen(dir_name) + ent_name_len + 2];
                    sprintf(new_dir_name, "%s/%s", dir_name, ent->d_name);
                    traverseDir(new_dir_name);
                }
            }
            else if (ent_name_len >= accepted_ext_len &&
                    memcmp(ent->d_name + ent_name_len - accepted_ext_len, accepted_ext, accepted_ext_len) == 0) {
                char * new_file_path = (char *)malloc(strlen(dir_name) + ent_name_len + 2);
                sprintf(new_file_path, "%s/%s", dir_name, ent->d_name);
                file_paths[file_count] = new_file_path;
                ++file_count;
            }
        }
        closedir(dir);
    } else {
        perror("");
    }
}

但是你可以用线程得到相同的结果,例如使用 C11 线程(注意:为了避免用许多全局变量污染全局范围,你可能应该把你的变量放在一个结构中):

#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <threads.h>
#include <dirent.h>

#define BUFFER_SIZE 3ul

void traverseDir(char * dir_name);

int child_func(void * arg);

char * file_paths[100];
int file_count = 0;

char shared_buffer[BUFFER_SIZE];

mtx_t full, empty;

int read_file_count = 0, num_read;

int main(int argc, char * * argv)
{
    if (argc < 2) {
        fprintf(stderr, "Usage: %s <dir_name>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    traverseDir(argv[1]);
    if (file_count <= 0 || 100 < file_count) {
        fprintf(stderr, "%d .txt files found in the directory; must be more than 0 and no more than 100.\n", file_count);
        exit(EXIT_FAILURE);
    }

    printf("Found %d files in the source directory.\n", file_count);
    for (int i = 0; i < file_count; ++i) {
        printf("%s\n", file_paths[i]);
    }

    mtx_init(&full, mtx_plain);
    mtx_init(&empty, mtx_plain);

    thrd_t thr;
    if (thrd_create(&thr, &child_func, NULL) != thrd_success) {
        fprintf(stderr, "Thread creation failed!\n");
        exit(EXIT_FAILURE);
    }

    while (read_file_count < file_count) {
        FILE * fp = fopen(file_paths[read_file_count], "r");
        if (fp == NULL) {
            fprintf(stderr, "Failed to open file %s!\n", file_paths[read_file_count]);
        } else {
            while ((num_read = fread(shared_buffer, sizeof(*shared_buffer), BUFFER_SIZE, fp)) > 0) {
                mtx_unlock(&full);
                printf("[Parent] Buffer of size %d from %p contains: %.*s\n",
                    num_read, shared_buffer, num_read, shared_buffer);
                mtx_lock(&empty);
            }
        }
        ++read_file_count;
    }
    mtx_unlock(&full);

    int child_res;
    thrd_join(thr, &child_res);

    mtx_destroy(&full);
    mtx_destroy(&empty);

    for (int i = 0; i < file_count; ++i) {
        free(file_paths[i]);
    }
}

int child_func(void * arg)
{
    mtx_lock(&full);
    while (read_file_count < file_count) {
        printf("[Child]  Buffer of size %d from %p contains: %.*s\n",
            num_read, shared_buffer, num_read, shared_buffer);
        mtx_unlock(&empty);
        mtx_lock(&full);
    }
    return 0;
}

void traverseDir(char * dir_name)
{
    DIR * dir;
    if ((dir = opendir(dir_name)) != NULL) {
        struct dirent *ent;
        while ((ent = readdir(dir)) != NULL) {
            size_t ent_name_len = strlen(ent->d_name);
            static char const accepted_ext[] = ".txt";
            size_t accepted_ext_len = sizeof(accepted_ext) - 1;
            if (ent->d_type == DT_DIR) {
                if (strcmp(ent->d_name, ".") != 0 && strcmp(ent->d_name, "..") != 0) {
                    char new_dir_name[strlen(dir_name) + ent_name_len + 2];
                    sprintf(new_dir_name, "%s/%s", dir_name, ent->d_name);
                    traverseDir(new_dir_name);
                }
            }
            else if (ent_name_len >= accepted_ext_len &&
                    memcmp(ent->d_name + ent_name_len - accepted_ext_len, accepted_ext, accepted_ext_len) == 0) {
                char * new_file_path = (char *)malloc(strlen(dir_name) + ent_name_len + 2);
                sprintf(new_file_path, "%s/%s", dir_name, ent->d_name);
                file_paths[file_count] = new_file_path;
                ++file_count;
            }
        }
        closedir(dir);
    } else {
        perror("");
    }
}

线程更容易使用,并且比分叉使用更少的资源(出于我在回答开始时提供的链接中解释的原因)。事实上,它们是为多处理而创建的,线程有时会非常频繁地创建和破坏,因此叉子的重量非常不合适。它们也更具便携性:许多图书馆长期以来一直提供,并且自 2011 年以来一直处于 C 标准中。(但不是标准的:它是一个 Unix 函数。dirent.h