分叉/等候C计划。我的输出应该是什么?我的输出是否正确?

时间:2017-02-21 14:39:56

标签: c fork wait child-process

我是初学者,我正在尝试学习fork()和wait()函数的工作方式。

有人可以运行我的代码并告诉我输出应该是什么吗?

现在我得到: 一个 乙 C 一个 乙 C 一个 d ë

然而,我的一个伙伴说它应该是: 一个 乙 C 一个 d Ë 一个 乙 ç

另一个人说它应该是: 一个 乙 C C d ë

由于wait()函数,我认为子进程必须在父进程之前完成。这就是为什么我希望输出结束于'E'。

那么可能的产出是什么?我不明白,当我运行它时,我得到了ABCABCADE。对于初始子进程,不应该只打印一次“A”吗?

#include <stdio.h>
#include <unistd.h>
#include <wait.h>

int main(void) {
int pid;

    pid= fork();
    if (pid == 0) {
        fprintf(stdout, "A\n");
        pid= fork();
        if (pid==0) {
            fprintf(stdout, "B\n");
            pid=fork();
            fprintf(stdout, "C\n");
        }
        else {
            wait(NULL);
            fprintf(stdout, "D\n");
        }
    }
    else {
        fprintf(stdout, "E\n");
        wait(NULL);
    }
    // your code goes here
    return(0);
}

3 个答案:

答案 0 :(得分:1)

E没有理由最后出现,因为在打印wait()之后 E之前没有fflush(stdout);

还有一个额外的复杂因素,你不一定使用行缓冲输出,如果在fork之前有任何挂起的输出,那么parent和child都将输出缓冲的文本。

我们在每个fork()之前添加A。如果我们这样做,我们将摆脱多个parent | | +------\ | | "E" "A" | | wait +------\ . | | . wait "B" . . | . . +------\ . . | | . . "C" "C" . . | . |<----exit . "D" . | |<----exit | 输出,我们可以推断其余的。这是一个时间表:

E

您可以看到D可以随时打印,但C只有在至少一个 fprintf(stdout, "E\n"); wait(NULL); (左手)之后才会打印。

如果你换了

的顺序
E

