只读共享内存分段故障

时间:2016-02-04 16:51:53

标签: c linux gcc shared-memory

我正忙着在linux paltform上使用共享内存。 Cosider以下代码:

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <semaphore.h>

#define SEM_NAME "mysem"

int main (int argc, char *argv[])
{
    int     fd, zero = 0;
    int     *ptr;
    sem_t   *mutex;
    pid_t   PID = getpid();

    int mmap_prot = PROT_WRITE;

    if (argc < 2)
    {
        printf(" Usage: Test [OPTION]\n\tW = Write Only\n\tR = Read Only\n");
        return 1;
    }

    if (*argv[1] == 'W')
    {
        fd = open("Test_SHM", O_RDWR | O_CREAT, -1);

        if (fd == -1)
            perror("open");

        write(fd, &zero, sizeof(int));
    }
    else
    {
        fd = open("Test_SHM", O_RDONLY| O_CREAT, -1);

        if (fd == -1)
            perror("open");

        mmap_prot = PROT_READ;
    }

    ptr = mmap(NULL, sizeof(int), mmap_prot, MAP_SHARED, fd, 0);

    close(fd);

    if (ptr == MAP_FAILED)
    {
        perror("mmap");

        return 1;
    }

    // create, initialize, and unlink semaphore
    mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);
    sem_unlink(SEM_NAME);

    setbuf(stdout, NULL);   /* stdout is unbuffered */

    printf("Shared Mem ready..\n");

    while(1)
    {
        sem_wait(mutex);
        printf("PID %d Count: %d\n", PID, (*ptr)++);
        sem_post(mutex);

        sleep(1);
    }

    return 0;
}

如果我为只读共享内存启动应用程序,我会按预期在第一次*ptr在主循环内递增时出现分段错误。

我正在开发一个抽象Linux共享内存的lib。

此lib将部署到第三方开发人员,这些开发人员将在嵌入式目标上实现我的应用程序的某些进程。

这个lib将在进程之间实现“全局变量”。我想知道我是否可以避免开发getset函数并简单地返回已分配内存的地址。

如果访问权限错误,我想向调用者信息提供其代码中的错误信息。在终端上读取分段故障并且进程终止不会给用户提供良好的信息。

EDIT2

在@Ctx回答之后我尝试了以下解决方案,但它可以解决第一个分段错误。第二个触发标准分段故障和pogram终止。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <signal.h>
#include <stdbool.h>
#include <setjmp.h>

#define SEM_NAME "mysem"

#define TEST 1

jmp_buf env;

void segvhandler(int arg) {
    longjmp(env, 1);
}

bool Test ( int *ptr, sem_t *mutex, pid_t PID)
{
#if (TEST == 1)
    signal(SIGSEGV, segvhandler);
#elif (TEST == 2)
    sig_t segvhandler_OLD = signal(SIGSEGV, segvhandler);
#endif

    int val = setjmp(env);

    if (val != 0)
    {
        printf("Segmentation fault catched.\n");

        sem_post(mutex);

        #if (TEST == 1)
            signal(SIGSEGV, SIG_DFL);
        #elif (TEST == 2)
            signal(SIGSEGV, segvhandler_OLD);
        #endif

        return false;
    }

    sem_wait(mutex);
    printf("PID %d Count: %d\n", PID, (*ptr)++);
    sem_post(mutex);

#if (TEST == 1)
    signal(SIGSEGV, SIG_DFL);
#elif (TEST == 2)
    signal(SIGSEGV, segvhandler_OLD);
#endif

    return true;
}

int main (int argc, char *argv[])
{
    int     fd, zero = 0;
    int     *ptr;
    sem_t   *mutex;
    pid_t   PID = getpid();

    int mmap_prot = PROT_WRITE;

    if (argc < 2)
    {
        printf(" Usage: Test [OPTION]\n\tW = Write Only\n\tR = Read Only\n");
        return 1;
    }

    if (*argv[1] == 'W')
    {
        fd = open("Test_SHM", O_RDWR | O_CREAT, -1);

        if (fd == -1)
            perror("open");

        write(fd, &zero, sizeof(int));
    }
    else
    {
        fd = open("Test_SHM", O_RDONLY| O_CREAT, -1);

        if (fd == -1)
            perror("open");

        mmap_prot = PROT_READ;
    }

    ptr = mmap(NULL, sizeof(int), mmap_prot, MAP_SHARED, fd, 0);

    close(fd);

    if (ptr == MAP_FAILED)
    {
        perror("mmap");

        return 1;
    }

    // create, initialize, and unlink semaphore
    mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);
    sem_unlink(SEM_NAME);

    setbuf(stdout, NULL);   /* stdout is unbuffered */

    printf("Shared Mem ready..\n");

    while(1)
    {
        Test (ptr, mutex, PID);

        sleep(1);
    }

    return 0;
}

4 个答案:

答案 0 :(得分:2)

