1

嗨,我需要一些有关并行下载程序的帮助。

目前,它是并行下载同一个文件,而不是同时下载多个文件。

fork和有问题fgets,不知道如何修复它们。谢谢你。

#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
FILE *file; /*declare the file pointer*/
#define LINE_MAX 1000
char line [LINE_MAX];
//Parent process
int main()
{
pid_t  pid;
file= fopen ("urls.txt", "rt"); /*open file and read it*/

if(!file)
{
    perror("fopen");
    exit(-1);
}

int numberOfChildren = 0;

while (!feof (file)) {

memset (line,'\0',1000);

char *urlPtr; 


while (!feof (file))
{
urlPtr= fgets (line,LINE_MAX, file);

if(urlPtr)
{

int lineLen = strlen(urlPtr);
urlPtr[lineLen-1] = '\0';
pid = fork();
++numberOfChildren;

    if (pid == 0) {  /* child process */
        execlp("/usr/bin/wget", "wget", urlPtr, NULL);
        }

    else if (pid < 0) { /* error occurred */
        fprintf(stderr, "Fork Failed");
        exit(-1);
        }
}
}
    while (numberOfChildren>0) { /* parent process */
    /* parent will wait for the child to complete */
        wait (NULL);
        --numberOfChildren;
        printf ("Child Complete");
        }



}   
fclose (file); /*close file command*/


return 0;
}
4

2 回答 2

3

您在 URL 读取循环之外进行了 fork() 检查。你首先阅读了大量的 URL 并产生了很多孩子,然后进行 pid 检查。尝试

while (!feof (file))
{
    urlPtr= fgets (line,LINE_MAX, file);
    pid = fork();
    if (pid == 0) {  /* child process */
         execlp("/usr/bin/wget", "wget", urlPtr, NULL);
    }
    else if (pid < 0) { /* error occurred */
        fprintf(stderr, "Fork Failed");
        exit(-1);
    }
    ++numberOfChildren;
}
于 2012-09-23T23:07:06.740 回答
1

您应该在 之后放置诊断打印并退出execlp()(但在 之后的子代码中if)。您可能还应该在执行之前关闭输入文件wget;该程序不需要它打开。这次没有造成太大的伤害,但保持整洁是件好事。您的父母可能不应该仅仅因为一个孩子失败而退出fork();一般来说,您还有其他孩子,您应该等待。不过,您可能会在此时停止处理该文件。你绝对应该忘记feof(); 使用while (fgets(line, sizeof(line), file) != 0),尽管这意味着您不需要urlPtr. memset()是多余的;fgets()正确初始化字符串。

修改有问题的代码

#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

FILE *file; /*declare the file pointer*/
#define LINE_MAX 1000
char line [LINE_MAX];

//Parent process
int main(void)
{
    pid_t  pid;
    file = fopen("urls.txt", "rt"); /*open file and read it*/

    if (!file)
    {
        perror("fopen");
        exit(-1);
    }
    int numberOfChildren = 0;
    memset(line,'\0',1000);
    char *urlPtr; 

    while (!feof(file))
    {
        urlPtr= fgets(line, sizeof(line), file);
        if (urlPtr)
        {
            int lineLen = strlen(urlPtr);
            urlPtr[lineLen-1] = '\0';
            pid = fork();

            ++numberOfChildren;

            if (pid == 0)
            {   /* child process */
                execlp("/usr/bin/wget", "wget", urlPtr, NULL);
                fprintf(stderr, "%d: wget failed\n", (int)getpid());
                exit(1);
            }
            else if (pid < 0)
            {   /* error occurred */
                fprintf(stderr, "Fork Failed\n");
                exit(-1);
            }
            else
                printf("%d: %s\n", (int)pid, urlPtr);
        }
    }

    /* JL: Moved block of code */
    while (numberOfChildren>0)
    {   /* parent process */
        /* parent will wait for the child to complete */
        int status;
        int corpse = wait(&status);
        --numberOfChildren;
        printf("Child %d Complete (0x%04X)\n", corpse, status);
    }

    fclose(file); /*close file command*/

    return 0;
}

请注意,一个while (!feof(file))循环已被删除,但还有更多不必要的代码可以去。给定数据文件

ftp://ftp.iana.org/tz/releases/tzcode2012f.tar.gz
ftp://ftp.iana.org/tz/releases/tzdata2012f.tar.gz

上面的代码可以并行获取两个文件。

替代代码

我喜欢使用函数,即使是相对较短的一段只使用过一次的代码。因此在be_childish()下面添加了功能。写出错误报告有点乏味,但这不是不这样做的借口。

我简要介绍了一个基于我自己的精心制作的库进行错误报告的最小函数,但它只会在此代码中使用两次(用于文件打开错误和execlp()返回后,总是无条件地指示失败),但决定把它排除在外。我有诸如 、 和 之类的函数 err_setarg0(),使用err_error()这些函数会将每个错误报告减少到一行(以及一些更复杂的函数,可以被告知自动包含 PID 等)。对我来说,拥有这样一个库是值得的,因为它使错误检查变得更加简单,因此痛苦更少并且不太可能被忽略。err_remark()err_usage()

