【问题标题】:How do I know if an C program's executable is run in foreground or background?我如何知道 C 程序的可执行文件是在前台还是后台运行?
【发布时间】:2010-03-11 12:57:35
【问题描述】:

在我的 C 程序中,我想知道我的可执行文件是否像这样在前台运行

$./a.out

或者像这样

$./a.out &

【问题讨论】:

  • 记住程序可以根据用户的意愿在fg和bg之间切换(在bash中:ctrl-z暂停fg进程,bg命令在bg中运行,可以移回fg,等)。
  • @Roger Pate:SIGTSTP 是 POSIX,不仅限于 bash。
  • @Tim:我试图用这个例子来表达,所以它被误解的可能性很小,而不是尽可能笼统。
  • 为什么要关闭投票?这绝对是一道 C 题。

标签: c linux unix solaris


【解决方案1】:

如果你是前台工作,

getpgrp() == tcgetpgrp(STDOUT_FILENO)

STDIN_FILENOSTDERR_FILENO 或您附加到控制终端的任何文件描述符。 (如果您不确定,open("/dev/tty") 将始终为您提供控制终端的文件描述符,如果存在的话。)

这就是openssh 所做的事情,如果您只想快速检查一下,这比处理SIGTTIN/SIGTTOU 更容易。

另一方面,你可能有背景

$ ./a.out ^Z [1]+ 已停止 ./a.out $ bg [1]+ ./a.out &

或前景化

$ fg ./a.out

在任何时间点。你不能指望你可以检查一次,以后它仍然是真(或假)。

【讨论】:

    【解决方案2】:

    来自Bash Reference Manual: Job Control Basics

    后台进程是那些进程组 id 与终端不同的进程;这样的过程不受键盘生成的信号的影响。只允许前台进程读取或写入终端。尝试从终端读取(写入)终端的后台进程由终端驱动程序发送一个 SIGTTIN (SIGTTOU) 信号,除非被捕获,否则该信号会挂起该进程。

    所以解决方案是为SIGTTIN 安装信号处理程序,然后尝试从stdin 读取(关闭缓冲,否则会阻塞)。如果你得到“0 bytes read”,那么你就在前台运行。

    [编辑] 请注意,进程的状态可以更改。您可以使用 shell 的作业控制命令(Ctrl-Z、bgfgjobs)来执行此操作。

    【讨论】:

      【解决方案3】:

      据我所知,这是不可能的,通常也没有必要。

      请解释为什么你想这样做。

      【讨论】:

      • 假设我的进程正在执行一项进程密集型工作,并希望通过 SIGALRM 的信号处理程序打印“#”来打印一个状态栏之类的东西,直到计算完成。如果我的进程在后台运行,我不想打印 '#'s 。这就是我想要区分的地方。
      • 只需添加一个 --quiet 标志,不需要输出的程序应将其称为 ./a.out --quiet。请参阅 getopt() 手册页。
      • 这有先例:例如scp在前台和后台传输文件,但它只在前台更新状态栏。
      • scp 有几个配置值和命令行参数来控制状态,但AFAIR 默认情况下没有状态栏(我很少使用 scp 所以我可能错了)。不管怎样:为什么要为一个简单的问题创建一个复杂的解决方案?如果调用者不想要状态栏(即使程序在前台),他添加一个参数并完成。简单的问题,简单的解决方案。如果您想要相反的行为,只需命名参数 --progress 并反转显示/隐藏。
      【解决方案4】:

      [无效]
      IIRC,getppid()(在 *nix 系统上)将为您提供父 ID。如果为 0,则“控制台”是您的父级,因此您在后台运行。
      [/无效]

      [编辑]

      int devtty;
      if ((devtty = open ("/dev/tty", O_RDWR)) < 0)
         printf ("daemon\n");
      


      请注意,这仅在 *nix 系统上有效(并且仅在没有人删除 /dev/tty 的情况下——无论出于何种原因)
      [/编辑]

      【讨论】:

      • 无法用示例程序重现这一点。 getppid() 为“./a.out”和“./a.out &”返回相同的值
      • 我怀疑 getppid() 可以返回 0。它要么返回 1(如果你与控制台分离并且内核使你成为 init 进程的子进程)或 bash 的 PID启动你的外壳。
      • /dev/tty 即使你是后台也可以打开,只要你还有一个控制终端。
      【解决方案5】:

      您可能有多个进程 在后台运行:

      $ jobs
      [1]   Stopped                 teamviewer
      [2]-  Stopped                 vim
      [3]+  Stopped                 firefox
      
      • 使用:fg %2 将 vim 进程发送回前台。

      • 要将最后一个进程发送回前台,只需使用:fg 没有 论据。

      • 您也可以输入 % process_name 来恢复停止的进程。

      要暂停在后台运行的进程,请使用:

      kill -19 %job_id.
      

      -19 信号是 SIGSTOP(Ctrl - Z 发出的信号)。

      您始终可以通过键入来查看列表 kill -l

      在后台/前台之间移动作业:

      如果您已经输入了一个命令并且忘记使用&amp;,您可以通过输入^Z (CTRL-Z) 暂停作业,然后输入bg 将其放到后台来将前台作业放入后台:

      $ sleep 99
      ^Z
      [1]+  Stopped                 sleep 99
      $ bg
      [1]+ sleep 99 &
      

      您可以使用jobs 命令列出当前shell 的作业。

      请记住,“退出 shell”也会影响作业:

      • shell 退出时在后台运行的作业保持运行状态。
      • shell 退出时暂停(“已停止”)的作业被终止。

      向作业和流程发送信号

      您可以使用 %(JOBID) 而不是进程号(PID) 的作业号向从当前 shell 启动的作业发送信号,包括终止信号:

      $ kill %1
      [1]+  Terminated              sleep 99
      

      要向不是从当前 shell 启动的进程或作业发送信号,首先需要使用ps 查找它们的进程号(PID)。

      你可以参考这个链接: processes and jobs

      Linux 中一般的作业控制命令有:

      • 职位 - 列出当前职位
      • fg - 恢复队列中的下一个作业
      • fg %[number] - 恢复工作 [number]
      • bg - 将队列中的下一个作业推到后台
      • bg %[number] - 将作业 [number] 推送到后台
      • kill %[number] - 杀死编号为 [number] 的作业
      • kill -[signal] %[number] - 发送信号 [signal] 到 job number [number]
      • disown %[number] - 不再拥有进程(不再拥有终端),因此即使关闭终端后命令仍然有效。

      这几乎就是所有这些。请注意命令中作业编号前面的 % - 这说明 kill 你在谈论作业而不是进程。

      【讨论】:

        猜你喜欢
        • 2011-05-02
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-01-08
        • 1970-01-01
        • 2012-01-07
        • 2011-10-18
        相关资源
        最近更新 更多