【问题标题】:Simple check in shared memory return SIGSEGV error 008b共享内存中的简单检查返回 SIGSEGV 错误 008b
【发布时间】:2019-04-09 14:37:36
【问题描述】:

我在 C 中实现了一个共享内存,让分叉的孩子相互交流,这是一个最小、完整和可验证的示例:

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

#define SHMEMORY

#define NUM_SEMS 2

#define LOCK                    \
    sops.sem_num = 1;           \
    sops.sem_op = -1;           \
    semop(sem_Id, &sops, 1);
#define UNLOCK                  \
    sops.sem_num = 1;           \
    sops.sem_op = 1;            \
    semop(sem_Id, &sops, 1);
#define TEST_ERROR    if (errno) {dprintf(STDERR_FILENO,        \
                      "%s:%d: PID=%5d: Error %d (%s)\n", \
                      __FILE__,         \
                      __LINE__,         \
                      getpid(),         \
                      errno,            \
                      strerror(errno));}

#define POP_SIZE 100 //number of child
#define TRUE 1

struct shared_data {
    /* index where next write will happen */
    unsigned long cur_idx;

    int invite_sent[POP_SIZE][POP_SIZE];
};

static void init();
static int invite_sent_check(int stud);
int maxMin_rand(int max,int min);
void handle_signal(int sig);

int sim_time = 10;

unsigned long next_num;
struct sembuf sops;
pid_t *kid_pids;
int mem_Id, sem_Id;

int main() {
    int i = 0;
    int j = 0;
    int status, cur_i;
    struct shared_data* corso;
    pid_t child_pid, my_pid;

    int stud = 0;
    int exit_loop = 0;

/*********************************************************/
    struct sigaction sa;
    sigset_t  my_mask;

    /* handler  SIGALRM
     */
    sa.sa_handler = handle_signal;
    sa.sa_flags = 0;
    sigemptyset(&my_mask);

    sa.sa_mask = my_mask;
    sigaction(SIGALRM, &sa, NULL);
/**********************************************************/
    mem_Id = shmget(IPC_PRIVATE, sizeof(*corso), 0600);
    TEST_ERROR;

    /* Attach the shared memory to a pointer */
    corso = shmat(mem_Id, NULL, 0);
    TEST_ERROR;

    corso->cur_idx = 0;   /* init first counter */

/*********************************************************/
    sem_Id = semget(IPC_PRIVATE, NUM_SEMS, 0600);
    TEST_ERROR;
    /* Sem 0 to syncronize the start of child processes */
    semctl(sem_Id, 0, SETVAL, 0);

#ifdef SHMEMORY
    semctl(sem_Id, 1, SETVAL, 1);
#endif  
    TEST_ERROR;

    sops.sem_num = 0;     /* check the 0-th semaphore */
    sops.sem_flg = 0;     /* no flag */

init();


    kid_pids = malloc(POP_SIZE*sizeof(*kid_pids));
    for (i = 0; i < POP_SIZE; i++) {

        switch (kid_pids[i] = fork()) {

        case -1:
            /* Handle error */
            TEST_ERROR;
            break;

        case 0:
            /* Wait for the green light */
            sops.sem_op = -1;
            semop(sem_Id, &sops, 1);

            while(exit_loop==0 || exit_loop==1){

                LOCK;

                if(exit_loop == 0){ 
                    stud = corso->cur_idx;
                    printf("%d %d\n",stud,getpid());
                    corso->cur_idx++;
                    exit_loop = 1;
                }

                if(invite_sent_check(stud) == 1){

                }


                UNLOCK;
                }

            exit(0);
            break;

        default:
            break;
        }
    }

    alarm(sim_time);

    while (shmctl(mem_Id, IPC_RMID, NULL)) { TEST_ERROR; }

    sops.sem_op = POP_SIZE;
    semop(sem_Id, &sops, 1);

    /* Waiting for all child POP_SIZEesses to terminate */
    while ((child_pid = wait(&status)) != -1) {
        dprintf(2,"PID=%d. Sender (PID=%d) terminated with status 0x%04X\n",
            getpid(),
            child_pid,
            status);
    }

    /* Now the semaphore may be deallocated */
    semctl(sem_Id, 0, IPC_RMID);

    exit(0);
}

