如何从 child 进程更新共享数据?

How do I update shared data from a child process?

我正在尝试制作一个分叉 5 children 的程序。 children all access shared memory to read in a number and increase it by 1 until the number reaches 100. children 能够读入共享内存中的值,但它们没有更新它。我做错了什么?

#include <stdio.h>
#include <stdlib.h>
#include <sys/mman.h>
#include <string.h>
#include <sched.h>

int main (int argc, char* argv[])
{
    //create shared memory with children and copy into it the starting number 0
    void* shmem = mmap(NULL, 4, PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0);
    char number[10] = "0";
    memcpy(shmem, number, sizeof(number));

    int i;
    for(i = 0; i < 5; ++i)
    {
            int childPID = fork();

            if(childPID == 0)
            {
                    //read a number in from shared memory
                    int origNum = atoi(shmem);

                    if(origNum < 100)
                    {
                            int newNum = origNum + 1;
                            //cast the new number to a string to be sent to shared memory
                            char newNumString[10];
                            sprintf(newNumString, "%i", origNum);
                            //send the new number to shared memory
                            memcpy(shmem, newNumString, sizeof(newNumString));

                            printf("I'm child %i. PID: %i. PPID: %i. Read in: %i and printed out %i\n", (i + 1), getpid(), getppid(), origNum, newNum);
                            printf("Current value in shmem: %s\n", shmem);

                            //yield process to allow other processes to run
                            sched_yield();
                    }
                    else
                    {
                            exit(0);
                    }
            }
            else
            {
                    printf("I'm a parent. PID: %i. Num: %s\n", getpid(), shmem);
            }
    }
}

最明显的问题是:

sprintf(newNumString, "%i", origNum);

从 'old' 值设置 'new' 字符串,因此共享内存中的数据永远不会更新为 'new' 值。

但是,还有其他几个问题涉及 'race' 条件。 这可以通过具有 'share' 属性的互斥锁来纠正。

注意:没有 'shared' 属性,多个进程访问同一个互斥量是未定义的行为。

警告:我没有包括检查调用系统函数的所有返回值,尤其是那些处理互斥锁设置的函数。您应该添加该检查。

警告:我没有包含破坏互斥锁的代码。您应该添加该声明。

警告:我没有包含释放内存映射内存的代码。您应该添加该声明。

存在子进程未全部退出就退出父进程的问题。这导致僵尸进程,而子进程曾经是。

有'problem'使用'magic'号。 'magic' 数字使代码更难理解、调试等。

在下面的代码中,我记录了为什么要包含每个头文件。这使得某些活动更容易完成。

我留下了一些原始代码(注释掉)来说明一些更改。

我在需要时使用了 'cast',以避免来自编译器的 'implicit conversion' 警告( gcc on linux ) 以下代码更正了问题,包括问题评论中列出的问题。

#include <stdio.h>     // printf(), perror(), sprintf()
#include <stdlib.h>    // atoi(), exit(), EXIT_FAILURE
#include <sys/mman.h>  // mmap(), MAP_FAILED
#include <string.h>    // strcpy(), memset(), 
#include <sched.h>     // sched_yield()

#include <unistd.h>    // fork(), pid_t, getpid(), getppid()
#include <pthread.h>   // pthread_mutex_lock(),
                       // pthread_mutex_unlock(),
                       // pthread_mutexattr_init(),
                       // pthread_mutex_init(),
                       // pthread_mutexattr_setpshared(),
                       // PTHREAD_PROCESS_SHARED,
                       // pthread_mutex_t,
                       // pthread_mutexattr_t
#include <sys/types.h>
#include <sys/wait.h>  // waitpid()

#define MAX_CHILDREN 5
#define MAX_NUM_LEN  10

struct shared
{
    char number[ MAX_NUM_LEN ];
    pthread_mutex_t mutex;
    pthread_mutexattr_t attr;
};

struct shared *shmem;

