You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 10:10:04