static void init(){
printf("INIT\n");

struct shared_data * corso;
corso = shmat(mem_Id, NULL, 0);
corso->cur_idx=0; 

int r, q, j;


    j = 0;
    q = 0;
    while(j < POP_SIZE){
        q = 0;
        while(q < POP_SIZE){ 
            corso->invite_sent[j][q] = -1; 
            q++;
        }
        j++;
    }
}

int maxMin_rand(int max, int min){      
          int reset;
          int randomics=12345;
          int w=0;
          while(w<reset) {
            randomics++; 
            w++;   
          }  
          next_num = next_num+randomics;
          next_num = next_num*1103515245 +12345;
          unsigned int result=(unsigned int) ((next_num*65536)%(max+1))+min;

          int reload;
          w=0;
          while(w<reload) {  
          next_num++; 
          w++; 
          }
          return result;
}

static int invite_sent_check(int stud){ 
    struct shared_data * corso;
    corso = shmat(mem_Id, NULL, 0);

    int i = 0;
    int q = 0;
    while(i < POP_SIZE){
        if(i == stud){
            q = 0;
            while(q < POP_SIZE){
                if(corso->invite_sent[i][q] != -1){
                    return 1;
                }
                q++;
            }

        }
        i++;
    }
    return 0;
}
void handle_signal(int signal){

    int child_pid;
    int status;
    struct shared_data * corso;
    corso = shmat(mem_Id, NULL, 0);

    switch (signal) {
        case SIGALRM:

    for(int i = 0; i < POP_SIZE; i++){
        kill(kid_pids[i], SIGKILL);
    }

    while (shmctl(mem_Id, IPC_RMID, NULL)) { 
        TEST_ERROR; 
    }


    while ((child_pid = wait(&status)) != -1) {
        dprintf(2,"PID=%d. Sender (PID=%d) terminated with status 0x%04X\n",
            getpid(),
            child_pid,
            status);
    }

    semctl(sem_Id, 0, IPC_RMID);

    exit(0);
    break;
    }   
}

只要计时器运行(sim_time = 10),分叉的孩子就会保持 LOCK 和 UNLOCK。然后 SIGNAL_HANDLER 杀死所有的孩子,并终止。 我不断从一个以状态 008B 终止并停止他的“兄弟”的 RANDOM 子进程收到 SIGSEGV 错误,直到处理程序杀死所有其他进程。据我所知,这个错误涉及共享内存中的指针,对吗?或者我错过了/我写了一些真正错误的东西?即使是这个检查 INVITE_SENT 矩阵中是否有至少 1 个值与 -1 不同的小方法也会导致崩溃,而不仅仅是返回 0。 感谢您的宝贵时间。

【问题讨论】:

  • 我看到您已经努力展示您的代码摘录,以传达基本信息,但是当您在追逐一个您还不太了解的问题时,这几乎不可能可靠地做到。我们寻找的是minimal reproducible example——一个小型的、独立的示例来演示该问题。有时它可以从原始代码的摘录中构建这样的东西,但有时从头开始构建它更容易。无论如何,您所展示的内容远不及 MCVE,缺少许多可能相关的细节。
  • @JohnBollinger 按照您的要求编辑了原始代码
  • 不,我绝对没有要求完整的原始代码。由于我必须简短的评论没有有效地传达这个想法,请点击我提供的超链接以获得更全面的解释。
  • @JohnBollinger 来了
  • 您的TEST_ERROR 宏错误地假定errno 在函数调用成功后为零。 errno 仅由记录在失败时设置它的失败函数设置。

标签: c fork shared-memory child-process segmentation-fault


【解决方案1】:

