Ошибка сегментации (сброс ядра) на виртуальном Ubuntu64bit

#ubuntu

#ubuntu

Вопрос:

Я пытаюсь изучить многопоточное и многопроцессорное программирование. Я очень новичок как в многопоточном / обработанном программировании, так и в среде Ubuntu. Я работал над приведенным ниже кодом добрых 10 часов и исправил все ошибки и предупреждения. Я начал кодировать это с помощью xCode, и он работает отлично и делает именно то, что я хочу, чтобы он делал, без каких-либо предупреждений или ошибок в этой среде. Но при попытке скомпилировать и запустить на Ubuntu я получаю ошибку сегментации (сброс ядра) Я не мог понять, какая часть кода привела к этой ошибке. Есть идеи, какая часть может вызвать ошибку? или почему я это получаю? Насколько я помню, в Linux нет ядра? Заранее большое вам спасибо!

 #include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <err.h>
#include <sys/types.h>
#include <dirent.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>

int pid, i, rc, pid1, counter;
char* iterator[500];
char* file[500];
enum {

    WALK_OK = 0,
    WALK_BADPATTERN,
    WALK_BADOPEN,
};

int walker(const char *dir, const char *pattern)
{
    struct dirent *entry;
    regex_t reg;
    DIR *d;
    counter=0;
    if (regcomp(amp;reg, pattern, REG_EXTENDED | REG_NOSUB))
        return WALK_BADPATTERN;
    if (!(d = opendir(dir)))
        return WALK_BADOPEN;
    while ((entry = (readdir(d))) ){
        if (!regexec(amp;reg, entry->d_name, 0, NULL, 0)){
            puts(entry->d_name);
            file[counter]=entry->d_name;
            counter=counter 1;}
    }
    closedir(d);
    regfree(amp;reg);
    return counter;
}


void* project_statistics(int i){

    FILE* f;
 //   size_t len;
    char* line;
    int read[3];
    int arr[1000];
    int p, m, fnl;

    int counter2=0;
    f=fopen(iterator[i], "r");

    if (f==NULL) {
        err(1, "%s", iterator[i]);

    }
    while((line=fgets((char*)read,sizeof(read),f))){

        sscanf(line, "%d %d %d",amp;p, amp;m, amp;fnl);
        arr[counter2]= p;
        counter2  ;
    }

    int *firstHalf = malloc((counter2) * sizeof(int));
    memcpy(firstHalf, arr, (counter2) * sizeof(int));

    //sort array;
    int k, l, tmp;

    for (k = 1; k < counter2; k  ) {

        l = k;

        while (l > 0 amp;amp; firstHalf[l - 1] > firstHalf[l]) {

            tmp = firstHalf[l];
            firstHalf[l] = firstHalf[l- 1];
            firstHalf[l- 1] = tmp;
            l--;

        }

    }

    printf("course %d project median: %d, project min: %d, project max: %dn", i 1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);

    if(!feof(f)){
        err(1, "getIn");
    }
    pthread_exit(NULL);

}

void* midterm_statistics(int i){

    FILE* f;
    int read[3];
    char* line;
    int arr2[1000];

    int p, m, fnl;

    int counter2=0;

    f=fopen(iterator[i], "r");

    if (f==NULL) {
        err(1, "%s", iterator[i]);

    }

    while((line=fgets((char*)read,sizeof(read),f))){

        sscanf(line, "%d %d %d",amp;p, amp;m, amp;fnl);
        arr2[counter2]=m;
        counter2  ;
    }
    int *firstHalf = malloc((counter2) * sizeof(int));
    memcpy(firstHalf, arr2, (counter2) * sizeof(int));

    //sort array;
    int k, l, tmp;

    for (k = 1; k < counter2; k  ) {

        l = k;

        while (l > 0 amp;amp; firstHalf[l - 1] > firstHalf[l]) {

            tmp = firstHalf[l];
            firstHalf[l] = firstHalf[l- 1];
            firstHalf[l- 1] = tmp;
            l--;

        }

    }

    printf("course %d project median: %d, project min: %d, project max: %dn", i 1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);
    if(!feof(f)){
        err(1, "getIn");
    }
    pthread_exit(NULL);

}

