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線程之前完成。那是你要的嗎? –