【问题标题】:Can a subprocess started by `start-process` outlive the parent emacs process?由“start-process”启动的子进程能否比父 emacs 进程寿命更长?
【发布时间】:2013-11-12 23:55:48
【问题描述】:

我正在 Emacs Lisp 中编写一些代码,我想在其中使用 start-process 或类似函数启动子进程。但是,当父 emacs 进程退出时,我不希望子进程退出。但是,似乎退出父 emacs 进程也总是会杀死所有子进程。以下代码 sn -p 演示了这一点:

(require 'async)
(async-sandbox
 (lambda () 
   (require 'package)
   (package-initialize)
   (require 'async)
   (shell-command "{ echo -n 'SPAWNING: '; date; } > ~/temp/ASYNC_LOG")
   (start-process-shell-command "subproc" nil
    "{ echo -n 'STARTED: '; date; } >> ~/temp/ASYNC_LOG;
     sleep 5;
     { echo -n 'FINISHED: '; date; } >> ~/temp/ASYNC_LOG;")
   (shell-command "{ echo -n 'SPAWNED: '; date; } >> ~/temp/ASYNC_LOG")
   (shell-command "sleep 2")
   (shell-command "{ echo -n 'FINISHED WAITING: '; date; } >> ~/temp/ASYNC_LOG")
   ))

此代码同步生成一个 emacs 进程(async-sandbox (lambda () ...),以便我们可以退出该进程以触发问题。然后该进程异步生成一个子进程(start-process-shell-command ...)。父进程休眠2秒,子进程休眠5秒,所以父进程会先退出。当我运行这段代码时,我从未在日志文件中看到“FINISHED”行,这表明子进程在 2 秒后被杀死。如果我将父级更改为等待 7 秒,那么我会在输出中看到 FINISHED 行。

那么有没有类似的方法来启动一个子进程,这样子进程就不会在它的父进程退出时被杀死?

【问题讨论】:

  • ASYNC_LOG 中的哪一行表示意外行为? SPAWNING: Mon Nov 11 12:53:25 UTC 2013; STARTED: Mon Nov 11 12:53:25 UTC 2013; SPAWNED: Mon Nov 11 12:53:25 UTC 2013; FINISHED WAITING: Mon Nov 11 12:53:27 UTC 2013; FINISHED: Mon Nov 11 12:53:30 UTC 2013?
  • 如果子进程的睡眠时间比父进程长,则永远不会出现FINISHED行,表示父进程退出时子进程被杀死。
  • 我倒过来让父进程休眠2秒,子进程休眠5秒,得到的就是这个:SPAWNING: Mon Nov 11 13:28:36 UTC 2013; STARTED: Mon Nov 11 13:28:36 UTC 2013; SPAWNED: Mon Nov 11 13:28:36 UTC 2013; FINISHED: Mon Nov 11 13:28:38 UTC 2013; FINISHED WAITING: Mon Nov 11 13:28:41 UTC 2013。我没有使用(require 'async),而是在不使用lambda () 的情况下将async-sandbox 转换为(interactive) 函数。我正在使用带有 Emacs Trunk 的 OSX Snow Leopard 10.6.8。
  • 我的示例已经让父进程休眠 2 秒,子进程休眠 5 秒。从您评论中的时间戳来看,您似乎做了相反的事情。无论如何,如果没有async-sandbox 调用,该示例将无法运行,因为关键是要让父进程(由async-sandbox 启动)在子进程(由start-process 启动)之前退出。让它工作的另一种方法是在emacs -batch 中运行它,因为无论如何这都是 async 所做的。
  • 感谢您帮助我更好地理解问题。我已经安装了 github.com/jwiegley/emacs-async 并且可以确认与您的问题中描述的相同的行为(使用 OSX 10.6.8 和相当新版本的 Emacs Trunk)。但是,我还没有解决方案——抱歉。

标签: asynchronous emacs elisp multiprocessing parent-child


【解决方案1】:

这是我从 dired 开始的方式:

(require 'dired-aux)
(setq dired-guess-shell-alist-user
      '(("\\.pdf\\'" "evince")
        ("\\.eps\\'" "evince")
        ("\\.jpe?g\\'" "eog")
        ("\\.png\\'" "eog")
        ("\\.gif\\'" "eog")
        ("\\.tex\\'" "pdflatex" "latex")
        ("\\.\\(?:mp4\\|mkv\\|avi\\|flv\\)\\'" "vlc")))
(defvar dired-filelist-cmd
  '(("vlc" "-L")))
(defun dired-start-process (cmd &optional file-list)
  (interactive
   (let ((files (dired-get-marked-files t current-prefix-arg)))
     (list
      (dired-read-shell-command "& on %s: " current-prefix-arg files)
      files))) 
  (apply
   #'start-process
   (list cmd nil shell-file-name shell-command-switch
         (format "nohup 1>/dev/null 2>/dev/null %s \"%s\""
                 (if (> (length file-list) 1)
                     (format "%s %s"
                             cmd
                             (cadr (assoc cmd dired-filelist-cmd)))
                   cmd)
                 (mapconcat #'expand-file-name file-list "\" \"")))))

【讨论】:

    【解决方案2】:

    Emacs 在终止时所做的是向其衍生的进程发送一个信号(我假设它们是存在于process-list 中的那些)。这种行为在 Emacs C 代码中是硬编码的(你可以尝试破解 process-list 删除你想比 Emacs 更长寿的进程,但我不确定这样做的副作用;可能它们会很丑)

    您可以轻松找出 Emacs 发送的信号(只需为您的一个进程编写信号处理程序)。我猜它是 SIGHUP,所以进程知道与用户(Emacs)的连接已经丢失。

    您可以做的是让您的进程以一种不会死亡的方式处理 SIGHUP,例如为该信号添加一个处理程序。如果您对该流程代码的控制为零,则可以用“nohup”包装它(这就是@abo-abo 基本上在做的事情;有关实现细节,请参见 abo-abo 其他答案)

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2015-09-20
      • 1970-01-01
      • 2011-01-04
      相关资源
      最近更新 更多