#include <sys/wait.h>
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>

static void be_childish(const char *urlPtr)
{
    const char *wget = "/usr/bin/wget";
    char *nl = strchr(urlPtr, '\n');
    if (nl != 0)
        *nl = '\0';
    printf("%d: %s\n", (int)getpid(), urlPtr);
    execlp(wget, "wget", urlPtr, NULL);
    fprintf(stderr, "%d: Failed to execute %s\n", (int)getpid(), wget);
    exit(EXIT_FAILURE);
}

int main(int argc, char **argv)
{
    FILE *file;
    char line [1024];
    pid_t  pid;
    const char *name = "urls.txt";
    int rc = EXIT_SUCCESS;

    if (argc == 2)
        name = argv[1];
    else if (argc > 2)
    {
        fprintf(stderr, "Usage: %s [filename]\n", argv[0]);
        exit(EXIT_FAILURE);
    }
    file = fopen(name, "rt");   /* Undefined behaviour per POSIX */
    int numberOfChildren = 0;

    if (file == 0)
    {
        fprintf(stderr, "Failed to open file %s\n", name);
        exit(EXIT_FAILURE);
    }

    while (fgets(line, sizeof(line), file) != 0)
    {
        if ((pid = fork()) == 0)
        {
            fclose(file);
            be_childish(line);
        }
        else if (pid < 0)
        {
            fprintf(stderr, "Fork Failed");
            rc = EXIT_FAILURE;
            break;
        }
        ++numberOfChildren;
    }
    fclose(file);

    /* Parent waits for the children to complete */
    while (numberOfChildren > 0)
    {
        int status;
        const char *result = "OK";
        pid = wait(&status);
        --numberOfChildren;
        if (status != 0)
        {
            result = "Failed";
            rc = EXIT_FAILURE;
        }
        printf("Child %d %s\n", pid, result);
    }

    return rc;
}

请注意,代码在命令行上采用文件名,默认为您的“urls.txt”。"rt"开放模式不是 POSIX 或标准 C 模式;它可能会起作用,但"r"足以在所有系统上打开文本文件("rb"打开二进制文件也适用于所有系统,并且符合 POSIX 和标准 C)。它报告哪个子进程正在处理列出的每个文件。它报告每个孩子的状态(成功或失败);只有当所有孩子都成功时,它自己的退出状态才是成功。

您可能可以从命令行控制详细程度。您可能还希望记录哪个孩子正在处理每个文件,以便您可以报告成功下载的文件,而不是用户真正不关心的进程。这使处理变得复杂,因为您需要在阅读每个 URL 时对其进行复制。

请注意,在将字符串 (URL) 传递给wget.


这段代码现在经过测试(在添加换行修正之后),它产生了两个文件。屏幕显示有点乱;那是因为每个副本都wget认为它是唯一的用户:

80334: ftp://ftp.iana.org/tz/releases/tzcode2012f.tar.gz
80335: ftp://ftp.iana.org/tz/releases/tzdata2012f.tar.gz
--2012-09-23 19:19:44--  ftp://ftp.iana.org/tz/releases/tzcode2012f.tar.gz
           => “tzcode2012f.tar.gz”
Resolving ftp.iana.org... --2012-09-23 19:19:44--  ftp://ftp.iana.org/tz/releases/tzdata2012f.tar.gz
           => “tzdata2012f.tar.gz”
Resolving ftp.iana.org... 192.0.32.8192.0.32.8, , 2620:0:2d0:200::82620:0:2d0:200::8

Connecting to ftp.iana.org|192.0.32.8|:21... Connecting to ftp.iana.org|192.0.32.8|:21... connected.
Logging in as anonymous ... connected.
Logging in as anonymous ... Logged in!
==> SYST ... Logged in!
==> SYST ... done.    ==> PWD ... done.    ==> PWD ... done.
==> TYPE I ... done.
==> TYPE I ... done.  ==> CWD (1) /tz/releases ... done.  ==> CWD (1) /tz/releases ... done.
==> SIZE tzdata2012f.tar.gz ... done.
==> SIZE tzcode2012f.tar.gz ... 206404
==> PASV ... 135543
==> PASV ... done.    ==> RETR tzdata2012f.tar.gz ... done.    ==> RETR tzcode2012f.tar.gz ... done.
Length: 206404 (202K) (unauthoritative)

 0% [                                                                               ] 0           --.-K/s              done.
Length: 135543 (132K) (unauthoritative)

100%[==============================================================================>] 135,543     72.7K/s   in 1.8s    

100%[==============================================================================>] 206,404     81.4K/s   in 2.5s    

2012-09-23 19:19:48 (72.7 KB/s) - “tzcode2012f.tar.gz” saved [135543]

Child 80334 OK
2012-09-23 19:19:48 (81.4 KB/s) - “tzdata2012f.tar.gz” saved [206404]

Child 80335 OK
于 2012-09-24T01:26:41.820 回答