用于Linux的进程(Ubuntu)

问题描述 投票:0回答:1

这是我想要做的:

编写一个带有整数命令行参数n的C程序,生成n个进程,每个进程生成-100到100之间的随机数,然后计算并打印出这些随机数的总和。每个进程都需要打印出它生成的随机数。

这是我到目前为止:

#include <stdio.h>
#include <time.h>
#include <stdlib.h>
#include <getopt.h>
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>


int main(int argc, char *argv[]){
    int command,processCheck; // processCheck: to check if fork was successful or not and to 
    char * strNumProcess = NULL;// check the status of child process
    while((command = getopt(argc, argv, "n:"))!=-1){
        if(command == 'n'){
            strNumProcess = optarg;
            break;
        }
    }

    int numProcess = atoi(strNumProcess);

    int pipes[numProcess][2];


    int randomNum; // Variable to store the random number
    int randomNumSum=0; // Initialized variable to store the sum of random number

    /** A loop that creates specified number of processes**/
    for(int i=0; i<numProcess; i++){
        processCheck = fork(); // creates a child process. Usually fork() = 2^n processes
        if(processCheck < 0){ // Checks for the error in fork()
            printf("Error");
            exit(1); // Terminates with error
        }
        else if(processCheck == 0){
            close(pipes[i][0]);
        /** Child process**/
            srand(time(NULL)+getpid()); // sets the randomness of the number associted with process id
            randomNum = rand()% 201 + (-100); // sets the range of random number from -100 to 100 and stores the random number in randomNum
            printf("%d\n" , randomNum); // Prints out the random number
            write(pipes[i][1], &randomNum, sizeof randomNum);
            close(pipes[i][1]);
            exit(0);// Terminates successfully
        }
        else{
            if(wait(NULL)){ // Waits for the child process to end and directs to parent process
                int v;
                if(read(pipes[i][0], &v, sizeof v)==sizeof(v)){
                    randomNumSum+=v;
                    close(pipes[i][0]);
                }
            }
        }
        close(pipes[i][1]);
    }

    printf("%d\n", randomNumSum); // Prints the sum of the random number

    return 0;
}

程序在第二个过程后进入无限循环。

c linux
1个回答
3
投票

编辑

OP已经对这个问题做出了重大改变,这与昨天的问题不同。这个答案今后可能再没有意义了。

结束编辑

原因是fork()用自己的虚拟内存创建了一个新的独立进程。它只继承父级的值,分叉的进程不与父级共享变量。所以randomNumSum是每个孩子的一个独特的变量,改变它不会影响父母的randomNumSum

您需要使用例如管道来进行父母和孩子之间的沟通,孩子们将结果写在管道中,父母从孩子那里读取。

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

int main(int argc, char **argv)
{
    if(argc != 2)
    {
        fprintf(stderr, "usage: %s num_of_children\n", argv[0]);
        return 0;
    }

    int noc = atoi(argv[1]);

    if(noc <= 0)
    {
        fprintf(stderr, "Invalid number of children\n");
        return 1;
    }

    int pipes[noc][2];
    pid_t pids[noc];

    for(size_t i = 0; i < noc; ++i)
    {
        if(pipe(pipes[i]) == -1)
        {
            perror("pipe");
            pids[i] = -2; // used later for error checking
            continue;
        }

        pids[i] = fork();

        if(pids[i] == -1)
        {
            perror("fork");
            continue;
        }

        if(pids[i] == 0)
        {
            // CHILD

            // closing reading end
            close(pipes[i][0]);
            srand(time(NULL)+getpid());
            int r = rand()% 201 + (-100);
            printf("Child %zu: r = %d\n", i, r);

            // sending value to parent
            write(pipes[i][1], &r, sizeof r);
            close(pipes[i][1]);
            return 0;
        }

        // closing writing end
        close(pipes[i][1]);
    }

    int sum = 0;
    for(size_t i = 0; i < noc; ++i)
    {
        if(pids[i] == -2)
        {
            fprintf(stderr, "Pipe could not be created for child %zu\n", i);
            continue;
        }

        if(pids[i] == -1)
        {
            fprintf(stderr, "Child %zu was not started\n", i);
            close(pipes[i][0]);
            continue;
        }

        int status;
        if(waitpid(pids[i], &status, 0) == -1)
        {
            fprintf(stderr, "Could not wait for child %zu\n", i);
            close(pipes[i][0]);
            continue;
        }

        if(WIFEXITED(status) && WEXITSTATUS(status) == 0)
        {
            int v;
            if(read(pipes[i][0], &v, sizeof v) != sizeof(v))
            {
                fprintf(stderr, "Could not read from child %zu\n", i);
                close(pipes[i][0]);
                continue;
            }

            sum += v;
            close(pipes[i][0]);
        } else
            printf("Child %zu did not exit normally\n", i);
    }

    printf("The sum is: %d\n", sum);

    return 0;
}

给我输出:

