用信号量保护共享内存段不起作用 [英] protect a shared memory segment with semaphore does not work

查看:57
本文介绍了用信号量保护共享内存段不起作用的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个程序可以创建 1000 个子进程.每个进程都应该访问一个 int 变量,该变量存储在共享内存段中.为了保护 int 变量,我创建了一个信号量:

I have a programm which creates 1000 child processes. Each process should access to a int variable, which is stored in a shared memory segment. To protect the int variable I have created a semaphore:

#define _XOPEN_SOURCE

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <sys/sem.h>
#include <sys/ipc.h>
#include <sys/shm.h>

#define KEY 1000
#define LOCK -1
#define UNLOCK 1

int *ptr;
int pid;
int shm_id;
int sem_id;

struct sembuf sema;

int main()
{
    if( ( sem_id = semget( KEY, 1, IPC_CREAT | 0666 ) ) < 0 )
    {
        printf( "semid error\n" );
        exit( EXIT_SUCCESS );
    }
    sema.sem_num = 1;
    sema.sem_op = 0;
    sema.sem_flg = SEM_UNDO;
    if( ( shm_id = shmget( KEY, 1, IPC_CREAT | 0666 ) ) < 0 )
    {
        printf( "ERROR\n" );
        exit( EXIT_SUCCESS );
    }
    ptr = shmat( shm_id, NULL, 0 );
    *ptr = 0;
    for( int i = 0; i < 10; ++i )
    {

        pid = fork();
        if( pid == 0 )
        {
            // critical part
            sema.sem_op = LOCK;
            if( semop( sem_id, &sema, 1 ) < 0 )
            {
                printf( "ERROR\n" );
            }
            ++( *ptr );
            sema.sem_op = UNLOCK;
            if( semop( sem_id, &sema, 1 ) < 0 )
            {
                printf( "ERROR\n" );
            }
            // end of the critical part
            exit( EXIT_SUCCESS );
        }
    }
    int return_stat;
    enum { debug = 1 };
    int corpse;

    while ( ( corpse = waitpid( ( pid_t )-1, &return_stat, 0 ) ) > 0 )
        if ( debug )
            printf( "PID %d died 0x%.4X\n", corpse, return_stat ); 
    //while( waitpid( pid, &return_stat, 0 ) == 0 );
    printf( "value   %d\n", *ptr );
    shmdt( NULL );
    semctl( sem_id, 1, IPC_RMID, 0 );
}

这是一个示例输出:

PID 7288 died 0x0000
PID 7289 died 0x0000
PID 7290 died 0x0000
PID 7291 died 0x0000
PID 7292 died 0x0000
PID 7293 died 0x0000
PID 7294 died 0x0000
PID 7295 died 0x0000
PID 7296 died 0x0000
PID 7297 died 0x0000
value   9

PID 7276 died 0x0000
PID 7277 died 0x0000
PID 7278 died 0x0000
PID 7279 died 0x0000
PID 7280 died 0x0000
PID 7281 died 0x0000
PID 7282 died 0x0000
PID 7283 died 0x0000
PID 7284 died 0x0000
PID 7285 died 0x0000
value   10

每次输出应该是1000,但输出不同.我不知道为什么这段代码不能正常工作.有人可以帮我解决我的问题吗?谢谢

The output should be 1000 every time, but the output vary. I do not know why this piece of code does not work properly. Can somebody help me with my problem? Thank you

推荐答案

你的进程清理循环错误:

Your process cleanup loop is wrong:

while( waitpid( pid, &return_stat, 0 ) == 0 );

由于 waitpid() 返回它报告的 PID,这不是您想要的循环——它只等待一个 PID 死亡.这可能是您需要的:

Since waitpid() returns the PID it is reporting on, that's not the loop you want -- it only waits for one PID to die. This might be what you need:

enum { debug = 1 };
int corpse;

while ((corpse = waitpid((pid_t)-1. &return_stat, 0)) > 0)
{
    if (debug)
        printf("PID %d died 0x%.4X\n", corpse, return_stat);
}

当您对它正常工作感到满意时,您可以设置 debug = 0.

You can set debug = 0 when you're satisfied it is working correctly.

您的更多问题出在子代码中:

Some more of your problem is in the child code:

    if( pid == 0 )
    {
        // critical part
        sema.sem_op = LOCK;
        if( semop( sem_id, &sema, 1 ) < 0 )
        ++( *ptr );
        sema.sem_op = UNLOCK;
        if( semop( sem_id, &sema, 1 ) < 0 )
        // end of the critical part
        exit( EXIT_SUCCESS );
    }

