Lua程序不响应SIGINT信号(CTRL+C)该如何解决?
如何让Lua程序响应CTRL+C(SIGINT信号)
我来帮你解决这个Lua程序没法响应CTRL+C的问题!你遇到的核心问题是:程序长时间阻塞在os.execute('sleep 10')或者http.request这类外部调用中,导致信号处理函数根本没机会被执行。下面给你几个实用的解决方案:
方案1:替换os.execute('sleep')为posix原生睡眠函数
os.execute('sleep 10')会启动一个独立的子进程,Lua主进程会一直等待子进程结束,这段时间里SIGINT信号完全没法触发处理逻辑。改用luaposix模块的posix.sleep()就不一样了——它是Lua进程自身的睡眠操作,期间信号处理函数能正常工作。
首先确保你已经安装了luaposix模块(通过LuaRocks安装:luarocks install luaposix),然后修改代码如下:
local ltn12 = assert(require('ltn12')) local cjson = assert(require('cjson')) local http = assert(require('socket.http')) local signal = require("posix.signal") local posix = require("posix") -- 注册SIGINT信号处理函数 signal.signal(signal.SIGINT, function(signum) io.write("\nInterrupting => 正在优雅退出...\n") -- 在这里添加你的状态保存逻辑,比如关闭文件、持久化数据等 os.exit(128 + signum) end) http.TIMEOUT = 120 local function execute() print('-- COMMANDER: Starting... ----------') posix.sleep(1) -- 替换os.execute('sleep 1') local ivy_url = 'http://localhost:3000/' if not ivy_url then print('-- COMMANDER: Error :: ivy_url not set ----------') execute() end local _heartbeat = 0 local last_timestamp = nil local url = nil while true do if _heartbeat % 3 == 0 then _heartbeat = 0 print('-- COMMANDER: Heartbeat... ----------') end _heartbeat = _heartbeat + 1 local response = {} local since = last_timestamp last_timestamp = os.date("%Y-%m-%dT%H:%M:%S") url = since and (ivy_url .. "/switch/sites?since=" .. since) or (ivy_url .. "/switch/sites") local _, code, _, _ = http.request { method = "GET", url = url, headers = { Authorization = "Basic " .. tostring("token")}, sink = ltn12.sink.table(response) } if code == 200 then local response_data = cjson.decode(response[1]) for _, site_desc in pairs(response_data['data']) do print(site_desc) end print('-- COMMANDER: Sent API request to Ivy to get site info updated_at ' .. tostring(since) .. ' ----------') elseif code == 400 then last_timestamp = since local response_data = cjson.decode(response[1]) print('-- COMMANDER: Got error in ivy API response : ' .. tostring(response_data['error']) .. ' at ' .. os.date("%Y-%m-%dT%H:%M:%S") .. ' ----------') else last_timestamp = since print('-- COMMANDER: Got error in ivy API response : ' .. tostring(code) .. ' at ' .. os.date("%Y-%m-%dT%H:%M:%S") .. ' ----------') end posix.sleep(10) -- 替换os.execute('sleep 10') end end execute()
方案2:优化HTTP请求的超时逻辑
你设置的http.TIMEOUT = 120意味着HTTP请求最多会阻塞2分钟,这段时间内信号也没法触发。可以把长超时拆分成多次短请求,或者确保luaposix的信号能打断socket的阻塞调用——只要你用的是最新版luaposix,这个功能是支持的。
方案3:协程配合信号处理(适合复杂场景)
如果你的程序有多个阻塞操作,可以用协程调度,让信号处理函数有机会插入执行:
local signal = require("posix.signal") local posix = require("posix") -- 注册信号处理函数,触发协程中断 local main_coro = coroutine.running() signal.signal(signal.SIGINT, function(signum) io.write("\n收到中断信号!\n") coroutine.resume(main_coro, "interrupted") end) local function execute() -- ... 你的初始化代码 ... while true do -- ... 你的API请求逻辑 ... -- 用协程等待,给信号处理留机会 local status = coroutine.yield() if status == "interrupted" then -- 执行清理逻辑后退出 print("正在清理资源...") os.exit(128 + signal.SIGINT) end posix.sleep(10) end end coroutine.wrap(execute)()
关键注意事项
- 一定要用luaposix模块(不是旧的posix模块),旧模块的信号处理逻辑有缺陷。
- 尽量避免用
os.execute执行长时间阻塞的命令,子进程运行时Lua主进程会彻底阻塞。 - 信号处理函数要尽量简洁,别在里面执行复杂操作,防止出现死锁或异常。
内容的提问来源于stack exchange,提问作者Ratatouille
相关产品推荐
相关产品推荐