void* final_statistics(int i){

    FILE* f;
    char* line;
    int arr3[1000];
    int read[3];
    int p, m, fnl;

    int counter2=0;

    f=fopen(iterator[i], "r");

    if (f==NULL) {
        err(1, "%s", iterator[i]);

    }

    while((line=fgets((char*)read,sizeof(read),f))){

        sscanf(line, "%d %d %d",amp;p, amp;m, amp;fnl);
        arr3[counter2]=fnl;
        counter2  ;
    }

    int *firstHalf = malloc((counter2) * sizeof(int));
    memcpy(firstHalf, arr3, (counter2) * sizeof(int));

    //sort array;
    int k, l, tmp;

    for (k = 1; k < counter2; k  ) {

        l = k;

        while (l > 0 amp;amp; firstHalf[l - 1] > firstHalf[l]) {

            tmp = firstHalf[l];
            firstHalf[l] = firstHalf[l- 1];
            firstHalf[l- 1] = tmp;
            l--;

        }

    }

    printf("course %d project median: %d, project min: %d, project max: %dn", i 1, firstHalf[counter2/2], firstHalf[0],firstHalf[counter2-1]);

    if(!feof(f)){
        err(1, "getIn");
    }
    pthread_exit(NULL);

}



int main(int argc, const char * argv[]) {

    char k[500];

    int counter1=walker("/home/ey/Desktop/sampleFolder/", ".\.txt");
    for (i=0; i<counter1; i  ) {
        strcpy(k, "/home/ey/Desktop/sampleFolder/");
        strcat(k, file[i]);
        iterator[i]=strdup(k);
        printf("%s",iterator[i]);
    }

    printf("nMaster is startingn");

    pthread_t tid1[counter1], tid2[counter1], tid3[counter1];
    pthread_attr_t attr;
    pthread_attr_init(amp;attr);
    pthread_attr_setdetachstate(amp;attr, PTHREAD_CREATE_JOINABLE);
    printf("nslave1 startn");
    printf("n~Project Statistics~n");

    sleep(2);
    for (i=0; i<counter1; i  ) {

        rc=pthread_create(amp;tid1[i], amp;attr, (void*)*project_statistics,(void*)(intptr_t)i);

    }

    sleep(2);

    printf("nslave1 donen");


    printf("nslave2 startn");
    printf("n~Midterm Statistics~n");

    pid=fork();
    sleep(2);
    if (pid==0) {
        for (i=0; i<counter1; i  ) {

            rc=pthread_create(amp;tid2[i], amp;attr,(void*)*midterm_statistics, (void*)(intptr_t)i);
        }

        sleep(2);
        printf("nslave2 donen");
        printf("nslave3 startn");
        printf("n~Final Statistics~n");
    }
    sleep(2);

    pid1=fork();
    sleep(2);

    if ((pid1==0)amp;amp;(pid==0)) {

        for (i=0; i<counter1; i  ) {

            rc=pthread_create(amp;tid3[i], amp;attr, (void*)*final_statistics, (void*)(intptr_t)i);
        }

        sleep(2);
        printf("nslave3 donen");
        printf("nMaster is donen");
    }




    sleep(1);
    pthread_attr_destroy(amp;attr);
    pthread_exit(NULL);

}
  

Комментарии:

1. «Файл ядра» — это копия памяти процесса плюс некоторая дополнительная информация. Это записывается в файл и может использоваться для отладки программы. Если вы не можете найти файл ядра, проверьте ‘ulimit -c’. Возможно, вам потребуется изменить свой пользовательский лимит, например, на 50000. (запустите команду ulimit -c 50000). Когда у вас есть и исполняемый файл, и файл ядра, запустите ‘gdb exefile corefile’, чтобы запустить отладчик gnu. Затем выполните команду ‘backtrace’ внутри gdb. Надеюсь, это должно показать, где ваш код завершается с ошибкой. Используйте параметры gcc -O0 -ggdb для получения информации об отладке.

2. Я никогда раньше не использовал отладчик gnu. Но когда я ввел ulimit -c, я получаю 0, это нормально? также, когда я ввел ulimit -c 50000, ничего не появилось.

3. Ничего не «отображается», но при следующем сбросе ядра вашей программы будет сгенерирован файл ядра. Предупреждение: Gdb — не самый простой отладчик, доступный в Linux. Возможно, вы захотите поискать альтернативы. ddd может быть лучше.

4. Спасибо! Я выполнил шаги, о которых вы упомянули, и получил что-то __strcat_sse2_unaligned () в ../ sysdeps/ x86_64 / multiarch / strcpy-sse2-unaligned.S: 296 0x00005608e09c06ae в main (), поэтому я предполагаю, что что-то, что я сделал в main, вызывает сбой, но есть ли способ узнать точную точку?

5. В gdb , когда он останавливается из-за сбоя в сегменте [или когда вы используете его для проверки файла ядра], введите tb . Это дает вам обратную трассировку стека. Некоторые из фреймов будут вашим кодом. Наиболее близким к ошибке является обычный подозреваемый. Убедитесь, что вы компилируете с -g [и, необязательно, с -O0 vs -O2 ]