仅当第一个 semop() 失败时才增加指针;仅当第二个 semop() 失败时,您才退出(成功).

You increment the pointer only if the first semop() fails; you exit (successfully) only if the second semop() fails.

您必须无条件退出.如果第一个 semop() 成功,你应该只做增量,如果第一个成功,你应该只做第二个 semop() .您可能需要在 if 语句之后添加一些错误报告代码.

You must exit unconditionally. You should only do the increment if the first semop() succeeds, and you should only do the second semop() if the first succeeds. You probably want some error reporting code after the if statements.

我看到的剩余问题是您的 LOCK 和 UNLOCK 值颠倒了.

The residual problem I'm seeing is that you have the LOCK and UNLOCK values inverted.

#define _XOPEN_SOURCE

#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/ipc.h>
#include <sys/sem.h>
#include <sys/shm.h>
#include <sys/wait.h>
#include <unistd.h>

#define KEY    1000
#define LOCK   +1
#define UNLOCK -1

static const char *arg0 = 0;
static void err_setarg0(char *argv0)
{
    arg0 = argv0;
}

static void err_syserr(const char *msg)
{
    int errnum = errno;
    fprintf(stderr, "%s: %s", arg0, msg);
    if (errnum != 0)
        fprintf(stderr, " (%d: %s)", errnum, strerror(errnum));
    fputc('\n', stderr);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
    int *ptr;
    int pid;
    int shm_id;
    int sem_id;
    struct sembuf sema;

    err_setarg0(argv[argc-argc]);

    if ((sem_id = semget(KEY, 1, IPC_CREAT | 0666)) < 0)
        err_syserr("semget()");
    sema.sem_num = 0;
    sema.sem_op = 0;
    sema.sem_flg = SEM_UNDO;
    if ((shm_id = shmget(KEY, 1, IPC_CREAT | 0666)) < 0)
        err_syserr("shmget()");
    ptr = shmat(shm_id, NULL, 0);
    if (ptr == (int *)-1)
        err_syserr("shmat()");
    *ptr = 0;

    printf("Looping\n");
    for (int i = 0; i < 10; ++i)
    {
        pid = fork();
        if (pid < 0)
            err_syserr("fork()");
        else if (pid == 0)
        {
            // critical part
            sema.sem_op = LOCK;
            if (semop(sem_id, &sema, 1) < 0)
                err_syserr("semop() lock");
            ++(*ptr);
            sema.sem_op = UNLOCK;
            if (semop(sem_id, &sema, 1) < 0)
                err_syserr("semop() unlock");
            // end of the critical part
            exit(EXIT_SUCCESS);
        }
    }
    printf("Looped\n");

    int return_stat;
    enum { debug = 1 };
    int corpse;

    while ((corpse = waitpid((pid_t)-1, &return_stat, 0)) > 0)
    {
        if (debug)
            printf("PID %d died 0x%.4X\n", corpse, return_stat);
    }
    printf("value   %d\n", *ptr);
    if (shmdt(ptr) == -1)
        err_syserr("shmdt()");
    if (semctl(sem_id, 1, IPC_RMID, 0) == -1)
        err_syserr("semctl()");
    if (shmctl(shm_id, IPC_RMID, 0) == -1)
        err_syserr("shmctl()");
    return 0;
}

示例运行:

$ ./semop
Looping
Looped
PID 17976 died 0x0000
PID 17977 died 0x0000
PID 17978 died 0x0000
PID 17979 died 0x0000
PID 17980 died 0x0000
PID 17981 died 0x0000
PID 17982 died 0x0000
PID 17983 died 0x0000
PID 17984 died 0x0000
PID 17985 died 0x0000
value   10
$

注意使用 err_syserr() 来简化错误报告.与 err_setarg0() 一起,它是我经常使用的更大的错误报告函数包的一部分.事实上,我的普通版本是一个类似 printf 的函数,带有一个格式字符串和一个变量参数列表,但是这个简单的版本对于这个程序来说已经足够了,而且更简单.

Note the use of err_syserr() to simplify error reporting. Along with err_setarg0(), it is a part of a larger package of error reporting functions that I use routinely. In fact, my normal version is a printf-like function with a format string and a variable argument list, but this simple version is adequate for this program and is simpler.

这篇关于用信号量保护共享内存段不起作用的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