如何更新子进程的共享数据?

时间:2017-09-27 07:38:26

标签: c fork shared-memory mmap memcpy

我正在尝试制作一个分配5个孩子的程序。子节点都访问共享内存以读取数字并将其增加1直到数字达到100.子节点能够读取共享内存中的值,但它们不会更新它。我做错了什么?

#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);
            }
    }
}

1 个答案:

答案 0 :(得分:2)

最明显的问题是:

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

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

然而,还有其他一些涉及“种族”条件的问题。 这可以通过具有'share'属性的互斥锁进行纠正。

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

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

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

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

在子进程全部退出之前存在退出父进程的问题。这导致Zombie进程,其中子进程曾经是。

使用“魔术”数字存在“问题”。 “魔术”数字使代码更难以理解,调试等。

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

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

我在需要时使用了'强制转换',以避免编译器发出“隐式转换”警告(linux上为gcc) 以下代码更正了问题,包括问题评论中列出的问题。

#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, '\0', 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 ] = {'\0'};
                        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