Vala - system signal agent
Unix系統的行程間通訊機制中,有一套稱為 signal 的信號機制。因為它被列在 POSIX 規範之中,所以本文將以 Posix signal 稱之。Psoxi signal 是一種簡單的事件通知機制。它將某些事件予以編號,例如 SIGHUP, SIGTERM 等。程序可以向系統註冊這些信號的處理函式。當特定事件發生時,系統就會打斷程序目前的流程,將執行點轉移到程序指定的處理函式。
然而從設計模式的眼光來看,Posix signal 對程序而言是 Singalton 模式,它只有一個實體。所以同一時間,每一個 Posix 信號只能註冊一個處理函式。當你的程序內有多個單元關心同一個 Posix 信號時,程序員必須要另行安排登記與分派機制,以免不同的單元彼此爭搶 Posix 信號的處理權。 GNOME 環境的程序員,可以利用 GObject 型別機制提供的 GObject signal 機制,實現 Posix signal 的分派機制。
實作說明
由於 Posix signal 係由作業系統提供,故我在此稱為 System Signal。我先以 Singalton 設計模式撰寫一個專責接收 Posix signal 的 Handler 類別,其內有一個基於 GObject 的實體。當 Handler 接收到 Posix signal 之後,會再透過 GObject signal 機制轉發給其他人。因此外部可同樣透過 GObject signal 去傾聽 Posix signal 。由 GObject signal 機制去負責 Posix signal 的再分派工作。
using Posix;
namespace SystemSignal {
private class Handler {
private static Handler _handler = null;
private Handler() {
Posix.stdout.printf("new singleton instance\n");
sigaction_t sig_act = {};
sig_act.sa_handler = signum => {
Posix.stdout.printf("handle signal (%d)\n", signum);
_handler.delivered(signum); // emit delivered signal.
};
sigfillset(sig_act.sa_mask);
sig_act.sa_flags = 0;
int[] signal_set = { // See also manpage signal(7)
SIGHUP, SIGINT, SIGQUIT, SIGILL,
SIGABRT, SIGFPE, SIGSEGV, SIGPIPE,
SIGALRM, SIGTERM
};
foreach (var i in signal_set)
sigaction(i, sig_act, null);
}
public static Handler singleton {
get {
if (_handler == null)
_handler = new Handler();
return _handler;
}
}
/**
To prompt system signal has been delivered.
This is a signal based on GObject type system.
*/
public signal void delivered(int signum);
}
public class SignalAgent {
public SignalAgent() {
var handler = Handler.singleton;
handler.delivered.connect(forward);
}
/**
To forward delivered signal of system signal to customer's routine.
*/
private void forward(int signum) {
Posix.stdout.printf("forward %d\n", signum);
delivered(signum);
}
/**
To prompt system signal has been delivered.
This is a signal based on GObject type system.
*/
public signal void delivered(int signum);
}
}
SystemSignal.Handler 提供類別方法 singalton() 讓外部取得其單一實體。當此實體接受到 Posix signal 時,則會發送名為 delivered 的 GObject signal,其夾帶的參數 signum 則為此次接受到的 Posix signal 代號。
SignalAgent 則實作了一個會其傾聽 SystemSignal.Handler 所發送的信號的類別。凡是對 Posix signal 有興趣的單元,都可以自己配置一個 SignalAgent 實體,並指派各自的處理函數。彼此獨立,不會發生爭搶 Posix signal 處理權的問題。
使用範例
signal_agent_demo.vala 示範了 SignalAgent 的使用方式。
// valac --pkg posix signal_agent_demo.vala system_signal_agent.vala
using SystemSignal;
public void main() {
var sp = new SignalAgent();
var sp2 = new SignalAgent();
sp.delivered.connect(signum => {
Posix.stdout.printf("1 callback %d\n", signum);
});
sp2.delivered.connect(signum => {
Posix.stdout.printf("2 callback %d\n", signum);
});
Posix.stdin.getc();
}
編譯之後執行它,會停下來等待使用者按下任意鍵後結束程式。在它等待的過程中,使用者可以透過 kill 指令對它發送系統信號。或者直接按下 Ctrl+C ,送出 SIGINT 信號。
範例程式使用 lambda (此處也可稱為匿名函數) 指派 Posix signal 發生時的處理函數。兩個 Agent 彼此獨立,老死不相往來。