//int main (int argc, char* argv[])
int main( void )
{
    //create shared memory with children and copy into it the starting number 0
    if( MAP_FAILED == (shmem = (struct shared*)mmap(NULL, sizeof( struct shared), PROT_READ | PROT_WRITE, MAP_ANONYMOUS | MAP_SHARED, -1, 0) ) )
    {
        perror( "mmap failed" );
        exit( EXIT_FAILURE );
    }

    // implied else, mmap successful

    //char number[10] = "0";
    memset( shmem, '[=11=]', sizeof( struct shared ) );
    //memcpy(shmem, number, sizeof(number));

    // setup the mutex, include the 'shared' attribute
    pthread_mutexattr_init(&shmem->attr);
    pthread_mutexattr_setpshared( &shmem->attr,  PTHREAD_PROCESS_SHARED );
    pthread_mutex_init(&shmem->mutex, &shmem->attr );

    //int i;
    pid_t childPID[ MAX_CHILDREN ] = {0};

    for( int i = 0; i < 5; ++i )
    {
        childPID[i] = fork();

        switch( childPID[i] )
        {
            case -1:
                perror( "fork failed" );
                break;

            case 0:
            {  // then child63

                while( 1 )
                {
                    pthread_mutex_lock( &shmem->mutex );

                    //read a number in from shared memory
                    int origNum = atoi( shmem->number );

                    if( origNum < 100 )
                    {
                        int newNum = origNum + 1;

                        //cast the new number to a string to be sent to shared memory
                        char newNumString[ MAX_NUM_LEN ] = {'[=11=]'};
                        sprintf(newNumString, "%i", newNum);

                        //send the new number to shared memory
                        strcpy( &shmem->number[0], newNumString );

                        printf( "I'm child %i. PID: %i. PPID: %i. Read in: %i and printed out %i\n",
                            (i + 1),
                            getpid(),
                            getppid(),
                            origNum,
                            newNum );

                        printf( "Current value in shmem: %s\n", shmem->number );

                        // allow other processes to access the number in shared memory
                        pthread_mutex_unlock( &shmem->mutex );

                        //yield process to allow other processes to run
                        sched_yield();
                    }

                    else
                    {
                        // allow other processes to access the number in shared memory
                        pthread_mutex_unlock( &shmem->mutex );
                        exit(0);
                    }
                }
            }
            break;

            default:
                // then parent
                printf("I'm a parent. PID: %i. Num: %s\n", getpid(), (char*)shmem);
                break;
        } // end switch
    } // end for each child

    for( int i=0; i< MAX_CHILDREN; i++ )
    {
        int status;

        if( -1 != childPID[i] )
        { // then child process exists
            waitpid( childPID[i], &status, 0 );
        }
    }
} // end function: main

这里是更正程序输出的摘录:

I'm a parent. PID: 25961. Num: 
I'm a parent. PID: 25961. Num: 1
I'm child 1. PID: 25962. PPID: 25961. Read in: 0 and printed out 1
I'm a parent. PID: 25961. Num: 1
Current value in shmem: 1
I'm child 2. PID: 25963. PPID: 25961. Read in: 1 and printed out 2
I'm a parent. PID: 25961. Num: 2
Current value in shmem: 2
I'm child 2. PID: 25963. PPID: 25961. Read in: 2 and printed out 3
Current value in shmem: 3
I'm a parent. PID: 25961. Num: 3
I'm child 4. PID: 25965. PPID: 25961. Read in: 3 and printed out 4
Current value in shmem: 4
...
I'm child 4. PID: 25965. PPID: 25961. Read in: 95 and printed out 96
Current value in shmem: 96
I'm child 3. PID: 25964. PPID: 25961. Read in: 96 and printed out 97
Current value in shmem: 97
I'm child 4. PID: 25965. PPID: 25961. Read in: 97 and printed out 98
Current value in shmem: 98
I'm child 3. PID: 25964. PPID: 25961. Read in: 98 and printed out 99
Current value in shmem: 99
I'm child 4. PID: 25965. PPID: 25961. Read in: 99 and printed out 100
Current value in shmem: 100