我是初学者,我正在尝试学习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);
}
答案 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)。