用于Linux的进程(Ubuntu)

这是我想要做的:

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

这是我到目前为止:

#include  #include  #include  #include  #include  #include  #include  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; } 

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

编辑

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

结束编辑

这样做的原因是fork()创建了一个具有自己的虚拟内存的新独立进程。 它只inheritance父级的值,分叉的进程不与父级共享变量。 因此randomNumSum为每个孩子提供一个唯一变量,并且更改它不会影响父项的randomNumSum

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

 #include  #include  #include  #include  #include  #include  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创建pipe 。 管道是单向数据通道,可用于进程间通信 引用 。 使用管道,两个进程可以相互通信,但管道只有一个方向。 在此示例中,子进程将写入管道,父进程将从管道中读取。

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

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

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


EDIT2

OP在评论中问道

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

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

  • Unix / POSIX中的退出状态是无符号的8位值,这意味着退出代码在[0,255]范围内。 因此,如果你的随机值是-1,那么父进程将看到255.在你的情况下,这不会是一个问题,因为你的值大于127,你可以减去256得到负值。

  • 您只能返回(无符号)8位值。 如果您的子进程必须发送更复杂的东西,如16位值, floatdouble floatstruct ,则不能使用退出状态,因此限制了可以返回父级的内容。 当你想要返回比8位值更“复杂”的东西时,管道就是完美的工具。

  • 我认为使用退出状态发送非错误值的其他信息是一种黑客行为。 退出状态的目的是一个进程可以通过返回0告诉它的父进程没有出现错误,或者它退出时出错并且退出状态有错误代码。 这就是为什么我认为它是一个黑客,对我而言,就像用螺丝刀代替锤子钉钉子一样。

  • 你的wait调用无效,因为wait需要指向int的指针,你需要使用宏WIFEXITEDWEXITSTATUS来获取退出状态。 但是在这种情况下使用wait的问题是wait在错误时返回-1并且你无法告诉它返回-1的子节点以及你必须等待多少等待其余子节点的等待。 孩子们不会以分叉的顺序结束,所以你需要跟踪哪个孩子已经wait() 。 使用waitpid要简单得多。 使用waitpid您可以等待特定的孩子。 我个人更喜欢这里的waitpid

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

 #include  #include  #include  #include  #include  #include  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