前提
NovaShell主线版本中exec命令未禁用终端raw模式会导致进程无法接收信号,此处手动修改NovaShell代码,在exec前禁用raw模式
问题发现
禁用raw模式后,使用测试程序测试是否能正常接收SIGINT
信号,代码如下:
#include <stdio.h>
#include <signal.h>
#include <unistd.h>
#include <stdlib.h>
// 信号处理函数
void handle_signal(int signal)
{
if (signal == SIGINT)
{
printf("Caught SIGINT (Ctrl+C). Exiting gracefully...\n");
// 你可以在这里执行清理操作
exit(0); // 终止程序
}
}
int main()
{
// 注册信号处理函数
signal(SIGINT, handle_signal);
printf("program pid: %d.\n", getpid());
// 模拟一个长时间运行的进程
while (1)
{
printf("Running... Press Ctrl+C to stop.\n");
sleep(5);
}
return 0;
}
发现依旧无法终止进程
调试过程
在内核的ProcessManager::exit()
方法中打印日志输出被终止进程的pid:
在NovaShell中打印shell进程的pid:
在测试程序中打印pid:
运行结果如下:
shell pid: 6
program pid: 7
pid to exit: 0
pid对比后发现都不符合,因此猜测:SIGINT信号没有正确地传到进程中,导致ctrl+c无法终止进程
根据日志中的
Pid(0)
猜测信号传递到init程序DragonReach中?
@longjin @GnoCiYeH
在你说的地方打印日志,发现的确会跳转到这里,并将pgid当成pid调用kill
于是我希望通过在应用程序中手动设置pgid让信号传递到正确的进程,但发现setpgid
系统调用未实现
我继续寻找其他方法试图修改pgid,然后发现这个pgid会在两个地方设置:
TtyDevice::open()
方法中调用proc_set_tty
方法,会设置pgid为当前进程pid
tty_job_control.rs (revision 52bcb59e9286def2b66d766f6bf6f46745795ec8) - OpenGrok cross reference for /DragonOS/kernel/src/driver/tty/tty_job_control.rs
ioctl
传递参数为TIOCSPGRP
,最终进入job_ctrl_ioctl
方法
tty_job_control.rs (revision 52bcb59e9286def2b66d766f6bf6f46745795ec8) - OpenGrok cross reference for /DragonOS/kernel/src/driver/tty/tty_job_control.rs
于是尝试在NovaShell中使用ioctl设置前台进程组,无效
打印日志发现ioctl调用成功,但没有走到设置pgid这一步
猜测是由于ctrl.session.unwrap() != current.pid()
这一判断条件导致,因为tty初始化时将session设置为当时的pid(为1),导致NovaShell进程(pid为6)调用ioctl时条件不符导致未能成功设置
我感觉这样的话是不是tty也有点问题,记录的前台进程会一直为1
@longjin @GnoCiYeH
1 个赞
这个地方我怀疑是不是需要在dragonreach中调用ioctl来设置前台进程?你可以试试把init程序换为NovaShell启动,再查看是否能切换前台进程。后续若有问题再讨论。
init程序换成NovaShell,可以切换前台进程了
这样修改一下dragonreach,应该就能初步实现ctrl+c终止信号的功能了
@GnoCiYeH @longjin
我查了关于会话和进程组的资料,发现shell应该是一个会话的控制进程,像这样:
所以正确的做法应该是在shell进程中开启新的会话并设置前台进程,而不是在init程序中设置前台进程
进一步查找得知使用ioctl()
+ TIOCSCTTY
命令可以设置tty的会话id,参考函数:tiocsctty
但是这样需要进程支持sid并实现setsid系统调用
看来前两天我跟你说的那个session还是得实现啊哈哈 @GnoCiYeH
@MemoryShore 要不看看有没有简单的实现方式?