Простая проверка в разделяемой памяти возвращает ошибку SIGSEGV 008b - PullRequest
0 голосов
/ 09 апреля 2019

Я реализовал в C разделяемую память, чтобы позволить разветвленным дочерним элементам соединяться друг с другом, вот пример Minimal, Complete и Verificable:

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

Разветвленные дочерние элементы сохраняют LOCK и UNLOCK до тех пор, пока идет таймер (sim_time = 10).Затем SIGNAL_HANDLER убивает всех детей и завершает работу.Я продолжаю получать ошибку SIGSEGV от СЛУЧАЙНОГО дочернего элемента, который завершается со статусом 008B и останавливает своих «братьев», пока обработчик не убьет все другие процессы.Насколько я знаю, эта ошибка касается указателя в разделяемой памяти, верно?Или я скучаю / я написал что-то действительно неправильно?Даже этот маленький метод, который проверяет, есть ли в матрице INVITE_SENT хотя бы 1 значение, отличное от -1, вызывает сбой, а не просто возвращает 0. Спасибо за ваше время.

1 Ответ

1 голос
/ 10 апреля 2019

Я не смог воспроизвести segfault локально, и 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.

...