1#[cfg(feature = "alloc")]
8extern crate alloc;
9
10#[cfg(feature = "alloc")]
11use alloc::vec::Vec;
12use core::sync::atomic::Ordering;
13
14use super::{
15 pcb::{Process, ProcessState},
16 table,
17 thread::ThreadId,
18 ProcessId,
19};
20#[allow(unused_imports)]
21use crate::{error::KernelError, println, sched};
22
23pub fn exit_process(exit_code: i32) {
25 if let Some(process) = super::current_process() {
26 crate::security::audit::log_process_exit(process.pid.0, exit_code);
28
29 println!(
30 "[PROCESS] Process {} exiting with code {}",
31 process.pid.0, exit_code
32 );
33
34 process.set_exit_code(exit_code);
36
37 #[cfg(feature = "alloc")]
39 {
40 let threads = process.threads.lock();
41 for (_, thread) in threads.iter() {
42 thread.set_state(super::thread::ThreadState::Zombie);
43 }
44 }
45
46 cleanup_process(process);
48
49 process.set_state(ProcessState::Zombie);
51
52 if let Some(parent_pid) = process.parent {
54 if let Some(parent) = table::get_process(parent_pid) {
55 if let Err(_e) = parent.send_signal(signals::SIGCHLD as usize) {
57 println!(
58 "[PROCESS] Warning: Failed to send SIGCHLD to parent {}: {:?}",
59 parent_pid.0, _e
60 );
61 }
62
63 let parent_state = parent.get_state();
65 if parent_state == ProcessState::Blocked {
66 parent.set_state(ProcessState::Ready);
67 sched::wake_up_process(parent_pid);
68 }
69 }
70 }
71
72 sched::exit_task(exit_code);
74 }
75}
76
77#[cfg(feature = "alloc")]
79pub fn wait_process(pid: Option<ProcessId>) -> Result<(ProcessId, i32), KernelError> {
80 wait_process_with_options(pid, WaitOptions::default())
81}
82
83#[derive(Debug, Clone, Copy, Default)]
85pub struct WaitOptions {
86 pub no_hang: bool,
88 pub untraced: bool,
90 pub continued: bool,
92}
93
94impl WaitOptions {
95 pub fn no_hang() -> Self {
97 Self {
98 no_hang: true,
99 untraced: false,
100 continued: false,
101 }
102 }
103}
104
105#[cfg(feature = "alloc")]
107pub fn wait_process_with_options(
108 pid: Option<ProcessId>,
109 options: WaitOptions,
110) -> Result<(ProcessId, i32), KernelError> {
111 let current = super::current_process().ok_or(KernelError::NotInitialized {
112 subsystem: "current process",
113 })?;
114 let current_pid = current.pid;
115
116 loop {
117 let children = table::PROCESS_TABLE.find_children(current_pid);
119
120 if children.is_empty() {
122 return Err(KernelError::NotFound {
123 resource: "child process",
124 id: 0,
125 });
126 }
127
128 let mut matching_child_exists = false;
130
131 for child_pid in &children {
132 let matches_filter = pid.is_none() || pid == Some(*child_pid);
134 if !matches_filter {
135 continue;
136 }
137
138 matching_child_exists = true;
139
140 if let Some(child) = table::get_process(*child_pid) {
141 let child_state = child.get_state();
142
143 if child_state == ProcessState::Zombie {
145 let exit_code = child.get_exit_code();
147
148 current.children.lock().retain(|&p| p != *child_pid);
150
151 table::remove_process(*child_pid);
153
154 println!(
155 "[PROCESS] Process {} reaped child {} (exit code {})",
156 current_pid.0, child_pid.0, exit_code
157 );
158
159 let wait_status = (exit_code & 0xff) << 8;
164 return Ok((*child_pid, wait_status));
165 }
166
167 if options.untraced && child_state == ProcessState::Blocked {
169 let status = 0x7f | (19 << 8);
172 return Ok((*child_pid, status));
173 }
174
175 if options.continued && child_state == ProcessState::Running {
177 let status = 0xffff; return Ok((*child_pid, status));
180 }
181 }
182 }
183
184 if pid.is_some() && !matching_child_exists {
186 return Err(KernelError::ProcessNotFound {
187 pid: pid.unwrap_or(ProcessId(0)).0,
188 });
189 }
190
191 if options.no_hang {
193 return Ok((ProcessId(0), 0));
195 }
196
197 #[cfg(target_arch = "x86_64")]
202 {
203 let mut ran_child = false;
204 for child_pid in &children {
205 let matches_filter = pid.is_none() || pid == Some(*child_pid);
206 if !matches_filter {
207 continue;
208 }
209 if let Some(child) = table::get_process(*child_pid) {
210 if child.get_state() == ProcessState::Ready {
211 let parent_tid = current
213 .threads
214 .lock()
215 .values()
216 .next()
217 .map(|t| t.tid)
218 .unwrap_or(super::thread::ThreadId(current_pid.0));
219 ran_child = crate::bootstrap::boot_run_forked_child(
220 *child_pid,
221 current_pid,
222 parent_tid,
223 );
224 break;
225 }
226 }
227 }
228 if ran_child {
229 continue;
231 }
232 }
233
234 println!(
236 "[PROCESS] Process {} blocking in wait() for child {}",
237 current_pid.0,
238 pid.map_or(-1, |p| p.0 as i64)
239 );
240
241 sched::block_process(current_pid);
242
243 current.set_state(ProcessState::Running);
245
246 if let Some(signum) = current.get_next_pending_signal() {
248 current.clear_pending_signal(signum);
250 return Err(KernelError::WouldBlock);
251 }
252 }
253}
254
255pub mod signals {
261 pub const SIGHUP: i32 = 1; pub const SIGINT: i32 = 2; pub const SIGQUIT: i32 = 3; pub const SIGILL: i32 = 4; pub const SIGTRAP: i32 = 5; pub const SIGABRT: i32 = 6; pub const SIGBUS: i32 = 7; pub const SIGFPE: i32 = 8; pub const SIGKILL: i32 = 9; pub const SIGUSR1: i32 = 10; pub const SIGSEGV: i32 = 11; pub const SIGUSR2: i32 = 12; pub const SIGPIPE: i32 = 13; pub const SIGALRM: i32 = 14; pub const SIGTERM: i32 = 15; pub const SIGSTKFLT: i32 = 16; pub const SIGCHLD: i32 = 17; pub const SIGCONT: i32 = 18; pub const SIGSTOP: i32 = 19; pub const SIGTSTP: i32 = 20; pub const SIGTTIN: i32 = 21; pub const SIGTTOU: i32 = 22; pub const SIGURG: i32 = 23; pub const SIGXCPU: i32 = 24; pub const SIGXFSZ: i32 = 25; pub const SIGVTALRM: i32 = 26; pub const SIGPROF: i32 = 27; pub const SIGWINCH: i32 = 28; pub const SIGIO: i32 = 29; pub const SIGPWR: i32 = 30; pub const SIGSYS: i32 = 31; }
293
294#[derive(Debug, Clone, Copy, PartialEq, Eq)]
296pub enum SignalAction {
297 Default,
299 Ignore,
301 Terminate,
303 CoreDump,
305 Stop,
307 Continue,
309 Handler(usize),
311}
312
313pub fn default_signal_action(signal: i32) -> SignalAction {
315 use signals::*;
316 match signal {
317 SIGHUP | SIGINT | SIGKILL | SIGPIPE | SIGALRM | SIGTERM | SIGUSR1 | SIGUSR2 => {
318 SignalAction::Terminate
319 }
320 SIGQUIT | SIGILL | SIGABRT | SIGFPE | SIGSEGV | SIGBUS | SIGSYS | SIGTRAP | SIGXCPU
321 | SIGXFSZ => SignalAction::CoreDump,
322 SIGSTOP | SIGTSTP | SIGTTIN | SIGTTOU => SignalAction::Stop,
323 SIGCONT => SignalAction::Continue,
324 SIGCHLD | SIGURG | SIGWINCH | SIGIO => SignalAction::Ignore,
325 _ => SignalAction::Terminate, }
327}
328
329pub fn kill_process(pid: ProcessId, signal: i32) -> Result<(), KernelError> {
331 if !(0..=31).contains(&signal) {
333 return Err(KernelError::InvalidArgument {
334 name: "signal",
335 value: "signal number out of range (0-31)",
336 });
337 }
338
339 if signal == 0 {
341 if table::get_process(pid).is_some() {
342 return Ok(());
343 } else {
344 return Err(KernelError::ProcessNotFound { pid: pid.0 });
345 }
346 }
347
348 let process = table::get_process(pid).ok_or(KernelError::ProcessNotFound { pid: pid.0 })?;
349
350 if !process.is_alive() {
351 return Err(KernelError::InvalidState {
352 expected: "alive",
353 actual: "dead",
354 });
355 }
356
357 println!("[PROCESS] Sending signal {} to process {}", signal, pid.0);
358
359 process.send_signal(signal as usize)?;
361
362 let handler = process.get_signal_handler(signal as usize).unwrap_or(0);
364 let action = if handler == 0 {
365 default_signal_action(signal)
366 } else if handler == 1 {
367 SignalAction::Ignore
368 } else {
369 SignalAction::Handler(handler as usize)
370 };
371
372 match signal {
374 signals::SIGKILL => {
375 force_terminate_process(process)?;
377 }
378 signals::SIGSTOP => {
379 process.set_state(ProcessState::Blocked);
381 sched::block_process(pid);
382 println!("[PROCESS] Process {} stopped by SIGSTOP", pid.0);
383
384 notify_parent_sigchld(process);
386 }
387 _ => {
388 match action {
390 SignalAction::Ignore => {
391 process.clear_pending_signal(signal as usize);
393 }
394 SignalAction::Terminate | SignalAction::CoreDump => {
395 force_terminate_process(process)?;
397 }
398 SignalAction::Stop => {
399 process.set_state(ProcessState::Blocked);
400 sched::block_process(pid);
401 println!("[PROCESS] Process {} stopped by signal {}", pid.0, signal);
402
403 notify_parent_sigchld(process);
405 }
406 SignalAction::Continue => {
407 if process.get_state() == ProcessState::Blocked {
408 process.set_state(ProcessState::Ready);
409 sched::wake_up_process(pid);
410 println!("[PROCESS] Process {} continued by signal {}", pid.0, signal);
411
412 notify_parent_sigchld(process);
414 }
415 process.clear_pending_signal(signal as usize);
416 }
417 SignalAction::Handler(_addr) => {
418 println!(
421 "[PROCESS] Signal {} queued for process {}, handler at {:#x}",
422 signal, pid.0, _addr
423 );
424
425 if process.get_state() == ProcessState::Blocked {
427 process.set_state(ProcessState::Ready);
428 sched::wake_up_process(pid);
429 }
430 }
431 SignalAction::Default => {
432 force_terminate_process(process)?;
434 }
435 }
436 }
437 }
438
439 Ok(())
440}
441
442fn notify_parent_sigchld(process: &Process) {
452 if let Some(parent_pid) = process.parent {
453 if let Some(parent) = table::get_process(parent_pid) {
454 if let Err(_e) = parent.send_signal(signals::SIGCHLD as usize) {
456 println!(
457 "[PROCESS] Warning: Failed to send SIGCHLD to parent {}: {:?}",
458 parent_pid.0, _e
459 );
460 }
461
462 if parent.get_state() == ProcessState::Blocked {
464 parent.set_state(ProcessState::Ready);
465 sched::wake_up_process(parent_pid);
466 }
467 }
468 }
469}
470
471fn force_terminate_process(process: &Process) -> Result<(), KernelError> {
477 let _pid = process.pid;
478 println!("[PROCESS] Force terminating process {}", _pid.0);
479
480 #[cfg(feature = "alloc")]
482 {
483 let threads = process.threads.lock();
484 for (_, thread) in threads.iter() {
485 thread.set_state(super::thread::ThreadState::Zombie);
486
487 if let Some(task_ptr) = thread.get_task_ptr() {
489 unsafe {
494 let task = task_ptr.as_ptr();
495 (*task).state = ProcessState::Dead;
496 }
498 }
499 }
500 }
501
502 cleanup_process(process);
504 process.set_state(ProcessState::Zombie);
505
506 if let Some(parent_pid) = process.parent {
508 if let Some(parent) = table::get_process(parent_pid) {
509 if let Err(_e) = parent.send_signal(signals::SIGCHLD as usize) {
511 println!(
512 "[PROCESS] Warning: Failed to send SIGCHLD to parent {}: {:?}",
513 parent_pid.0, _e
514 );
515 }
516
517 if parent.get_state() == ProcessState::Blocked {
518 parent.set_state(ProcessState::Ready);
519 sched::wake_up_process(parent_pid);
520 }
521 }
522 }
523
524 Ok(())
525}
526
527pub fn cleanup_process(process: &Process) {
529 println!(
530 "[PROCESS] Cleaning up resources for process {}",
531 process.pid.0
532 );
533
534 {
536 let mut memory_space = process.memory_space.lock();
537 memory_space.clear();
539 }
540
541 #[cfg(feature = "alloc")]
552 {
553 let threads = process.threads.lock();
554 for (_, thread) in threads.iter() {
555 let frame_num = thread.kernel_stack.phys_frame.load(Ordering::Acquire);
556 let page_count = thread.kernel_stack.phys_page_count.load(Ordering::Acquire);
557 if frame_num != 0 && page_count > 0 {
558 let frame = crate::mm::FrameNumber::new(frame_num);
559 if let Err(_e) = crate::mm::FRAME_ALLOCATOR
560 .lock()
561 .free_frames(frame, page_count)
562 {
563 println!(
564 "[PROCESS] Warning: failed to free kernel stack frames for tid {}: {:?}",
565 thread.tid.0, _e
566 );
567 }
568 thread.kernel_stack.phys_frame.store(0, Ordering::Release);
570 thread
571 .kernel_stack
572 .phys_page_count
573 .store(0, Ordering::Release);
574 }
575 }
576 }
577
578 {
580 let cap_space = process.capability_space.lock();
581 cap_space.clear();
583 }
584
585 #[cfg(feature = "alloc")]
587 {
588 use crate::ipc;
589
590 match ipc::remove_process_endpoints(process.pid) {
592 Ok(count) => {
593 if count > 0 {
594 println!(
595 "[PROCESS] Removed {} IPC endpoints for process {}",
596 count, process.pid.0
597 );
598 }
599 }
600 Err(_e) => {
601 println!(
602 "[PROCESS] Warning: Failed to remove IPC endpoints for process {}: {:?}",
603 process.pid.0, _e
604 );
605 }
606 }
607
608 process.ipc_endpoints.lock().clear();
610 }
611
612 {
617 let file_table = process.file_table.lock();
618 let open_fds = file_table.count_open();
619 if open_fds > 3 {
620 println!(
621 "[PROCESS] Warning: process {} exiting with {} open fds (expected <= 3)",
622 process.pid.0, open_fds
623 );
624 }
625 file_table.close_all();
626 }
627
628 #[cfg(feature = "alloc")]
630 {
631 let children: Vec<ProcessId> = process.children.lock().clone();
632 if !children.is_empty() && process.get_state() != ProcessState::Zombie {
633 if let Some(init_process) = table::get_process_mut(ProcessId(1)) {
634 for child_pid in children {
635 if let Some(child) = table::get_process_mut(child_pid) {
636 child.parent = Some(ProcessId(1));
637 init_process.children.lock().push(child_pid);
638 println!("[PROCESS] Reparented process {} to init", child_pid);
639 }
640 }
641 }
642 process.children.lock().clear();
643 }
644 }
645
646 let _cpu_time = process.cpu_time.load(Ordering::Relaxed);
648 println!(
649 "[PROCESS] Process {} used {} microseconds of CPU time",
650 process.pid.0, _cpu_time
651 );
652}
653
654#[cfg(feature = "alloc")]
660pub fn cleanup_thread(process: &Process, tid: ThreadId) -> Result<(), KernelError> {
661 let mut threads = process.threads.lock();
663
664 if let Some(thread) = threads.remove(&tid) {
665 println!("[PROCESS] Cleaning up thread {}", tid.0);
666
667 thread.set_state(super::thread::ThreadState::Dead);
669
670 if let Some(task_ptr) = thread.get_task_ptr() {
672 unsafe {
677 let task = task_ptr.as_ptr();
678
679 (*task).thread_ref = None;
681
682 (*task).state = ProcessState::Dead;
684
685 }
687 }
688
689 if thread.user_stack.size > 0 {
694 let stack_base = thread.user_stack.base;
695 let stack_size = thread.user_stack.size;
696
697 let memory_space = process.memory_space.lock();
698 let num_pages = stack_size / 0x1000;
701 for i in 0..num_pages {
702 let page_addr = stack_base + i * 0x1000;
703 let _ = memory_space.unmap(page_addr, 0x1000);
704 }
705 println!(
706 "[PROCESS] Freed user stack at {:#x}, size {}",
707 stack_base, stack_size
708 );
709 }
710
711 {
715 let frame_num = thread
716 .kernel_stack
717 .phys_frame
718 .load(core::sync::atomic::Ordering::Acquire);
719 let page_count = thread
720 .kernel_stack
721 .phys_page_count
722 .load(core::sync::atomic::Ordering::Acquire);
723 if frame_num != 0 && page_count > 0 {
724 let frame = crate::mm::FrameNumber::new(frame_num);
725 if let Err(_e) = crate::mm::FRAME_ALLOCATOR
726 .lock()
727 .free_frames(frame, page_count)
728 {
729 println!(
730 "[PROCESS] Warning: Failed to free kernel stack for tid {}: {:?}",
731 tid.0, _e
732 );
733 } else {
734 println!(
735 "[PROCESS] Freed kernel stack for tid {} ({} frames)",
736 tid.0, page_count
737 );
738 }
739 thread
741 .kernel_stack
742 .phys_frame
743 .store(0, core::sync::atomic::Ordering::Release);
744 thread
745 .kernel_stack
746 .phys_page_count
747 .store(0, core::sync::atomic::Ordering::Release);
748 }
749 }
750
751 {
753 let tls = thread.tls.lock();
754 if tls.base != 0 && tls.size > 0 {
755 let memory_space = process.memory_space.lock();
757 if let Err(_e) = memory_space.unmap(tls.base, tls.size) {
758 println!(
759 "[PROCESS] Warning: Failed to unmap TLS at {:#x}: {}",
760 tls.base, _e
761 );
762 } else {
763 println!(
764 "[PROCESS] Freed TLS area at {:#x}, size {}",
765 tls.base, tls.size
766 );
767 }
768 }
769 }
770
771 Ok(())
772 } else {
773 Err(KernelError::ThreadNotFound { tid: tid.0 })
774 }
775}
776
777#[cfg(feature = "alloc")]
779pub fn reap_zombie_threads(process: &Process) -> Vec<(ThreadId, i32)> {
780 let mut reaped = Vec::new();
781 let threads = process.threads.lock();
782
783 let zombies: Vec<ThreadId> = threads
785 .iter()
786 .filter(|(_, thread)| thread.get_state() == super::thread::ThreadState::Zombie)
787 .map(|(tid, _)| *tid)
788 .collect();
789
790 drop(threads);
791
792 for tid in zombies {
794 if let Ok(()) = cleanup_thread(process, tid) {
795 if let Some(thread) = process.get_thread(tid) {
797 let exit_code = thread.exit_code.load(Ordering::Acquire) as i32;
798 reaped.push((tid, exit_code));
799 }
800 }
801 }
802
803 reaped
804}
805
806#[cfg(feature = "alloc")]
812pub struct ProcessStats {
813 pub total_processes: usize,
814 pub running_processes: usize,
815 pub blocked_processes: usize,
816 pub zombie_processes: usize,
817 pub total_threads: usize,
818 pub total_cpu_time: u64,
819 pub total_memory_usage: u64,
820}
821
822#[cfg(feature = "alloc")]
824pub fn get_process_stats() -> ProcessStats {
825 let mut stats = ProcessStats {
826 total_processes: 0,
827 running_processes: 0,
828 blocked_processes: 0,
829 zombie_processes: 0,
830 total_threads: 0,
831 total_cpu_time: 0,
832 total_memory_usage: 0,
833 };
834
835 table::PROCESS_TABLE.for_each(|process| {
836 stats.total_processes += 1;
837 stats.total_threads += process.thread_count();
838 stats.total_cpu_time += process.get_cpu_time();
839 stats.total_memory_usage += process
840 .memory_stats
841 .virtual_size
842 .load(core::sync::atomic::Ordering::Relaxed);
843
844 match process.get_state() {
845 ProcessState::Running => stats.running_processes += 1,
846 ProcessState::Blocked => stats.blocked_processes += 1,
847 ProcessState::Zombie => stats.zombie_processes += 1,
848 _ => {}
849 }
850 });
851
852 stats
853}