⚠️ VeridianOS Kernel Documentation - This is low-level kernel code. All functions are unsafe unless explicitly marked otherwise. no_std

veridian_kernel/process/
pcb.rs

1//! Process Control Block (PCB) implementation
2//!
3//! The PCB is the core data structure representing a process in the kernel.
4//! It contains all the information needed to manage a process.
5
6use core::sync::atomic::{AtomicU32, AtomicU64, Ordering};
7
8#[cfg(feature = "alloc")]
9extern crate alloc;
10
11#[cfg(feature = "alloc")]
12use alloc::{collections::BTreeMap, string::String, vec::Vec};
13
14use spin::Mutex;
15
16use super::thread::{Thread, ThreadId};
17#[allow(unused_imports)]
18use crate::{
19    cap::{CapabilityId, CapabilitySpace},
20    error::KernelError,
21    fs::file::FileTable,
22    ipc::EndpointId,
23    mm::VirtualAddressSpace,
24    println,
25};
26
27/// Process ID type
28#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord, Hash)]
29pub struct ProcessId(pub u64);
30
31impl core::fmt::Display for ProcessId {
32    fn fmt(&self, f: &mut core::fmt::Formatter<'_>) -> core::fmt::Result {
33        write!(f, "{}", self.0)
34    }
35}
36
37/// Process state
38#[repr(u8)]
39#[derive(Debug, Clone, Copy, PartialEq, Eq)]
40pub enum ProcessState {
41    /// Process is being created
42    Creating = 0,
43    /// Process is ready to run
44    Ready = 1,
45    /// Process is currently running
46    Running = 2,
47    /// Process is blocked waiting
48    Blocked = 3,
49    /// Process is sleeping
50    Sleeping = 4,
51    /// Process has exited but not yet reaped
52    Zombie = 5,
53    /// Process has been terminated
54    Dead = 6,
55}
56
57/// Process priority
58#[repr(u8)]
59#[derive(Debug, Clone, Copy, PartialEq, Eq, PartialOrd, Ord)]
60pub enum ProcessPriority {
61    /// Real-time priority (highest)
62    RealTime = 0,
63    /// System priority
64    System = 1,
65    /// Normal user priority
66    Normal = 2,
67    /// Low priority
68    Low = 3,
69    /// Idle priority (lowest)
70    Idle = 4,
71}
72
73/// Process Control Block
74pub struct Process {
75    /// Process ID
76    pub pid: ProcessId,
77
78    /// Parent process ID (None for init)
79    pub parent: Option<ProcessId>,
80
81    /// Process name
82    #[cfg(feature = "alloc")]
83    pub name: String,
84
85    /// Process state
86    pub state: AtomicU32,
87
88    /// Priority
89    pub priority: Mutex<ProcessPriority>,
90
91    /// Virtual address space
92    pub memory_space: Mutex<VirtualAddressSpace>,
93
94    /// Capability space
95    pub capability_space: Mutex<CapabilitySpace>,
96
97    /// File descriptor table
98    pub file_table: Mutex<FileTable>,
99
100    /// Threads in this process
101    #[cfg(feature = "alloc")]
102    pub threads: Mutex<BTreeMap<ThreadId, Thread>>,
103
104    /// IPC endpoints owned by this process
105    #[cfg(feature = "alloc")]
106    pub ipc_endpoints: Mutex<BTreeMap<EndpointId, CapabilityId>>,
107
108    /// Child processes
109    #[cfg(feature = "alloc")]
110    pub children: Mutex<Vec<ProcessId>>,
111
112    /// Exit code (set when process exits)
113    pub exit_code: AtomicU32,
114
115    /// CPU time used (in microseconds)
116    pub cpu_time: AtomicU64,
117
118    /// Memory usage statistics
119    pub memory_stats: MemoryStats,
120
121    /// Creation timestamp
122    pub created_at: u64,
123
124    /// User ID (for future use)
125    pub uid: u32,
126
127    /// Group ID (for future use)
128    pub gid: u32,
129
130    /// Process group ID (initialized to pid)
131    pub pgid: AtomicU64,
132
133    /// Session ID (initialized to pid)
134    pub sid: AtomicU64,
135
136    /// Environment variables (populated during exec, inherited on fork)
137    #[cfg(feature = "alloc")]
138    pub env_vars: Mutex<alloc::collections::BTreeMap<String, String>>,
139
140    /// Signal handlers (signal number -> handler action)
141    /// 0 = default, 1 = ignore, other values = handler address
142    pub signal_handlers: Mutex<[u64; 32]>,
143
144    /// Pending signals bitmap
145    pub pending_signals: AtomicU64,
146
147    /// Signal mask (blocked signals)
148    pub signal_mask: AtomicU64,
149
150    /// File creation mask (umask). Default 0o022.
151    pub umask: AtomicU32,
152
153    /// TLS FS_BASE address for x86_64 (Thread-Local Storage).
154    /// Set by exec_process when loading an ELF with PT_TLS segment.
155    /// Read by sys_exec before enter_usermode to set MSR 0xC0000100.
156    pub tls_fs_base: AtomicU64,
157
158    /// Container ID (0 = not containerized). Inherited by forked children
159    /// so processes cannot escape their container namespace.
160    pub container_id: AtomicU64,
161
162    /// User-space address to zero and futex-wake on thread exit
163    /// (set by set_tid_address syscall, used by pthread_join).
164    pub clear_child_tid: AtomicU64,
165
166    /// User-space address of robust futex list head
167    /// (set by set_robust_list syscall for cleanup on abnormal exit).
168    pub robust_list_head: AtomicU64,
169}
170
171/// Memory usage statistics
172#[derive(Debug, Default)]
173pub struct MemoryStats {
174    /// Virtual memory size (bytes)
175    pub virtual_size: AtomicU64,
176    /// Resident set size (bytes)
177    pub resident_size: AtomicU64,
178    /// Shared memory size (bytes)
179    pub shared_size: AtomicU64,
180}
181
182impl Process {
183    /// Create a new process
184    #[cfg(feature = "alloc")]
185    pub fn new(
186        pid: ProcessId,
187        parent: Option<ProcessId>,
188        name: String,
189        priority: ProcessPriority,
190    ) -> Self {
191        Self {
192            pid,
193            parent,
194            name,
195            state: AtomicU32::new(ProcessState::Creating as u32),
196            priority: Mutex::new(priority),
197            memory_space: Mutex::new(VirtualAddressSpace::new()),
198            capability_space: Mutex::new(CapabilitySpace::new()),
199            file_table: Mutex::new(FileTable::new()),
200            threads: Mutex::new(BTreeMap::new()),
201            ipc_endpoints: Mutex::new(BTreeMap::new()),
202            children: Mutex::new(Vec::new()),
203            exit_code: AtomicU32::new(0),
204            cpu_time: AtomicU64::new(0),
205            memory_stats: MemoryStats::default(),
206            created_at: crate::arch::timer::get_ticks(),
207            uid: 0,
208            gid: 0,
209            pgid: AtomicU64::new(pid.0),
210            sid: AtomicU64::new(pid.0),
211            env_vars: Mutex::new(BTreeMap::new()),
212            signal_handlers: Mutex::new([0u64; 32]),
213            pending_signals: AtomicU64::new(0),
214            signal_mask: AtomicU64::new(0),
215            umask: AtomicU32::new(0o022),
216            tls_fs_base: AtomicU64::new(0),
217            container_id: AtomicU64::new(0),
218            clear_child_tid: AtomicU64::new(0),
219            robust_list_head: AtomicU64::new(0),
220        }
221    }
222
223    /// Get process state
224    pub fn get_state(&self) -> ProcessState {
225        match self.state.load(Ordering::Acquire) {
226            0 => ProcessState::Creating,
227            1 => ProcessState::Ready,
228            2 => ProcessState::Running,
229            3 => ProcessState::Blocked,
230            4 => ProcessState::Sleeping,
231            5 => ProcessState::Zombie,
232            6 => ProcessState::Dead,
233            _ => ProcessState::Dead,
234        }
235    }
236
237    /// Set process state
238    pub fn set_state(&self, state: ProcessState) {
239        self.state.store(state as u32, Ordering::Release);
240    }
241
242    /// Get the main thread ID of this process
243    #[cfg(feature = "alloc")]
244    pub fn get_main_thread_id(&self) -> Option<ThreadId> {
245        let threads = self.threads.lock();
246        // The main thread is typically the first one created (lowest TID)
247        threads.values().min_by_key(|t| t.tid.0).map(|t| t.tid)
248    }
249
250    /// Add a thread to this process
251    #[cfg(feature = "alloc")]
252    pub fn add_thread(&self, thread: Thread) -> Result<(), KernelError> {
253        let tid = thread.tid;
254        let mut threads = self.threads.lock();
255
256        if threads.len() >= super::MAX_THREADS_PER_PROCESS {
257            return Err(KernelError::ResourceExhausted {
258                resource: "threads per process",
259            });
260        }
261
262        if threads.contains_key(&tid) {
263            return Err(KernelError::AlreadyExists {
264                resource: "thread",
265                id: tid.0,
266            });
267        }
268
269        threads.insert(tid, thread);
270        Ok(())
271    }
272
273    /// Remove a thread from this process
274    #[cfg(feature = "alloc")]
275    pub fn remove_thread(&self, tid: ThreadId) -> Option<Thread> {
276        self.threads.lock().remove(&tid)
277    }
278
279    /// Get a thread by ID
280    #[cfg(feature = "alloc")]
281    pub fn get_thread(&self, tid: ThreadId) -> Option<&Thread> {
282        // This is a bit tricky - we need to return a reference that outlives the lock
283        // In a real implementation, we'd use more sophisticated synchronization
284        // SAFETY: The Thread is stored in a BTreeMap behind a Mutex, providing
285        // a stable heap address. Casting to *const and back to a reference
286        // extends the borrow lifetime beyond the lock scope. Sound because
287        // threads are not moved or deallocated while references exist in the
288        // current kernel model.
289        unsafe {
290            let threads = self.threads.lock();
291            threads.get(&tid).map(|t| &*(t as *const Thread))
292        }
293    }
294
295    /// Get number of threads
296    #[cfg(feature = "alloc")]
297    pub fn thread_count(&self) -> usize {
298        self.threads.lock().len()
299    }
300
301    /// Check if process is alive
302    pub fn is_alive(&self) -> bool {
303        !matches!(self.get_state(), ProcessState::Dead | ProcessState::Zombie)
304    }
305
306    /// Update CPU time
307    pub fn add_cpu_time(&self, microseconds: u64) {
308        self.cpu_time.fetch_add(microseconds, Ordering::Relaxed);
309    }
310
311    /// Get total CPU time
312    pub fn get_cpu_time(&self) -> u64 {
313        self.cpu_time.load(Ordering::Relaxed)
314    }
315
316    /// Set exit code
317    pub fn set_exit_code(&self, code: i32) {
318        self.exit_code.store(code as u32, Ordering::Release);
319    }
320
321    /// Get exit code
322    pub fn get_exit_code(&self) -> i32 {
323        self.exit_code.load(Ordering::Acquire) as i32
324    }
325
326    /// Set process priority
327    pub fn set_priority(&self, new_priority: ProcessPriority) {
328        *self.priority.lock() = new_priority;
329    }
330
331    /// Set user-space address to zero and futex-wake on thread exit.
332    /// Called by set_tid_address syscall; the kernel will write 0 to this
333    /// address and issue a futex wake when the thread terminates, enabling
334    /// pthread_join to detect thread completion.
335    pub fn set_clear_child_tid(&self, addr: usize) {
336        self.clear_child_tid.store(addr as u64, Ordering::Release);
337    }
338
339    /// Set user-space address of the robust futex list head.
340    /// Called by set_robust_list syscall; the kernel walks this list on
341    /// abnormal thread exit to unlock any held robust mutexes.
342    pub fn set_robust_list(&self, addr: usize) {
343        self.robust_list_head.store(addr as u64, Ordering::Release);
344    }
345
346    /// Get mutable reference to memory space
347    pub fn memory_space_mut(&mut self) -> Option<&mut VirtualAddressSpace> {
348        Some(self.memory_space.get_mut())
349    }
350
351    /// Set process name
352    #[cfg(feature = "alloc")]
353    pub fn set_name(&mut self, name: String) {
354        self.name = name;
355    }
356
357    /// Get main thread (first thread created)
358    #[cfg(feature = "alloc")]
359    pub fn get_main_thread_mut(&mut self) -> Option<&mut Thread> {
360        self.threads.get_mut().values_mut().next()
361    }
362
363    /// Reset all signal handlers to default (used during exec)
364    pub fn reset_signal_handlers(&self) {
365        let mut handlers = self.signal_handlers.lock();
366        for handler in handlers.iter_mut() {
367            *handler = 0; // 0 = default action
368        }
369        // Clear pending signals that were ignored
370        self.pending_signals.store(0, Ordering::Release);
371    }
372
373    /// Set a signal handler
374    /// handler: 0 = default, 1 = ignore, other = handler address
375    pub fn set_signal_handler(&self, signum: usize, handler: u64) -> Result<u64, KernelError> {
376        if signum >= 32 {
377            return Err(KernelError::InvalidArgument {
378                name: "signum",
379                value: "signal number out of range (0-31)",
380            });
381        }
382        // SIGKILL (9) and SIGSTOP (19) cannot be caught or ignored
383        if signum == 9 || signum == 19 {
384            return Err(KernelError::PermissionDenied {
385                operation: "change handler for SIGKILL or SIGSTOP",
386            });
387        }
388        let mut handlers = self.signal_handlers.lock();
389        let old = handlers[signum];
390        handlers[signum] = handler;
391        Ok(old)
392    }
393
394    /// Get a signal handler
395    pub fn get_signal_handler(&self, signum: usize) -> Option<u64> {
396        if signum >= 32 {
397            return None;
398        }
399        Some(self.signal_handlers.lock()[signum])
400    }
401
402    /// Send a signal to this process
403    pub fn send_signal(&self, signum: usize) -> Result<(), KernelError> {
404        if signum >= 32 {
405            return Err(KernelError::InvalidArgument {
406                name: "signum",
407                value: "signal number out of range (0-31)",
408            });
409        }
410        // Set the signal bit in pending signals
411        let mask = 1u64 << signum;
412        self.pending_signals.fetch_or(mask, Ordering::AcqRel);
413        Ok(())
414    }
415
416    /// Check if a signal is pending
417    pub fn is_signal_pending(&self, signum: usize) -> bool {
418        if signum >= 32 {
419            return false;
420        }
421        let pending = self.pending_signals.load(Ordering::Acquire);
422        let mask = self.signal_mask.load(Ordering::Acquire);
423        let effective_pending = pending & !mask;
424        (effective_pending & (1u64 << signum)) != 0
425    }
426
427    /// Get the next pending signal (lowest numbered, unmasked)
428    pub fn get_next_pending_signal(&self) -> Option<usize> {
429        let pending = self.pending_signals.load(Ordering::Acquire);
430        let mask = self.signal_mask.load(Ordering::Acquire);
431        let effective_pending = pending & !mask;
432        if effective_pending == 0 {
433            return None;
434        }
435        // Find lowest set bit
436        Some(effective_pending.trailing_zeros() as usize)
437    }
438
439    /// Clear a pending signal
440    pub fn clear_pending_signal(&self, signum: usize) {
441        if signum < 32 {
442            let mask = !(1u64 << signum);
443            self.pending_signals.fetch_and(mask, Ordering::AcqRel);
444        }
445    }
446
447    /// Set signal mask (returns old mask)
448    pub fn set_signal_mask(&self, new_mask: u64) -> u64 {
449        // Cannot mask SIGKILL (9) or SIGSTOP (19)
450        let protected = (1u64 << 9) | (1u64 << 19);
451        let actual_mask = new_mask & !protected;
452        self.signal_mask.swap(actual_mask, Ordering::AcqRel)
453    }
454
455    /// Get current signal mask
456    pub fn get_signal_mask(&self) -> u64 {
457        self.signal_mask.load(Ordering::Acquire)
458    }
459}
460
461impl Drop for Process {
462    fn drop(&mut self) {
463        println!("[PROCESS] Dropping process {}", self.pid.0);
464        // Cleanup will be handled by the process lifecycle manager
465    }
466}
467
468#[cfg(test)]
469mod tests {
470    use super::*;
471
472    fn make_process(pid: u64, name: &str) -> Process {
473        Process::new(
474            ProcessId(pid),
475            None,
476            alloc::string::String::from(name),
477            ProcessPriority::Normal,
478        )
479    }
480
481    // --- ProcessState tests ---
482
483    #[test]
484    fn test_initial_state_is_creating() {
485        let proc = make_process(1, "test");
486        assert_eq!(proc.get_state(), ProcessState::Creating);
487    }
488
489    #[test]
490    fn test_state_transitions() {
491        let proc = make_process(2, "test_transitions");
492
493        proc.set_state(ProcessState::Ready);
494        assert_eq!(proc.get_state(), ProcessState::Ready);
495
496        proc.set_state(ProcessState::Running);
497        assert_eq!(proc.get_state(), ProcessState::Running);
498
499        proc.set_state(ProcessState::Blocked);
500        assert_eq!(proc.get_state(), ProcessState::Blocked);
501
502        proc.set_state(ProcessState::Sleeping);
503        assert_eq!(proc.get_state(), ProcessState::Sleeping);
504
505        proc.set_state(ProcessState::Zombie);
506        assert_eq!(proc.get_state(), ProcessState::Zombie);
507
508        proc.set_state(ProcessState::Dead);
509        assert_eq!(proc.get_state(), ProcessState::Dead);
510    }
511
512    #[test]
513    fn test_get_state_unknown_value() {
514        let proc = make_process(3, "unknown_state");
515        // Force an invalid state value
516        proc.state.store(255, Ordering::Release);
517        // Should default to Dead for unknown values
518        assert_eq!(proc.get_state(), ProcessState::Dead);
519    }
520
521    // --- is_alive tests ---
522
523    #[test]
524    fn test_is_alive_creating() {
525        let proc = make_process(4, "alive_test");
526        assert!(proc.is_alive());
527    }
528
529    #[test]
530    fn test_is_alive_ready() {
531        let proc = make_process(5, "alive_ready");
532        proc.set_state(ProcessState::Ready);
533        assert!(proc.is_alive());
534    }
535
536    #[test]
537    fn test_is_alive_running() {
538        let proc = make_process(6, "alive_running");
539        proc.set_state(ProcessState::Running);
540        assert!(proc.is_alive());
541    }
542
543    #[test]
544    fn test_is_not_alive_zombie() {
545        let proc = make_process(7, "zombie");
546        proc.set_state(ProcessState::Zombie);
547        assert!(!proc.is_alive());
548    }
549
550    #[test]
551    fn test_is_not_alive_dead() {
552        let proc = make_process(8, "dead");
553        proc.set_state(ProcessState::Dead);
554        assert!(!proc.is_alive());
555    }
556
557    // --- CPU time tests ---
558
559    #[test]
560    fn test_cpu_time_initial_zero() {
561        let proc = make_process(10, "cpu_time");
562        assert_eq!(proc.get_cpu_time(), 0);
563    }
564
565    #[test]
566    fn test_add_cpu_time() {
567        let proc = make_process(11, "cpu_time_add");
568        proc.add_cpu_time(100);
569        assert_eq!(proc.get_cpu_time(), 100);
570        proc.add_cpu_time(200);
571        assert_eq!(proc.get_cpu_time(), 300);
572    }
573
574    // --- Exit code tests ---
575
576    #[test]
577    fn test_exit_code_initial_zero() {
578        let proc = make_process(12, "exit_code");
579        assert_eq!(proc.get_exit_code(), 0);
580    }
581
582    #[test]
583    fn test_set_exit_code() {
584        let proc = make_process(13, "exit_set");
585        proc.set_exit_code(42);
586        assert_eq!(proc.get_exit_code(), 42);
587    }
588
589    #[test]
590    fn test_set_exit_code_negative() {
591        let proc = make_process(14, "exit_neg");
592        proc.set_exit_code(-1);
593        assert_eq!(proc.get_exit_code(), -1);
594    }
595
596    // --- Priority tests ---
597
598    #[test]
599    fn test_initial_priority() {
600        let proc = make_process(15, "priority");
601        assert_eq!(*proc.priority.lock(), ProcessPriority::Normal);
602    }
603
604    #[test]
605    fn test_set_priority() {
606        let proc = make_process(16, "priority_set");
607        proc.set_priority(ProcessPriority::RealTime);
608        assert_eq!(*proc.priority.lock(), ProcessPriority::RealTime);
609
610        proc.set_priority(ProcessPriority::Idle);
611        assert_eq!(*proc.priority.lock(), ProcessPriority::Idle);
612    }
613
614    // --- Signal tests ---
615
616    #[test]
617    fn test_signal_handler_default() {
618        let proc = make_process(20, "sig_default");
619        // All signal handlers should initially be 0 (default action)
620        for i in 0..32 {
621            assert_eq!(proc.get_signal_handler(i), Some(0));
622        }
623    }
624
625    #[test]
626    fn test_signal_handler_invalid_signal() {
627        let proc = make_process(21, "sig_invalid");
628        assert_eq!(proc.get_signal_handler(32), None);
629        assert_eq!(proc.get_signal_handler(100), None);
630    }
631
632    #[test]
633    fn test_set_signal_handler() {
634        let proc = make_process(22, "sig_set");
635        let result = proc.set_signal_handler(2, 0xDEAD_BEEF);
636        assert!(result.is_ok());
637        assert_eq!(result.unwrap(), 0); // Old handler was default (0)
638        assert_eq!(proc.get_signal_handler(2), Some(0xDEAD_BEEF));
639    }
640
641    #[test]
642    fn test_set_signal_handler_sigkill_refused() {
643        let proc = make_process(23, "sig_kill");
644        let result = proc.set_signal_handler(9, 1); // SIGKILL
645        assert!(result.is_err());
646        assert_eq!(
647            result.unwrap_err(),
648            KernelError::PermissionDenied {
649                operation: "change handler for SIGKILL or SIGSTOP",
650            }
651        );
652    }
653
654    #[test]
655    fn test_set_signal_handler_sigstop_refused() {
656        let proc = make_process(24, "sig_stop");
657        let result = proc.set_signal_handler(19, 1); // SIGSTOP
658        assert!(result.is_err());
659    }
660
661    #[test]
662    fn test_set_signal_handler_out_of_range() {
663        let proc = make_process(25, "sig_range");
664        let result = proc.set_signal_handler(32, 1);
665        assert!(result.is_err());
666    }
667
668    #[test]
669    fn test_send_signal() {
670        let proc = make_process(26, "sig_send");
671        assert!(proc.send_signal(2).is_ok());
672        assert!(proc.is_signal_pending(2));
673    }
674
675    #[test]
676    fn test_send_signal_invalid() {
677        let proc = make_process(27, "sig_send_inv");
678        assert!(proc.send_signal(32).is_err());
679    }
680
681    #[test]
682    fn test_signal_pending_with_mask() {
683        let proc = make_process(28, "sig_mask");
684        proc.send_signal(3).unwrap();
685
686        // Before masking, signal is pending
687        assert!(proc.is_signal_pending(3));
688
689        // Mask signal 3
690        proc.set_signal_mask(1u64 << 3);
691
692        // Now it should NOT be seen as pending (masked)
693        assert!(!proc.is_signal_pending(3));
694    }
695
696    #[test]
697    fn test_signal_mask_protects_sigkill_sigstop() {
698        let proc = make_process(29, "sig_mask_protect");
699        // Try to mask SIGKILL (9) and SIGSTOP (19)
700        let old = proc.set_signal_mask(0xFFFF_FFFF_FFFF_FFFF);
701        assert_eq!(old, 0); // Previous mask was 0
702
703        // SIGKILL and SIGSTOP should NOT be masked
704        let mask = proc.get_signal_mask();
705        assert_eq!(mask & (1u64 << 9), 0, "SIGKILL should not be maskable");
706        assert_eq!(mask & (1u64 << 19), 0, "SIGSTOP should not be maskable");
707    }
708
709    #[test]
710    fn test_get_next_pending_signal() {
711        let proc = make_process(30, "sig_next");
712        assert!(proc.get_next_pending_signal().is_none());
713
714        proc.send_signal(5).unwrap();
715        proc.send_signal(3).unwrap();
716
717        // Should return lowest numbered pending signal
718        assert_eq!(proc.get_next_pending_signal(), Some(3));
719    }
720
721    #[test]
722    fn test_clear_pending_signal() {
723        let proc = make_process(31, "sig_clear");
724        proc.send_signal(7).unwrap();
725        assert!(proc.is_signal_pending(7));
726
727        proc.clear_pending_signal(7);
728        assert!(!proc.is_signal_pending(7));
729    }
730
731    #[test]
732    fn test_reset_signal_handlers() {
733        let proc = make_process(32, "sig_reset");
734        // Set some handlers
735        proc.set_signal_handler(2, 0x1000).unwrap();
736        proc.set_signal_handler(15, 0x2000).unwrap();
737        proc.send_signal(5).unwrap();
738
739        proc.reset_signal_handlers();
740
741        // All handlers should be reset to default
742        assert_eq!(proc.get_signal_handler(2), Some(0));
743        assert_eq!(proc.get_signal_handler(15), Some(0));
744        // Pending signals should be cleared
745        assert!(!proc.is_signal_pending(5));
746    }
747
748    // --- Process identity tests ---
749
750    #[test]
751    fn test_process_pid() {
752        let proc = make_process(100, "pid_test");
753        assert_eq!(proc.pid, ProcessId(100));
754    }
755
756    #[test]
757    fn test_process_parent() {
758        let proc = Process::new(
759            ProcessId(50),
760            Some(ProcessId(1)),
761            alloc::string::String::from("child"),
762            ProcessPriority::Normal,
763        );
764        assert_eq!(proc.parent, Some(ProcessId(1)));
765    }
766
767    #[test]
768    fn test_process_no_parent() {
769        let proc = make_process(1, "init");
770        assert_eq!(proc.parent, None);
771    }
772
773    #[test]
774    fn test_process_name() {
775        let mut proc = make_process(60, "original_name");
776        assert_eq!(proc.name, "original_name");
777
778        proc.set_name(alloc::string::String::from("new_name"));
779        assert_eq!(proc.name, "new_name");
780    }
781
782    // --- Thread management tests ---
783
784    #[test]
785    fn test_thread_count_initially_zero() {
786        let proc = make_process(70, "threads");
787        assert_eq!(proc.thread_count(), 0);
788    }
789
790    // --- ProcessId display ---
791
792    #[test]
793    fn test_process_id_display() {
794        let pid = ProcessId(42);
795        let display = alloc::format!("{}", pid);
796        assert_eq!(display, "42");
797    }
798
799    // --- ProcessPriority ordering ---
800
801    #[test]
802    fn test_priority_ordering() {
803        assert!(ProcessPriority::RealTime < ProcessPriority::System);
804        assert!(ProcessPriority::System < ProcessPriority::Normal);
805        assert!(ProcessPriority::Normal < ProcessPriority::Low);
806        assert!(ProcessPriority::Low < ProcessPriority::Idle);
807    }
808}
809
810/// Process builder for convenient process creation
811#[cfg(feature = "alloc")]
812pub struct ProcessBuilder {
813    name: String,
814    parent: Option<ProcessId>,
815    priority: ProcessPriority,
816    uid: u32,
817    gid: u32,
818}
819
820#[cfg(feature = "alloc")]
821impl ProcessBuilder {
822    /// Create a new process builder
823    pub fn new(name: String) -> Self {
824        Self {
825            name,
826            parent: None,
827            priority: ProcessPriority::Normal,
828            uid: 0,
829            gid: 0,
830        }
831    }
832
833    /// Set parent process
834    pub fn parent(mut self, pid: ProcessId) -> Self {
835        self.parent = Some(pid);
836        self
837    }
838
839    /// Set priority
840    pub fn priority(mut self, priority: ProcessPriority) -> Self {
841        self.priority = priority;
842        self
843    }
844
845    /// Set user ID
846    pub fn uid(mut self, uid: u32) -> Self {
847        self.uid = uid;
848        self
849    }
850
851    /// Set group ID
852    pub fn gid(mut self, gid: u32) -> Self {
853        self.gid = gid;
854        self
855    }
856
857    /// Build the process.
858    ///
859    /// Note: The VAS is created but not initialized (no page table root).
860    /// Callers that need a real address space must call
861    /// `memory_space.lock().init()` afterwards (as
862    /// `create_process_with_options` does), or clone from an existing
863    /// address space (as `fork_process` does).
864    pub fn build(self) -> Process {
865        let pid = super::alloc_pid();
866        let mut process = Process::new(pid, self.parent, self.name, self.priority);
867        process.uid = self.uid;
868        process.gid = self.gid;
869        process
870    }
871
872    /// Build the process with an initialized address space.
873    ///
874    /// Allocates a root page table frame and maps kernel regions into the
875    /// new address space. This is the preferred method for creating
876    /// standalone processes (not forked from an existing process).
877    pub fn build_with_address_space(self) -> Result<Process, KernelError> {
878        let pid = super::alloc_pid();
879        let mut process = Process::new(pid, self.parent, self.name, self.priority);
880        process.uid = self.uid;
881        process.gid = self.gid;
882
883        // Initialize the virtual address space with a real page table root
884        // and kernel space mappings
885        {
886            let mut memory_space = process.memory_space.lock();
887            memory_space.init()?;
888        }
889
890        println!(
891            "[PROCESS] Created process {} with initialized address space",
892            pid.0
893        );
894
895        Ok(process)
896    }
897}