【问题标题】:Checking if input is a valid shell command, Linux检查输入是否是有效的 shell 命令,Linux
【发布时间】:2016-05-25 22:08:08
【问题描述】:

我正在为一项任务开发一个简单的 shell。我读取了用户输入的一条命令,将其标记为fork(),然后在子进程中使用execvp()在后台执行该命令。

问题是我需要实现一个记录有效命令的历史功能。我知道检查用户输入的字符串是否是有效命令的唯一方法是检查execvp() 是否返回-1。这是检查无效命令的好方法,但是由于对execvp() 的调用发生在子进程中,并且我用于历史记录的数据结构被复制到fork() 上的子进程而不是共享,所以我可以' t 使用子级中execvp() 的结果更新历史记录(因为历史记录结构是副本,所以我所做的任何更改都不会反映在父级结构的副本中)。

有什么方法可以检查execvp() 在没有实际调用它的情况下返回 -1(即在分叉之前或之后)?如果我能找到一种方法,我将能够在父进程中验证 execvp() 是否会成功并使用该信息正确更新我的历史数据结构。

【问题讨论】:

    标签: c linux shell concurrency fork


    【解决方案1】:

    您要求的是一个系统调用,它可以让您实现经典的 check-before-do 竞争条件。

    在该错误中,程序会验证某个操作是否可行,然后执行该操作,从而可能会在检查后立即发生某些外部事件,从而使该操作非法。

    因此,即使程序检查了它是否可行,该操作也会失败。这通常会导致混乱。

    您应该避免这种反模式,并且系统 API 应该帮助您避免使用系统调用来诱惑您,而这些调用只会让您自己陷入困境。在这种情况下,系统做正确的事;没有这样的 API。

    父进程最终必须检索子进程的退出状态。那是您需要更新(或不更新)历史记录的时刻。如果失败的 execvp 导致子进程以失败状态代码退出(),则父进程会注意到失败,并且可以通过不将命令行添加到历史记录来做出反应。


    稍微思考后补充的一些注释:

    1. 要获取子进程的状态码,父进程会调用waitwaitpid。对于同步执行,父进程可能会立即执行;对于异步执行,父进程在收到SIGCHLD 信号时会这样做。但是父进程必须这样做,以避免僵尸进程。

    2. 在异步执行的情况下,不能使用该策略来避免将无效命令放入历史记录,因为异步命令在启动时必须记录在历史记录中。出于类似的原因,即使命令无效,Posix shell 也会将命令的异步执行视为成功。

    3. 虽然这个练习无疑具有教学价值(我希望这个答案能证明这一点),但它实际上是一种糟糕的 shell 历史记录方式。虽然 shell 用户偶尔会使用历史记录来检索和重新执行成功的命令,但历史记录功能对于检索和编辑不成功的命令更为有用。无法从历史功能中进行更正非常烦人。 (许多 Android 应用程序在搜索历史中恰恰表现出这个令人讨厌的缺陷:在给您带来不想要的结果的搜索之后,您可以检索不正确的搜索并重新运行它,但不能修改它。我很高兴地说,自从我第一次搜索以来,情况已经有所改善安卓)

    【讨论】:

    • 当你说退出代码时,你指的是当孩子调用它时传递给exit()的参数吗?如果是,家长如何检查此代码?
    • @tripleee 如果我希望子进程在后台运行(即我不想让父进程等待子进程退出)怎么办?
    • 除非你必须打电话,否则不要打电话给wait()。孩子完成后,您将收到SIGCHLD。但是你最终应该在你 fork 的每个孩子上 wait()
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2015-04-12
    • 2020-10-25
    • 2017-06-14
    • 2019-08-10
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多