2022 Pintos Part2 User Program
2022 Pintos Part2 User Program
Youjip Won
Overview
Objective
Execute a user program in Pintos.
Background
Topics
Parameter Passing
Youjip Won 2
Background
Youjip Won 3
To run a program
Youjip Won 4
Pintos filesystem
Create virtual disk: in userprog/build
pintos-mkdisk filesys.dsk -–filesys-size=2
filesys.dsk: partition name
Filesystem size: 2MByte
Youjip Won 5
Pintos VM layout
kernel space
grows downward
grows upward
Uninitialized Data
Program (BSS)
file Initialized Data
(Data)
Text
0x08048000 (128 Mbyte)
Youjip Won 6
Running a program in pintos
Youjip Won 7
Executing a program
scheduling( scheduling(
) )
execute
exit
Youjip Won 8
Executing a program
Execute “file_name”.
pintos/src/userprog/process.c
tid_t process_execute (const char *file_name)
{
char *fn_copy;
tid_t tid;
...
tid = thread_create (file_name, PRI_DEFAULT, start_process,
fn_copy);
...
return tid;
}
.
. new thread
tid = thread_create ();
.
.
Youjip Won 9
Creating a thread
thread_create()
Create “struct thread” and initialize it.
Allocate the kernel stack.
Register the function to run: start_process.
Add it to ready list.
Youjip Won 10
Creating a thread
pintos/src/threads/thread.c – thread_create()
Youjip Won 11
Starting a process
pintos/src/userprog/process.c
Youjip Won 12
start_process
Youjip Won 13
Loading a program.
Youjip Won 14
bool load (const char *file_name, void (**eip) (void), void **esp) {
struct thread *t = thread_current ();
struct Elf32_Ehdr ehdr;
struct file *file = NULL;
...
t->pagedir = pagedir_create (); /* create page directory */
process_activate (); /* set cr3 register*/
file = filesys_open (file_name); /* Open the file*/
/* parse the ELF file and get the ELF header*/
if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr
|| memcmp (ehdr.e_ident, "\177ELF\1\1\1", 7)
|| ehdr.e_type != 2
|| ehdr.e_machine != 3
|| ehdr.e_version != 1
|| ehdr.e_phentsize != sizeof (struct Elf32_Phdr)
|| ehdr.e_phnum > 1024)
/* load segment information */
struct Elf32_Phdr phdr;
if (file_ofs < 0 || file_ofs > file_length (file))
file_seek (file, file_ofs);
if (file_read (file, &phdr, sizeof phdr) != sizeof phdr)
...
/* load the executable file */
if (!load_segment (file, file_page, (void *) mem_page,
read_bytes, zero_bytes, writable))
...
if (!setup_stack (esp)) /* initializing user stack*/
*eip = (void (*) (void)) ehdr.e_entry; /*initialize entry point*/
}
Youjip Won 15
Passing the arguments and creating a
thread
Youjip Won 16
Overview
For “echo x y z”
Original:
Thread name: “echo x y z”
Find program with file name “echo x y z”
Arguments “echo”, “x”, “y”, and ”z” are not passed
After modification
Thread name: “echo”
Find program with file name “echo”
Push the arguments to user stack.
Files to modify
pintos/src/userprog/process.*
Youjip Won 17
Parse the arguments and push them to the user stack
pintos/src/userprog/process.c
Youjip Won 18
Tokenizing
Result
‘String’
‘to’
‘tokenize.’
Youjip Won 19
Program Name
Thread name
Before: Entire command line is passed to thread_create()
After modification: Forward only first token of command line to first argument of
thread_create()
“echo x y z” → only use “echo” for name of process
pintos/src/userprog/process.c
tid_t process_execute (const char *file_name)
{
...
/* Parse command line and get program name */
...
/* Create a new thread to execute FILE_NAME. */
tid = thread_create (file_name, PRI_DEFAULT,
start_process, fn_copy);
Name of thread
...
}
Youjip Won 20
start_process
Youjip Won 21
Getting into and out of kernel
Youjip Won 22
Getting into and out of kernel
kernel
kernel space
space
stack stack
esp
stack grows
stack stack
esp esp eip
cs
int N cflags iret
int N esp
ss
Interrupt frame
User User
space space
Youjip Won
struct intr_frame
struct intr_frame {
/* Pushed by intr_entry in intr-stubs.S.
These are the interrupted task's saved registers. */
uint32_t edi;
uint32_t esi;
/* Saved EDI. */
/* Saved ESI. */
It is in the kernel stack.
uint32_t ebp; /* Saved EBP. */
uint32_t esp_dummy;
uint32_t ebx;
/* Not used. */
/* Saved EBX. */
It stores user process’ regis-
uint32_t edx; /* Saved EDX. */
uint32_t ecx; /* Saved ECX. */ ters.
uint32_t eax; /* Saved EAX. */
uint16_t gs, :16; /* Saved GS segment register. */
uint16_t fs, :16; /* Saved FS segment register. */
uint16_t es, :16; /* Saved ES segment register. */
uint16_t ds, :16; /* Saved DS segment register. */
Youjip Won 24
Getting into kernel.
int n
Youjip Won 25
Entering the kernel
struct intr_frame {
/* Pushed by intr_entry in intr-stubs.S.
These are the interrupted task's saved registers. */
uint32_t edi; /* Saved EDI. */ es After interrupt han-
uint32_t esi; /* Saved ESI. */ p dler, intr_entry
uint32_t ebp; /* Saved EBP. */
uint32_t esp_dummy; /* Not used. */
uint32_t ebx; /* Saved EBX. */
uint32_t edx; /* Saved EDX. */
uint32_t ecx; /* Saved ECX. */
uint32_t eax; /* Saved EAX. */
uint16_t gs, :16; /* Saved GS segment register. */
uint16_t fs, :16; /* Saved FS segment register. */
uint16_t es, :16; /* Saved ES segment register. */
uint16_t ds, :16; /* Saved DS segment register. */
Youjip Won 26
Loading
pintos/src/userprog/process.c
Youjip Won 27
static void
start_process (void *file_name_)
{
char *file_name = file_name_;
struct intr_frame if_;
bool success;
asm volatile ("movl %0, %%esp; jmp intr_exit" : : "g" (&if_) : "memory");
jmp intr_exit
executes intr_exit
Youjip Won 29
Getting out of the kernel
struct intr_frame {
/* Pushed by intr_entry in intr-stubs.S.
These are the interrupted task's saved registers. */
uint32_t edi; /* Saved EDI. */ es
uint32_t esi; /* Saved ESI. */ p
uint32_t ebp; /* Saved EBP. */
uint32_t esp_dummy; /* Not used. */
uint32_t ebx; /* Saved EBX. */
uint32_t edx; /* Saved EDX. */
uint32_t ecx; /* Saved ECX. */
uint32_t eax; /* Saved EAX. */
uint16_t gs, :16; /* Saved GS segment register. */
uint16_t fs, :16; /* Saved FS segment register. */
uint16_t es, :16; /* Saved ES segment register. */
uint16_t ds, :16; /* Saved DS segment register. */
“esp” field of the interrupt frame contains the stack top of the User process
user stack.
Stack
&if_.esp
sample_function(int argc,
char* argv[],
void **stackpointer)
Uninitialized Data
(BSS)
Youjip Won 31
80x86 Calling Convention
argc=4
argv[0] = “bin/ls”, argv[1]= ”–l”, argv[2] = “foo”, argv[3] = “bar”
1. Push arguments
1. Push character strings from left to write.
2. Place padding if necessary to align it by 4 Byte.
3. Push start address of the character strings.
Youjip Won 32
User stack layout in function call
Youjip Won 33
Interim Check
pintos/src/userprog/process.c
static void start_process (void *file_name_)
{
...
success = load (file_name, &if_.eip, &if_.esp);
... 추가
argument_stack(parse , count , &if_.esp);
hex_dump(if_.esp , if_.esp , PHYS_BASE – if_.esp ,
true);
Youjip Won 34
Intermediate Check (Cont.)
Result
$pintos –v -- run ‘echo x’
return address
argc argv echo x
(fake)
Youjip Won 35
System Calls and Handlers
Youjip Won 36
Overview
Main goal
Original: system call handler table is empty.
After modification:
Fill system call handler of pintos out.
Add system calls to provide services to users
Process related: halt, exit, exec, wait
File related: create, remove, open, filesize, read, write, seek, tell, close
Files to modify
pintos/src/threads/thread.*
pintos/src/userprog/syscall.*
pintos/src/userprog/process.*
Youjip Won 37
System call
Youjip Won 38
Call process of System call (Pintos)
User Kernel
User program Interrupt Vector table
0x0
main() divide_error() Part to implement
{ debug()
write(“……”)
debug()
debug() pintos/src/userprog/syscall.c
} nmi()
nmi()
nmi() syscall_handler(
intr_frame *f)
return call ……
{
printf ("system
pintos/src/lib/user/syscall.c 0x30 call!\n");
syscall_handler( thread_exit ();
int write(…)
) }
{ return …
syscall3(…); }
… User stack
syscall3(…)
{ .
… .
push arg2 .
push arg1
arg2
push arg0
push number arg1
int 0x30 arg0
} number
Stack Pointer(esp)
Youjip Won 39
System call handler
Call the system call from the system call handler using the system call
number.
The system call number is defined in pintos/src/lib/syscall_nr.h
pintos/src/userprog/syscall.c
syscall_handler(
pintos/src/lib/syscall_nr.h intr_frame *f)
{
0 SYS_HALT
switch(number)
1 SYS_EXIT …
case SYS_HALT :
… halt()
case SYS_EXIT :
5 SYS_REMOVE exit()
… case SYS_EXEC :
exec()
…
}
Youjip Won 40
Requirement for System Call handler
Youjip Won 41
Address Validation
Kernel need to detect invalidity of pointers and terminating process without harm to the
kernel or other running processes.
How to detect?
Method 1: Verify the validity of a user-provided pointer.
The simplest way to handle user memory access.
Use the functions in ‘userprog/pagedir.c’ and in ‘threads/vaddr.h’
Youjip Won 42
Accessing User Memory (cont.)
lock or malloc
time
page fault! In the case, before terminating, we need to be sure release the
lock or free the page.
Youjip Won 43
Accessing User Memory (cont.)
You also modify the page_fault (): set eax to 0xffffffff and copies its former value into
eip.
Youjip Won 44
Add system calls: Process related system calls
void halt(void)
Shutdown pintos
Use void shutdown_power_off(void)
Youjip Won 45
Process Hierarchy
child_list.head child_list.tail
parent
parent parent
next next
oldest child child youngest child
prev prev
Youjip Won 46
wait() system call
If pid did not call exit, but was terminated by the kernel, re-
turn -1.
A parent process can call wait for the child process that has
terminated.
return exit status of the terminated child process.
int
process_wait (tid_t child_tid UNUSED)
{
return -1;
}
Insert the infinite loop so that the kernel does not finish. For now…
Youjip Won 48
Correct implementation: process_wait()
process_wait()
Search the descriptor of the child process by using child_tid.
Semaphore
Add a semaphore for “wait” to thread structure.
Semaphore is initialized to 0 when the thread is first created.
In wait(tid), call sema_down for the semaphore of tid.
In exit() of process tid, call sema_up.
Where do we need to place sema_down and sema_up?
Exit status
Add a field to denote the exit status to the thread structure.
Youjip Won 49
Flow of parent calling wait and child
run_action()
─
run_task()
Kernel
─
Space process_wait(process_excute()
─
)
start_process() Kernel
sema_down() Space
load()
waiting..
Run user program User
waiting.. Space
...
waiting..
exit()
waiting..
thread_exit() Kernel
waiting.. Space
sema_up()
Return exit status
exit process
Kernel ...
Space ─
shutdown_power_off()
─
Shutdown Pintos
Youjip Won 50
exec() system call
Youjip Won 51
Kernel function for exec(): process_execute()
Parent should wait until it knows the child process has successfully created
and the binary file is successfully loaded.
Semaphore
Add a semaphore for “exec()” to thread structure.
Semaphore is initialized by 0 when the thread is first created.
Call sema_down to wait for the successful load of the executable file of
the child process.
Call sema_up when the executable file is successfully loaded.
Where do we need to place sema_down and sema_up?
load status
In the thread structure, we need a field to represent whether the file is
successfully loaded or not.
Youjip Won 52
Current flow of the parent calling exec and the child
─
exec()
─
tid =
start_process()
process_execute() Kernel
Set interrupt stack
Kernel Space
Return Child Process PID
Space load()
Run parent process
wait for load() to finish
...
Run user program User
Space
...
Flow
Scheduling
Youjip Won 53
Correct Flow of the parent calling exec and the child
Youjip Won 54
exit()
Youjip Won 55
Kernel function for exit(): thread_exit
Exit status
Store the status to the status of process.
Semaphore
Call sema_up for the current process.
/* Deschedules the current thread and destroys it. Never
returns to the caller. */
void
thread_exit (void)
{
ASSERT (!intr_context ());
#ifdef USERPROG
process_exit ();
#endif
/* Remove thread from all threads list, set our status to dying,
and schedule another process. That process will destroy us
when it calls thread_schedule_tail(). */
intr_disable ();
list_remove (&thread_current()->allelem);
thread_current ()->status = THREAD_DYING;
schedule ();
NOT_REACHED ();
}
Youjip Won 56
File Manipulation
Youjip Won 57
File Descriptor in Unix
Process Descriptor
Table
Keyboard
Part to implement
File Object
File Descriptor keyboard
Table
0 : STDIN Monitor
File Object
1 : STDOUT
2 : STDERR
3 : File Monitor
4 : File File Object Monitor
.
. I/O
. File Object
Disk
Youjip Won 58
File Descriptor Table
Youjip Won 59
Allocate File Descriptor Table
Define FDT as a part of thread structure. struct thread {
…
struct file fdt[64];
int next_fd;
…
}; Thread A
Allocate FDT at kernel memory area, and add the associated pointer to at the
thread structure.
struct thread {
…
struct file **fdt;
int next_fd;
…
Kernel Memory Pool
}; Thread A
A’s File Descriptor Table
struct thread { B’s File Descriptor Table
…
struct file **fdt;
int next_fd;
…
}; Thread B
Youjip Won 60
File Descriptor Table
Youjip Won 61
Modify page_fault() for test
Some tests check whether your kernel handles the bad process properly.
Pintos needs to kill the process and print the thread name and the exit status
-1 when page fault occurs.
We have to modify page_fault() to satisfy test’s requirements.
pintos/src/userprog/exception.c
static void page_fault (struct intr_frame *f)
{
...
not_present = (f->error_code & PF_P) == 0;
write = (f->error_code & PF_W) != 0;
user = (f->error_code & PF_U) != 0;
/* Call exit(-1) */
...
}
Youjip Won 62
Add system calls: File related system calls
Youjip Won 63
Add system calls: File related system calls (Cont.)
If fd is 0, it reads from keyboard using input_getc(), otherwise reads from file using
file_read() function.
uint8_t input_getc(void)
off_t file_read(struct file *file, void *buffer, off_t size)
Youjip Won 64
Add system calls: File related system calls (Cont.)
Youjip Won 65
Add system calls: File related system calls (Cont.)
Youjip Won 66
Denying writes to executable
static bool load (const char *cmdline, void (**eip) (void), void **esp)
Call file_deny_write() when program file is opened.
Add a running file structure to thread structure.
void process_exit (void)
Modify current process to close the running file.
Youjip Won 67
Result
$make grade
Youjip Won 68
Summary
Is NO
user static void start_process() Initialize Interrupt Frame
Test Program NO
pro-
Execution
gram?
YES bool load() Load executable
YES
static void run_task()
NO void
Success?
Parsing the name of thread_exit()
tid_t process_execute()
the program to run
YES
Create thread and
add it to ready-list tid_t thread_create() User Program
Execution
int process_wait()
Youjip Won 69