c-父级和子级访问相同的缓冲存储器,但接收不同的数据



代码的作用:(底部提供完整代码)Parent(Producer)进程逐个读取数组中的文件、3字节乘3字节并使用fread()将其放入shared_buffer(3 bytes of memory size)中,子(消费者)进程从shared_buffer中读取。通过与_full__empty__mutex_这三个互斥体的同步来确保读写互斥。

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

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

This is a test file of 8 words.

我的代码:(提取)

main:

#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 %jdn", (intmax_t)getpid());
printf("address of the shared buffer in parent: %pn", (void *)shared_buffer);
printf("parent: address of full: %p; empty: %p; mutex: %pn", (void *)_full_, (void *)_empty_, (void *)_mutex_);
int value; sem_getvalue(_full_, &value);
printf("Value of _full_ semaphore: %dn", value);
sem_getvalue(_empty_, &value);
printf("Value of _empty_ semaphore: %dn", value);
sem_getvalue(_mutex_, &value);
printf("Value of _mutex_ semaphore: %dn", 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 %jdn", (intmax_t)getpid());
printf("address of the shared buffer in child: %pn", (void *)shared_buffer);
printf("child: address of full: %p; empty: %p; mutex: %pn", (void *)_full_, (void *)_empty_, (void *)_mutex_);
sem_getvalue(_full_, &value); printf("Value of _full_ semaphore: %dn", value);
sem_getvalue(_empty_, &value); printf("Value of _empty_ semaphore: %dn", value);
sem_getvalue(_mutex_, &value); printf("Value of _mutex_ semaphore: %dn", 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: %dn", word_count);
word_count += wordCount(shared_buffer);
printf("child counted words : %dn", 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个字,为什么会发生这种情况?如何修复?

不要介意单词Count函数和打印出来的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("%sn", 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 %jdn", (intmax_t)getpid());




printf("address of the shared buffer in parent: %pn", (void *)shared_buffer);
printf("parent: address of full: %p; empty: %p; mutex: %pn", (void *)_full_, (void *)_empty_, (void *)_mutex_);
int value;
sem_getvalue(_full_, &value);
printf("Value of _full_ semaphore: %dn", value);
sem_getvalue(_empty_, &value);
printf("Value of _empty_ semaphore: %dn", value);
sem_getvalue(_mutex_, &value);
printf("Value of _mutex_ semaphore: %dn", 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 %jdn", (intmax_t)getpid());
printf("address of the shared buffer in child: %pn", (void *)shared_buffer);
printf("child: address of full: %p; empty: %p; mutex: %pn", (void *)_full_, (void *)_empty_, (void *)_mutex_);
sem_getvalue(_full_, &value);
printf("Value of _full_ semaphore: %dn", value);
sem_getvalue(_empty_, &value);
printf("Value of _empty_ semaphore: %dn", value);
sem_getvalue(_mutex_, &value);
printf("Value of _mutex_ semaphore: %dn", 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: %dn", word_count);
word_count += wordCount(shared_buffer);
printf("child counted words : %dn", 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] != ''){ 
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功能,可以忽略

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

它们实际上访问相同的内存,为什么输出不同的

这无关紧要–是的,由于您的"共享内存操作",它们访问相同的内存,但即使您只使用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 %dn", &myVar, myVar);
printf("*myPtr: %p %dn", myPtr, *myPtr);
}

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

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

正如我所观察到的,孩子读到的实际上是父母读到的3个字符之后的下3个字符,为什么会发生这种情况?

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

如何修复它?

您应该小心地在一个相邻的块中锁定和解锁互斥对象,其间没有其他内容(例如循环的条件部分):

  • 父级:
    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: %.*sn",
    *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: %.*sn",
    *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("%sn", 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: %.*sn",
*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: %.*sn",
*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("%sn", 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: %.*sn",
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: %.*sn",
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("");
}
}

线程比fork更容易使用,并且使用的资源要少得多(原因在我回答的开头提供的链接中解释)。事实上,它们是为多处理而创建的,有时会非常频繁地创建和销毁线程,因此叉子的重量非常不合适。它们也更具可移植性:由许多库长期提供,并自2011年起在C标准中提供。(但dirent.h不是标准的:它是Unix函数。)