使用 select() 向 child 进程发送信号
use select() to send signal to child process
我正在尝试根据不同的用户输入从 parent 进程向 child 进程发送一些信号(例如 SIGSTOP、SIGUSR1)。 parent 进程一直等待用户输入并将相应的信号发送到 child。如果没有用户输入,child 会执行自己的工作。
我将我的 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),我应该如何以及在哪里做?
谢谢!
不太清楚你要做什么。以下是对您显示的代码的一些评论。
parent 进程似乎正在读取它自己创建的管道 (pr
)。但是你说 parent 进程正在等待用户输入。用户输入不会显示在您自己创建的管道中。
几乎总是通过读取标准输入来寻找用户输入,Unix.stdin
。
该代码创建了另一个管道,似乎旨在供 child 进程使用,但没有安排 child 访问该管道的文件描述符。 child 将改为读取 parent 的标准输入并写入 parent 的标准输出。
您的代码调用 select
时超时为 0.1 秒。这意味着调用将每秒 return 10 次,无论管道中是否有任何输入。每次 returns 它都会写一个换行符。因此输出将是一连串以每秒 10 条左右的速度出现的换行符。
你说你想定义信号,但完全不清楚这意味着什么。如果您的意思是要为 child 定义信号 处理程序 ,这在您显示的代码中是不可能的。信号处理程序不会在对 Unix.execvp
的调用中保留。如果您考虑一下,这是唯一可行的方法,因为 execvp
调用会删除 parent 进程中的所有代码,并将其替换为来自其他可执行文件的代码。
分叉一个child然后向它发送信号并不难。但不清楚您要对管道和 select 做什么。并且不清楚您希望信号在 child 过程中做什么。如果你把这些都解释清楚,就更容易给出更详细的答案。
更新
一般来说,将您 post 编辑到 Whosebug 的代码修改为 Whosebug 的代码不是一个好主意(除了修复拼写错误),因为以前的评论和答案对新代码不再有意义.最好post更新代码。
您的新代码看起来更像是您试图通过管道从 child 读取输入 。这更明智,但那不是我所说的 "user input."
你没有指定 child 的输入应该来自哪里,但我怀疑你正计划通过另一个管道发送输入。
如果是这样,由于 child 进程及其管道之间的缓冲,这是一个众所周知的不可靠设置。如果您没有自己编写 child 进程的代码,则无法确定它会从读取管道读取适当大小的数据并在适当的时间将其输出刷新到写入管道。通常的结果是事情立即停滞不前。
如果您正在为 child 进程编写代码,您需要确保它读取的数据大小与 parent 正在写入的数据相同。如果 child 要求的数据多于此,它将阻塞。如果 parent 正在等待答案出现在其读取管道中,您将遇到死锁(这是通常的结果,除非您非常小心)。
您还需要确保 child 随时刷新其输出,以供 parent 进程读取。并且您还需要随时刷新 parent 进程的输出,以便 child 进程读取它。 (并且 parent 必须以 child 预期的大小写入数据。)
你还没有解释你想用信号做什么,所以我帮不上忙。读取 child 进程写入的整数值并将信号发送到 child 进程作为响应没有多大意义。
这是一些有效的代码。它对信号没有任何作用,因为我不明白你想做什么。但是它正确地设置了管道并通过 child 进程发送了一些 fixed-length 行文本。 child 过程只是将所有字符更改为大写。
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 个字节,您将看到人们尝试设置此 dual-pipe 方案时通常会发生的死锁。
你发送信号的代码看起来不错,但我不知道你发送信号时期望发生什么。
我正在尝试根据不同的用户输入从 parent 进程向 child 进程发送一些信号(例如 SIGSTOP、SIGUSR1)。 parent 进程一直等待用户输入并将相应的信号发送到 child。如果没有用户输入,child 会执行自己的工作。
我将我的 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),我应该如何以及在哪里做?
谢谢!
不太清楚你要做什么。以下是对您显示的代码的一些评论。
parent 进程似乎正在读取它自己创建的管道 (pr
)。但是你说 parent 进程正在等待用户输入。用户输入不会显示在您自己创建的管道中。
几乎总是通过读取标准输入来寻找用户输入,Unix.stdin
。
该代码创建了另一个管道,似乎旨在供 child 进程使用,但没有安排 child 访问该管道的文件描述符。 child 将改为读取 parent 的标准输入并写入 parent 的标准输出。
您的代码调用 select
时超时为 0.1 秒。这意味着调用将每秒 return 10 次,无论管道中是否有任何输入。每次 returns 它都会写一个换行符。因此输出将是一连串以每秒 10 条左右的速度出现的换行符。
你说你想定义信号,但完全不清楚这意味着什么。如果您的意思是要为 child 定义信号 处理程序 ,这在您显示的代码中是不可能的。信号处理程序不会在对 Unix.execvp
的调用中保留。如果您考虑一下,这是唯一可行的方法,因为 execvp
调用会删除 parent 进程中的所有代码,并将其替换为来自其他可执行文件的代码。
分叉一个child然后向它发送信号并不难。但不清楚您要对管道和 select 做什么。并且不清楚您希望信号在 child 过程中做什么。如果你把这些都解释清楚,就更容易给出更详细的答案。
更新
一般来说,将您 post 编辑到 Whosebug 的代码修改为 Whosebug 的代码不是一个好主意(除了修复拼写错误),因为以前的评论和答案对新代码不再有意义.最好post更新代码。
您的新代码看起来更像是您试图通过管道从 child 读取输入 。这更明智,但那不是我所说的 "user input."
你没有指定 child 的输入应该来自哪里,但我怀疑你正计划通过另一个管道发送输入。
如果是这样,由于 child 进程及其管道之间的缓冲,这是一个众所周知的不可靠设置。如果您没有自己编写 child 进程的代码,则无法确定它会从读取管道读取适当大小的数据并在适当的时间将其输出刷新到写入管道。通常的结果是事情立即停滞不前。
如果您正在为 child 进程编写代码,您需要确保它读取的数据大小与 parent 正在写入的数据相同。如果 child 要求的数据多于此,它将阻塞。如果 parent 正在等待答案出现在其读取管道中,您将遇到死锁(这是通常的结果,除非您非常小心)。
您还需要确保 child 随时刷新其输出,以供 parent 进程读取。并且您还需要随时刷新 parent 进程的输出,以便 child 进程读取它。 (并且 parent 必须以 child 预期的大小写入数据。)
你还没有解释你想用信号做什么,所以我帮不上忙。读取 child 进程写入的整数值并将信号发送到 child 进程作为响应没有多大意义。
这是一些有效的代码。它对信号没有任何作用,因为我不明白你想做什么。但是它正确地设置了管道并通过 child 进程发送了一些 fixed-length 行文本。 child 过程只是将所有字符更改为大写。
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 个字节,您将看到人们尝试设置此 dual-pipe 方案时通常会发生的死锁。
你发送信号的代码看起来不错,但我不知道你发送信号时期望发生什么。