使用select()向子进程发送信号

时间:2019-11-30 03:33:24

标签: process ocaml signals fork

我正在尝试根据对子进程的不同用户输入,从父进程发送一些信号(例如SIGSTOP,SIGUSR1)。父进程一直在等待用户输入,并将相应的信号发送给子进程。如果没有用户输入,则孩子将完成自己的工作。

我将我的Ocaml代码放在这里,但是我不确定我是否使用正确的方法来执行此操作。 我使用OCaml编写,但也欢迎使用其他语言(例如C / Python)的解决方案。

let cr, pw = Unix.pipe () in
let pr, cw = Unix.pipe () in

match Unix.fork () with
| 0 -> (* child *)
    Unix.close pr;
    Unix.close pw;
    Unix.dup2 cw Unix.stdout;
    Unix.execvp ... (* execute something *)
| pid -> (* parent *)
    Unix.close cr;
    Unix.close cw;
    Unix.dup2 pr Unix.stdin;
    while true do
        try
            match Unix.select [pr] [] [] 0.1 with
            | ([], [], []) -> (* no user input *)
              (* I assume it should do next iteration and wait for next user input *)
              raise Exit
            | (_, _, _) -> (* some user input *)
              let i = read_int () in
              (* send signal to the child process *)
              if i = 1 then Unix.kill pid Sys.sigstop
              else if i = 2 then Unix.kill pid Sys.sigusr1;
        with Exit -> ()
    done

同时,如果我想定义一些信号(使用SIGUSR1),应该怎么做?在哪里?

谢谢!

1 个答案:

答案 0 :(得分:0)

不清楚您要做什么。这是您显示的代码的一些注释。

父进程似乎正在读取它自己创建的管道(pr)。但是您说父进程正在等待用户输入。用户输入不会显示在您自己创建的管道中。

几乎总是通过阅读标准输入Unix.stdin来寻找用户输入。

该代码创建了另一个似乎旨在使用子进程的管道,但是没有安排子管道访问该管道的文件描述符。子级将改为读取父级的标准输入并写入父级的标准输出。

您的代码调用select,超时为0.1秒。这意味着无论管道中是否有任何输入,该调用每秒将返回10次。每次返回它都会写一个换行符。因此,输出将以每秒10个左右的速度出现换行。

您说您想定义信号,但是这尚不清楚。如果您要为孩子定义信号 handlers ,则在显示的代码中无法实现。信号处理程序不会在对Unix.execvp的调用中保留。如果您考虑一下,这是唯一可行的方法,因为execvp调用将清除父进程中的所有代码,并将其替换为其他可执行文件中的代码。

派生一个孩子然后发送信号并不难。但是尚不清楚您要对管道和选择做什么。目前尚不清楚您期望信号在子进程中做什么。如果您对这些内容进行了说明,那么给出更详细的答案将更加容易。

更新

通常,修改已发布到StackOverflow的代码不是一个好主意(除了修正拼写错误),因为以前的注释和答案对于新代码不再有意义。最好发布更新的代码。

您的新代码看起来更像是您试图通过管道从子项中读取输入内容 。这比较明智,但是那不是我所说的“用户输入”。

您尚未指定孩子的输入应该来自哪里,但我怀疑您打算通过另一条管道发送输入。

如果是这样,由于子进程与其管道之间存在缓冲,这是众所周知的不可靠设置。如果您尚未为子进程编写代码,则无法确保它将从读取管道读取适当大小的数据,并在适当的时候将其输出刷新到写入管道。通常的结果是事情立即停滞不前,没有进展。

如果您正在为子进程编写代码,则需要确保其读取的数据大小与父进程正在编写的大小相同。如果孩子要求的数据更多,它将阻止。如果父母正在等待答案出现在其读取管道中,您将陷入僵局(除非非常小心,否则这是通常的结果)。

您还需要确保子级随时准备好被父进程读取时刷新其输出。而且,当您希望子进程读取父进程的输出时,还需要刷新它。 (并且父母必须以孩子期望的大小写入数据。)

您还没有解释您要使用信号做什么,所以我对此无能为力。对我而言,读取子进程写入的整数值并作为响应向子进程发送信号没有太大意义。

这是一些有效的代码。它对信号没有任何作用,因为我不明白您要做什么。但是它正确设置了管道,并通过子进程发送了一些固定长度的文本行。子进程只是将所有字符更改为大写。

let cr, pw = Unix.pipe ()
let pr, cw = Unix.pipe ()

let () =
match Unix.fork () with
| 0 -> (* Child process *)
  Unix.close pr;
  Unix.close pw;
  Unix.dup2 cr Unix.stdin;
  Unix.dup2 cw Unix.stdout;
  Unix.close cr;
  Unix.close cw;
  let rec loop () =
    match really_input_string stdin 6 with
    | line ->
      let ucline = String.uppercase_ascii line in
      output_string stdout ucline;
      flush stdout;
      loop ()
    | exception End_of_file -> exit 0
  in
  loop ()
| pid ->
  (* Parent process *)
  Unix.close cr;
  Unix.close cw;
  Unix.dup2 pr Unix.stdin;
  Unix.close pr;
  List.iter (fun s ->
    let slen = String.length s in
    ignore (Unix.write_substring pw s 0 slen);
    let (rds, _, _) =
      Unix.select [Unix.stdin] [] [] (-1.0)
    in
    if rds <> [] then
      match really_input_string stdin 6 with
      | line -> output_string stdout line
      | exception End_of_file ->
        print_endline "unexpected EOF"
    )
    ["line1\n"; "again\n"];
  Unix.close pw;
  exit 0

当我运行它时,我看到以下内容:

$ ocaml unix.cma m.cmo
LINE1
AGAIN

如果将任一测试行的长度更改为小于6个字节,则会看到死锁,通常在人们尝试设置此双管道方案时会发生死锁。

您发送信号的代码看起来还不错,但是我不知道您发送信号时会发生什么。