2012-05-25 4 views
2

Я пытаюсь написать простую программу. Он должен читать ссылки из stdin и загружать эти ссылки в отдельные потоки. Я написал следующий код, но я получаю ошибку segmetation. Может кто-нибудь догадаться, почему?Segfault в многопотоковой программе для скачивания

#include <stdio.h> 
#include <stdlib.h> 
#include <string.h> /* memcpy */ 
#include <curl/curl.h> 
#include <pthread.h> 
#define NUMTHREADS 3 

struct downloadfile { 
    char *filename; 
    FILE *stream; 
}; 

pthread_mutex_t mutex; 

/* writedata: custom fwrite for curl writefunction */ 
static size_t writedata(void *buffer, size_t size, size_t nmemb, void *stream) 
{ 
    struct downloadfile *out = (struct downloadfile *) stream; 
    if (out && !out->stream) { 
     out->stream = fopen(out->filename, "w"); 
     if (!out->stream) 
      return -1; /* can't open file to write */ 
    } 
    return fwrite(buffer, size, nmemb, out->stream); 
} 

/* getfilename: gets a file's name from a link. */ 
char *getfilename(const char *link) 
{ 
    const char *fnstart = NULL; /* start of filename*/ 
    size_t len = 0; /* length of filename*/ 

    for (; *link != '\0'; ++link) { 
     if (*link == '/') { 
      fnstart = link + 1; 
      len = 0; 
     } else { 
      ++len; 
     } 
    } 

    char *filename = malloc(len + 1); 
    memcpy(filename, fnstart, len); 
    filename[len] = '\0'; 
    return filename; 
} 

/* downloadthread: get a line from stdin, and try to donwload it.*/ 
void *downloadthread(void *ignored) 
{ 

    puts("in a download thread"); 
    CURL *curl; 
    curl = curl_easy_init(); 
    ssize_t read; /* number of characters read from a line */ 

    if (!curl) { /* couldn't get curl handle */ 
     fputs("Couldn't get curl handle", stderr); 
     pthread_exit(NULL); 
    } 

    for (;;) { /* readline and download loop */ 
     size_t n; /* argument to getline */ 
     char *lineptr = NULL; /* argument to getline */ 
     struct downloadfile ofile; 

      /* I think I need mutex protect the getline, but I am not sure */ 
     pthread_mutex_lock(&mutex); 
     read = getline(&lineptr, &n, stdin); 
     pthread_mutex_unlock(&mutex); 

     if (read == EOF) 
        break; 

     ofile.filename = getfilename(lineptr); 
     curl_easy_setopt(curl, CURLOPT_URL,lineptr); 

     /* follow http redirects */ 
     curl_easy_setopt(curl, CURLOPT_FOLLOWLOCATION ,1L); 
     curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, writedata); 
     curl_easy_setopt(curl, CURLOPT_WRITEDATA, &ofile); 

     curl_easy_perform(curl); 

     free(ofile.filename); 
     free(lineptr); 

     if (ofile.stream) 
      fclose(ofile.stream); 
    } 
    curl_easy_cleanup(curl); 
    pthread_exit(NULL); 
} 

int main() 
{ 
    size_t i; 
    int rc; 
    pthread_t threads[NUMTHREADS]; 

    curl_global_init(CURL_GLOBAL_ALL); 
    pthread_mutex_init(&mutex, NULL); 

    /* fire up threads */ 
    for (i = 0; i < NUMTHREADS; i++) { 
     rc = pthread_create(&threads[i], NULL, downloadthread, NULL); 
     if (rc) { 
      printf("Error, return code from pthread is %d\n", rc); 
      exit(-1); 
     } 
    } 

    /* join all threads before cleaning up */ 
    for (i = 0; i < NUMTHREADS; i++) 
     pthread_join(threads[i], NULL); 

    /* cleanup and exit */ 
    pthread_mutex_destroy(&mutex); 
    pthread_exit(NULL); 
} 

Редактировать: Здесь выводятся данные gdb. Мне это не очень понравилось.

[New Thread 0xb61feb40 (LWP 3778)] 
[New Thread 0xb57ffb40 (LWP 3779)] 
[New Thread 0xb4ffeb40 (LWP 3780)] 
[Thread 0xb61feb40 (LWP 3778) exited] 
[Thread 0xb57ffb40 (LWP 3779) exited] 
[Thread 0xb4ffeb40 (LWP 3780) exited] 

Program received signal SIGSEGV, Segmentation fault. 
[Switching to Thread 0xb7b25b40 (LWP 3773)] 
0xb7e02310 in fwrite() from /lib/libc.so.6 
(gdb) bt 
#0 0xb7e02310 in fwrite() from /lib/libc.so.6 
#1 0xb7f6dd53 in ??() from /usr/lib/libcurl.so.4 
#2 0xb7f85a5e in ??() from /usr/lib/libcurl.so.4 
#3 0xb7f86bb5 in ??() from /usr/lib/libcurl.so.4 
#4 0xb7f87573 in curl_easy_perform() from /usr/lib/libcurl.so.4 
#5 0x08048d99 in downloadthread (ignored=0x0) at downloader.c:91 
#6 0xb7f47ce8 in start_thread() from /lib/libpthread.so.0 
#7 0xb7e874de in clone() from /lib/libc.so.6 
+2

Слишком много кода. Пожалуйста, уменьшите его как можно больше до минимального образца, который воспроизводит проблему. –

+0

Угадание звучит неэффективно. Почему бы не запустить его в 'gdb' и получить трассировку стека, чтобы вы точно знали, какая строка кода является проблемой? – mpontillo

+0

Если вы получаете ошибки сегментации, первое, что вам нужно сделать, - запустить вашу программу в отладчике. Это поможет вам определить местоположение, а также позволить вам просматривать переменные, чтобы помочь вам выяснить причину. –

ответ

2

При объявлении struct downloadfile ofile, его stream поле заполняется мусором и, вероятно, не 0. Когда ofile затем передается writedata обратного вызова (в результате вызова curl_easy_perform), условие out && !out->stream таким образом, может быть ложным и вызвать writedata позвонить fwrite на нераспечатанный поток.

Так что просто замените заявление ofile на struct downloadfile ofile = { 0, 0 };.

+0

Большое спасибо. Вот и все :) – yasar

0
for (; *link != '\0'; ++link) { 

В случае, если путь не содержит '/':

for (fnstart=link ; *link != '\0'; ++link) { 

из (ниже петли) if (!fnstart) return BAD_STUFF;

0

Проверьте функцию char *getfilename(const char *link). Если массив символов, переданный как параметр, не содержит /, переменная const char *fnstart останется NULL, вы в конечном счете попытаетесь установить memcpy по крайней мере один байт из NULL.