我正在为一项任务开发一个简单的shell。我读取了用户输入的命令,将其标记为fork()
,然后在子进程中使用execvp()
在后台执行命令。
问题是我需要实现记录有效命令的历史记录功能。我知道检查用户输入的字符串是否有效的唯一方法是检查execvp()
是否返回-1。这是检查无效命令的好方法,但由于对execvp()
的调用发生在子进程中,我用于历史记录的数据结构被复制到fork()
上的子进程而不是共享,我无法使用子内execvp()
的结果更新历史记录(因为历史结构是副本,我所做的任何更改都不会反映在父结构的副本中)。
有没有什么方法可以检查execvp()
是否返回-1而没有实际调用它(即在fork之前或之后)?如果我能找到一种方法,我将能够在父进程中验证execvp()
是否成功并使用该信息来正确更新我的历史数据结构。
答案 0 :(得分:3)
您要求的是一个系统调用,它可以让您实现经典的先接后退竞赛条件。
在该错误中,程序会验证某个操作是否可行然后执行操作,从而在检查之后发生某些外部事件的可能性,这会使操作非法。
然后,即使程序检查到可能,操作也会失败。这通常会导致混乱。
你应该避免使用这种反模式,并且系统API应该有助于不用你只会用来让你自己陷入麻烦的系统调用来诱惑你。在这种情况下,系统做正确的事情;没有这样的API。
父进程最终必须检索子进程的退出状态。那是您需要更新(或不更新)历史记录的那一刻。如果失败的execvp导致子进程使用失败状态代码退出(),则父进程将注意到失败,并且可以通过不将命令行添加到历史记录来做出反应。
要检索子流程的状态代码,家长会调用wait
或waitpid
。对于同步执行,父母可能会立即执行;对于异步执行,父节点在收到SIGCHLD
信号时会这样做。但是父母必须这样做,以避免僵尸进程。
在异步执行的情况下,不可能使用此策略来避免将无效命令放入历史记录中,因为异步命令必须在启动时记录在历史记录中。出于类似的原因,即使命令无效,Posix shell也会将命令的异步执行计为成功。
虽然这个练习无疑具有教学价值(我希望这个答案能够证明),但它实际上是一种做壳历史的可怕方法。虽然shell用户偶尔会使用历史记录来检索和重新执行成功的命令,但历史记录功能对于检索和编辑不成功的命令更有用。无法通过历史记录功能进行更正非常烦人。 (许多Android应用程序恰好展现了搜索历史中令人讨厌的缺陷:在搜索结果为您提供不需要的结果后,您可以检索错误的搜索并重新运行,但不能修改它。我很高兴地说事情已经改善了我的第一个Android。)