Child 0: r = -6
Child 1: r = 63
Child 3: r = 78
Child 2: r = 77
Child 4: r = -47
The sum is: 165

所以这里的技术是使用pipe创建管道。管道是单向数据通道,可用于进程间通信cite。使用管道,两个进程可以相互通信,但管道只有一个方向。在此示例中,子进程将写入管道,父进程将从管道中读取。

这就是为什么在做fork之前,父母创建管道,做fork然后关闭它的管道末端。孩子关闭它的管道读数端。然后,子计算该值并将其计算的值写入管道,并以状态0存在。

在创建孩子后,父母等待孩子终止。如果子节点正常终止且退出状态为0,则父节点从管道读取并获取子节点的计算值。

顺便说一句,正如David C. Rankin在评论中指出的那样,你在[-100,100]范围内获得随机值的方法是不正确的。 rand()% 201 + (-100)会给出介于-100和100之间的值,因为rand()%201会给出0到200之间的值。


Aaditi

OP在评论中问道

根据我的理解,我可以返回randonNum而不是exit(0)并进行计算,我调用wait(NULL)call wait(randomNum)?

是的,您可以使用进程的退出状态将信息发送回父级,而无需创建管道。但我认为由于以下原因,这不是一个特别好的解决方案:

  • Unix / POSIX中的退出状态是无符号的8位值,这意味着退出代码在[0,255]范围内。因此,如果您的随机值是-1,那么父进程将看到255.在您的情况下,这不会是一个问题,因为您的值大于127,您可以减去256以获得负值。
  • 您只能返回(无符号)8位值。如果您的子进程必须发送更复杂的内容,如16位值,floatdoublestruct,则不能使用退出状态,因此您限制了可以返回到父级的内容。当你想要返回比8位值更“复杂”的东西时,管道就是完美的工具。
  • 我认为使用退出状态发送非错误值的其他信息是一种黑客行为。退出状态的目的是一个进程可以通过返回0告诉它的父进程没有出现错误,或者它退出时出错并且退出状态有错误代码。这就是为什么我认为这是一个黑客,对我而言,就像用螺丝刀代替锤子钉钉子一样。
  • 你的wait调用无效,因为wait期望指向int,你需要使用宏WIFEXITEDWEXITSTATUS来获得退出状态。但是在这种情况下使用wait的问题是wait在出错时返回-1,你将无法判断它返回-1的子节点以及你必须等待多少等待其余子节点的等待。孩子们不会以你分叉的顺序结束,所以你需要跟踪哪个孩子被wait()ed。使用waitpid要简单得多。有了waitpid,你可以等一个特定的孩子。我个人更喜欢waitpid

因此,更改代码以在没有管道和使用退出状态的情况下执行相同的操作:

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

int main(int argc, char **argv)
{
    if(argc != 2)
    {
        fprintf(stderr, "usage: %s num_of_children\n", argv[0]);
        return 0;
    }

    int noc = atoi(argv[1]);

    if(noc <= 0)
    {
        fprintf(stderr, "Invalid number of children\n");
        return 1;
    }

    pid_t pids[noc];

    for(size_t i = 0; i < noc; ++i)
    {

        pids[i] = fork();

        if(pids[i] == -1)
        {
            perror("fork");
            continue;
        }

        if(pids[i] == 0)
        {
            // CHILD

            srand(time(NULL)+getpid());
            int r = rand()% 201 + (-100);
            printf("Child %zu: r = %d\n", i, r);

            exit(r);
        }
    }

    int sum = 0;
    for(size_t i = 0; i < noc; ++i)
    {
        if(pids[i] == -1)
        {
            fprintf(stderr, "Child %zu was not started\n", i);
            continue;
        }

        int status;
        if(waitpid(pids[i], &status, 0) == -1)
        {
            fprintf(stderr, "Could not wait for child %zu\n", i);
            continue;
        }

        if(WIFEXITED(status))
        {
            int v = WEXITSTATUS(status);

            // checking if the child wrote a 8-bit negative value
            // in 2-complement format
            if(v > 127)
                v -= 256;

            printf("Parent: child %zu returned %d\n", i, v);
            sum += v;
        } else
            fprintf(stderr, "Child %zu did exit abnormally, ignoring\n", i);
    }

    printf("The sum is: %d\n", sum);

    return 0;
}

给我10个孩子的输出:

Child 0: r = -59
Child 1: r = 73
Child 2: r = 61
Child 3: r = 98
Child 4: r = 18
Child 6: r = 31
Child 5: r = -88
Parent: child 0 returned -59
Parent: child 1 returned 73
Parent: child 2 returned 61
Child 8: r = 58
Parent: child 3 returned 98
Parent: child 4 returned 18
Parent: child 5 returned -88
Child 7: r = 53
Parent: child 6 returned 31
Child 9: r = -43
Parent: child 7 returned 53
Parent: child 8 returned 58
Parent: child 9 returned -43
The sum is: 202
© www.soinside.com 2019 - 2024. All rights reserved.