在操作系统中运行的应用程序如何保持执行状态,即使它们已经完成了所请求的任务,例如我打开一个文本编辑器应用程序,编写一些文本并保存它,但应用程序仍然在执行中,即它是跑步。程序不是在完成所有指令后立即退出执行(即终止)吗?那么这是如何实现的呢? 我很好奇应用程序开发框架是如何做到这一点的。 类似地,网络服务器如何始终运行,理想情况下应该如此。
一种天真的方法可能是使用始终为真的循环,即
while(1) {
// perform the operations
}
我还向 AI 询问过,它建议应用程序可能使用阻塞 I/O 系统调用,如
select()
、poll()
或 epoll_wait()
。
但我不确定它是什么以及其幕后工作原理。
这实际上在不同的计算级别上表现不同。
在CPU级别,除非你告诉它做一些特定的事情以避免运行(例如“停止”或“调用系统调用1234,恰好是
exit()
”,否则CPU将继续运行程序的代码段。所以如果编译器没有插入结束程序的指令,并且代码段现在在最后一条指令之后包含垃圾,CPU 将执行这些垃圾指令(直到发生某些情况,例如段错误)。
在编程语言层面,如果我们以C为例,如果你有这个
main()
:
int main(int argc, char **argv) {
printf("hello world\n");
return 0;
}
然后,当您链接程序时实际发生的情况是,生成的二进制文件包含一个入口点,大致行为如下:
void _start() {
c_runtime_setup();
int ret = main(argc, argv);
exit(ret);
}
所以语言会使用 exit() 来避免运行垃圾代码,但是 main() 内部的程序如果愿意也可以调用 exit()。 (就此而言,设置阶段也可以在进入 main() 之前退出)。
在框架级别,如果您用伪代码绘制它们的 main() 草图,您会得到类似的东西(严重过度简化):
int main() {
register_handlers();
while (true) {
event = wait_event();
process_event(event);
}
}
地点:
epoll_wait()
和朋友是需要监视一组资源的操作系统系统调用,操作系统会阻塞调用线程,直到(至少)其中一个资源经历了事件(或外部事件,如超时或信号)已提出)。在许多此类框架中,此“事件循环”实际上可能在专用线程而不是主线程上运行,并且在许多框架中,事件处理程序在单独的线程上运行(或预期运行)。
这意味着当保存到文件写入磁盘的事件处理程序仍在运行时,事件循环已经在等待下一个事件,而不是退出。
通常注册的一个事件是“用户单击了‘退出’菜单项”,其处理程序是“退出程序”。
通常退出程序的另一个事件是“收到操作系统中断信号”(Unix 上的 SIGINT)。