【问题标题】:Segfault in multithreaded download program多线程下载程序中的段错误
【发布时间】:2023-03-16 12:15:01
【问题描述】:

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

#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

【问题讨论】:

  • 代码太多。请尽可能将其减少到重现问题的最小样本。
  • 猜测听起来并不高效。为什么不在gdb 中运行它并获取堆栈跟踪,这样您就可以确切地知道哪行代码是问题所在?
  • 如果您遇到分段错误,您应该做的第一件事是在调试器中运行您的程序。这将帮助您确定位置,还可以让您检查变量以帮助您找出原因。
  • 字符 lineptr = NULL; / getline 的参数 */ 为什么你不给 lineptr 分配内存?我认为这可能是问题
  • @Raghuram 因为如果 lineptr 为 NULL,getline 会分配所需的内存。这就是手册页所说的。

标签: c multithreading curl pthreads segmentation-fault


【解决方案1】:

当你声明struct downloadfile ofile 时,它的stream 字段被填满垃圾并且可能不是0。当ofile 然后被传递给writedata 回调时(作为调用curl_easy_perform 的结果),条件@因此 987654326@ 可能是错误的,并导致 writedata 在未打开的流上调用 fwrite

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

【讨论】:

    【解决方案2】:
    for ( ; *link != '\0'; ++link) {
    

    如果路径不包含'/':

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

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

    【讨论】:

      【解决方案3】:

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

      【讨论】:

        猜你喜欢
        • 2015-06-09
        • 2014-01-15
        • 2011-12-29
        • 1970-01-01
        • 2018-04-25
        • 1970-01-01
        • 2015-09-20
        相关资源
        最近更新 更多