Ответ №1:

В main , ваш strcat сбой.

Адрес источника — file[i] . file это глобальный массив char * указателей. Но, по-видимому, это никогда ни к чему не инициализируется.

Таким образом, strcat вызов будет иметь второй аргумент NULL , который вызывает ошибку segfault.

Это может произойти, если walker возвращает ненулевое значение, которое было бы, если бы каталог не существовал (т. Е. возвращаемое значение равно WALK_BADOPEN ). Это может объяснить, почему это работает в одной системе, но не в другой (т. Е. каталог существует в одной, но не в другой).

Итак, walker используется возврат кода ошибки, но main это возвращаемое значение используется в качестве подсчета. Эта логика неверна. Я полагаю, вам нужно будет изменить возвращаемое значение walker или main получить счетчик другим способом.

Простой способ исправить это — присвоить кодам ошибок отрицательные значения и main проверить это. Затем walker можно вернуть количество правильно.

Итак, если каталог не существует, возвращаемое значение равно 2. Цикл в main завершится с ошибкой file[0] , потому что ничего в file не было установлено ни на что.


Обновить:

Но на данный момент, поскольку я знаю, что каталог существует, могу ли я пытаться открыть его неправильным способом?

Не существует «неправильного» способа использования opendir — он либо открывается, либо завершается сбоем, с которым вы уже справляетесь.

Но внутри walker вы не можете полагаться на d_name значение от итерации цикла к итерации, поэтому вам приходится использовать strdup .

Изменение:

 file[counter] = entry->d_name;
  

В:

 file[counter] = strdup(entry->d_name);
  

Кроме того, вам следует ограничить проверку по максимуму для file (например, в настоящее время только 500)


ОБНОВЛЕНИЕ # 2:

В ваших потоковых функциях вы выполняли fgets into read [не удачный выбор из-за read функции libc]. Но это было:

 int read[3];
  

Итак, длина линейного буфера составляла всего 12 байт. Это может привести к fgets считыванию строки как двух частично разделенных строк. Это может привести к arr переполнению массива

Я изменил это на:

 char buf[1000];
  

Я объединил реплицированный код потоковых функций в общий.

Обратите внимание, что firstHalf было выделено, но никогда не освобождалось. Итак, это была «утечка». Я добавил free вызов для этого.

Также обратите внимание, что не было ничего, fclose(f) что могло бы привести к fopen возврату NULL (т. Е. другого источника для segfault).

Я также переработал объединение потоков и fork логику и добавил waitpid . Также обратите внимание на добавление exit(0) в дочерний код форка.

Пытаясь разобраться, я все упрощал, так что следующее — это справедливая доработка, которая поначалу может показаться немного «чуждой» [пожалуйста, простите за беспричинную очистку стиля]:

 #include <stdio.h>
#include <unistd.h>
#include <pthread.h>
#include <err.h>
#include <sys/types.h>
#include <dirent.h>
#include <regex.h>
#include <string.h>
#include <stdlib.h>
#include <sys/wait.h>

#if 1
#define MYDIR   "/home/ey/Desktop/sampleFolder/"
#else
#define MYDIR   "/tmp/data/"
#endif

#define dbgprt(_fmt...) 
    do { 
        if (opt_dbg) 
            printf(_fmt); 
    } while (0)

int opt_dbg;

int pid;
int i;
int rc;
int pid1;
int counter;

char *iterator[500];
char *file[500];

enum {
    WALK_OK = 0,
    WALK_BADPATTERN = -1,
    WALK_BADOPEN = -2,
};

int
walker(const char *dir,const char *pattern)
{
    struct dirent *entry;
    regex_t reg;
    DIR *d;

    counter = 0;
    if (regcomp(amp;reg,pattern,REG_EXTENDED | REG_NOSUB))
        return WALK_BADPATTERN;

    d = opendir(dir);
    if (d == NULL)
        return WALK_BADOPEN;

    while (1) {
        entry = readdir(d);
        if (entry == NULL)
            break;

        if (!regexec(amp;reg,entry->d_name,0,NULL,0)) {
            puts(entry->d_name);
            file[counter] = strdup(entry->d_name);
            counter = counter   1;
        }
    }

    closedir(d);
    regfree(amp;reg);
    return counter;
}

