pipe is one of the resources of the process control block
Issues to consider when implementing signals
what are signals?
How to use the signal?
How to manage signals?
Understand Signals
A signal is a software interrupt in the kernel that notifies an application.
Preparation Phase
Set the integer value of signal
Establish a routine signal_handler to respond to a certain signal value
Execution phase
Send a signal to a process, interrupt the current execution of the process, and switch to signal_handler for execution
Signal sample program (user mode)
...//usr/src/bin/sig_simple.rsfnfunc() { //signal_handlerprintln!("user_sig_test succsess");
sigreturn(); //return to the position before signal handling and continue execution
}
}
pubfnmain() -> i32 {
letmut new = SignalAction::default(); //new signal configurationlet old = SignalAction::default(); //Old signal configuration
new.handler = func asusize; //Set up a new signal handling routineif sigaction(SIGUSR1, &new, &old) < 0 { //setup signal_handlerpanic!("Sigaction failed!");
}
if kill(getpid() asusize, SIGUSR1) <0{ //send SIGUSR1 to itself
...
}
...
Relationship between Signals and Processes
signal is one of the resources of the process control block
Outline
Lab arrangement
Lab objectives
Overall idea
History background
Practical steps
Code structure
Design and Implementation of Pipes
Design and Implementation of Signals
Pipes: The Most Remarkable Invention in Unix
The concept of pipes was introduced by Douglas McIlroy of Bell Laboratories in an internal memorandum he wrote in 1964, in which he proposed the idea of connecting and twisting together multiple programs "like garden hoses" so that data could flow between different programs.
Around the second half of 1972, Ken Thompson, inspired by McIlroy's idea of pipes, quickly implemented the pipe mechanism in UNIX.
Signal: Error-Prone Software Interrupts in Unix
Signal has been supported in Unix since the first version, but they were a bit different from what we know today and required different system calls to catch different types of signals. Starting with Version 4, improvements were made so that all signals could be caught with a single system call.
Outline
Lab arrangement
Lab objectives
Overall idea
History background
Practical steps
Code structure
Design and Implementation of Pipes
Design and Implementation of Signals
Practical steps
git clone https://github.com/rcore-os/rCore-Tutorial-v3.git
cd rCore-Tutorial-v3
git checkout ch7
cd os
make run
Reference output
[RustSBI output]
...
filetest_simple
fantastic_text
***************/
Rust user shell
>>
After the operating system boots up its shell, users can execute applications by typing in the application name in the shell.
Test case: pipetest
Here, we run the test case pipetest of this chapter:
>> pipe test
Read OK, child process exited!
pipe test passed!
>>
This application passes the string "Hello, world!" between its parent and child processes using a pipe.
Test case: sig_simple
Here we run the test case sig_simple of this chapter:
This application establishes a signal handler func for the SIGUSR1 signal, and then sends a signal SIGUSR1 to itself through kill, and finally func will be called.
impl TaskControlBlock {
pubfnexec(&self, elf_data: &[u8], args: Vec<String>) {
...
// push arguments on user stack
}
The a0/a1 registers in the trap context, with a0 indicating the number of command line arguments and a1 indicating the starting address of argv_base in the blue region in the figure.
// user/src/bin/user_shell.rs
{
let pid = fork();
if pid == 0 {
let input_fd = open(input, ...); // Input redirection -- B child process
close(0); // Close file descriptor 0
dup(input_fd); // File descriptor 0 and input_fd point to the same file
close(input_fd); // Close input_fd file descriptor// Orlet output_fd = open(output, ...);// Output redirection -- A child process
close(1); // Close file descriptor 1
dup(output_fd); // File descriptor 1 and output_fd point to the same file
close(output_fd); // Close output_fd file descriptor// Execute new program after I/O redirection
exec(args_copy[0].as_str(), args_addr.as_slice());
}...
Outline
Lab arrangement
Code structure
Design and Implementation of Pipes
4. Design and Implementation of Signals
System calls related to signals
Core data structure of signals
Establishsignal handler
Support kill system call
System Calls Related to Signals
sigaction: Set signal handling routine
sigprocmask: Set signals to be blocked
kill: Send a signal to a process
sigreturn: Clear stack frame and return from signal handling routine
System Calls Related to Signals
// Set signal handler// signum: the signal to be set// action: new signal handling configuration// old_action: old signal handling configuration
sys_sigaction(signum: i32,
action: *const SignalAction,
old_action: *const SignalAction)
-> isizepubstructSignalAction {
// Address of the signal handlerpub handler: usize,
// signal maskpub mask: SignalFlags
}
System Calls Related to Signals
// Set signals to be blocked// mask: signal mask
sys_sigprocmask(mask: u32) -> isize
// Clear stack frame and return from signal handler
sys_sigreturn() -> isize
// Send a signal to a process// pid: process pid// signal: signal integer code
sys_kill(pid: usize, signal: i32) -> isize
Core Data Structure of Signals
Signal core data structure in process control block
pubstructTaskControlBlockInner {
...
pub signals: SignalFlags, // Signals to be responded topub signal_mask: SignalFlags, // Signals to be blockedpub handling_sig: isize, // Signal being handledpub signal_actions: SignalActions, // Signal handling routine tablepub killed: bool, // Whether the task has been killedpub frozen: bool, // Whether the task has been pausedpub trap_ctx_backup: Option<TrapContext> // Interrupted trap context
}
Establish signal_handler
fnsys_sigaction(signum: i32, action: *const SignalAction,
old_action: *mut SignalAction) -> isize {
// Save the old signal_handler address to old_actionlet old_kernel_action = inner.signal_actions.table[signum asusize];
*translated_refmut(token, old_action) = old_kernel_action;
// Set the new signal_handler address to TCB's signal_actionslet ref_action = translated_ref(token, action);
inner.signal_actions.table[signum asusize] = *ref_action;
For the signal number signum that needs to be modified:
Save the old signal_handler address to old_action
Set action as the new signal_handler address
Send signals through kill
fnsys_kill(pid: usize, signum: i32) -> isize {
letSome(task) = pid2task(pid);
// insert the signal if legalletmut task_ref = task.inner_exclusive_access();
task_ref.signals.insert(flag);
...
Send a signal with value signum to the process with process ID pid:
Find TCB based pid
Insert the signum signal value in the signals in the TCB
Send and Handle Signals through kill
From when process pid enters the kernel, the execution process before returning to the user mode from the kernel:
Execute APP --> __alltraps
--> trap_handler
--> handle_signals
--> check_pending_signals
--> call_kernel_signal_handler
--> call_user_signal_handler
--> // backup trap Context
// modify trap Context
trap_ctx.sepc = handler; // Set the entry point of the interrupt handler
trap_ctx.x[10] = sig; // Put the signal value into Reg[10]
--> trap_return // Find and jump to the `__restore` assembly function located in the trampoline page
--> __restore // Restore the modified trap context and execute sret
Execute the signal_handler function of APP
Resume Normal Execution of APP
After the process with process ID pid has finished executing the body of the signal_handler function, it will issue a sys_sigreturn system call:
fnsys_sigreturn() -> isize {
...
// Restore the trap context that was previously backed uplet trap_ctx = inner.get_trap_cx();
*trap_ctx = inner.trap_ctx_backup.unwrap();
...
Execute APP --> __alltraps
--> trap_handler
--> Handling sys_sigreturn system call
--> trap_return // Find and jump to the `__restore` assembly function located in the trampoline page
--> __restore // Restore the modified trap context and execute sret
Execute APP at the point where it was interrupted
The 11th day after the task is assigned (May 28, 2023);
Velociraptor possesses stealth and cunning. Velociraptor is smarter, has teamwork ability, and is good at teamwork Operating system
Those things about linux signal http://blog.chinaunix.net/uid-24774106-id-4061386.html
Douglas McIlroy (born 1932) is a mathematician, engineer and programmer. Since 2007 he is an adjunct professor of computer science at MIT and received his Ph.D.
McIlroy is best known for the original Unix pipeline implementation, software component parts and several Unix tools such as computer virus, diff, sort, join, talk, and tr.
The function of killed is to mark whether the current process has been killed. Because the process will not end immediately when it receives the kill signal, but will exit at an appropriate time. At this time, killed is needed as a mark to exit the unnecessary signal processing loop.
The frozen flag is related to the two signals SIGSTOP and SIGCONT. SIGSTOP will suspend the execution of the process, that is, set frozen to true. At this time, the current process will block waiting for SIGCONT (that is, the unfreezing signal). When the signal receives SIGCONT, set frozen to false, exit the cycle of waiting for the signal, and return to user mode to continue execution.