X-Git-Url: https://pintos-os.org/cgi-bin/gitweb.cgi?a=blobdiff_plain;ds=sidebyside;f=solutions%2Fp3.patch;h=d3a5ddee4b8075905b2c477bebb86a649dce6986;hb=45716d7c0001861ebf8848217cdc446c4ba2c47a;hp=c45293112d49fc53b2391ddda5c2ccb1e9df7a73;hpb=209c14555dd67960a38e65b2c3228837ac167cbd;p=pintos-anon diff --git a/solutions/p3.patch b/solutions/p3.patch index c452931..d3a5dde 100644 --- a/solutions/p3.patch +++ b/solutions/p3.patch @@ -1,12 +1,12 @@ Index: src/Makefile.build diff -u src/Makefile.build~ src/Makefile.build ---- src/Makefile.build~ 2005-06-18 20:20:47.000000000 -0700 -+++ src/Makefile.build 2006-05-16 13:44:56.000000000 -0700 +--- src/Makefile.build~ ++++ src/Makefile.build @@ -53,7 +53,9 @@ userprog_SRC += userprog/gdt.c # GDT in userprog_SRC += userprog/tss.c # TSS management. # No virtual memory code yet. --#vm_SRC = vm/filename.c # Some file. +-#vm_SRC = vm/file.c # Some file. +vm_SRC = vm/page.c +vm_SRC += vm/frame.c +vm_SRC += vm/swap.c @@ -15,8 +15,8 @@ diff -u src/Makefile.build~ src/Makefile.build filesys_SRC = filesys/filesys.c # Filesystem core. Index: src/devices/timer.c diff -u src/devices/timer.c~ src/devices/timer.c ---- src/devices/timer.c~ 2005-07-06 13:45:36.000000000 -0700 -+++ src/devices/timer.c 2006-05-16 13:44:56.000000000 -0700 +--- src/devices/timer.c~ ++++ src/devices/timer.c @@ -23,6 +23,9 @@ static volatile int64_t ticks; Initialized by timer_calibrate(). */ static unsigned loops_per_tick; @@ -36,7 +36,7 @@ diff -u src/devices/timer.c~ src/devices/timer.c } /* Calibrates loops_per_tick, used to implement brief delays. */ -@@ -87,15 +92,36 @@ timer_elapsed (int64_t then) +@@ -93,16 +93,37 @@ return timer_ticks () - then; } @@ -52,7 +52,8 @@ diff -u src/devices/timer.c~ src/devices/timer.c + return a->wakeup_time < b->wakeup_time; +} + - /* Suspends execution for approximately TICKS timer ticks. */ + /* Sleeps for approximately TICKS timer ticks. Interrupts must + be turned on. */ void timer_sleep (int64_t ticks) { @@ -75,7 +76,7 @@ diff -u src/devices/timer.c~ src/devices/timer.c + sema_down (&t->timer_sema); } - /* Suspends execution for approximately MS milliseconds. */ + /* Sleeps for approximately MS milliseconds. Interrupts must be @@ -132,6 +158,16 @@ timer_interrupt (struct intr_frame *args { ticks++; @@ -95,8 +96,8 @@ diff -u src/devices/timer.c~ src/devices/timer.c /* Returns true if LOOPS iterations waits for more than one timer Index: src/threads/init.c diff -u src/threads/init.c~ src/threads/init.c ---- src/threads/init.c~ 2006-04-25 11:35:56.000000000 -0700 -+++ src/threads/init.c 2006-05-16 13:44:56.000000000 -0700 +--- src/threads/init.c~ ++++ src/threads/init.c @@ -33,6 +33,8 @@ #include "filesys/filesys.h" #include "filesys/fsutil.h" @@ -105,7 +106,7 @@ diff -u src/threads/init.c~ src/threads/init.c +#include "vm/swap.h" /* Amount of physical memory, in 4 kB pages. */ - size_t ram_pages; + size_t init_ram_pages; @@ -124,6 +126,9 @@ main (void) filesys_init (format_filesys); #endif @@ -118,8 +119,8 @@ diff -u src/threads/init.c~ src/threads/init.c /* Run actions specified on kernel command line. */ Index: src/threads/interrupt.c diff -u src/threads/interrupt.c~ src/threads/interrupt.c ---- src/threads/interrupt.c~ 2006-04-25 11:35:56.000000000 -0700 -+++ src/threads/interrupt.c 2006-05-16 13:44:56.000000000 -0700 +--- src/threads/interrupt.c~ ++++ src/threads/interrupt.c @@ -354,6 +354,8 @@ intr_handler (struct intr_frame *frame) in_external_intr = true; yield_on_return = false; @@ -131,8 +132,8 @@ diff -u src/threads/interrupt.c~ src/threads/interrupt.c If there is no handler, invoke the unexpected interrupt Index: src/threads/thread.c diff -u src/threads/thread.c~ src/threads/thread.c ---- src/threads/thread.c~ 2006-04-25 11:35:57.000000000 -0700 -+++ src/threads/thread.c 2006-05-16 13:44:56.000000000 -0700 +--- src/threads/thread.c~ ++++ src/threads/thread.c @@ -13,6 +13,7 @@ #include "threads/vaddr.h" #ifdef USERPROG @@ -173,28 +174,18 @@ diff -u src/threads/thread.c~ src/threads/thread.c /* Stack frame for kernel_thread(). */ kf = alloc_frame (t, sizeof *kf); -@@ -253,16 +254,19 @@ thread_tid (void) +@@ -288,10 +289,11 @@ thread_tid (void) void thread_exit (void) { -+ struct thread *t = thread_current (); -+ ASSERT (!intr_context ()); + syscall_exit (); #ifdef USERPROG process_exit (); #endif -- -+ - /* Just set our status to dying and schedule another process. - We will be destroyed during the call to schedule_tail(). */ - intr_disable (); -- thread_current ()->status = THREAD_DYING; -+ t->status = THREAD_DYING; - schedule (); - NOT_REACHED (); - } + + /* Remove thread from all threads list, set our status to dying, @@ -406,17 +410,28 @@ is_thread (struct thread *t) /* Does basic initialization of T as a blocked thread named NAME. */ @@ -227,8 +218,8 @@ diff -u src/threads/thread.c~ src/threads/thread.c Index: src/threads/thread.h diff -u src/threads/thread.h~ src/threads/thread.h ---- src/threads/thread.h~ 2006-04-13 13:53:34.000000000 -0700 -+++ src/threads/thread.h 2006-05-16 13:44:56.000000000 -0700 +--- src/threads/thread.h~ ++++ src/threads/thread.h @@ -2,8 +2,10 @@ #define THREADS_THREAD_H @@ -289,13 +280,13 @@ diff -u src/threads/thread.h~ src/threads/thread.h + struct semaphore dead; /* 1=child alive, 0=child dead. */ + }; + - void thread_init (void); - void thread_start (void); - + /* If false (default), use round-robin scheduler. + If true, use multi-level feedback queue scheduler. + Controlled by kernel command-line options "-o mlfqs". Index: src/userprog/exception.c diff -u src/userprog/exception.c~ src/userprog/exception.c ---- src/userprog/exception.c~ 2006-01-29 13:32:56.000000000 -0800 -+++ src/userprog/exception.c 2006-05-16 13:44:56.000000000 -0700 +--- src/userprog/exception.c~ ++++ src/userprog/exception.c @@ -4,6 +4,7 @@ #include "userprog/gdt.h" #include "threads/interrupt.h" @@ -324,10 +315,10 @@ diff -u src/userprog/exception.c~ src/userprog/exception.c not_present ? "not present" : "rights violation", Index: src/userprog/pagedir.c diff -u src/userprog/pagedir.c~ src/userprog/pagedir.c ---- src/userprog/pagedir.c~ 2006-04-25 11:35:58.000000000 -0700 -+++ src/userprog/pagedir.c 2006-05-16 13:44:56.000000000 -0700 +--- src/userprog/pagedir.c~ ++++ src/userprog/pagedir.c @@ -35,15 +35,7 @@ pagedir_destroy (uint32_t *pd) - ASSERT (pd != base_page_dir); + ASSERT (pd != init_page_dir); for (pde = pd; pde < pd + pd_no (PHYS_BASE); pde++) if (*pde & PTE_P) - { @@ -345,8 +336,8 @@ diff -u src/userprog/pagedir.c~ src/userprog/pagedir.c Index: src/userprog/process.c diff -u src/userprog/process.c~ src/userprog/process.c ---- src/userprog/process.c~ 2006-04-25 11:35:58.000000000 -0700 -+++ src/userprog/process.c 2006-05-16 13:44:56.000000000 -0700 +--- src/userprog/process.c~ ++++ src/userprog/process.c @@ -14,12 +14,26 @@ #include "threads/flags.h" #include "threads/init.h" @@ -358,26 +349,26 @@ diff -u src/userprog/process.c~ src/userprog/process.c +#include "vm/page.h" +#include "vm/frame.h" - static thread_func execute_thread NO_RETURN; + static thread_func start_process NO_RETURN; -static bool load (const char *cmdline, void (**eip) (void), void **esp); +static bool load (const char *cmd_line, void (**eip) (void), void **esp); + +/* Data structure shared between process_execute() in the -+ invoking thread and execute_thread() in the newly invoked ++ invoking thread and start_process() in the newly invoked + thread. */ +struct exec_info + { -+ const char *filename; /* Program to load. */ ++ const char *file_name; /* Program to load. */ + struct semaphore load_done; /* "Up"ed when loading complete. */ + struct wait_status *wait_status; /* Child process. */ + bool success; /* Program successfully loaded? */ + }; /* Starts a new thread running a user program loaded from - FILENAME. The new thread may be scheduled (and may even exit) + FILE_NAME. The new thread may be scheduled (and may even exit) @@ -28,29 +42,37 @@ static bool load (const char *cmdline, v tid_t - process_execute (const char *filename) + process_execute (const char *file_name) { - char *fn_copy; + struct exec_info exec; @@ -385,23 +376,23 @@ diff -u src/userprog/process.c~ src/userprog/process.c + char *save_ptr; tid_t tid; -- /* Make a copy of FILENAME. +- /* Make a copy of FILE_NAME. - Otherwise there's a race between the caller and load(). */ - fn_copy = palloc_get_page (0); - if (fn_copy == NULL) - return TID_ERROR; -- strlcpy (fn_copy, filename, PGSIZE); +- strlcpy (fn_copy, file_name, PGSIZE); + /* Initialize exec_info. */ -+ exec.filename = filename; ++ exec.file_name = file_name; + sema_init (&exec.load_done, 0); - /* Create a new thread to execute FILENAME. */ -- tid = thread_create (filename, PRI_DEFAULT, execute_thread, fn_copy); + /* Create a new thread to execute FILE_NAME. */ +- tid = thread_create (file_name, PRI_DEFAULT, start_process, fn_copy); - if (tid == TID_ERROR) - palloc_free_page (fn_copy); -+ strlcpy (thread_name, filename, sizeof thread_name); ++ strlcpy (thread_name, file_name, sizeof thread_name); + strtok_r (thread_name, " ", &save_ptr); -+ tid = thread_create (thread_name, PRI_DEFAULT, execute_thread, &exec); ++ tid = thread_create (thread_name, PRI_DEFAULT, start_process, &exec); + if (tid != TID_ERROR) + { + sema_down (&exec.load_done); @@ -417,20 +408,20 @@ diff -u src/userprog/process.c~ src/userprog/process.c /* A thread function that loads a user process and starts it running. */ static void --execute_thread (void *filename_) -+execute_thread (void *exec_) +-start_process (void *file_name_) ++start_process (void *exec_) { -- char *filename = filename_; +- char *file_name = file_name_; + struct exec_info *exec = exec_; struct intr_frame if_; bool success; -@@ -59,10 +81,28 @@ execute_thread (void *filename_) +@@ -59,10 +81,28 @@ start_process (void *file_name_) if_.gs = if_.fs = if_.es = if_.ds = if_.ss = SEL_UDSEG; if_.cs = SEL_UCSEG; if_.eflags = FLAG_IF | FLAG_MBS; -- success = load (filename, &if_.eip, &if_.esp); -+ success = load (exec->filename, &if_.eip, &if_.esp); +- success = load (file_name, &if_.eip, &if_.esp); ++ success = load (exec->file_name, &if_.eip, &if_.esp); + + /* Allocate wait_status. */ + if (success) @@ -441,7 +432,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c + } - /* If load failed, quit. */ -- palloc_free_page (filename); +- palloc_free_page (file_name); + /* Initialize wait_status. */ + if (success) + { @@ -457,7 +448,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c if (!success) thread_exit (); -@@ -76,18 +116,47 @@ execute_thread (void *filename_) +@@ -76,18 +116,47 @@ start_process (void *file_name_) NOT_REACHED (); } @@ -559,11 +550,11 @@ diff -u src/userprog/process.c~ src/userprog/process.c and its initial stack pointer into *ESP. Returns true if successful, false otherwise. */ bool --load (const char *filename, void (**eip) (void), void **esp) +-load (const char *file_name, void (**eip) (void), void **esp) +load (const char *cmd_line, void (**eip) (void), void **esp) { struct thread *t = thread_current (); -+ char filename[NAME_MAX + 2]; ++ char file_name[NAME_MAX + 2]; struct Elf32_Ehdr ehdr; struct file *file = NULL; off_t file_ofs; @@ -572,7 +563,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c int i; /* Allocate and activate page directory. */ -@@ -220,13 +318,28 @@ load (const char *filename, void (**eip) +@@ -220,13 +318,28 @@ load (const char *file_name, void (**eip) goto done; process_activate (); @@ -582,27 +573,27 @@ diff -u src/userprog/process.c~ src/userprog/process.c + goto done; + hash_init (t->pages, page_hash, page_less, NULL); + -+ /* Extract filename from command line. */ ++ /* Extract file_name from command line. */ + while (*cmd_line == ' ') + cmd_line++; -+ strlcpy (filename, cmd_line, sizeof filename); -+ cp = strchr (filename, ' '); ++ strlcpy (file_name, cmd_line, sizeof file_name); ++ cp = strchr (file_name, ' '); + if (cp != NULL) + *cp = '\0'; + /* Open executable file. */ -- file = filesys_open (filename); -+ t->bin_file = file = filesys_open (filename); +- file = filesys_open (file_name); ++ t->bin_file = file = filesys_open (file_name); if (file == NULL) { - printf ("load: %s: open failed\n", filename); + printf ("load: %s: open failed\n", file_name); goto done; } + file_deny_write (t->bin_file); /* Read and verify executable header. */ if (file_read (file, &ehdr, sizeof ehdr) != sizeof ehdr -@@ -301,7 +414,7 @@ load (const char *filename, void (**eip) +@@ -301,7 +414,7 @@ load (const char *file_name, void (**eip) } /* Set up stack. */ @@ -611,7 +602,7 @@ diff -u src/userprog/process.c~ src/userprog/process.c goto done; /* Start address. */ -@@ -311,14 +424,11 @@ load (const char *filename, void (**eip) +@@ -311,14 +424,11 @@ load (const char *file_name, void (**eip) done: /* We arrive here whether the load is successful or not. */ @@ -808,8 +799,8 @@ diff -u src/userprog/process.c~ src/userprog/process.c } Index: src/userprog/syscall.c diff -u src/userprog/syscall.c~ src/userprog/syscall.c ---- src/userprog/syscall.c~ 2005-06-18 20:21:21.000000000 -0700 -+++ src/userprog/syscall.c 2006-05-16 14:17:02.000000000 -0700 +--- src/userprog/syscall.c~ ++++ src/userprog/syscall.c @@ -1,20 +1,598 @@ #include "userprog/syscall.h" #include @@ -817,11 +808,11 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c #include +#include "userprog/process.h" +#include "userprog/pagedir.h" -+#include "devices/kbd.h" ++#include "devices/input.h" ++#include "devices/shutdown.h" +#include "filesys/directory.h" +#include "filesys/filesys.h" +#include "filesys/file.h" -+#include "threads/init.h" #include "threads/interrupt.h" +#include "threads/malloc.h" +#include "threads/palloc.h" @@ -988,7 +979,7 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c +static int +sys_halt (void) +{ -+ power_off (); ++ shutdown_power_off (); +} + +/* Exit system call. */ @@ -1159,7 +1150,7 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c + + for (i = 0; i < read_amt; i++) + { -+ char c = kbd_getc (); ++ char c = input_getc (); + if (!page_lock (udst, true)) + thread_exit (); + udst[i] = c; @@ -1414,8 +1405,8 @@ diff -u src/userprog/syscall.c~ src/userprog/syscall.c +} Index: src/userprog/syscall.h diff -u src/userprog/syscall.h~ src/userprog/syscall.h ---- src/userprog/syscall.h~ 2004-09-05 22:38:45.000000000 -0700 -+++ src/userprog/syscall.h 2006-05-16 13:44:56.000000000 -0700 +--- src/userprog/syscall.h~ ++++ src/userprog/syscall.h @@ -2,5 +2,6 @@ #define USERPROG_SYSCALL_H @@ -1425,8 +1416,8 @@ diff -u src/userprog/syscall.h~ src/userprog/syscall.h #endif /* userprog/syscall.h */ Index: src/vm/frame.c diff -u src/vm/frame.c~ src/vm/frame.c ---- src/vm/frame.c~ 1969-12-31 16:00:00.000000000 -0800 -+++ src/vm/frame.c 2006-05-16 13:44:56.000000000 -0700 +--- src/vm/frame.c~ ++++ src/vm/frame.c @@ -0,0 +1,162 @@ +#include "vm/frame.h" +#include @@ -1452,7 +1443,7 @@ diff -u src/vm/frame.c~ src/vm/frame.c + + lock_init (&scan_lock); + -+ frames = malloc (sizeof *frames * ram_pages); ++ frames = malloc (sizeof *frames * init_ram_pages); + if (frames == NULL) + PANIC ("out of memory allocating page frames"); + @@ -1592,8 +1583,8 @@ diff -u src/vm/frame.c~ src/vm/frame.c +} Index: src/vm/frame.h diff -u src/vm/frame.h~ src/vm/frame.h ---- src/vm/frame.h~ 1969-12-31 16:00:00.000000000 -0800 -+++ src/vm/frame.h 2006-05-16 13:44:56.000000000 -0700 +--- src/vm/frame.h~ ++++ src/vm/frame.h @@ -0,0 +1,23 @@ +#ifndef VM_FRAME_H +#define VM_FRAME_H @@ -1620,8 +1611,8 @@ diff -u src/vm/frame.h~ src/vm/frame.h +#endif /* vm/frame.h */ Index: src/vm/page.c diff -u src/vm/page.c~ src/vm/page.c ---- src/vm/page.c~ 1969-12-31 16:00:00.000000000 -0800 -+++ src/vm/page.c 2006-05-16 13:44:56.000000000 -0700 +--- src/vm/page.c~ ++++ src/vm/page.c @@ -0,0 +1,293 @@ +#include "vm/page.h" +#include @@ -1694,7 +1685,7 @@ diff -u src/vm/page.c~ src/vm/page.c + return false; + + /* Copy data into the frame. */ -+ if (p->sector != (disk_sector_t) -1) ++ if (p->sector != (block_sector_t) -1) + { + /* Get data from swap. */ + swap_in (p); @@ -1832,7 +1823,7 @@ diff -u src/vm/page.c~ src/vm/page.c + + p->frame = NULL; + -+ p->sector = (disk_sector_t) -1; ++ p->sector = (block_sector_t) -1; + + p->file = NULL; + p->file_offset = 0; @@ -1918,14 +1909,14 @@ diff -u src/vm/page.c~ src/vm/page.c +} Index: src/vm/page.h diff -u src/vm/page.h~ src/vm/page.h ---- src/vm/page.h~ 1969-12-31 16:00:00.000000000 -0800 -+++ src/vm/page.h 2006-05-16 13:44:56.000000000 -0700 +--- src/vm/page.h~ ++++ src/vm/page.h @@ -0,0 +1,50 @@ +#ifndef VM_PAGE_H +#define VM_PAGE_H + +#include -+#include "devices/disk.h" ++#include "devices/block.h" +#include "filesys/off_t.h" +#include "threads/synch.h" + @@ -1945,7 +1936,7 @@ diff -u src/vm/page.h~ src/vm/page.h + struct frame *frame; /* Page frame. */ + + /* Swap information, protected by frame->frame_lock. */ -+ disk_sector_t sector; /* Starting sector of swap area, or -1. */ ++ block_sector_t sector; /* Starting sector of swap area, or -1. */ + + /* Memory-mapped file information, protected by frame->frame_lock. */ + bool private; /* False to write back to file, @@ -1973,8 +1964,8 @@ diff -u src/vm/page.h~ src/vm/page.h +#endif /* vm/page.h */ Index: src/vm/swap.c diff -u src/vm/swap.c~ src/vm/swap.c ---- src/vm/swap.c~ 1969-12-31 16:00:00.000000000 -0800 -+++ src/vm/swap.c 2006-05-16 13:44:56.000000000 -0700 +--- src/vm/swap.c~ ++++ src/vm/swap.c @@ -0,0 +1,85 @@ +#include "vm/swap.h" +#include @@ -1982,12 +1973,11 @@ diff -u src/vm/swap.c~ src/vm/swap.c +#include +#include "vm/frame.h" +#include "vm/page.h" -+#include "devices/disk.h" +#include "threads/synch.h" +#include "threads/vaddr.h" + -+/* The swap disk. */ -+static struct disk *swap_disk; ++/* The swap device. */ ++static struct block *swap_device; + +/* Used swap pages. */ +static struct bitmap *swap_bitmap; @@ -1996,20 +1986,21 @@ diff -u src/vm/swap.c~ src/vm/swap.c +static struct lock swap_lock; + +/* Number of sectors per page. */ -+#define PAGE_SECTORS (PGSIZE / DISK_SECTOR_SIZE) ++#define PAGE_SECTORS (PGSIZE / BLOCK_SECTOR_SIZE) + +/* Sets up swap. */ +void +swap_init (void) +{ -+ swap_disk = disk_get (1, 1); -+ if (swap_disk == NULL) ++ swap_device = block_get_role (BLOCK_SWAP); ++ if (swap_device == NULL) + { -+ printf ("no swap disk--swap disabled\n"); ++ printf ("no swap device--swap disabled\n"); + swap_bitmap = bitmap_create (0); + } + else -+ swap_bitmap = bitmap_create (disk_size (swap_disk) / PAGE_SECTORS); ++ swap_bitmap = bitmap_create (block_size (swap_device) ++ / PAGE_SECTORS); + if (swap_bitmap == NULL) + PANIC ("couldn't create swap bitmap"); + lock_init (&swap_lock); @@ -2024,13 +2015,13 @@ diff -u src/vm/swap.c~ src/vm/swap.c + + ASSERT (p->frame != NULL); + ASSERT (lock_held_by_current_thread (&p->frame->lock)); -+ ASSERT (p->sector != (disk_sector_t) -1); ++ ASSERT (p->sector != (block_sector_t) -1); + + for (i = 0; i < PAGE_SECTORS; i++) -+ disk_read (swap_disk, p->sector + i, -+ p->frame->base + i * DISK_SECTOR_SIZE); ++ block_read (swap_device, p->sector + i, ++ p->frame->base + i * BLOCK_SECTOR_SIZE); + bitmap_reset (swap_bitmap, p->sector / PAGE_SECTORS); -+ p->sector = (disk_sector_t) -1; ++ p->sector = (block_sector_t) -1; +} + +/* Swaps out page P, which must have a locked frame. */ @@ -2051,8 +2042,8 @@ diff -u src/vm/swap.c~ src/vm/swap.c + + p->sector = slot * PAGE_SECTORS; + for (i = 0; i < PAGE_SECTORS; i++) -+ disk_write (swap_disk, p->sector + i, -+ p->frame->base + i * DISK_SECTOR_SIZE); ++ block_write (swap_device, p->sector + i, ++ p->frame->base + i * BLOCK_SECTOR_SIZE); + + p->private = false; + p->file = NULL; @@ -2063,8 +2054,8 @@ diff -u src/vm/swap.c~ src/vm/swap.c +} Index: src/vm/swap.h diff -u src/vm/swap.h~ src/vm/swap.h ---- src/vm/swap.h~ 1969-12-31 16:00:00.000000000 -0800 -+++ src/vm/swap.h 2006-05-16 13:44:56.000000000 -0700 +--- src/vm/swap.h~ ++++ src/vm/swap.h @@ -0,0 +1,11 @@ +#ifndef VM_SWAP_H +#define VM_SWAP_H 1