常见功能开发中,需要获取其他命令的输出数据时,通常可以采用 popen
打开子进程进行读操作,但是 popen
并没有提供终止子进程的接口,pclose
会阻塞等待子进程自己结束然后才返回。导致在一些会持续输出的命令调用中,无法方便的结束子进程。
一种解决方法是使用 fork
来自行实现类似 popen
的接口,比如 Kill a process started with popen,但是在一些简单场景,可以尝试更为简单的方法。
popen
在 Linux 上通常是调用 shell 去创建会话执行命令,可以利用 shell 的方法来获取会话进程的 pid,然后在输出中输出 pid ,这样在调用 pclose
之前可以使用 pkill
调用去终止会话及子进程以避免 pclose
阻塞。
shell(常见 bash)中,$$
变量表示当前 shell 的进程 pid, 我们可以利用该变量来控制子进程。以 ping 指令为例,使用如下命令创建子进程:
1
2
3
|
# 需要注意 \$\$ 而非 $$,否则当前 shell 会直接解译该变量导致输出为当前 shell pid,
# 而非会话子进程
sh -c "echo pid=\$\$; ping 223.5.5.5"
|
其输出:
1
2
3
4
5
6
7
|
pid=62853
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=116 time=24.1 ms
64 bytes from 223.5.5.5: icmp_seq=2 ttl=116 time=23.3 ms
64 bytes from 223.5.5.5: icmp_seq=3 ttl=116 time=24.4 ms
64 bytes from 223.5.5.5: icmp_seq=4 ttl=116 time=23.4 ms
...
|
这时,在另外 shell 中执行 pkill -P 62883
来终止会话子进程(该命令无需 root 权限, pkill -P
只终止父进程 pid 匹配的进程, shell 进程在子进程命令结束后自然退出),子会话及其子进程会终止退出:
1
2
3
4
5
6
7
8
9
10
11
|
pid=62853
PING 223.5.5.5 (223.5.5.5) 56(84) bytes of data.
64 bytes from 223.5.5.5: icmp_seq=1 ttl=116 time=24.6 ms
64 bytes from 223.5.5.5: icmp_seq=2 ttl=116 time=24.7 ms
64 bytes from 223.5.5.5: icmp_seq=3 ttl=116 time=24.5 ms
64 bytes from 223.5.5.5: icmp_seq=4 ttl=116 time=24.2 ms
64 bytes from 223.5.5.5: icmp_seq=5 ttl=116 time=22.6 ms
64 bytes from 223.5.5.5: icmp_seq=6 ttl=116 time=21.2 ms
64 bytes from 223.5.5.5: icmp_seq=7 ttl=116 time=26.3 ms
64 bytes from 223.5.5.5: icmp_seq=8 ttl=116 time=24.7 ms
Terminated
|
利用该特性,在实践中可以在 popen
后,利用 fgets
从输出中获取会话 pid,然后在 pclose
前,调用 system("pkill -P xxxx")
来终止会话子进程,然后再调用 pclose
去释放资源,此时 pclose
调用不会阻塞, 该方法虽不优雅,但足够 quick-and-dirty.