Segfault在多线程下载程序中

时间:2012-05-25 09:39:09

标签: c multithreading curl pthreads segmentation-fault

我正在尝试编写一个简单的程序。它应该从stdin读取链接,并在单独的线程中下载这些链接。我写了下面的代码,但是我遇到了分段错误。谁能想到为什么?

#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

3 个答案:

答案 0 :(得分:2)

当您声明struct downloadfile ofile时,其stream字段将填充垃圾,可能不是0.当ofile传递给writedata回调时(由于调用curl_easy_perform},条件out && !out->stream因此可能是错误的,并导致writedata在未打开的流上调用fwrite

所以只需将ofile声明替换为struct downloadfile ofile = { 0, 0 };

答案 1 :(得分:0)

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

如果路径不包含'/':

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

(循环下方)if (!fnstart) return BAD_STUFF;

答案 2 :(得分:0)

检查char *getfilename(const char *link)功能。如果作为参数传递的字符数组不包含任何/,则const char *fnstart变量将保持为NULL,您最终将尝试memcpy至少一个NULL字节。