【问题标题】:Segmentation fault (core dumped) on Virtual Ubuntu64bit虚拟 Ubuntu64 位上的分段错误(核心转储)
【发布时间】:2016-10-22 19:54:59
【问题描述】:

我正在尝试学习多线程和多进程编程。我对多线程/处理编程和 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(&reg, pattern, REG_EXTENDED | REG_NOSUB))
        return WALK_BADPATTERN;
    if (!(d = opendir(dir)))
        return WALK_BADOPEN;
    while ((entry = (readdir(d))) ){
        if (!regexec(&reg, entry->d_name, 0, NULL, 0)){
            puts(entry->d_name);
            file[counter]=entry->d_name;
            counter=counter+1;}
    }
    closedir(d);
    regfree(&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",&p, &m, &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 && 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: %d\n", 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",&p, &m, &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 && 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: %d\n", 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",&p, &m, &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 && 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: %d\n", 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 starting\n");

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

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

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

    }

    sleep(2);

    printf("\nslave1 done\n");


    printf("\nslave2 start\n");
    printf("\n~Midterm Statistics~\n");

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

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

        sleep(2);
        printf("\nslave2 done\n");
        printf("\nslave3 start\n");
        printf("\n~Final Statistics~\n");
    }
    sleep(2);

    pid1=fork();
    sleep(2);

    if ((pid1==0)&&(pid==0)) {

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

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

        sleep(2);
        printf("\nslave3 done\n");
        printf("\nMaster is done\n");
    }




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

}

【问题讨论】:

  • “核心文件”是进程内存加上一些额外信息的副本。它被写入文件,可用于调试程序。如果找不到核心文件,请检查“ulimit -c”。您可能需要将用户限制更改为例如50000。(运行命令 ulimit -c 50000)。当您拥有可执行文件和核心文件时,运行“gdb exefile corefile”来启动 gnu 调试器。然后在 gdb 中发出命令“backtrace”。希望这应该显示您的代码失败的地方。使用 gcc 选项 -O0 -ggdb 获取调试信息。
  • 我以前从未使用过 gnu 调试器。但是当我输入 ulimit -c 时,我得到一个 0 是正常的吗?同样,当我输入 ulimit -c 50000 时,什么也没有出现。
  • 没有“显示”,但是下次您的程序进行核心转储时,将生成核心文件。警告:Gdb 不是 Linux 上最简单的调试器。您可能想寻找替代品。 ddd 可能会更好。
  • 谢谢!我通过你提到的步骤得到了一些东西 __strcat_sse2_unaligned () at../sysdeps/x86_64/multiarch/strcpy-sse2-unaligned.S:296 0x00005608e09c06ae in main() 所以我假设我在主要原因中做了什么导致崩溃但是有没有办法知道确切的点?
  • gdb 中,当它停在段错误上[或当您使用它检查核心文件时],键入tb。这为您提供了堆栈回溯。一些框架将是您的代码。最接近错误的是通常的嫌疑人。确保您使用 -g [以及,可选地,-O0-O2] 进行编译]

标签: ubuntu


【解决方案1】:

main 中,您的strcat 出现故障。

源地址是file[i]filechar * 指针的全局数组。但是,它 [显然] 从未初始化为任何东西。

所以strcat 调用将有第二个参数NULL,这会导致段错误。

如果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 转换为 read [不是因为 libc 的 read 函数是一个好的选择]。但是,它是:

int read[3];

因此,行缓冲区只有 12 个字节长。这可能会导致fgets 将一行读取为两个 部分拆分的行。这可能会导致arr 数组溢出

我把它改成了:

char buf[1000];

我已将线程函数的复制代码组合成一个通用代码。

请注意,firstHalf 已分配但从未释放。所以,它是“泄漏”的。我添加了一个free 呼叫。

另请注意,没有fclose(f) 可能导致fopen 返回NULL(即段错误的另一个来源)。

