xref: /DragonOS/kernel/src/ipc/signal.rs (revision 3c82aa56d1b784ea7371100b3e906365be8332fd)
1 use core::sync::atomic::compiler_fence;
2 
3 use alloc::sync::Arc;
4 
5 use crate::{
6     arch::ipc::signal::{SigCode, SigFlags, SigSet, Signal},
7     ipc::signal_types::SigactionType,
8     kwarn,
9     libs::spinlock::SpinLockGuard,
10     process::{pid::PidType, Pid, ProcessControlBlock, ProcessFlags, ProcessManager},
11     syscall::SystemError,
12 };
13 
14 use super::signal_types::{
15     SaHandlerType, SigInfo, SigType, Sigaction, SignalStruct, SIG_KERNEL_STOP_MASK,
16 };
17 
18 impl Signal {
19     /// 向目标进程发送信号
20     ///
21     /// ## 参数
22     ///
23     /// - `sig` 要发送的信号
24     /// - `info` 要发送的信息
25     /// -  `pid` 进程id(目前只支持pid>0)
26     pub fn send_signal_info(
27         &self,
28         info: Option<&mut SigInfo>,
29         pid: Pid,
30     ) -> Result<i32, SystemError> {
31         // TODO:暂时不支持特殊的信号操作,待引入进程组后补充
32         // 如果 pid 大于 0,那么会发送信号给 pid 指定的进程
33         // 如果 pid 等于 0,那么会发送信号给与调用进程同组的每个进程,包括调用进程自身
34         // 如果 pid 小于 -1,那么会向组 ID 等于该 pid 绝对值的进程组内所有下属进程发送信号。向一个进程组的所有进程发送信号在 shell 作业控制中有特殊有途
35         // 如果 pid 等于 -1,那么信号的发送范围是:调用进程有权将信号发往的每个目标进程,除去 init(进程 ID 为 1)和调用进程自身。如果特权级进程发起这一调用,那么会发送信号给系统中的所有进程,上述两个进程除外。显而易见,有时也将这种信号发送方式称之为广播信号
36         // 如果并无进程与指定的 pid 相匹配,那么 kill() 调用失败,同时将 errno 置为 ESRCH(“查无此进程”)
37         if pid.lt(&Pid::from(0)) {
38             kwarn!("Kill operation not support: pid={:?}", pid);
39             return Err(SystemError::EOPNOTSUPP_OR_ENOTSUP);
40         }
41         compiler_fence(core::sync::atomic::Ordering::SeqCst);
42         // 检查sig是否符合要求,如果不符合要求,则退出。
43         if !self.is_valid() {
44             return Err(SystemError::EINVAL);
45         }
46         let mut retval = Err(SystemError::ESRCH);
47         let pcb = ProcessManager::find(pid);
48 
49         if pcb.is_none() {
50             kwarn!("No such process.");
51             return retval;
52         }
53         // println!("Target pcb = {:?}", pcb.as_ref().unwrap());
54         compiler_fence(core::sync::atomic::Ordering::SeqCst);
55         // 发送信号
56         retval = self.send_signal(info, pcb.unwrap(), PidType::PID);
57         compiler_fence(core::sync::atomic::Ordering::SeqCst);
58         return retval;
59     }
60 
61     /// @brief 判断是否需要强制发送信号,然后发送信号
62     /// 进入函数后加锁
63     ///
64     /// @return SystemError 错误码
65     fn send_signal(
66         &self,
67         info: Option<&mut SigInfo>,
68         pcb: Arc<ProcessControlBlock>,
69         pt: PidType,
70     ) -> Result<i32, SystemError> {
71         // 是否强制发送信号
72         let mut force_send = false;
73         // signal的信息为空
74 
75         if let Some(ref siginfo) = info {
76             force_send = matches!(siginfo.sig_code(), SigCode::Kernel);
77         } else {
78             // todo: 判断signal是否来自于一个祖先进程的namespace,如果是,则强制发送信号
79             //详见 https://opengrok.ringotek.cn/xref/linux-6.1.9/kernel/signal.c?r=&mo=32170&fi=1220#1226
80         }
81 
82         if !self.prepare_sianal(pcb.clone(), force_send) {
83             return Err(SystemError::EINVAL);
84         }
85         // kdebug!("force send={}", force_send);
86         let pcb_info = pcb.sig_info();
87         let pending = if matches!(pt, PidType::PID) {
88             pcb_info.sig_shared_pending()
89         } else {
90             pcb_info.sig_pending()
91         };
92         compiler_fence(core::sync::atomic::Ordering::SeqCst);
93         // 如果是kill或者目标pcb是内核线程,则无需获取sigqueue,直接发送信号即可
94         if matches!(self, Signal::SIGKILL) || pcb.flags().contains(ProcessFlags::KTHREAD) {
95             //避免死锁
96             drop(pcb_info);
97             self.complete_signal(pcb.clone(), pt);
98         }
99         // 如果不是实时信号的话,同一时刻信号队列里只会有一个待处理的信号,如果重复接收就不做处理
100         else if !self.is_rt_signal() && pending.queue().find(self.clone()).0.is_some() {
101             return Ok(0);
102         } else {
103             // TODO signalfd_notify 完善 signalfd 机制
104             // 如果是其他信号,则加入到sigqueue内,然后complete_signal
105             let new_sig_info = match info {
106                 Some(siginfo) => {
107                     // 已经显式指定了siginfo,则直接使用它。
108                     (*siginfo).clone()
109                 }
110                 None => {
111                     // 不需要显示指定siginfo,因此设置为默认值
112                     SigInfo::new(
113                         self.clone(),
114                         0,
115                         SigCode::User,
116                         SigType::Kill(ProcessManager::current_pcb().pid()),
117                     )
118                 }
119             };
120             drop(pcb_info);
121             pcb.sig_info_mut()
122                 .sig_pending_mut()
123                 .queue_mut()
124                 .q
125                 .push(new_sig_info);
126 
127             if pt == PidType::PGID || pt == PidType::SID {}
128             self.complete_signal(pcb.clone(), pt);
129         }
130         compiler_fence(core::sync::atomic::Ordering::SeqCst);
131         return Ok(0);
132     }
133 
134     /// @brief 将信号添加到目标进程的sig_pending。在引入进程组后,本函数还将负责把信号传递给整个进程组。
135     ///
136     /// @param sig 信号
137     /// @param pcb 目标pcb
138     /// @param pt siginfo结构体中,pid字段代表的含义
139     fn complete_signal(&self, pcb: Arc<ProcessControlBlock>, pt: PidType) {
140         // kdebug!("complete_signal");
141         // todo: 将信号产生的消息通知到正在监听这个信号的进程(引入signalfd之后,在这里调用signalfd_notify)
142         // 将这个信号加到目标进程的sig_pending中
143         pcb.sig_info_mut()
144             .sig_pending_mut()
145             .signal_mut()
146             .insert(self.clone().into());
147         compiler_fence(core::sync::atomic::Ordering::SeqCst);
148         // ===== 寻找需要wakeup的目标进程 =====
149         // 备注:由于当前没有进程组的概念,每个进程只有1个对应的线程,因此不需要通知进程组内的每个进程。
150         //      todo: 当引入进程组的概念后,需要完善这里,使得它能寻找一个目标进程来唤醒,接着执行信号处理的操作。
151 
152         // let _signal = pcb.sig_struct();
153 
154         let mut _target: Option<Arc<ProcessControlBlock>> = None;
155 
156         // 判断目标进程是否想接收这个信号
157         if self.wants_signal(pcb.clone()) {
158             _target = Some(pcb.clone());
159         } else if pt == PidType::PID {
160             /*
161              * There is just one thread and it does not need to be woken.
162              * It will dequeue unblocked signals before it runs again.
163              */
164             return;
165         } else {
166             /*
167              * Otherwise try to find a suitable thread.
168              * 由于目前每个进程只有1个线程,因此当前情况可以返回。信号队列的dequeue操作不需要考虑同步阻塞的问题。
169              */
170             return;
171         }
172 
173         // TODO:引入进程组后,在这里挑选一个进程来唤醒,让它执行相应的操作。
174         compiler_fence(core::sync::atomic::Ordering::SeqCst);
175         // TODO: 到这里,信号已经被放置在共享的pending队列中,我们在这里把目标进程唤醒。
176         if _target.is_some() {
177             let guard = pcb.sig_struct();
178             signal_wake_up(pcb.clone(), guard, *self == Signal::SIGKILL);
179         }
180     }
181 
182     /// @brief 本函数用于检测指定的进程是否想要接收SIG这个信号。
183     /// 当我们对于进程组中的所有进程都运行了这个检查之后,我们将可以找到组内愿意接收信号的进程。
184     /// 这么做是为了防止我们把信号发送给了一个正在或已经退出的进程,或者是不响应该信号的进程。
185     #[inline]
186     fn wants_signal(&self, pcb: Arc<ProcessControlBlock>) -> bool {
187         // 如果改进程屏蔽了这个signal,则不能接收
188         if pcb.sig_info().sig_block().contains(self.clone().into()) {
189             return false;
190         }
191 
192         // 如果进程正在退出,则不能接收信号
193         if pcb.flags().contains(ProcessFlags::EXITING) {
194             return false;
195         }
196 
197         if *self == Signal::SIGKILL {
198             return true;
199         }
200 
201         if pcb.sched_info().state().is_blocked() {
202             return false;
203         }
204 
205         // todo: 检查目标进程是否正在一个cpu上执行,如果是,则返回true,否则继续检查下一项
206 
207         // 检查目标进程是否有信号正在等待处理,如果是,则返回false,否则返回true
208         if pcb.sig_info().sig_pending().signal().bits() == 0 {
209             assert!(pcb.sig_info().sig_pending().queue().q.is_empty());
210             return true;
211         } else {
212             return false;
213         }
214     }
215 
216     /// @brief 判断signal的处理是否可能使得整个进程组退出
217     /// @return true 可能会导致退出(不一定)
218     #[allow(dead_code)]
219     #[inline]
220     fn sig_fatal(&self, pcb: Arc<ProcessControlBlock>) -> bool {
221         let action = pcb.sig_struct().handlers[self.clone() as usize - 1].action();
222         // 如果handler是空,采用默认函数,signal处理可能会导致进程退出。
223         match action {
224             SigactionType::SaHandler(handler) => handler.is_sig_default(),
225             SigactionType::SaSigaction(sigaction) => sigaction.is_none(),
226         }
227         // todo: 参照linux的sig_fatal实现完整功能
228     }
229 
230     /// 检查信号是否能被发送,并且而且要处理 SIGCONT 和 STOP 信号
231     ///
232     /// ## 参数
233     ///
234     /// - `pcb` 要发送信号的目标pcb
235     ///
236     /// - `force` 是否强制发送(指走 fast path , 不加入 sigpending按顺序处理,直接进入 complete_signal)
237     ///
238     /// ## 返回值
239     ///
240     /// - `true` 能够发送信号
241     ///
242     /// - `false` 不能发送信号
243     fn prepare_sianal(&self, pcb: Arc<ProcessControlBlock>, _force: bool) -> bool {
244         let flush: SigSet;
245         if !(self.into_sigset() & SIG_KERNEL_STOP_MASK).is_empty() {
246             flush = Signal::SIGCONT.into_sigset();
247             pcb.sig_info_mut()
248                 .sig_shared_pending_mut()
249                 .flush_by_mask(&flush);
250             // TODO 对每个子线程 flush mask
251         } else if *self == Signal::SIGCONT {
252             flush = SIG_KERNEL_STOP_MASK;
253             assert!(!flush.is_empty());
254             pcb.sig_info_mut()
255                 .sig_shared_pending_mut()
256                 .flush_by_mask(&flush);
257             let _r = ProcessManager::wakeup_stop(&pcb);
258             // TODO 对每个子线程 flush mask
259             // 这里需要补充一段逻辑,详见https://opengrok.ringotek.cn/xref/linux-6.1.9/kernel/signal.c#952
260         }
261 
262         // 一个被阻塞了的信号肯定是要被处理的
263         if pcb.sig_info().sig_block().contains(self.into_sigset()) {
264             return true;
265         }
266         return !pcb.sig_struct().handlers[self.clone() as usize - 1].is_ignore();
267 
268         //TODO 仿照 linux 中的prepare signal完善逻辑,linux 中还会根据例如当前进程状态(Existing)进行判断,现在的信号能否发出就只是根据 ignored 来判断
269     }
270 }
271 
272 /// 因收到信号而唤醒进程
273 ///
274 /// ## 参数
275 ///
276 /// - `pcb` 要唤醒的进程pcb
277 /// - `_guard` 信号结构体锁守卫,来保证信号结构体已上锁
278 /// - `fatal` 表明这个信号是不是致命的(会导致进程退出)
279 #[inline]
280 fn signal_wake_up(pcb: Arc<ProcessControlBlock>, _guard: SpinLockGuard<SignalStruct>, fatal: bool) {
281     // 如果是 fatal 的话就唤醒 stop 和 block 的进程来响应,因为唤醒后就会终止
282     // 如果不是 fatal 的就只唤醒 stop 的进程来响应
283     // kdebug!("signal_wake_up");
284     // 如果目标进程已经在运行,则发起一个ipi,使得它陷入内核
285     let r = ProcessManager::wakeup_stop(&pcb);
286     if r.is_ok() {
287         ProcessManager::kick(&pcb);
288     } else {
289         if fatal {
290             let _r = ProcessManager::wakeup(&pcb).map(|_| {
291                 ProcessManager::kick(&pcb);
292             });
293         }
294     }
295 }
296 
297 /// @brief 当一个进程具有多个线程之后,在这里需要重新计算线程的flag中的TIF_SIGPENDING位
298 fn recalc_sigpending() {
299     // todo:
300 }
301 
302 /// @brief 刷新指定进程的sighand的sigaction,将满足条件的sigaction恢复为Default
303 ///     除非某个信号被设置为ignore且force_default为false,否则都不会将其恢复
304 ///
305 /// @param pcb 要被刷新的pcb
306 /// @param force_default 是否强制将sigaction恢复成默认状态
307 pub fn flush_signal_handlers(pcb: Arc<ProcessControlBlock>, force_default: bool) {
308     compiler_fence(core::sync::atomic::Ordering::SeqCst);
309     // kdebug!("hand=0x{:018x}", hand as *const sighand_struct as usize);
310     let actions = &mut pcb.sig_struct().handlers;
311 
312     for sigaction in actions.iter_mut() {
313         if force_default || !sigaction.is_ignore() {
314             sigaction.set_action(SigactionType::SaHandler(SaHandlerType::SigDefault));
315         }
316         // 清除flags中,除了DFL和IGN以外的所有标志
317         sigaction.set_restorer(None);
318         sigaction.mask_mut().remove(SigSet::all());
319         compiler_fence(core::sync::atomic::Ordering::SeqCst);
320     }
321     compiler_fence(core::sync::atomic::Ordering::SeqCst);
322 }
323 
324 pub(super) fn do_sigaction(
325     sig: Signal,
326     act: Option<&mut Sigaction>,
327     old_act: Option<&mut Sigaction>,
328 ) -> Result<(), SystemError> {
329     if sig == Signal::INVALID {
330         return Err(SystemError::EINVAL);
331     }
332     let pcb = ProcessManager::current_pcb();
333     // 指向当前信号的action的引用
334     let action: &mut Sigaction = &mut pcb.sig_struct().handlers[sig as usize - 1];
335 
336     // 对比 MUSL 和 relibc , 暂时不设置这个标志位
337     // if action.flags().contains(SigFlags::SA_FLAG_IMMUTABLE) {
338     //     return Err(SystemError::EINVAL);
339     // }
340 
341     // 保存原有的 sigaction
342     let old_act: Option<&mut Sigaction> = {
343         if old_act.is_some() {
344             let oa = old_act.unwrap();
345             *(oa) = (*action).clone();
346             Some(oa)
347         } else {
348             None
349         }
350     };
351     // 清除所有的脏的sa_flags位(也就是清除那些未使用的)
352     let act = {
353         if act.is_some() {
354             let ac = act.unwrap();
355             *ac.flags_mut() &= SigFlags::SA_ALL;
356             Some(ac)
357         } else {
358             None
359         }
360     };
361 
362     if old_act.is_some() {
363         *old_act.unwrap().flags_mut() &= SigFlags::SA_ALL;
364     }
365 
366     if act.is_some() {
367         let ac = act.unwrap();
368         // 将act.sa_mask的SIGKILL SIGSTOP的屏蔽清除
369         ac.mask_mut()
370             .remove(SigSet::from(Signal::SIGKILL.into()) | SigSet::from(Signal::SIGSTOP.into()));
371 
372         // 将新的sigaction拷贝到进程的action中
373         *action = *ac;
374         /*
375         * 根据POSIX 3.3.1.3规定:
376         * 1.不管一个信号是否被阻塞,只要将其设置SIG_IGN,如果当前已经存在了正在pending的信号,那么就把这个信号忽略。
377         *
378         * 2.不管一个信号是否被阻塞,只要将其设置SIG_DFL,如果当前已经存在了正在pending的信号,
379               并且对这个信号的默认处理方式是忽略它,那么就会把pending的信号忽略。
380         */
381         if action.is_ignore() {
382             let mut mask: SigSet = SigSet::from_bits_truncate(0);
383             mask.insert(sig.into());
384             pcb.sig_info_mut().sig_pending_mut().flush_by_mask(&mask);
385             // todo: 当有了多个线程后,在这里进行操作,把每个线程的sigqueue都进行刷新
386         }
387     }
388     return Ok(());
389 }
390 
391 /// 设置当前进程的屏蔽信号 (sig_block),待引入 [sigprocmask](https://man7.org/linux/man-pages/man2/sigprocmask.2.html) 系统调用后要删除这个散装函数
392 ///
393 /// ## 参数
394 ///
395 /// - `new_set` 新的屏蔽信号bitmap的值
396 pub fn set_current_sig_blocked(new_set: &mut SigSet) {
397     new_set.remove(SigSet::from(Signal::SIGKILL.into()) | SigSet::from(Signal::SIGSTOP.into()));
398     //TODO 把这个散装函数用 sigsetops 替换掉
399     let pcb = ProcessManager::current_pcb();
400 
401     /*
402         如果当前pcb的sig_blocked和新的相等,那么就不用改变它。
403         请注意,一个进程的sig_blocked字段不能被其他进程修改!
404     */
405     if pcb.sig_info().sig_block().eq(new_set) {
406         return;
407     }
408 
409     let guard = pcb.sig_struct_irq();
410     // todo: 当一个进程有多个线程后,在这里需要设置每个线程的block字段,并且 retarget_shared_pending(虽然我还没搞明白linux这部分是干啥的)
411 
412     // 设置当前进程的sig blocked
413     *pcb.sig_info_mut().sig_block_mut() = *new_set;
414     recalc_sigpending();
415     drop(guard);
416 }
417