这是我想要做的:
编写一个带有整数命令行参数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;
}
程序在第二个过程后进入无限循环。
编辑
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)?
是的,您可以使用进程的退出状态将信息发送回父级,而无需创建管道。但我认为由于以下原因,这不是一个特别好的解决方案:
float
,double
或struct
,则不能使用退出状态,因此您限制了可以返回到父级的内容。当你想要返回比8位值更“复杂”的东西时,管道就是完美的工具。wait
调用无效,因为wait
期望指向int
,你需要使用宏WIFEXITED
和WEXITSTATUS
来获得退出状态。但是在这种情况下使用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