根据<html> <head> <!-- script in head --> <script type = "javascript/text"> function myFunction() { var a; var text = ""; for ( a = 0; a < 5; a++ ){ // if statements within the loop if( a === 0 || a === 4 ) { // All conditionals must be within one set of parenthesis. You can have others within, but all must be contained in one. text += "$<br />"; } if( a === 1 || a === 3 ) { text += "$$<br />"; } if ( a === 2 ) { text += "$$$<br />"; } } document.getElementById( "dollar" ).innerHTML = text; } </script> </head> <body> <!-- button in body --> <button onclick="myFunction()">Click This Button</button> <p id="dollar"></p> </body> </html> man page

mmap()

答案 1 :(得分:1)

如果要在修改不起作用的情况下继续,可以为SIGSEGV安装信号处理程序,并使用(sig)setjmp / longjmp在定义的点继续执行:

#include <setjmp.h>
#include <signal.h>
#include <stdlib.h>
#include <stdio.h>

jmp_buf env;

void segvhandler(int arg) {
    siglongjmp(env, 1);
}

void somefunc(void) {
    char *ptr = NULL;
    signal(SIGSEGV, segvhandler);
    if (!sigsetjmp(env, 1)) {
        // Direct invocation, try the memory access
        *ptr++;
    }
    signal(SIGSEGV, SIG_DFL);
}

int main (void) {
    while (1) {
        somefunc();
        printf("One more iteration...\n");
    }

    exit(EXIT_SUCCESS); // Never reached
}

sigsetjmp(env, 1)还会将阻塞的信号保存在env中,当它的第二个参数为非零时,siglongjmp()会恢复这些信号。否则,信号仍将在longjmp()后被阻止,因为它不是来自信号处理程序的真正的 return

请记住,在进行相关内存访问之前,您应该只安装处理程序,然后将其卸载。

答案 2 :(得分:0)

调试器几分钟后显示程序在调用sem_wait()时崩溃。

在调用sem_open()之后插入:

    if( SEM_FAILED == mutex )
    {
        perror( "sem_open failed" );
        exit( EXIT_FAILURE );
    }

然后移动声明:

sem_unlink(SEM_NAME);

到声明之前:

mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);

然后很明显,剩下的问题出在本声明中:

printf("PID %d Count: %d\n", PID, (*ptr)++);

导致总线错误信号升高。这个总线错误信号在第一次通过while()循环时发生。

原因很简单。

printf()语句,最后一个参数是尝试读取和写入内存映射文件,但内存映射仅用于(取决于命令行参数)&#39; PROT_READ&#39;允许阅读或'PROT_WRITE&#39;这允许写作。调用mmap()的参数需要同时包含两个功能和open()的调用 有模式:O_RDWR。 (open()和mmap()模式需要匹配

答案 3 :(得分:0)

这是Ctx回答后的更正代码。我还发现THIS有助于说明为什么longjmp不是带信号的正确解决方案。

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/mman.h>
#include <semaphore.h>
#include <signal.h>
#include <stdbool.h>
#include <setjmp.h>

#define SEM_NAME "mysem"

#define TEST 1

jmp_buf env;

void segvhandler(int arg) {
    siglongjmp(env, 1);
}

bool Test ( int *ptr, sem_t *mutex, pid_t PID)
{
#if (TEST == 1)
    signal(SIGSEGV, segvhandler);
#elif (TEST == 2)
    sig_t segvhandler_OLD = signal(SIGSEGV, segvhandler);
#endif

    int val = sigsetjmp(env, 1);

    if (val != 0)
    {
        printf("Segmentation fault catched.\n");

        sem_post(mutex);

        #if (TEST == 1)
            signal(SIGSEGV, SIG_DFL);
        #elif (TEST == 2)
            signal(SIGSEGV, segvhandler_OLD);
        #endif

        return false;
    }

    sem_wait(mutex);
    printf("PID %d Count: %d\n", PID, (*ptr)++);
    sem_post(mutex);

#if (TEST == 1)
    signal(SIGSEGV, SIG_DFL);
#elif (TEST == 2)
    signal(SIGSEGV, segvhandler_OLD);
#endif

    return true;
}

int main (int argc, char *argv[])
{
    int     fd, zero = 0;
    int     *ptr;
    sem_t   *mutex;
    pid_t   PID = getpid();

    int mmap_prot = PROT_WRITE;

    if (argc < 2)
    {
        printf(" Usage: Test [OPTION]\n\tW = Write Only\n\tR = Read Only\n");
        return 1;
    }

    if (*argv[1] == 'W')
    {
        fd = open("Test_SHM", O_RDWR | O_CREAT, -1);

        if (fd == -1)
            perror("open");

        write(fd, &zero, sizeof(int));
    }
    else
    {
        fd = open("Test_SHM", O_RDONLY| O_CREAT, -1);

        if (fd == -1)
            perror("open");

        mmap_prot = PROT_READ;
    }

    ptr = mmap(NULL, sizeof(int), mmap_prot, MAP_SHARED, fd, 0);

    close(fd);

    if (ptr == MAP_FAILED)
    {
        perror("mmap");

        return 1;
    }

    // create, initialize, and unlink semaphore
    mutex = sem_open(SEM_NAME, O_CREAT | O_EXCL, -1, 1);
    sem_unlink(SEM_NAME);

    setbuf(stdout, NULL);   /* stdout is unbuffered */

    printf("Shared Mem ready..\n");

    while(1)
    {
        Test (ptr, mutex, PID);

        sleep(1);
    }

    return 0;
}