void *
thread_common(void *arg,int column)
{
    intptr_t i = (intptr_t) arg;
    FILE *f;

    // size_t len;
    char *line;
    int data[3];
    char buf[1000];
    int arr[1000];

    int counter2 = 0;

    f = fopen(iterator[i],"r");
    if (f == NULL) {
        err(1,"%s",iterator[i]);
    }

    dbgprt("DEBUG reading ...n");
    while (1) {
        line = fgets(buf,sizeof(buf),f);
        if (line == NULL)
            break;

        sscanf(line,"%d %d %d",amp;data[0],amp;data[1],amp;data[2]);
        arr[counter2] = data[column];

        counter2  ;
        dbgprt("DEBUG line %d %sn",counter2,iterator[i]);
        if (counter2 >= 1000) {
            printf("overflow %sn",iterator[i]);
            exit(1);
        }
    }

    if (!feof(f)) {
        err(1,"getIn");
    }

    fclose(f);

    int *firstHalf = malloc((counter2) * sizeof(int));
    memcpy(firstHalf,arr,(counter2) * sizeof(int));

    // sort array;
    int k,
     l,
     tmp;

    dbgprt("DEBUG sorting ...n");
    for (k = 1; k < counter2; k  ) {
        for (l = k;  (l > 0) amp;amp; (firstHalf[l - 1] > firstHalf[l]);  l--) {
            tmp = firstHalf[l];
            firstHalf[l] = firstHalf[l - 1];
            firstHalf[l - 1] = tmp;
            l--;
        }
    }

    printf("course %ld project median: %d, project min: %d, project max: %dn",
        i   1,firstHalf[counter2 / 2],firstHalf[0],firstHalf[counter2 - 1]);

    free(firstHalf);

    return (void *) 0;
}

void *
project_statistics(void *arg)
{

    return thread_common(arg,0);
}

void *
midterm_statistics(void *arg)
{

    return thread_common(arg,1);
}

void *
final_statistics(void *arg)
{

    return thread_common(arg,2);
}

int
main(int argc,char **argv)
{
    intptr_t i;
    char *cp;
    char krkt[500];

    --argc;
      argv;

    for (;  argc > 0;  --argc,   argv) {
        cp = *argv;
        if (*cp != '-')
            break;

        switch (cp[1]) {
        case 'd':
            opt_dbg = 1;
            break;

        default:
            break;
        }
    }

    int counter1 = walker(MYDIR,".\.txt");
    dbgprt("main: walker returned %dn",counter1);
    if (counter1 <= 0)
        exit(1);

    for (i = 0; i < counter1; i  ) {
        strcpy(krkt,MYDIR);
        if (file[i] == NULL)
            exit(3);
        strcat(krkt,file[i]);
        iterator[i] = strdup(krkt);
        printf("%sn",iterator[i]);
    }

    printf("nMaster is startingn");

    pthread_t tid1[counter1];
    pthread_t tid2[counter1];
    pthread_t tid3[counter1];
    pthread_attr_t attr;

    pthread_attr_init(amp;attr);
    pthread_attr_setdetachstate(amp;attr,PTHREAD_CREATE_JOINABLE);
    printf("nslave1 startn");
    printf("n~Project Statistics~n");

    //sleep(2);
    for (i = 0; i < counter1; i  )
        rc = pthread_create(amp;tid1[i],amp;attr,project_statistics,(void *) i);

    for (i = 0; i < counter1; i  )
        rc = pthread_join(tid1[i],NULL);
    printf("nslave1 donen");

    pid = fork();
    if (pid == 0) {
        printf("nslave2 startn");
        printf("n~Midterm Statistics~n");

        for (i = 0; i < counter1; i  )
            rc = pthread_create(amp;tid2[i],amp;attr,midterm_statistics,(void *) i);

        for (i = 0; i < counter1; i  )
            rc = pthread_join(tid2[i],NULL);

        printf("nslave2 donen");
        exit(0);
    }

    pid1 = fork();
    if (pid1 == 0) {
        printf("nslave3 startn");
        printf("n~Final Statistics~n");

        for (i = 0; i < counter1; i  )
            rc = pthread_create(amp;tid3[i],amp;attr,final_statistics,(void *) i);

        for (i = 0; i < counter1; i  )
            rc = pthread_join(tid3[i],NULL);

        printf("nslave3 donen");
        exit(0);
    }

    waitpid(pid,NULL,0);
    waitpid(pid1,NULL,0);
    printf("nMaster is donen");

    pthread_attr_destroy(amp;attr);

    return 0;
}
  

ОБНОВЛЕНИЕ # 3:

Также начало main мне не очень понятно, почему мы ждем ‘d’ и делаем switch-case, зачем нужно добавлять argv и argc в код? Поскольку код в некотором роде зависит от argv и argc, может ли мой способ компиляции вызывать проблему?

