xref: /DragonOS/kernel/src/process/exit.rs (revision edaf015400f83967c2fc940f07be0dbb5792246f)
1 use core::intrinsics::likely;
2 
3 use alloc::sync::Arc;
4 
5 use crate::{
6     arch::{
7         ipc::signal::{SigChildCode, Signal},
8         sched::sched,
9         CurrentIrqArch,
10     },
11     exception::InterruptArch,
12     syscall::{user_access::UserBufferWriter, SystemError},
13 };
14 
15 use super::{
16     abi::WaitOption, pid::PidType, resource::RUsage, Pid, ProcessControlBlock, ProcessManager,
17     ProcessState,
18 };
19 
20 /// 内核wait4时的参数
21 #[derive(Debug)]
22 pub struct KernelWaitOption<'a> {
23     pub pid_type: PidType,
24     pub pid: Pid,
25     pub options: WaitOption,
26     pub ret_status: i32,
27     pub ret_info: Option<WaitIdInfo>,
28     pub ret_rusage: Option<&'a mut RUsage>,
29     pub no_task_error: Option<SystemError>,
30 }
31 
32 #[derive(Debug, Clone)]
33 pub struct WaitIdInfo {
34     pub pid: Pid,
35     pub status: i32,
36     pub cause: i32,
37 }
38 
39 impl<'a> KernelWaitOption<'a> {
40     pub fn new(pid_type: PidType, pid: Pid, options: WaitOption) -> Self {
41         Self {
42             pid_type,
43             pid,
44             options,
45             ret_status: 0,
46             ret_info: None,
47             ret_rusage: None,
48             no_task_error: None,
49         }
50     }
51 }
52 
53 pub fn kernel_wait4(
54     mut pid: i64,
55     wstatus_buf: Option<UserBufferWriter<'_>>,
56     options: WaitOption,
57     rusage_buf: Option<&mut RUsage>,
58 ) -> Result<usize, SystemError> {
59     // i64::MIN is not defined
60     if pid == i64::MIN {
61         return Err(SystemError::ESRCH);
62     }
63 
64     // 判断pid类型
65     let pidtype: PidType;
66 
67     if pid == -1 {
68         pidtype = PidType::MAX;
69     } else if pid < 0 {
70         pidtype = PidType::PGID;
71         kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n");
72         pid = -pid;
73     } else if pid == 0 {
74         pidtype = PidType::PGID;
75         kwarn!("kernel_wait4: currently not support pgid, default to wait for pid\n");
76         pid = ProcessManager::current_pcb().pid().data() as i64;
77     } else {
78         pidtype = PidType::PID;
79     }
80 
81     let pid = Pid(pid as usize);
82 
83     // 构造参数
84     let mut kwo = KernelWaitOption::new(pidtype, pid, options);
85 
86     kwo.options.insert(WaitOption::WEXITED);
87     kwo.ret_rusage = rusage_buf;
88 
89     // 调用do_wait,执行等待
90     let r = do_wait(&mut kwo)?;
91 
92     // 如果有wstatus_buf,则将wstatus写入用户空间
93     if let Some(mut wstatus_buf) = wstatus_buf {
94         let wstatus = if let Some(ret_info) = &kwo.ret_info {
95             ret_info.status
96         } else {
97             kwo.ret_status
98         };
99         wstatus_buf.copy_one_to_user(&wstatus, 0)?;
100     }
101 
102     return Ok(r);
103 }
104 
105 /// 参考 https://opengrok.ringotek.cn/xref/linux-6.1.9/kernel/exit.c#1573
106 fn do_wait(kwo: &mut KernelWaitOption) -> Result<usize, SystemError> {
107     let mut retval: Result<usize, SystemError>;
108     // todo: 在signal struct里面增加等待队列,并在这里初始化子进程退出的回调,使得子进程退出时,能唤醒当前进程。
109 
110     loop {
111         kwo.no_task_error = Some(SystemError::ECHILD);
112         let child_pcb = ProcessManager::find(kwo.pid).ok_or(SystemError::ECHILD);
113         if kwo.pid_type != PidType::MAX && child_pcb.is_err() {
114             if let Some(err) = &kwo.no_task_error {
115                 retval = Err(err.clone());
116             } else {
117                 retval = Ok(0);
118             }
119 
120             if !kwo.options.contains(WaitOption::WNOHANG) {
121                 retval = Err(SystemError::ERESTARTSYS);
122                 if ProcessManager::current_pcb()
123                     .sig_info_irqsave()
124                     .sig_pending()
125                     .has_pending()
126                     == false
127                 {
128                     // todo: 增加子进程退出的回调后,这里可以直接等待在自身的child_wait等待队列上。
129                     continue;
130                 } else {
131                     break;
132                 }
133             } else {
134                 break;
135             }
136         }
137 
138         if kwo.pid_type == PidType::PID {
139             let child_pcb = child_pcb.unwrap();
140             // 获取weak引用,以便于在do_waitpid中能正常drop pcb
141             let child_weak = Arc::downgrade(&child_pcb);
142             let r = do_waitpid(child_pcb, kwo);
143             if r.is_some() {
144                 return r.unwrap();
145             } else {
146                 child_weak.upgrade().unwrap().wait_queue.sleep();
147             }
148         } else if kwo.pid_type == PidType::MAX {
149             // 等待任意子进程
150             // todo: 这里有问题!如果正在for循环的过程中,子进程退出了,可能会导致父进程永远等待。
151             let current_pcb = ProcessManager::current_pcb();
152             let rd_childen = current_pcb.children.read();
153             let irq_guard = unsafe { CurrentIrqArch::save_and_disable_irq() };
154             for pid in rd_childen.iter() {
155                 let pcb = ProcessManager::find(*pid).ok_or(SystemError::ECHILD)?;
156                 if pcb.sched_info().state().is_exited() {
157                     kwo.ret_status = pcb.sched_info().state().exit_code().unwrap() as i32;
158                     drop(pcb);
159                     unsafe { ProcessManager::release(pid.clone()) };
160                     return Ok(pid.clone().into());
161                 } else {
162                     unsafe { pcb.wait_queue.sleep_without_schedule() };
163                 }
164             }
165             drop(irq_guard);
166             sched();
167         } else {
168             // todo: 对于pgid的处理
169             kwarn!("kernel_wait4: currently not support {:?}", kwo.pid_type);
170             return Err(SystemError::EINVAL);
171         }
172     }
173 
174     return retval;
175 }
176 
177 fn do_waitpid(
178     child_pcb: Arc<ProcessControlBlock>,
179     kwo: &mut KernelWaitOption,
180 ) -> Option<Result<usize, SystemError>> {
181     let state = child_pcb.sched_info().state();
182     // 获取退出码
183     match state {
184         ProcessState::Runnable => {
185             if kwo.options.contains(WaitOption::WNOHANG)
186                 || kwo.options.contains(WaitOption::WNOWAIT)
187             {
188                 if let Some(info) = &mut kwo.ret_info {
189                     *info = WaitIdInfo {
190                         pid: child_pcb.pid(),
191                         status: Signal::SIGCONT as i32,
192                         cause: SigChildCode::Continued.into(),
193                     };
194                 } else {
195                     kwo.ret_status = 0xffff;
196                 }
197 
198                 return Some(Ok(0));
199             }
200         }
201         ProcessState::Blocked(_) | ProcessState::Stopped => {
202             // todo: 在stopped里面,添加code字段,表示停止的原因
203             let exitcode = 0;
204             // 由于目前不支持ptrace,因此这个值为false
205             let ptrace = false;
206 
207             if (!ptrace) && (!kwo.options.contains(WaitOption::WUNTRACED)) {
208                 kwo.ret_status = 0;
209                 return Some(Ok(0));
210             }
211 
212             if likely(!(kwo.options.contains(WaitOption::WNOWAIT))) {
213                 kwo.ret_status = (exitcode << 8) | 0x7f;
214             }
215             if let Some(infop) = &mut kwo.ret_info {
216                 *infop = WaitIdInfo {
217                     pid: child_pcb.pid(),
218                     status: exitcode,
219                     cause: SigChildCode::Stopped.into(),
220                 };
221             }
222 
223             return Some(Ok(child_pcb.pid().data()));
224         }
225         ProcessState::Exited(status) => {
226             let pid = child_pcb.pid();
227             // kdebug!("wait4: child exited, pid: {:?}, status: {status}\n", pid);
228 
229             if likely(!kwo.options.contains(WaitOption::WEXITED)) {
230                 return None;
231             }
232 
233             // todo: 增加对线程组的group leader的处理
234 
235             if let Some(infop) = &mut kwo.ret_info {
236                 *infop = WaitIdInfo {
237                     pid,
238                     status: status as i32,
239                     cause: SigChildCode::Exited.into(),
240                 };
241             }
242 
243             kwo.ret_status = status as i32;
244 
245             drop(child_pcb);
246             // kdebug!("wait4: to release {pid:?}");
247             unsafe { ProcessManager::release(pid) };
248             return Some(Ok(pid.into()));
249         }
250     };
251 
252     return None;
253 }
254