我还重新设计了线程连接和fork 逻辑并添加了waitpid。还要注意在 fork 的子代码中添加了 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(&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(&reg,entry->d_name,0,NULL,0)) {
            puts(entry->d_name);
            file[counter] = strdup(entry->d_name);
            counter = counter + 1;
        }
    }

    closedir(d);
    regfree(&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",&data[0],&data[1],&data[2]);
        arr[counter2] = data[column];

        counter2++;
        dbgprt("DEBUG line %d %s\n",counter2,iterator[i]);
        if (counter2 >= 1000) {
            printf("overflow %s\n",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) && (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: %d\n",
        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 %d\n",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("%s\n",iterator[i]);
    }

    printf("\nMaster is starting\n");

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

    pthread_attr_init(&attr);
    pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_JOINABLE);
    printf("\nslave1 start\n");
    printf("\n~Project Statistics~\n");

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

    for (i = 0; i < counter1; i++)
        rc = pthread_join(tid1[i],NULL);
    printf("\nslave1 done\n");

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

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

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

        printf("\nslave2 done\n");
        exit(0);
    }

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

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

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

        printf("\nslave3 done\n");
        exit(0);
    }

    waitpid(pid,NULL,0);
    waitpid(pid1,NULL,0);
    printf("\nMaster is done\n");

    pthread_attr_destroy(&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 调用程序,仅当我遇到“严重”错误(例如段错误)时。 gdb我可以识别故障线路吗?然后,我添加了诸如assert、调试打印等内容,以预先缓解问题。

我有点理解逻辑,但我无法运行代码。我的意思是它仍在 xcode 上工作。

我修复的bug同样适用于xcode版本。

但在 Linux 中它不会给出任何错误或警告,但是当输入 ./main 时我什么也得不到...

如果您在 linux 上运行,请使用 -d。然后,注意调用walker 后第一个dbgprt 的输出。

我最好的猜测是 walker 返回一个负值(即目录不存在——模式没问题,所以剩下的就是这样)。或者,返回 0 表示目录没有文件,或者没有与模式匹配的文件。

程序应以exit(1) 终止,因此请检查错误代码(例如echo $?

可以 [在考虑之后,可能应该] 将第一个 dbgprt 更改回 printf,因此它始终会打印,即使您不要指定-d。这样一来,您就不会遇到“静默”失败,但程序会提前告诉您是否有问题。


帮助调试此问题的一种方法使用gdb。做gdb ./main。然后,执行b walkerwalker 上设置断点,然后键入rungdb 将在walker 的第一条语句处停止程序。

然后您可以键入s 来“单步执行”程序。你可以一直重复这个。当你有提示时,你可以使用 gdb 的p 命令来打印变量。这将允许您查看 walker 的作用。

当一行调用libc 函数时,例如opendirreaddirstrdup 等,执行s 将尝试单步执行这些函数。冗长,而且不是那么有用。因此,在这样的行上,请改用n。如果你错误地输入了s,你可以输入finish

当你认为你已经足够了,你可以输入c,它会继续全速执行程序。

gdb 有很多命令,以上只是一些。它有内联帮助,所以在提示符下输入help。或者,help b等。有很多教程可供使用。

【讨论】:

  • 但是这次我知道该目录确实存在,我会不会尝试以错误的方式打开它?
  • 谢谢!我做了你上面提到的改变(strdup),但我仍然遇到分段错误。既然您说没有要打开的目录时可能会发生这种情况,所以我仔细检查了是否为 walker 函数提供了格式正确的路径,并且我是。我还尝试初始化 char* 文件 [500],唯一没有出错的是使用 {NULL} 进行初始化,这对我来说没有任何意义..
  • 我在我的系统上得到了不同的结果,但这是意料之中的。您可以编辑您的问题并发布数据文件吗?或者,三个等级值的取值范围应该是多少[我可以生成测试文件]。我在这里积极编辑,线程和进程的同步/等待需要工作。那么,您能否编辑您的问题以解释总体意图。在第一个 pthread_create 之后执行 sleep 是不确定的。最好在pthread_join 上循环。这使得所有 slave1 线程在启动 slave2 线程之前完成。这就是你想要的吗?
  • default 是可连接的,所以不需要。我发现了其他潜在的段错误机会。我将线程代码组合成一个通用函数,因为唯一的区别是使用哪一列数据(即 1、2 或 3)。否则,所有线程函数都是相同的并相互复制。
猜你喜欢
  • 1970-01-01
  • 2018-10-25
  • 2013-11-28
  • 1970-01-01
  • 2015-06-25
  • 2021-06-03
相关资源
最近更新 更多