네, 우리가 함께 완성한 POSIX 시그널 처리 라이브러리의 전체 소스 코드를 재출력합니다.
이 코드는 다음과 같은 파일들로 구성됩니다.
posix-c.ads
: C 기본 타입 및 시그널 상수 정의posix-unistd.ads
/ adb
: pipe
, read
, write
등 unistd
함수 래퍼posix-signals.ads
/ adb
: sigaction
함수 저수준 래퍼posix-signal_handler.ads
/ adb
: 최종 고수준 시그널 핸들러 (듀얼 모델 지원)
1. posix-c.ads
-- posix-c.ads
with Interfaces.C;
package POSIX.C is
pragma Preelaborate;
type C_int is new Interfaces.C.int;
type PID_T is new C_int;
type Signal_Number is new C_int;
-- Standard Signal Numbers
SIGHUP : constant Signal_Number := 1;
SIGINT : constant Signal_Number := 2;
SIGQUIT : constant Signal_Number := 3;
SIGILL : constant Signal_Number := 4;
SIGTRAP : constant Signal_Number := 5;
SIGABRT : constant Signal_Number := 6;
SIGKILL : constant Signal_Number := 9;
SIGSEGV : constant Signal_Number := 11;
SIGPIPE : constant Signal_Number := 13;
SIGALRM : constant Signal_Number := 14;
SIGTERM : constant Signal_Number := 15;
SIGUSR1 : constant Signal_Number := 10;
SIGUSR2 : constant Signal_Number := 12;
SIGCHLD : constant Signal_Number := 17;
SIGCONT : constant Signal_Number := 18;
SIGSTOP : constant Signal_Number := 19;
-- Return codes
SUCCESS : constant C_int := 0;
FAILURE : constant C_int := -1;
end POSIX.C;
2. posix-unistd.ads
-- posix-unistd.ads
with Ada.Streams;
with Interfaces.C;
package POSIX.Unistd is
-- Custom exception for POSIX errors
POSIX_Error : exception;
-- Type-safe declarations for common identifiers
type File_Descriptor is new Interfaces.C.int;
type Process_ID is new Interfaces.C.int;
type User_ID is new Interfaces.C.unsigned;
type Group_ID is new Interfaces.C.unsigned;
-- Standard file descriptors
STDIN_FD : constant File_Descriptor := 0;
STDOUT_FD : constant File_Descriptor := 1;
STDERR_FD : constant File_Descriptor := 2;
-- For the pipe() system call
type Pipe_Ends is record
Read_End : File_Descriptor;
Write_End : File_Descriptor;
end record;
-- Process identifier functions
function get_process_id return Process_ID;
function get_parent_process_id return Process_ID;
function fork return Process_ID;
-- I/O operations
procedure close_file (fd : in File_Descriptor);
function read_from
(fd : in File_Descriptor;
buffer : in out Ada.Streams.Stream_Element_Array
) return Natural;
function write_to
(fd : in File_Descriptor;
buffer : in Ada.Streams.Stream_Element_Array
) return Natural;
-- Pipe creation
procedure create_pipe (ends : out Pipe_Ends);
end POSIX.Unistd;
3. posix-unistd.adb
-- posix-unistd.adb
with Interfaces.C;
with System;
package body POSIX.Unistd is
-- § Low-level C bindings (private to this package body)
-------------------------------------------------------
package C is
function c_getpid return Process_ID;
pragma Import (C, c_getpid, "getpid");
function c_getppid return Process_ID;
pragma Import (C, c_getppid, "getppid");
function c_fork return Process_ID;
pragma Import (C, c_fork, "fork");
function c_close (fd : File_Descriptor) return Interfaces.C.int;
pragma Import (C, c_close, "close");
function c_pipe (fds : System.Address) return Interfaces.C.int;
pragma Import (C, c_pipe, "pipe");
function c_read
(fd : File_Descriptor;
buf : System.Address;
len : Interfaces.C.size_t)
return Interfaces.C.long; -- ssize_t
pragma Import (C, c_read, "read");
function c_write
(fd : File_Descriptor;
buf : System.Address;
len : Interfaces.C.size_t)
return Interfaces.C.long; -- ssize_t
pragma Import (C, c_write, "write");
end C;
-- § High-level Ada wrappers
---------------------------
function get_process_id return Process_ID is
begin
return C.c_getpid;
end get_process_id;
function get_parent_process_id return Process_ID is
begin
return C.c_getppid;
end get_parent_process_id;
function fork return Process_ID is
pid : constant Process_ID := C.c_fork;
begin
if pid < 0 then
raise POSIX_Error with "fork failed";
end if;
return pid;
end fork;
procedure close_file (fd : in File_Descriptor) is
begin
if C.c_close (fd) /= 0 then
raise POSIX_Error with "close failed";
end if;
end close_file;
procedure create_pipe (ends : out Pipe_Ends) is
fd_array : aliased array (0 .. 1) of Interfaces.C.int;
begin
if C.c_pipe (fd_array'Address) /= 0 then
raise POSIX_Error with "pipe failed";
end if;
ends := (Read_End => File_Descriptor (fd_array (0)), Write_End => File_Descriptor (fd_array (1)));
end create_pipe;
function read_from
(fd : in File_Descriptor;
buffer : in out Ada.Streams.Stream_Element_Array)
return Natural
is
bytes_read : constant Interfaces.C.long :=
C.c_read (fd, buffer (buffer'First)'Address, buffer'Length);
begin
if bytes_read < 0 then
raise POSIX_Error with "read failed";
end if;
return Natural (bytes_read);
end read_from;
function write_to
(fd : in File_Descriptor;
buffer : in Ada.Streams.Stream_Element_Array)
return Natural
is
bytes_written : constant Interfaces.C.long :=
C.c_write (fd, buffer (buffer'First)'Address, buffer'Length);
begin
if bytes_written < 0 then
raise POSIX_Error with "write failed";
end if;
return Natural (bytes_written);
end write_to;
end POSIX.Unistd;
4. posix-signals.ads
-- posix-signals.ads
with System;
with POSIX.C;
package POSIX.Signals is
Signal_Error : exception;
type Signal_Set is private;
type Signal_Handler is access procedure (Signo : POSIX.C.Signal_Number);
pragma Convention (C, Signal_Handler);
-- Default and Ignore handlers
HANDLER_DEFAULT : constant Signal_Handler := null;
HANDLER_IGNORE : constant Signal_Handler := new procedure (Signo : POSIX.C.Signal_Number) is null;
-- Flags for sa_flags field
SA_NOCLDSTOP : constant POSIX.C.C_int := 1;
SA_SIGINFO : constant POSIX.C.C_int := 4;
SA_RESTART : constant POSIX.C.C_int := 16#10000000#;
SA_RESETHAND : constant POSIX.C.C_int := 16#80000000#;
type Sigaction_Record is record
Sa_Handler : Signal_Handler := HANDLER_DEFAULT;
Sa_Mask : Signal_Set;
Sa_Flags : POSIX.C.C_int := 0;
end record;
-- High-level wrapper for sigaction(2)
procedure set_action (
signum : in POSIX.C.Signal_Number;
action : in Sigaction_Record;
old_action : out Sigaction_Record
);
-- Signal set management
procedure empty_set (set : out Signal_Set);
private
pragma Convention (C, Signal_Set);
SIGSET_SIZE_BYTES : constant := 128;
type Signal_Set is array (1 .. SIGSET_SIZE_BYTES) of Interfaces.C.unsigned_char;
end POSIX.Signals;
5. posix-signals.adb
-- posix-signals.adb
with System;
with Interfaces.C;
package body POSIX.Signals is
-- Low-level C bindings, private to the package body
----------------------------------------------------
type C_Sigaction_Record is record
Sa_Handler : Signal_Handler;
Sa_Mask : Signal_Set;
Sa_Flags : POSIX.C.C_int;
Sa_Restorer : System.Address;
end record;
pragma Convention (C, C_Sigaction_Record);
function c_sigaction (
signum : POSIX.C.Signal_Number;
act : access C_Sigaction_Record;
oldact : access C_Sigaction_Record
) return POSIX.C.C_int;
pragma Import (C, c_sigaction, "sigaction");
function c_sigemptyset (set : access Signal_Set) return POSIX.C.C_int;
pragma Import (C, c_sigemptyset, "sigemptyset");
-- High-level Ada implementations
---------------------------------
procedure set_action (
signum : in POSIX.C.Signal_Number;
action : in Sigaction_Record;
old_action : out Sigaction_Record
) is
c_new_action : aliased C_Sigaction_Record;
c_old_action : aliased C_Sigaction_Record;
result : POSIX.C.C_int;
begin
c_new_action.Sa_Handler := action.Sa_Handler;
c_new_action.Sa_Mask := action.Sa_Mask;
c_new_action.Sa_Flags := action.Sa_Flags;
c_new_action.Sa_Restorer := System.Null_Address;
result := c_sigaction (signum, c_new_action'Access, c_old_action'Access);
if result /= POSIX.C.SUCCESS then
raise Signal_Error with "sigaction failed";
end if;
old_action.Sa_Handler := c_old_action.Sa_Handler;
old_action.Sa_Mask := c_old_action.Sa_Mask;
old_action.Sa_Flags := c_old_action.Sa_Flags;
end set_action;
procedure empty_set (set : out Signal_Set) is
begin
if c_sigemptyset (set'Access) /= POSIX.C.SUCCESS then
raise Signal_Error with "sigemptyset failed";
end if;
end empty_set;
end POSIX.Signals;
6. posix-signal_handler.ads
-- posix-signal_handler.ads
with POSIX.C;
with POSIX.Unistd;
package POSIX.Signal_Handler is
Signal_Error : exception;
type Signal_Set is array (Positive range <>) of POSIX.C.Signal_Number;
type Signal_Callback is access procedure (signo : in POSIX.C.Signal_Number);
-- § 모델 A: 내부 태스크와 콜백을 사용하는 간편 모드
procedure install_callback_handler (
for_signals : in Signal_Set;
on_signal : in Signal_Callback
);
-- § 모델 B: 외부 루프를 위해 Pipe FD를 반환하는 수동 모드
function install_polling_handler (
for_signals : in Signal_Set
) return POSIX.Unistd.File_Descriptor;
-- § 공통 자원 정리 함수
procedure terminate_handler;
end POSIX.Signal_Handler;
7. posix-signal_handler.adb
-- posix-signal_handler.adb
with Ada.Streams;
with Ada.Text_IO;
with POSIX.Signals;
with POSIX.Unistd;
package body POSIX.Signal_Handler is
-- § 패키지 내부 상태 변수
private_pipe : POSIX.Unistd.Pipe_Ends;
is_terminated : Boolean := False;
-- § Async-signal-safe 핸들러
procedure pipe_writing_handler (signo : POSIX.C.Signal_Number) is
signal_byte : aliased constant Ada.Streams.Stream_Element := Ada.Streams.Stream_Element(signo);
buffer : constant Ada.Streams.Stream_Element_Array(1 .. 1) := (1 => signal_byte);
bytes_written : Natural;
begin
if not is_terminated then
bytes_written := POSIX.Unistd.write_to (private_pipe.Write_End, buffer);
end if;
exception
when others => null;
end pipe_writing_handler;
-- § 모델 A를 위한 내부 태스크 및 콜백 변수
-------------------------------------------
task type Callback_Dispatcher;
dispatcher : Callback_Dispatcher;
user_callback : Signal_Callback;
task body Callback_Dispatcher is
buffer : aliased Ada.Streams.Stream_Element_Array(1 .. 1);
bytes_read : Natural;
signo : POSIX.C.Signal_Number;
begin
loop
bytes_read := POSIX.Unistd.read_from (private_pipe.Read_End, buffer);
exit when bytes_read = 0 or else is_terminated;
signo := POSIX.C.Signal_Number (buffer (1));
if user_callback /= null then
begin
user_callback (signo);
exception
when others => null; -- 사용자 콜백의 예외가 태스크를 중단시키지 않도록 함
end;
end if;
end loop;
exception
when POSIX.Unistd.POSIX_Error =>
null; -- 파이프가 닫히면 예외 발생 가능
end Callback_Dispatcher;
-- § 공통 핸들러 설치 로직
--------------------------
procedure install_common_handler (for_signals : in Signal_Set) is
action : POSIX.Signals.Sigaction_Record;
old_action : POSIX.Signals.Sigaction_Record;
begin
is_terminated := False;
POSIX.Unistd.create_pipe (private_pipe);
action.Sa_Handler := pipe_writing_handler'Access;
POSIX.Signals.empty_set (action.Sa_Mask);
action.Sa_Flags := POSIX.Signals.SA_RESTART;
for signo of for_signals loop
POSIX.Signals.set_action (signo, action, old_action);
end loop;
exception
when others =>
raise Signal_Error with "Failed to install common signal handler";
end install_common_handler;
-- § 공개 인터페이스 구현
--------------------------
procedure install_callback_handler (
for_signals : in Signal_Set;
on_signal : in Signal_Callback)
is
begin
user_callback := on_signal;
install_common_handler (for_signals);
-- 디스패처 태스크는 자동으로 시작됨
end install_callback_handler;
function install_polling_handler (
for_signals : in Signal_Set
) return POSIX.Unistd.File_Descriptor is
begin
install_common_handler (for_signals);
return private_pipe.Read_End;
end install_polling_handler;
procedure terminate_handler is
begin
if is_terminated then
return;
end if;
is_terminated := True;
-- 파이프를 닫아 블로킹된 read나 select를 깨운다.
POSIX.Unistd.close_file (private_pipe.Read_End);
POSIX.Unistd.close_file (private_pipe.Write_End);
-- 모델 A의 경우, 태스크가 종료되도록 잠시 기다릴 수 있음
-- delay 0.1; -- 필요한 경우
exception
when POSIX.Unistd.POSIX_Error =>
null; -- 이미 닫혔을 수 있음
end terminate_handler;
end POSIX.Signal_Handler;
댓글 영역
획득법
① NFT 발행
작성한 게시물을 NFT로 발행하면 일주일 동안 사용할 수 있습니다. (최초 1회)
② NFT 구매
다른 이용자의 NFT를 구매하면 한 달 동안 사용할 수 있습니다. (구매 시마다 갱신)
사용법
디시콘에서지갑연결시 바로 사용 가능합니다.