我一直无法在本地重现段错误,并且 Valgrind 没有检测到任何无效的内存访问。段错误还有其他可能的原因,但它们并不常见。由于无法在本地重现该问题,我无法确定其来源,但代码中存在许多问题,大多是次要问题。

似乎与您的问题有关的唯一问题是您在函数invite_sent_check() 中对共享内存段的冗余附加,特别是考虑到您使用shmat() 的返回值而不检查它((void*)-1 在失败时返回)。诸如此类的冗余附件是明确允许的,但调用者只需将其现有指针传递给该段的原始附件点会更清洁、更有效。此外,如果您在该函数中形成本地附件,则必须确保在该函数返回之前再次分离。不这样做很可能是问题的根源,因为由此产生的许多附件的元数据和地址空间保留可能会耗尽可用资源。


其他问题包括

  • dprintf() 不是异步信号安全的,而是从信号处理程序调用(显式调用和通过宏 TEST_ERROR)。

  • shmat() 不是异步信号安全的,但它是从信号处理程序调用的。此外,这似乎没有必要,因为处理程序中没有使用新的段附件。此外,它也没有分离。

  • semctl() 不是异步信号安全的,但它是从信号处理程序中调用的。

  • exit() 不是异步信号安全的,但它是从信号处理程序调用的。您可以改用_Exit() 或_exit(),但这个处理程序似乎完全不应该退出程序,因为主进程似乎还有其他要清理的工作。

    李>
  • 考虑到收到SIGALRM 时您似乎想做的所有事情,其中​​许多不是异步信号安全的,您应该考虑使用sigsupend() 同步接收信号,然后调用完成这项工作的常规功能。如果你朝那个方向走,那么最安全、最可靠的方法是在发出警报之前首先阻塞SIGALRM,然后将信号掩码传递给sigsuspend(),以允许该信号。这将防止在进程准备好之前传递信号的任何机会。

  • 函数init() 冗余地附加共享内存段。这是允许的,但调用者最好只传递一个指向要初始化的struct shared_data 的指针。此函数也无法分离。

  • 如果您想通过检查 errno 来检查错误,您必须确保在要检查的调用之前将其设置为 0(并在执行其他任何操作之前立即对其进行测试)。但是,更好的做法是使用函数的返回值来检测是否发生了错误,并仅依靠errno 来辨别哪个。

  • 函数maxMin_rand() 的名称和签名表明它打算返回一个介于max 和min 之间的数字,但它看起来可以返回一个与max + min 一样大的数字。

【讨论】:

  • 你是我的主人。我在每个函数中用shmdt(corso) 分离了每个指针,现在错误似乎消失了。即使我的函数没有返回值,我也必须在方法结束之前shmdt(*pointer),对吧?
  • 您可以像malloc 和free 一样查看它:每个shmat() 都应该与一个(正好一个)对应的shmdt() 配对。这实际上是一个很好的类比,因为原因是一样的:避免资源泄漏。
  • 好的,我明白了。那么在main 中我应该将shmdt 命令放在哪里?就在while (shmctl(mem_Id, IPC_RMID, NULL)) { TEST_ERROR; } 之前?因为我用shmctl(mem_Id, IPC_RMID, NULL) 解除了共享内存,所以我在此之前分离了指针,对吧?
  • 这行得通,@Reverendo,但是可以在连接共享内存段时将其标记为删除。该段只要附加到任何进程就一直存在。您的main 可能会在不再需要该段后的任何时候将其分离,所以我认为这将在fork() 循环之后的任何地方。或者,就像malloc-ed 内存一样,当进程终止时,进程的共享内存段附件会自动分离。显式分离它们是最干净的,但如果您的 main()(仅)依赖于终止时的隐式分离,则不会出现问题。
猜你喜欢
  • 2019-03-07
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2019-04-05
  • 1970-01-01
  • 2021-02-19
  • 2022-01-18
  • 2013-05-05
相关资源
最近更新 更多