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