将数据从父进程传递到其子进程——IPC、UNIX

Pass data from parent process to its child process — IPC, UNIX

我正在尝试用 C for Unix 创建一个程序。给定一个数组,我应该通过共享内存和使用信号量将数组的每一项从父进程传递给它的子进程。 这就是我得到的。

const key_t sem_key = (key_t)0x12345611;
const key_t shm_key = (key_t)0x12339611;

int sem_wait(int semafor_id)
{
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = -1;
    sb.sem_flg = SEM_UNDO;

    return semop(semafor_id, &sb, 1);
}

int sem_signal(int semafor_id)
{
    struct sembuf sb;
    sb.sem_num = 0;
    sb.sem_op = 1;
    sb.sem_flg = SEM_UNDO;

    return semop(semafor_id, &sb, 1);
}

int main(int argc, char *argv[])
{
    int status;
    char str[3];
    str[0] = 'z';
    str[1] = 'x';
    str[2] = 'y';

    // Create shared memory
    int memory_id = shmget(shm_key, 1, 0600 | IPC_CREAT | IPC_EXCL);
    if (memory_id < 0)
    {
        printf("Shared memory creating failed\n");
        return 1;
    }

    // Create semafor
    int semafor_id = semget(sem_key, 10, 0600 | IPC_CREAT | IPC_EXCL);
    if (semafor_id < 0)
    {
        printf("SEMAFOR creating failed\n");
        return 1;
    }
    semctl(semafor_id, 0, SETVAL, 1); // init semafor

    pid_t pid = fork();
    if (pid < 0)
        printf("FORK FAILED\n");

    else if (pid > 0)
    {
        printf("PARENT\n");
        void *address = shmat(memory_id, NULL, 0);
        if (address == NULL)
        {
            printf("Failed to attach memory\n");
            return 1;
        }
        if (sem_wait(semafor_id) < 0) // sem_wait() decrements (locks) the semaphore pointed to by sem.
        {
            printf("Failed wait parent\n");
            return 1;
        }

        for (int i = 0; i < 3; i++)
        {
            void *address = shmat(memory_id, NULL, 0);
            if (address == NULL)
            {
                printf("Failed to attach memory\n");
                return 1;
            }
            printf("writer started.\n");
            char *data = (char *) address;
            *data = str[i];
            printf("writer ended.\n");
        }

        if (shmdt(address) != 0)
            printf("Failed to detach shared memory\n");

        if (sem_signal(semafor_id) < 0)
            printf("Failed signal parent\n");
        else
            printf("Parent leave generating number\n");


        // wait for child
        wait(&status);
        printf("Destroy semafor\n");
        semctl(semafor_id, 0, IPC_RMID, 0);
        printf("Destroy shared memory\n");
        shmctl(memory_id, IPC_RMID, 0);

        return 0;
    } // close parent case
    else
    {
        printf("CHILD\n");
        if (sem_wait(semafor_id) < 0)
            printf("FAILED wait child\n");
        else
        {
            void *address = shmat(memory_id, NULL, 0);
            if (address == NULL)
            {
                printf("Failed to atach memory in child\n");
                return 1;
            }
            char *data = (char *) address;
            printf("Child read data %c\n", *data);

            if (shmdt(address) != 0)
                printf("Failed to detach shared memory in child\n");
        }
    }

    if (sem_signal(semafor_id) < 0)
        printf("Failed signal child\n");
    else
        printf("Leave data reading\n");

    return 0;
}

当前输出是这样的:

writer started
writer ended
writer started
writer ended
writer started
writer ended
child read data y
destroy semafor
destroy shared memory 

如你所见,父进程在共享内存中写入 3 次,然后子进程获得共享内存的访问权限,打印共享内存的数据。 我想要的输出是这样的:

writer started
writer ended
child read data z
writer started
writer ended
child read data x 
writer started
writer ended
child read data y
destroy semafor
destroy shared memory  

问题出在逻辑上。去除样板代码后,逻辑如下:

Get shared memory
Get semaphore with value 1
fork
in parent:
    wait semaphore (problem #1, #2) <-- supposed to decrement it to 0

    for(i=0;i<3;++i)
    {
        write next char <<-- no synchronization here :-(
    }

    signal semaphore 

    wait for child
    STOP

in child:
    wait semaphore (problem #1)
    read next char
    signal semaphore
    (problem #3)
    STOP

英语:

the writer acquires write lock
writes 3 values into the same location
then signals the reader to read data.
At this moment writer moves on to wait for child PID to die.

The reader reads the last value once
signals the semaphore
then exits.

At this moment writer sees reader died
cleans up
...and exits

问题 #1:parent 和 child 同时处理 运行。 他们中的任何一个都可以首先在 sem_wait 成功。如果 child 首先这样做,它会 从共享内存和 parent 打印出乱码的初始未初始化值 将被锁定,永远等待信号量。 尝试在等待信号量之前将虚数(或非虚数)的 sleep(1) 添加到 parent 中。 这将模拟处理器上下文切换。

问题 #2:你 wait/signal 只有一次。例如。你写下所有的值,然后才发出信号。 这就是为什么只打印最后一个值的原因。

问题 #3:Child 没有任何循环。他应该如何读取 3 个值?!

逻辑应该是:

Writer
    Do 3 times:
        acquire write semaphore
        write next value
        signal reader semaphore
    wait child to die
    clean up
    exit

reader:
    Do 3 times:
       acquire reader semaphore
       read next value
       signal writer semaphore
    exit