我真的被这个任务困住了。这是一个非常简单的程序,我必须用C编写。这个想法是演示fork()和wait()的使用。 指令说“在终止前完成15次迭代。在每次迭代中,程序将创建10个子进程(p0到p9)。每个进程在退出之前将打印其十进制数字而没有换行。创建10个子进程后,父进程将等待它的10个子进程完成,然后在进入下一次迭代或退出之前打印换行符。没有10次显式调用fork(),使用只有一个fork()调用实例的循环。为了等待所有子进程完成,在循环中使用wait(NULL),直到wait(NULL)的返回值为-1。 这是一个示例输出:
9856724310
8149765320
2789654310
0139874265
8765320149
2145367809
0123456798
0124356789
0123546789
9854320761
0123678594
0142685379
0123456789
6795438210
2394567081
这就是我所拥有的,但似乎每一种方式我尝试这个数字只是按顺序打印,没有任何不可预测性。
#include <cstdlib>
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
using namespace std;
int main(int argc, char** argv) {
pid_t pid;
for(int i=0; i<15; i++){
//fork();
for(int j=0; j<10; j++){
pid = fork();
printf("%d", j);
}
wait(NULL);
printf("\n");
}
return 0;
}
任何帮助是极大的赞赏!谢谢!
您需要区分要在父进程或子进程中运行的代码(现在您在两个进程上运行相同的代码)。这可以使用fork()的返回值来完成,因为fork将child的pid返回给父级,并将0返回给子级。因此,您可以执行以下检查:
pid = fork();
if (pid == 0) {
//Code for child process
exit(0);
}
//Code for parent process
你写过一个fork bomb。这是一个进程产生子进程然后产生子进程然后产生子进程的子进程...直到所有内存都被消耗或操作系统杀死它为止。
fork
可能很难理解。当它被调用时,父进程和子进程都继续执行代码。因此,分离子代码并确保子代退出很重要。如果父母和孩子正在运行相同的代码,您如何确保孩子停止并且父母继续?
父级中的fork
返回子进程的ID。子中的fork
返回0.如果出现错误,则返回-1。所以典型的习语看起来像这样:
pid_t pid = fork();
if( pid == 0 ) {
// child
exit(0);
}
else if( pid < 0 ) {
// error
perror("fork failed");
}
// parent
完成后,您必须等待所有孩子完成。当任何子进程完成时,wait
将返回,返回子进程。您需要继续调用wait
,直到它返回<0表示没有更多的子进程。
void wait_all() {
while(wait(NULL) > 0);
}
将它们放在一起,并删除15次的汇编...
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
// print
printf("%d", j);
// job's done
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
}
wait_all();
printf("\n");
即便如此,我仍然按顺序完成它们。可能是因为每个孩子都在执行完全相同的非常简单和可预测的事情。它们都需要大约相同的执行时间,所以它们可能会以相同的顺序出现。
旁注:stdout
是行缓冲的,这意味着它只会在看到换行符时显示或刷新其内容。 printf("%d", j);
将不会显示,直到打印换行符或stdout
被刷新。 exit
将刷新并关闭所有流,所以没关系。
但是,子进程继承了父进程的缓冲区。如果父级在缓冲区上留下任何内容,这可能会导致一些非常奇怪的行为。例如...
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
printf("child: %d, ", j);
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
printf("parent: %d, ", j);
}
wait_all();
printf("\n");
我们得到:
child: 0, parent: 0, child: 1, parent: 0, parent: 1, child: 2, parent: 0, parent: 1, parent: 2, child: 3, parent: 0, parent: 1, parent: 2, parent: 3, child: 4, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, child: 5, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, child: 6, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, child: 7, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, child: 8, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, child: 9, parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, parent: 9,
这是怎么回事?这让我感到困惑了一段时间。如果我们在孩子的印刷品中添加换行符,那就更清楚了。
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
printf("child: %d\n", j);
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
printf("parent: %d, ", j);
}
wait_all();
printf("\n");
child: 0
parent: 0, child: 1
parent: 0, parent: 1, child: 2
parent: 0, parent: 1, parent: 2, child: 3
parent: 0, parent: 1, parent: 2, parent: 3, child: 4
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, child: 5
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, child: 6
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, child: 7
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, child: 8
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, child: 9
parent: 0, parent: 1, parent: 2, parent: 3, parent: 4, parent: 5, parent: 6, parent: 7, parent: 8, parent: 9,
每次孩子分叉时,它都会得到父母的stdout
缓冲区的副本。每次循环时,父级都会将parent: %d,
添加到该缓冲区而不进行刷新。当孩子做printf("child: %d\n", j);
时,它会添加到现有缓冲区然后将其刷新。
""
,加上child: 0\n
和冲洗。parent: 0,
添加到stdout
。parent: 0,
,添加child: 1\n
和冲洗。parent: 1,
添加到stdout
,现在是parent: 0, parent: 1,
parent: 0, parent: 1,
,添加child: 2\n
和冲洗。等等。
在父进行部分打印后刷新stdout
可以避免这种情况,并确保在发生时显示所有内容。
for(int j=0; j<10; j++){
pid_t pid = fork();
if( pid == 0 ) {
printf("child: %d\n", j);
exit(0);
}
else if( pid < 0 ) {
perror("fork failed");
}
printf("parent: %d, ", j);
fflush(stdout);
}
wait_all();
printf("\n");
parent: 0, child: 0
parent: 1, child: 1
parent: 2, child: 2
parent: 3, child: 3
parent: 4, child: 4
parent: 5, child: 5
parent: 6, child: 6
parent: 7, child: 7
parent: 8, child: 8
parent: 9, child: 9
无论何时使用子进程,始终要区分父代码和子代码。通过使用pid=0
来识别孩子,而根据pid在执行时具有的内容,父进程将具有pid > 0
。此外,如果在分叉子进程时发生错误,fork将返回值<0
,因此您应该考虑这种情况。
在你的代码中没有这样的东西。经典的方法是做这样的事情:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <unistd.h>
#include <sys/wait.h>
int main(void) {
pid_t pid;
for(int i=0;i<15;i++){
for(int j=0; j<10; j++){
if((pid = fork())<0){
perror("Fork Failed.");
exit(EXIT_FAILURE);
}
if(pid==0){ /*Child Code here*/
printf("%d", j);
exit(EXIT_SUCCESS);
}
}
while(wait(NULL)!=-1); //while loop to wait
printf("\n");
}
return 0;
}
你明白错了。在fork()上,您可以想象fork()的代码同时执行,两个进程共享相同的代码(因为内存被复制)。区分父级和子级的方法是通过if子句,因为fork()为每个进程返回不同的值。如下所示:
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/wait.h>
int main(void) {
for (int i=0; i<15; i++){
for(int j=0; j<10; j++){
if (fork()==0){
//child process return 0 from fork()
printf("%d", j);
exit(0);
}
else{
//parent process return pid of the forked process from fork()
continue;
}
}
while (wait(NULL)!=-1);//wait for all child processes. wait() only return -1 when no more children to wait for
printf("\n");
}
return 0;
}
我试试这个数字只是打印顺序,没有任何不可预测性。
当一个线程调用fork()
时:
请注意,如果您不知道程序将在哪个内核/操作系统上运行,或者是否存在/不会有多个CPU,则所有这些都是“不可预测的”;但是在同一台计算机上使用“相同的内核/操作系统”,它们都是完全可预测的并且不会改变(例如,内核不会在运行时重新设计自己)。
然而:
fork()
之前几乎使用了它的所有时间片,并且内核决定旧任务在fork()
完成后立即使用了它的整个时间片;这打乱了时机请注意,即使在同一台计算机上使用“相同的内核/操作系统”,这些内容也会产生“不可预测性”;但所有这些都是相对罕见的。你可能需要fork()
几百万次才能获得幸运,其中一个事情会让时间变得足以改变“谁先到达printf()
”。
考虑到这一点;演示“同一台计算机上同一内核/操作系统的罕见不可预测性”;我可能会写一个程序:
fork()
exit()
这样一来,通过检查整个100万行的输出,您将有相对较高的机会看到一些“罕见的不可预测性”。