您可以确保D始终在C之后(后者至少在C之后),但另一个 namespace test\MedBundle\Entity; use Doctrine\ORM\Mapping as ORM; use Symfony\Component\Validator\Constraints as Assert; use Symfony\Component\HttpFoundation\File\UploadedFile; use Symfony\Bridge\Doctrine\Validator\Constraints\UniqueEntity; /** * Apps * * @ORM\Table(name="apps",uniqueConstraints={@ORM\UniqueConstraint(columns={"nom", "prenom","age","class"})} ) * @ORM\Entity(repositoryClass="test\MedBundle\Repository\AppsRepository") * @ORM\HasLifecycleCallbacks() * @UniqueEntity(fields={"nom","prenom","class","age"}, message="Cette champ existe déja.") */ class Apps { /** * @var int * * @ORM\Column(name="id", type="integer") * @ORM\Id * @ORM\GeneratedValue(strategy="AUTO") */ private $id; /** * @var string * * @ORM\Column(name="nom", type="string", length=255) */ private $nom; /** * @var string * * @ORM\Column(name="class", type="string", length=255) */ private $class; /** * @var string * * @ORM\Column(name="prenom", type="string", length=255) */ private $prenom; /** * @var string * * @ORM\Column(name="age", type="string", length=255) */ private $age; /** * @ORM\Column(type="string", length=255) */ public $path; /** * @Assert\File(maxSize="6000000") */ private $file; /** * @ORM\ManyToOne(targetEntity="Med",inversedBy="apps") * @ORM\JoinColumn(name="id_med", referencedColumnName="id",nullable=true) */ private $med; /** * @ORM\OneToMany(targetEntity="test\MedBundle\Entity\Affichage",mappedBy="apps") */ private $comment; /** * @ORM\Column(name="updated_at", type="datetime", nullable=true) */ private $updatedAt; /** * @ORM\Column(name="nbr_modif", type="string", nullable=true) */ private $unbr_modif; /** * @ORM\PreUpdate */ public function nbremodif() { $this->setUnbrModif($this->unbr_modif+1); } /** * @ORM\PreUpdate */ public function updateDate() { $this->setUpdatedAt(new \Datetime()); } /** *@ORM\ */ /** * Get id * * @return integer */ public function getId() { return $this->id; } /** * Set nom * * @param string $nom * @return Apps */ public function setNom($nom) { $this->nom = $nom; return $this; } /** * Get nom * * @return string */ public function getNom() { return $this->nom; } /** * Set prenom * * @param string $prenom * @return Apps */ public function setPrenom($prenom) { $this->prenom = $prenom; return $this; } /** * Get prenom * * @return string */ public function getPrenom() { return $this->prenom; } /** * Set age * * @param string $age * @return Apps */ public function setAge($age) { $this->age = $age; return $this; } /** * Get age * * @return string */ public function getAge() { return $this->age; } /** * Set class * * @param string $class * @return Apps */ public function setClass($class) { $this->class = $class; return $this; } /** * Get class * * @return string */ public function getClass() { return $this->class; } public function getAbsolutePath() { return null === $this->path ? null : $this->getUploadRootDir().'/'.$this->path; } public function getWebPath() { return null === $this->path ? null : $this->getUploadDir().'/'.$this->path; } protected function getUploadRootDir() { // the absolute directory path where uploaded // documents should be saved return __DIR__.'/../../../../web/'.$this->getUploadDir(); } protected function getUploadDir() { // get rid of the __DIR__ so it doesn't screw up // when displaying uploaded doc/image in the view. return 'uploads'; } /** * Sets file. * * @param UploadedFile $file */ public function setFile(UploadedFile $file = null) { $this->file = $file; } /** * Get file. * * @return UploadedFile */ public function getFile() { return $this->file; } public function upload() { // the file property can be empty if the field is not required if (null === $this->getFile()) { return; } // use the original file name here but you should // sanitize it at least to avoid any security issues // move takes the target directory and then the // target filename to move to $this->getFile()->move( $this->getUploadRootDir(), $this->getFile()->getClientOriginalName() ); // set the path property to the filename where you've saved the file $this->path = $this->getFile()->getClientOriginalName(); // clean up the file property as you won't need it anymore $this->file = null; } /** * Set path * * @param string $path * @return Article */ public function setPath($path) { $this->path = $path; return $this; } /** * Get path * * @return string */ public function getPath() { return $this->path; } /** * Set advert * * @param \test\MedBundle\Entity\Med $advert * * @return Apps */ public function setAdvert(\test\MedBundle\Entity\Med $advert) { $this->advert = $advert; return $this; } /** * Get advert * * @return \test\MedBundle\Entity\Med */ public function getAdvert() { return $this->advert; } /** * Set med * * @param \test\MedBundle\Entity\Med $med * * @return Apps */ public function setMed(\test\MedBundle\Entity\Med $med = null) { $this->med = $med; return $this; } /** * Get med * * @return \test\MedBundle\Entity\Med */ public function getMed() { return $this->med; } /** * Constructor */ public function __construct() { $this->comment = new \Doctrine\Common\Collections\ArrayCollection(); } /** * Add comment * * @param \test\MedBundle\Entity\Affichage $comment * * @return Apps */ public function addComment(\test\MedBundle\Entity\Affichage $comment) { $this->comment[] = $comment; return $this; } /** * Remove comment * * @param \test\MedBundle\Entity\Affichage $comment */ public function removeComment(\test\MedBundle\Entity\Affichage $comment) { $this->comment->removeElement($comment); } /** * Get comment * * @return \Doctrine\Common\Collections\Collection */ public function getComment() { return $this->comment; } /** * Set updatedAt * * @param \DateTime $updatedAt * * @return Apps */ public function setUpdatedAt($updatedAt) { $this->updatedAt = $updatedAt; return $this; } /** * Get updatedAt * * @return \DateTime */ public function getUpdatedAt() { return $this->updatedAt; } /** * Set unbrModif * * @param string $unbrModif * * @return Apps */ public function setUnbrModif($unbrModif) { $this->unbr_modif = $unbrModif; return $this; } /** * Get unbrModif * * @return string */ public function getUnbrModif() { return $this->unbr_modif; } } 可能仍然是最后一个,与该流程的退出没有排序关系。

答案 1 :(得分:0)

未指定父项或子项是否在fork()时首先运行,或者无论如何运行该进程多长时间或在另一次接管之前它到达多远,或者两者实际上是否在不同的核心上同时运行。如果父级成功wait()为其子级,那么只要wait()返回,就确定该子级已终止。但是,如果没有其他同步手段,则无法预测父母在fork()和通过wait()收集孩子之间的行为相对于孩子所执行的行为的顺序。< / p>

还要观察涉及fork()次调用的返回值的条件。成功的fork()在孩子(仅)中返回0,因此这些行为将大部分程序的行为与每个行为完全相关联。

但这里还有另一个因素:多个句柄在相同的打开文件描述上的交互。当您fork()时,您最终会得到两个stdout个流(两者都指同一个打开的文件描述),之前您有一个。 POSIX将some restrictions放在程序必须处理这种情况的位置。如果您的程序的标准输出是行缓冲的,这是连接到终端时的默认输出,由于要打印的每个字符串末尾的换行符,程序行为很好地定义。但是,如果stdout是完全缓冲的,就像它连接到管道时那样,那么在分叉定义行为之前必须fflush(stdout)。因此,在分叉之前,fflush()是最安全的,确保无论执行环境如何都定义了程序行为。

如果您根据这些注意事项分析程序,并假设您的程序运行的方式完全定义了它,那么您将看到有多个可能的输出,但您的提议不在其中。如果你的程序以一种未定义其行为的方式运行,那么输出就无法说明了。

答案 2 :(得分:0)

输出不完全确定,因此您可能会在不同的运行中获得不同的结果。此外,如果管道输出或将其重定向到文件,则会得到与您不同的结果;有关详细信息,请参阅printf() anomaly after fork()

以下是您的代码的修订版,它可以强制刷新输出(它也使用POSIX标准<sys/wait.h>标头而不是非标准<wait.h>标头。

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

static int flush = 0;

static void print(const char *str)
{
    printf("%s\n", str);
    if (flush)
        fflush(stdout);
}

int main(int argc, char **argv)
{
    if (argc > 1)
        flush = (argv[argc] == 0);
    int pid = fork();
    if (pid == 0)
    {
        print("A");
        pid = fork();
        if (pid == 0)
        {
            print("B");
            pid = fork();
            print("C");
        }
        else
        {
            wait(NULL);
            print("D");
        }
    }
    else
    {
        print("E");
        wait(NULL);
    }
    return(0);
}

对我来说最常见的是,当它自由运行时(没有重定向,没有命令行参数),E首先出现:

E
A
B
C
C
D

当通过管道传输到cat时,输出会有所不同,但会变成主题的变体:

A
B
C
A
D
A
B
C
E

A
B
C
A
B
C
A
D
E

当使用参数运行时,为了强制进行刷新,我会始终如一地得到这个:

E
A
B
C
D
C

允许调度程序以不同的顺序运行进程;我机器上的调度程序以不同的顺序运行进程。父进程通常会一直运行,直到它到达wait()但子进程没有立即进行调度,因此它的输出可以出现在任何子进程之前。

因人而异。

在Mac上测试(macOS Sierra 10.12.3,GCC 6.3.0)。