argc/argv Код просто анализирует аргументы опции. Это довольно стандартный шаблон.

В этом случае, если вы это сделаете, ./main -d это установится opt_d . Затем dbgprt макрос проверяет это и, если установлен, выполняет printf . Таким образом, все, printf относящиеся к отладочным выводам, были изменены на dbgprt .

Это не изменяет выполнение программы, просто добавляет дополнительный отладочный вывод. Вы можете добавить больше dbgprt , если хотите.

И вы можете добавить свои собственные параметры командной строки, добавив их в switch/case .

Этот метод для «отладки printf» довольно распространен. Я предпочитаю использовать это gdb там, где это возможно. Лично я пытаюсь вызвать программу с помощью gdb только тогда, когда у меня «серьезная» ошибка, такая как segfault. gdb могу ли я определить строку сбоя. Затем я добавляю такие вещи, как assert отладочные распечатки и т.д., Чтобы заранее устранить проблему.

Я вроде понимаю логику, но мне не удалось запустить код. Я имею в виду, что он все еще работает над xcode.

Исправленные мной ошибки применимы и к xcode версии.

Но в Linux это не выдает никаких ошибок или предупреждений, но при вводе ./ main Я ничего не получаю…

Если вы работаете на Linux, используйте -d . Затем обратите внимание на вывод первого dbgprt после вызова walker .

Мое лучшее предположение заключается в том, что walker возвращает отрицательное значение (т. Е. Каталог не существует — шаблон в порядке, так что это то, что осталось). Или возвращает 0, указывающее, что в каталоге нет файлов или нет файлов, соответствующих шаблону.

Программа должна завершиться с exit(1) , поэтому проверьте код ошибки (например echo $? )

Вы могли [и, подумав об этом, вероятно, должны] сначала изменить это dbgprt обратно на printf , чтобы оно всегда печаталось, даже если вы не указываете -d . Таким образом, вы не получите «тихий» сбой, но программа заранее сообщит вам, если что-то не так.


Один из способов помочь в отладке этого использовать gdb . Выполняйте gdb ./main . Затем сделайте, b walker чтобы установить точку останова на walker , Затем введите run . gdb остановит программу при первой инструкции walker .

Затем вы можете ввести s в «один шаг» программы. Вы можете продолжать повторять это. Когда у вас появится запрос, вы можете p выполнить команду gdb для печати переменных. Это позволит вам увидеть, что walker делает.

Когда в строке есть вызов libc функции, такой как opendir , readdir , strdup и т.д., Выполнение s будет пытаться выполнить эти функции одним шагом. Длинный и не очень полезный. Итак, в такой строке используйте n вместо этого. Если вы делаете это s по ошибке, вы можете ввести finish .

Когда вы решите, что выполнили достаточно шагов, вы можете ввести c , который продолжит выполнение программы на полной скорости.

gdb имеет много команд, приведенные выше — лишь некоторые. У него есть встроенная справка, поэтому введите help в командной строке. Или, help b и т.д. Доступно множество руководств.

Комментарии:

1. Но на данный момент, поскольку я знаю, что каталог существует, могу ли я пытаться открыть его неправильным способом?

2. Спасибо! Я внес изменение, о котором вы упомянули выше (strdup), но я все еще получаю ошибку сегментации. Поскольку вы сказали, что это может произойти, когда нет каталога для открытия, я дважды проверил, правильно ли я указываю отформатированный путь к функции walker, и это так. Я также пытался инициализировать файл char * [500], и единственное, что не выдает ошибку, — это инициализация с помощью {NULL}, что для меня не имеет никакого смысла..

3. Я получаю разные результаты в своей системе, но этого следовало ожидать. Можете ли вы отредактировать свой вопрос и опубликовать файл данных. Или, каким должен быть диапазон значений для трех значений grade [я могу сгенерировать тестовые файлы]. Я активно редактирую здесь, и синхронизация / ожидание потоков и процессов нуждается в доработке. Итак, можете ли вы отредактировать свой вопрос, чтобы объяснить общий замысел. Выполнение sleep после первого pthread_create не является детерминированным. Лучше зацикливаться pthread_join . Это приводит к завершению всех потоков slave1 перед запуском потоков slave2. Это то, чего вы хотите?

4. По умолчанию можно объединить, поэтому не требуется. Я нашел другие потенциальные возможности segfault. Я объединяю код потока в общую функцию, потому что разница заключается только в том, какой столбец данных используется (т. Е. 1, 2 или 3). В противном случае все функции потока идентичны и копируют друг друга.