2016-10-22 37 views
1

我正在尝试学习多线程和多进程编程。我对于多线程/编程和Ubuntu环境都很新。我在下面的代码上工作了10个小时,并修复了所有错误和警告。我开始用xCode对其进行编码,并且它运行完美,并且完全按照我希望的方式执行,而不会在该环境中发出任何警告或错误。但是,当试图在Ubuntu上编译和运行时,我得到了分段错误(核心转储),我无法理解导致此错误的代码的哪一部分。任何想法的哪一部分可能会导致错误?或为什么我得到那个?正如我记得Linux没有核心?提前感谢你!虚拟Ubuntu64位上的分段错误(核心转储)

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

} 
+1

“核心文件”是进程内存的副本以及一些额外的信息。它被写入文件并可用于调试程序。 如果找不到核心文件,请选中'ulimit -c'。您可能需要将您的用户限制更改为50000.(运行命令ulimit -c 50000)。 当你有可执行文件和核心文件时,运行'gdb exefile corefile'来启动gnu调试器。然后在gdb中发出命令'backtrace'。这应该显示你的代码失败,希望。使用gcc选项-O0 -ggdb来获取调试信息。 –

+0

我以前从未使用过gnu调试器。但是,当我输入ulimit -c,我得到一个0是正常的?当我输入ulimit -c 50000时也没有出现。 – Valentino

+1

没有'显示',但下一次你的程序内核时,核心文件将被生成。警告词:Gdb不是Linux上最简单的调试器。你可能想要寻找替代品。 DDD可能会更好。 –

回答

1

main,你strcat的断层。

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

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

如果walker返回一个非零值,如果目录不存在(即返回值为WALK_BADOPEN),则可能发生这种情况。这可以解释为什么它在一个系统上工作而不是另一个系统(即目录存在于一个系统上而不是另一个上)。

因此,walker正在使用返回错误代码,但main正在使用此返回值作为计数。这个逻辑是不正确的。我相信你需要改变返回值walker或让main以不同的方式获得计数。

解决此问题的简单方法是将错误代码设置为负值,并且检查此错误代码为main。然后,walker可以正确返回计数。

因此,如果目录不存在,则返回值是2中main循环将在file[0]的错,因为没有在file已设置为任何东西。


UPDATE:

但对于这个时间,因为我知道目录确实存在,可我试图以错误的方式来打开它?

没有“错误”的方式使用opendir - 它可以打开或失败,你已经处理。

但是,在walker之内,不能依赖从循环迭代到迭代的d_name值,因此,必须使用strdup

变化:

file[counter] = entry->d_name; 

分为:

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

此外,你应该限制对证最大限度地为file(例如目前仅500)


更新#2:

在你的线程功能,你在做fgetsread [因为libc中的read功能一个不错的选择。但是,它是:

int read[3]; 

因此,行缓冲器是只有 12字节长。这可能导致fgets读取一行为两个部分分割行。这可能导致arr阵列溢出

我把它改为:

char buf[1000]; 

我已经合并的线程函数的代码复制到一个常见的一种。

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

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

我也重写了线程连接和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(&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:

也是主要的开始不是那么清楚,我,为什么我们要等待一个“d”,使一个开关的情况下,为什么它是需要添加argv an d argc的代码?由于代码在某种程度上取决于argv和argc,我的编译方式会导致问题吗?

argc/argv代码只是解析选项参数。这是非常标准的样板。

在这种情况下,如果你做./main -d它设置opt_d。然后,dbgprt宏测试这个,如果设置了,则执行printf。因此,所有与调试输出相关的printf都被更改为dbgprt

这样做不是改变程序的执行,只是增加了额外的调试输出。如果您愿意,您可以添加更多dbgprt

而且,您可以通过将它们添加到switch/case来添加自己的命令行选项。

这种“printf调试”技术相当普遍。我更喜欢在可能的情况下使用gdb。就我个人而言,只有当我有一个“严重”的错误,例如段错误时,我才会尝试使用gdb来调用该程序。 gdb我可以识别断层线。然后,我添加诸如assert之类的东西,调试打印等,以预先减轻问题。

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

我修正的错误也适用于xcode版本。

但对于Linux来说它不给任何错误或警告,但是当输入./main我什么也得不到......

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

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

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

可以 [和思考之后,可能应该]改变第一dbgprtprintf,所以它始终打印,即使您没有指定-d。这样,你就不会得到“沉默”的失败,但是如果有什么不妥之处,程序会先告诉你。


一种方法来帮助调试这个使用gdb。做gdb ./main。然后,做b walkerwalker上设置断点,然后输入rungdb将在第一条语句walker处停止该程序。

然后,您可以输入s为“单步”程序。你可以继续重复这一点。当你有提示时,你可以用gdb的p命令来打印变量。这将让你看到walker做什么。

当线具有到libc函数的调用,如opendirreaddirstrdup等做s将试图单一步骤这些功能。冗长,并没有那么有用。所以,在这样的路线上,改为使用n。如果您错误地输入s,您可以输入finish

当你觉得你已经足够多了,你可以键入c,这将继续全速执行程序。

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

+0

但是因为我知道这个目录确实存在,我可以试图以错误的方式打开它吗? – Valentino

+0

谢谢!我做了上面提到的更改(strdup),但我仍然遇到分段错误。既然你说过,当没有目录打开的时候我可能会发生,如果我给正确格式化的walker功能路径,我会检查一下,我是。我也尝试初始化char *文件[500],只有不会给出错误的是用{NULL}初始化,这对我没有任何意义。 – Valentino

+0

我在我的系统上得到了不同的结果,但这是可以预料的。你可以编辑你的问题并发布数据文件。或者,三个等级值的值应该是多少[我可以生成测试文件]。我在这里积极编辑,线程和进程的同步/等待需要工作。所以,你可以编辑你的问题来解释整体意图。在第一个pthread_create之后做'睡眠'是非确定性的。更好地循环'pthread_join'。这使得所有slave1线程在启动slave2线程之前完成。那是你要的吗? –