You are on page 1of 20

Bansilal Ramnath Agarwal Charitable Trust’s

Vishwakarma Institute of Information Technology, Pune-48


(An Autonomous Institute affiliated to Savitribai Phule Pune University)

Assignment no. 1

AIM : To write a java program for multiprocessor operating systems implementing semaphores.

THEORY:

Synchronization with Semaphores


The too-much-milk solution is much too complicated. The problem is that the mutual exclusion
mechanism was too simple-minded: it used only atomic reads and writes. This is sufficient, but
unpleasant. It would be unbearable to extend that mechanism to many processes. Let’s look at
more powerful, higher-level mechanisms.

Requirements for a mutual exclusion mechanism:


•Must allow only one process into a critical section at a time.
•If several requests at once, must allow one process to proceed.
•Processes must be able to go on vacation outside critical section.

Desirable properties for a mutual exclusion mechanism:


•Fair: if several processes waiting, let each in eventually.
•Efficient: don’t use up substantial amounts of resources when waiting. E.g.
no busy waiting.
•Simple: should be easy to use (e.g. just bracket the critical sections).

Desirable properties of processes using the mechanism:


•Always lock before manipulating shared data.
•Always unlock after manipulating shared data.
•Do not lock again if already locked.
•Do not unlock if not locked by you (usually: there are a few exceptions to
this).
•Do not spend large amounts of time in critical section.

Semaphore : A synchronization variable that takes on positive integer values.


• P(semaphore): an atomic operation that waits for semaphore to become greater than zero, then
decrements it by 1 (“proberen” in Dutch).
• V(semaphore): an atomic operation that increments semaphore by 1(“verhogen” in Dutch).
Semaphores are simple and elegant and allow the solution of many interesting problems. They
do a lot more than just mutual exclusion.
Binary semaphores are those that can only take on two values, 0 and 1. Semaphores aren’t
provided by hardware. (I’ll describe implementation later.) But they have several attractive
properties:
• Machine independent.
• Simple.
• Work with many processes.
• Can have many different critical sections with different semaphores.

1
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

• Can acquire many resources simultaneously (multiple P’s).


• Can permit multiple processes into the critical section at once, if that is
desirable.
Desirability of layering: picking powerful and flexible intermediate solutions to
problems. A synchronization kernel is appropriate for one layer.
Semaphores are used in two different ways:
• Mutual exclusion : to ensure that only one process is accessing shared
information at a time. If there are separate groups of data that can be
accessed independently, there may be separate semaphores, one for each
group of data. These semaphores are always binary semaphores.
• Condition synchronization : to permit processes to wait for certain things to
happen. If there are different groups of processes waiting for different things
to happen, there will usually be a different semaphore for each group of
processes. These semaphores aren’t necessarily binary semaphores.

ALGORTHIM:
1. Import the header files.
2. Declare three processor a, b, c.
3. Assign processors ‘a’, ‘b’, ‘c’ to “A”, “B”, “C”
4. Using semaphores the processors are synchronized5.
5. Check the following conditions:
a) A B must be output before any C's can be output.
b) B's and C's must alternate in the output string, that is, after the first B is output,another
B cannot be output until a C is output
Similarly, once a C is output, another C cannot be output until a B is output.

c) The total number of B's and C's which has been output at any given point in the output
string cannot exceed the number of A's which have been output up to that point

6. If the condition is valid, then print the result using acquire () function.

2
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

OUTPUT:

3
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

Assignment no. 2

AIM :
To write a java program for multiprocessor operating systems implementing multiple sleeping
barbers problem.

THEORY :
Another classical IPC problem takes place in a barber shop. The barber shop has one barber, one
barber chair, and n chairs for waiting customers, if any, to sit on. If there are no customers
present, the barber sits down in the barber chair and falls asleep.
When a customer arrives, he has to wake up the sleeping barber. If additional customers arrive
while the barber is cutting a customer's hair, they either sit down (if there are empty chairs) or
leave the shop (if all chairs are full). The problem is to program the barber and the customers
without getting into race conditions. This problem is similar to various queueing situations, such
as a multiperson helpdesk with a computerized call waiting system for holding a limited number
of incoming calls.
Our solution uses three semaphores, customers, which counts waiting customers (excluding the
customer in the barber chair, who is not waiting), barbers, the number of barbers (0 or 1) who are
idle, waiting for customers, and mutex, which is used for mutual exclusion. We also need a
variable, waiting, which also counts the waiting customers. The reason for having waiting is that
there is no way to read the current value of a semaphore. In this solution, a customer entering the
shop has to count the number of waiting customers. If it is less than the number of chairs, he
stays; otherwise, he leaves.
Our solution is shown [below]. When the barber shows up for work in the morning, he executes
the procedure barber, causing him to block on the semaphore customersbecause it is initially 0.
The barber then goes to sleep, He stays asleep until the first customer shows up. When a
ustomer arrives, he executes customer, starting by acquiring mutex to enter a critical region. If
another customer enters shortly thereafter, the second one will no be able to do anything until the
first one has released mutex. The customer then checks to see if the number of waiting customers
is less than the number of chairs. If not, he releases mutex and leaves without a haircut.
If there is an available chair, the customer increments the integer variable, waiting. Then he does
an Up on the semaphore customers, thus waking up the barber. At this point, the customer and
the barber are both awake. When the customer releases mutex the barber grabs it, does some
housekeeping, and begins the haircut. When the haircut is over, the customer exits the procedure
and leaves the shop. Unlike our earlier examples, there is no loop for the customer because each
one gets only one haircut. The barber loops, however, to try to get the next customer. If one is
present, a haircut is given. If not, the barber goes to sleep.

ALGORITHM :
1. Import the header files.
2. Declare the number of chairs and barbers in the shop
3. Enter the number of customer.
4. If there is no customer to be served, the barber goes to sleep.

4
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

5. If a customer enters the barber shop whole chairs are occupied, then the customer leaves
the shop.
6. If the barber is busy, but chairs are available in waiting room then the customer sits in
one of the free chairs until the barber is idle.
7. Barber class which extends threads is used to simulate multiple sleeping barbers.

OUPUT :

5
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

Assignment no. 3

AIM : To write a program to implement page table management.

THEORY :

In this lab, you will add paged virtual memory management to PIOS. So far PIOS's "processes"
have all been running in the same address space as the kernel, with full ability to read or modify
any part of physical memory, which of course makes the whole system vulnerable to bugs or
misbehavior in any process even when processes are executing in user mode. We will now use
the x86's page-based address translation facilities to give each process an independent user-level
address space, providing remaining key ingredient for protection between processes by
preventing processes from accessing either the kernel's or other processes' address spaces. We
will also enhance PIOS's system call API to allow a process to copy data into and out of child
processes, using copy-on-write optimization to minimize the cost of copying, These system call
enhancements will allow the parent process not only to "fork" child processes with cloned
address spaces as in Unix, but also — moving a step beyond typical Unix APIs — allow the
parent to merge results that a child process computes directly back into the parent's own address
space without having to communicate indirectly through pipes or files.

This lab contains the following implementation components:

1. Page Table Management: Building paging structures for the kernel and for user
processors, and enabling page translation.
2. Loading and Running an ELF Executable: Loading an ELF executable image into the
first user-space process and preparing it for execution.
3. User Space Copyin/Copyout: Copying memory-based system call arguments out of or
into user space while protecting against invalid arguments or traps.
4. Memory Management with Copy-on-Write: Efficiently "copying" memory via read-
only shared mappings, and lazily copying actual page content only on demand.
5. Virtual Memory Merge: Merging memory changes made in one process since a
memory snapshot into another process's address space.

Part 1: Page Table Management

Before doing anything else, make sure you thoroughly understand the x86's protected-mode
memory management architecture for both segmentation and page translation.

Next, carefully review all the paging-related definitions in the PIOS source files inc/mmu.h and
kern/pmap.h. The former in particular has a variety of macros and constants that will be
extremely useful in this lab if used appropriately.

6
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

Setting Up the Kernel's Address Space


Once paging is enabled on an x86 processor, it is enabled all the time, whether the processor is
executing in kernel or user mode. While some processor architectures give the kernel a "special"
way to access physical memory directly while running in kernel mode, the x86 is not such an
architecture. Once an x86 kernel enables paging, the only way any running code, including the
kernel, can access physical memory or I/O devices is through the paged memory management
system. This means that to use page memory management at all, the kernel must first initialize at
least one page directory/page table structure with the mappings it will need in order to continue
executing, and must load this paging structure into the processor before enabling paging.

In PIOS, the kernel expects to access physical memory and I/O devices at virtual addresses
identical to their physical addresses. This means that before enabling paging we will have to
create a set of identity mappings, which map a given virtual address to the same physical address.

You should initialize all page directory entries corresponding to the user-mode address space,
between VM_USERLO and VM_USERHI, to the constant PTE_ZERO, defined earlier in
pmap.c. Note that this constant is not the same as NULL: it is the (nonzero!) physical address of
a particular page that the kernel will keep permanently set to all zeros. Using PTE_ZERO instead
of NULL in unmapped page directory and page table entries will slightly simplify code later in
this lab, since the kernel can safely enable read permission on PTE_ZERO mappings and allow
user code to read (but not write!) this all-zero page.

We have included code further down in pmap_init() to enable the processor paging features that
PIOS uses (namely 4MB pages and Global mappings), load the bootstrap page directory into the
CR3 register, and turn on paging. Once you have correctly initialized the bootstrap page
directory, you should be able to execute past the final lcr0 instruction. If something is wrong
with your page directory, the processor is likely to triple-fault and reset at this instruction,
because once paging is enabled with a bad page directory, the processor is unlikely to be able to
do just about anything — including dispatch a page fault to the kernel's page fault handler.

Per-Process Page Directories


Now that the kernel can execute with paging enabled, it is time to start building the mapping
structures we will need to give each user process its own independent address space. Recall that
the x86's basic 32-bit paging system uses a two-level mapping structure: a page directory
represents the entire 32-bit address space, and the page tables that the page directory refers to
each contain page mappings for a 4MB (2^22 byte) address region.

In PIOS, each process will always have two page directories exclusively for the use of that
process: a working page directory (proc.pdir) and a reference page directory (proc.rpdir). The
working page directory is the one the kernel loads into the processor when executing that
process. We will see the purpose of the reference page directory in part 5 of this lab; for now all
you need to know is that both page directories need to be allocated when a new process is

7
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

allocated, and both get initialized to a copy of the pmap_bootpdir: i.e., with the standard
mappings for the kernel part of the address space and an empty user address space region.

Page Table Management


You will now implement the "boilerplate" code the kernel will need to create and manage full
two-level page tables for user processes. This basic management code consists of the following
three functions:

 pte_t *pmap_walk(pde_t *pdir, uint32_t va, bool writing): This is the main function to
"walk" the 2-level structure and allocate page tables as needed by the kernel. The purpose
of the function is to locate the page table entry (PTE) — the particular 32-bit word in the
second-level table — that maps the 4KB page containing virtual address va. Note that va
can be an arbitrary address within a page: it need not point to the beginning of a page.

Since the kernel initializes all page directory entries (PDEs) in a new page directory to
PTE_ZERO, however, this means that a new directory has no associated page tables:
instad the kernel allocates and initialize page tables on demand the first time it needs to
map something into the 4MB region covered by a given PDE. This is one of the
secondary tasks of pmap_walk().

The behavior of pmap_walk() varies depending on whether it is being used for "reading"
or "writing" the paging structures, as indicated by the caller with the writing flag. If
writing is zero (false) and pmap_walk() encounters a missing page table, it just returns
NULL and assumes the caller will know how to proceed. If writing is nonzero (true),
however, then pmap_walk() will attempt to "fill in" a missing PDE by allocating and
initializing a new page table, returning NULL only if the mem_alloc() fails. When
pmap_walk() allocates and inserts a new page table into the page directory, it initializes
all the PTEs in the new page table to PTE_ZERO, but inserts the page table itself into the
page directory with permissions allowing the processor to use the new page table, for
both read and write accesses and in either user or kernel mode.

 pte_t *pmap_insert(pde_t *pdir, pageinfo *pi, uint32_t va, int perm): Given a pointer to a
pageinfo structure describing a given physical page, this function maps that page in the
paging structure at virtual address va and with PTE permissions perm. If another page
was already mapped at the same virtual address, this function first unmaps the previous
page.

Both the unmapping and mapping processes adjust the reference counts associated
physical pages (in the pageinfo structure) appropriately: incrementing the reference count
for the page being mapped, and decrementing the reference count for any page being
unmapped. If unmapping the old page releases the last reference to that page, then the old
page must be freed as well. We have provided mem_incref() and mem_decref() functions
in kern/mem.c to increment and decrement the per-page reference counts atomically: this
8
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

means that you don't have to use spinlocks to maintain the consistency of the reference
counts when multiple processors may be adding and releasing references to the same
page concurrently. The mem_decref() function also calls mem_free() automatically once
the reference count reaches zero: this means that you must be really done with the page
when you call mem_decref(), since another processor could immediately allocate and
start using the page the moment you release the last reference.

 void pmap_remove(pde_t *pdir, uint32_t va, size_t size): This function removes an
arbitrary contiguous range of page mappings from a virtual address space, starting at va
(which must be page-aligned), and covering an address region of size size (which must
likewise be a multiple of PAGESIZE). Unlike the above two functions, which deal with
only a single 4KB page mapping at a time, this function iterates through the virtual
address space, possibly removing many page mappings in the process. Also, if the
address region to be removed covers the entire 4MB regions represented by one or more
page tables, then pmap_remove() not only removes all the mappings in those page tables,
but also unmaps and releases the page tables themselves.

As with pmap_insert(), this function decrements the reference counts of all unmapped
pages, and frees any page whose last reference has been released. Unmapped page table
entries are left in the same state that PTEs in freshly allocated page tables are initialized
with: namely, set to PTE_ZERO.

Since both pmap_insert() and pmap_remove() can affect page mappings that have already been
used by the processor and loaded into the processor's translation lookaside buffer (TLB), these
functions also ensure that all the affected mappings are flushed from the TLB if the address
space being modified is the address space of the currently running process.

In most multiprocessor operating systems, operations like pmap_insert() and pmap_remove()


might have to flush not only the current processor's TLB, but the TLBs of other concurrently
running processors. This procedure is known as TLB shootdown. Why do they have to do this?
Think about what happens if multiple user-level threads share the same process's address space
space.

For now, you can ignore the text in the comment for pmap_walk about copying read-shared page
tables: you will deal with that, if necessary, in part 4 of the lab.

When you have completed this exercise correctly, you should be able to get through
pmap_check() successfully.

Part 2: Loading and Running an ELF Executable

Now that you have code to set up paging structures, it's time to set up some paging structures:
namely those required to run a "root" user-mode process in user address space and from an
executable image separately linked from that of the kernel. We have provided a test program,

9
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

user/testvm.c, which exercises and tests your virtual memory system, but you need to load that
executable into the root process's address space and start it running.

You already encountered Executable and Linkable Format (ELF) files earlier while exploring the
xv6 and PIOS boot process: the boot loader code in boot/main.c already loaded the kernel into
physical memory from an ELF executable. Now you will write a very similar, and not much
more complicated, loader in the kernel to load the first user-level program into memory. Now
would be a good time to have a close look at the ELF specification and the definitions in
inc/elf.h.

The main differences between what the boot loader did for the kernel and what your kernel code
needs to do now are:

 Whereas physical memory was already "just sitting there" waiting for the boot loader to
load something into it, the root process initially contains no accessible memory at all in
the user address space area where you need to load the program. The kernel must allocate
physical pages and map them into the user virtual memory area as it loads the program.
 An ELF executable can contain multiple segments intended to be loaded with different
memory access permissions: e.g., read-only for code and constant strings, read/write for
initialized and uninitialized data (bss). The flags in the p_flags member of the ELF
Program Header (proghdr) structure, in particular, indicates how a segment should be
mapped. The boot loader simply ignored these flags since there is no way to assign access
permissions to physical memory. While loading the first user process into virtual
memory, however, the kernel can and should set its memory permissions correctly, for
good measure and aid in debugging. The only permissions you need to worry about are
read and write, because until recently the x86 architecture didn't support page-level
execute permissions.

Note: The relevant bit values for p_flags are defined in inc/elf.h and are not the same
values as the x86 PTE permissions: you have to translate them when setting the page
permissions. Also, an ELF program segment may span many virtual memory pages, and
will not necessarily start or end on a page boundary. However, no two ELF program
segments will load onto the same virtual memory page. (The linker enforces this rule
while it is laying out the executable.)

 The boot loader only loads the initialized portion of each program segment, represented
by the p_filesz in the ELF Program Headers: i.e., the portion of the program segment
contained in the ELF file. Any program segment can also contain an uninitialized portion,
above p_filesz but below the in-memory segment size indicated by the Program Header's
p_memsz member. An ELF program loader is expected to map this uninitialized data area
but set its contents to zero. The PIOS boot loader just neglected this duty and instead let
the kernel do that itself: that's what the memset(edata, 0, end - edata) does at the
beginning of kern/init.c. When the kernel loads the first user-mode process, however, it
should do it "the right way", mapping and initializing the entire p_memsz bytes of each
program segment correctly. (In practice there is generally only one program segment for

10
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

which p_memsz is greater than p_filesz, but there is no reason this fact needs to affect the
ELF loader.)

Note: Neither the initialized nor uninitialized portions of a program segment will
necessarily start or end on a page boundary. This implies that one page of a program
segment — the one containing the "boundary" — may contain both initialized (from the
ELF file) and uninitialized (cleared to zero) data.

Besides describing the segments to be loaded into memory, the ELF header also indicates where
the program should start executing: i.e., the user-level EIP of the first instruction in the program.
In addition to loading the program itself as described by the ELF image, the kernel will need to
give the root process a stack to execute on. A small one-page stack should be sufficient; the root
process can later allocate a bigger stack for itself if it needs one. The high end of the user virtual
memory area — the last page just before VM_USERHI — would probably be a suitable place
for the root process's stack.

Hint: There are at least two general approaches to the loading process:

1. For each virtual page affected by a program segment, allocate and map a physical page
appropriately, and copy the correct data and/or zeros into that page using the page's
physical address as the destination. This approach doesn't (yet) require the mappings to
work, but slicing and dicing program segments into pages can require moderately
complicated arithmetic.
2. First allocate and map all the pages a program segment covers, then initialize the segment
all at once by accessing it at its virtual address. This approach may make the loading
arithmetic simpler, but you'll need to make sure the processor is using the correct page
directory — and how do you write to the virtual mapping of a program segment that
(eventually) needs to be read-only?

Hint 2: Testing your program loader is likely to reveal bugs in your mapping structure
management code. If something is wrong with your mappings, the processor will probably take a
page fault, so set a breakpoint at trap() to catch these. Also, make sure your process management
code does something sensible when trying to "reflect" a trap that occurs in the root process: since
the root process has no parent to reflect the trap to, you might want to dump the trapframe and
panic, for example. When the root process "returns" gracefully via sys_ret(), however, your
kernel should simply call done(), which will help the grade scripts know when the root process is
finished with all tests.

Once you have the program loader working, you should be able to step through proc_run() and
into the user mode process. GDB initially won't know where you are, because testvm was linked
separately from the kernel and GDB only has symbols for the kernel. You can fix this problem
by typing this command into GDB:

add-symbol-file obj/user/testvm 0x40000000

11
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

This will augment the debugging symbols GDB already has for the kernel with the debugging
symbols contained in the ELF image obj/user/testvm. The 0x40000000 tells GDB where testvm
is loaded in virtual memory. GDB requires this argument because this command is normally
used to load symbol tables for shared libraries, which are normally position-independent code
(PIC). PIOS's root process is not position-independent, so GDB technically doesn't need the load
location argument in our case, but GDB apparently doesn't know that.

Challenge: Add proper support for page-level execute permissions when running on AMD
processors, via AMD's extension for "No Execute" bits in page table entries. This isn't as easy as
it may sound, unfortunately, because at the time AMD introduced this feature there were no
more bits available in 32-bit page table entries, so the "No Execute" bit is available only in
conjunction with Intel's "Page Address Extensions" to support 64-bit PTEs.

Background: A number of years ago, long before the introduction of true 64-bit x86 processors,
the 32-bit physical address space introduced by the 80386 processor started getting cramped, and
hardware vendors wanted to build PCs with more than 4GB of RAM — even though users were
still running 32-bit operating systems. In response to this demand, Intel created the Page Address
Extensions (PAE), which allow 32-bit operating systems to use 64GB (36 bits worth) of physical
RAM. All this RAM obviously can't be mapped into the kernel's — or any single user process's
— 32-bit address space at once, but it can be used if the kernel doesn't need to have all physical
RAM mapped into its address space all the time (as PIOS does) and if this physical RAM is
distributed among several user processes.

PAE works by rearranging the paging structures: it increases all page table entries from 32 to 64
bits in size, thus halving the number of entries per page table or page directory, while making
room for more physical address bits and other features. But halving the number of entries meant
one page table level could translate only 9 bits of virtual address rather than 10, thus necessiating
a third (small) level of translation. This "level 3" page table, called the page directory pointer
table, contains the four "page directory pointers" necessary to map a full 32-bit virtual address
space with 64-bit PTEs. Thus, making use of PAE does not exactly represent a trivial change to
the kernel's page table management code, although nothing has changed fundamentally.

AMD later enhanced PAE mode further by adding the ability to disable code execution at page
granularity, via a new "No Execute" (NX) bit in each PTE. This was touted as a major security
feature, because it makes it more difficult for viruses and other malware to exploit buffer
overflow bugs by injecting code into the heap or stack and then causing that code to be executed
(from the heap or stack). Both Intel and AMD now support execute-disable in the new 64-bit
mode, although only AMD supports it in 32-bit PAE mode (see AMD's latest architecture
manuals for details). So if you try this challenge problem, make sure you have an AMD
processor to test on (or be prepared to rewrite your kernel to run in 64-bit mode)!

Part 3: User Space Copyin/Copyout

Now that virtual memory has provided us some hope of protecting the kernel's state from user-
level processes and protecting processes from each other, we need to reconsider how the kernel

12
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

interacts with user-level code during system calls. System calls generally have arguments, which
need to be passed from user space to the kernel. PIOS system calls pass simple arguments in
registers: the user-mode system call stubs in lib/syscall.c just load the arguments into the
appropriate registers before executing the INT T_SYSCALL instruction, and the kernel's system
call handling code in kern/syscall.c retrieves these arguments from the user-level trapframe that
the trap entrypoint code pushed on the kernel stack.

Many system calls take arguments that don't fit in registers, however, such as the string argument
to sys_cputs and the cpustate pointer to sys_put and sys_get. For such arguments, PIOS's user-
space system call stub just leaves the argument data in user space and passes a pointer to the data
in a register. The kernel then needs to read the contents of the argument data from user space —
or write system call results into user space, in the case of output arguments such as the cpustate
structure that sys_get fills in. But what if the user code passes an invalid pointer for such an
argument? Consider what would happen in your current system call handling code if a user mode
program:

 passes a pointer into the kernel's address space region, outside the VM_USERLO and
VM_USERHI region.
 passes a pointer into user space, but the area of virtual memory pointed to is either fully
or partially unmapped (no access permissions).
 passes a pointer to a user space data area that the system call will write to, such as the
cpustate argument to sys_get, but part or all of that data area is mapped read-only.

Part 4: Memory Management with Copy-on-Write

We have created a root process in a fully virtual address space, and finally taken proper measures
to protect the kernel from user processes; what's still missing is a way for user processes
themselves to change their own virtual address spaces, to set up virtual address spaces for their
child processes, and to communicate data inputs and results with their children. Instead of adding
more new system calls for this purpose, we will simply extend the existing GET and PUT system
calls to enable the caller to perform memory management operations along with the existing
functions of these system calls. This way, processes can easily combine several related
operations into one GET or PUT system call for efficiency: e.g., a parent can set up both a child's
register memory state and start the child running with one PUT system call, and can later
synchronize with the child, retrieve its register state, and retrieve results from its virtual address
space with one GET system call. The process specifies memory management operations to
perform by ORing the following values into the system call command code:

 Memory operation code in the SYS_MEMOP bit field. Must be one of the following
values:
o SYS_ZERO: Remove all permissions from a range of virtual memory in the
destination process (child for PUT, parent for GET), and replace the content of
this virtual memory range with all zeros. This operation effectively reverts the

13
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

given memory range to the "primordial" state all user memory starts out in when a
user process is first created.
o SYS_COPY: Copy a virtual memory range from the source process (parent for
PUT, child for GET) into the parent process (child for PUT, parent for GET). To
make the copy more efficient, the kernel initially copies only memory mappings,
making all affected mappings read-only in both the source and destination, and
then copies actual page content only on demand as user code tries to write to the
copied pages.
o SYS_MERGE (only available with GET): Copy the differences between the
child's reference address space snapshot and its current working address space,
within a given virtual memory range in the child's space, back into the parent's
address space. This operation is described later in Part 5.

In all of these memory operations, for simplicity the memory region affected must start
on a 4MB boundary and must be a multiple of 4MB in size: that is, these memory
operations always affect complete page tables at a time worth of address space, and are
not available to the user at page granularity. Taking advantage of this assumption will
greatly simplify your code.

On entry to the GET/PUT system call, user code specifies the relevant memory ranges in
the following registers:

o ECX: contains the memory region size. Must be a multiple of 4MB.


o ESI: contains the start of the source region in the relevant process (child for GET,
parent for PUT). Must be a multiple of 4MB.
o EDI: contains the start of the destination region in the relevant process (parent for
GET, child for PUT). Must be a multiple of 4MB.
 SYS_PERM: If the calling process ORs this flag into the GET/PUT command code, then
after performing any memory operation specified in the SYS_MEMOP bit field as
described above (or after performing no memory operation if the SYS_MEMOP bits are
zero), the kernel sets the nominal page permissions on all the pages in the destination
memory operation range to the values specified in the SYS_READ and SYS_WRITE bits
of the system call command code. Thus:
o SYS_PERM alone: If the caller specifies specifies just SYS_PERM without
SYS_READ or SYS_WRITE, then the GET/PUT removes all access permissions
from all pages in the destination memory region. This removal of access does not
deallocate or zero the content of the destination memory region: the actual page
contents remain associated with the relevant locations in the virtual address space,
but merely temporarily "hidden" and inaccessible. A subsequent SYS_PERM
operation can reinstate access permissions to the inaccessible pages, and the old
content in these pages will then be reinstated unchanged. Only the SYS_ZERO
operation above actually clears both permissions and page content.
o SYS_PERM | SYS_READ: Sets the permissions on all pages in the destination
region to permit read-only access (but not write access), regardless of any of these
pages' previous permissions. If the caller performs this operation on a destination

14
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

o
o memory region that has never been used, or on pages that have been reset with
SYS_ZERO, these pages become accessible read-only and filled with zeros.
o SYS_PERM | SYS_READ | SYS_WRITE: Sets the permissions on all pages in
the destination region to permit both read and write access, regardless of any of
these pages' previous permissions. If the caller performs this operation on a
destination memory region that has never been used, or on pages that have been
reset with SYS_ZERO, these pages are initially filled with zeros but become
accessible read/write, and thus may be used as "newly allocated" virtual memory.
Calling SYS_PERM | SYS_READ | SYS_WRITE on a never-before-used or
SYS_ZERO'd region is essentially PIOS's equivalent to sbrk() in Unix or xv6.

Although the memory SYS_MEMOP memory operations are restricted to operating on


4MB-aligned memory regions a multiple of 4MB in size, if the caller does not request a
memory operation but only specifies SYS_PERM, then SYS_PERM allows the
destination memory region to have arbitrary page alignment in both start and size. This
allows processes to manage access permissions at page granularity, to set up page
permissions when loading child processes from ELF executables for example, even
though the kernel's "bulk memory management" facilities only support the much larger
4MB granularity.

 SYS_SNAP (only available with PUT): If the calling process ORs in this flag in a PUT
system call, then after performing any memory operation and/or permission change
specified above, the kernel copies the child's entire working address space into the child's
reference address space snapshot, represented by proc->rpdir, again using copy-on-write.
This flag is discussed further in Part 5 below.

Hint: Take full advantage of the fact that "empty" page table entries in new page tables allocated
by pdir_walk(), as well as page table entries removed by pmap_remove(), are set to PTE_ZERO
— which is not just NULL but the physical address of a page full of zeros. You can set the
nominal permissions in such a PTE according to the caller's request, without actually having to
allocate a page for that PTE. You can even set the actual permissions on such a PTE to PTE_P,
allowing the user process to read this all-zero page. You cannot enable PTE_W in such a page
mapping, of course, since that would allow the user process to scribble on this page that's only
ever supposed to hold zeros. But if the user does try to write to a PTE_ZERO page with nominal
permissions of SYS_READ | SYS_WRITE, just make sure your page fault handler knows how
to make a new, exclusive copy of the zero page just as it would copy a read-shared page from a
copy-on-write: the only real difference will be in the reference count handling, since PTE_ZERO
mappings do not represent counted references. In effect, you are reusing your copy-on-write
code to implement demand zero, or clearing "newly allocated" virtual pages on demand only as
the user process actually starts writing to them.

Challenge: Enhance the memory operations above so that they all work on arbitrary 4KB page
boundaries, not just 4MB page table boundaries as specified above. Also write some testing code
to exercise all of these system calls at non-4MB boundaries, including testing "exotic" boundary

15
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

cases, such as performing memory operations on a memory range that doesn't start or end on a
4MB boundary but is big enough to cover one or more complete 4MB page tables "in the
middle" of the region.

Part 5: Virtual Memory Merge

You have seen how xv6 implements the Unix fork() system call, by simply copying the parent
process's entire memory segment into a new memory segment for the child process. And now
you have implemented, in the context of PIOS's rather different system call API, the same basic
copy-on-write mechanism that all modern Unix kernels use to make their implementations of
fork() efficient, especially in the common case where the child process actually writes to very
few pages before it exec()s another program. Now we will take a step beyond the functionality
Unix kernels offer and provide a mechanism to merge a child's results directly back into the
parent's address space after executing independently for some (perhaps long) time period.

Recall that Unix's fork() effectively initializes the child process's state with a "one-time
snapshot" of the parent process's state at the time of the fork(), but after that time the parent and
child processes evolve separately and can communicate only via the small (typically 8-bit) return
code that the child returns to the parent on exit(), or else via separate abstractions such as pipes,
sockets, or files in the globally shared file system. In order to keep PIOS as simple as possible,
however, we wish to minimize the number of abstractions the PIOS kernel needs to implement.
Processes need some way to communicate with each other, and since processes need virtual
memory in any case, PIOS uses virtual memory instead of pipes or files as the basic abstraction
for inter-process communication.

The GET/PUT system call API you implemented above already provides a "coarse-grained" way
for processes to communicate: a parent process can use SYS_PUT | SYS_COPY to copy itself or
another program, together with suitable input data, into a child process's address space, run the
child process, and then use SYS_GET | SYS_COPY to retrieve results the child left in its address
space back into the parent's address space for further use. This is a coarse-grained mechanism,
however, operating on a minimum granularity of 4MB address space regions (or 4KB pages even
if you implement that challenge problem). And the parent has to "know" in exactly which
contiguous region(s) the child will deposit its results and what region(s) in the parent's address
space to copy those results to, and avoid clobbering any important data in the parent with the
result data copied from the child. If the parent and child processes are closely related, the parent
may prefer to have the child produce results at a much finer granularity: e.g., to have the child
compute new values of a few particular word-size variables scattered throughout the parent's
address space, and perhaps intermixed with memory areas that the child is not expected to
modify — but which the parent itself or other children might modify in the meantime. Providing
such a capability is the purpose of PIOS's virtual memory merge facility. Don't be afraid of the
fancy-sounding name: it's not much more complicated than plain copy-on-write copying, but
requires some explanation since it's likely to be an unfamiliar concept.

16
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

ALGORITHM :

1. Make a pmap_init() with code to set up identity-mappings for the kernel portion
of PIOS's virtual address space, which is all the virtual address space below _USERLO
and above VM_USERHI. The easiest way to do this is to use 4MB "super-pages", as
described in the IA-32 System Programming Guide. Also, since these page mappings will
never need to change when the processor context switches among user processes, it is
worthwhile to mark these mappings global, again as described in the System
Programming Guide: that way, the processor knows not to flush these mappings when the
kernel reloads the CR3 register to change address spaces
2. Add code to proc_alloc() to allocate and initialize a process's working and reference
page directories when the process is created. We have pmap_newpdir() and
pmap_freepdir() functions in pmap.c that may be useful.
3. Implement the above three functions in kern/pmap.c. Be careful to maintain all
reference counts properly, both when allocating and releasing page tables and when
inserting and removing page mappings. Be careful to set all the permission bits
correctly at both the page directory and page table levels. Also, when iterating through
the address space in pmap_remove, be careful to handle the nontrivial cases properly:
e.g., when given an address region that does not start or end on 4MB boundaries
represented by particular page tables, but may nevertheless cover entire 4MB regions
whose page tables must be unmapped and released.
4. Implement systrap, sysrecover, checkva, and usercopy in kern/syscall.c, to provide the
logic necessary to access user memory safely using user-provided pointers. Then modify the
system call handlers to use usercopy when copying system call argument data into or out of
user space.
5. Implement the pmap_copy() function in kern/pmap.c, which provides the basis for copy-
on-write used by SYS_COPY (and SYS_SNAP, described below, but you don't have to
worry about that while implementing pmap_copy()).
6. Now implement a kernel page fault handler to copy-on-write faults transparently to user
mode code. We have provided a template function pmap_pagefault() in kern/pmap.c; you
will need to fill it in and hook it into trap() at the appropriate point.
7. Implement the memory operations SYS_ZERO and SYS_COPY, as described above.
But be sure to check the memory region arguments for validity: e.g., they should not
allow user processes to modify or copy data out of the kernel part of the address space. If
user code attempts such evilness, the system call handler should call systrap() to issue a
general protection fault (T_GPFLT).
8. Finally, implement the SYS_PERM operation, which happens after the SYS_ZERO or
SYS_COPY (if requested) in a GET or PUT system call.
9. Implement SYS_SNAP and SYS_MERGE as described above. We have provided the
skeleton functions pmap_merge() and pmap_mergepage() in kern/pmap.c to provide an
outline for how to implement the merge function. The testvm program contains several
parallel computations to exercise and test your merge implementation.

17
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

Assignment no. 4

AIM : To implement Network operating system in our lab.

ALGORITHM :
1. Start the Program
2. Get the IP address and the MAC address from user
3. Get the inet address using InetAddress.getByName(IP address)
4. Create a packet using DatagramPacket()
5. Create a Socket using DatagramSocket()
6. Send the packet using socket.send(packet);
7. Close the Socket using socket.close();
8. End the Program.

TO FIND MAC ADDRESS AND IP ADDRESS

ipconfig/> IP ConfigurationHost

Name . . . . . . . . . . . . : Sam-PCPrimary Dns Suffix . . . . . . . :


Node Type . . . . . . . . . . . . : Mixed
IP Routing Enabled. . . . . . . . : No
WINS Proxy Enabled. . . . . . . . : No
PPP adapter 3G Modem:Connection-specific DNS
Suffix . :Description . . . . . . . . . . . : 3G
ModemPhysical Address. . . . . . . . . :DHCP
Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
IPv4 Address. . . . . . . . . . . : 117.230.1.244(Preferred)
Subnet Mask . . . . . . . . . . . : 255.255.255.255
Default Gateway . . . . . . . . . : 0.0.0.0
DNS Servers . . . . . . . . . . . : 218.248.241.3218.248.240.180
NetBIOS over Tcpip. . . . . . . . : Disabled
Wireless LAN adapter Wireless Network Connection:Media
State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :Description . . . . . . . . . . . : Atheros AR9285
Wireless Network Adapter
Physical Address. . . . . . . . . : B4-82-FE-DC-C4-99DHCP
Enabled. . . . . . . . . . . : YesAutoconfiguration Enabled . . . . : Yes
Ethernet adapter Local Area Connection:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :
Description . . . . . . . . . . . : Marvell Yukon 88E8059 Family PCI-E Gigabit Ethernet Controller
Physical Address. . . . . . . . . : 00-24-54-7D-6E-52DHCP
Enabled. . . . . . . . . . . : Yes

18
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

Autoconfiguration Enabled . . . . : Yes


Tunnel adapter isatap.{770C717E-3DB5-44F6-9C70-4BB9BE70A198}:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :Description . . . . . . . . . . . : Microsoft ISATAP Adapter
Physical Address. . . . . . . . . : 00-00-00-00-00-00-00-E0DHCP
Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
Tunnel adapter isatap.{4A6B2BFB-A001-4943-9B9A-64EB6F88819B}:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :Description . . . . . . . . . . . : Microsoft ISATAP Adapter
#2Physical Address. . . . . . . . . : 00-00-00-00-00-00-00-E0DHCP
Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
Tunnel adapter isatap.{DA2C2A91-BE38-C2D6-433E-836237474E70}:
Media State . . . . . . . . . . . : Media disconnected
Connection-specific DNS Suffix . :Description . . . . . . . . . . . : Microsoft ISATAP Adapter
#3Physical Address. . . . . . . . . : 00-00-00-00-00-00-00-E0DHCP
Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
Tunnel adapter Teredo Tunneling Pseudo-Interface:Connection-specific DNS Suffix .
:Description . . . . . . . . . . . : Teredo Tunneling Pseudo-InterfacePhysical Address. . . . . . . . . : 00-
00-00-00-00-00-00-E0DHCP
Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : YesIPv6 Address. . . . . . . . . . . :
2001:0:4137:9e76:457:1c5f:8a19:fe0b(Preferred)Link-local IPv6 Address . . . . . :
fe80::457:1c5f:8a19:fe0b%16(Preferred)Default Gateway . . . . . . . . . :NetBIOS over Tcpip. . . . .
. . . : DisabledTunnel adapter 6TO4 Adapter:
Connection-specific DNS Suffix . :Description . . . . . . . . . . . : Microsoft 6to4 Adapter
#2Physical Address. . . . . . . . . : 00-00-00-00-00-00-00-E0DHCP Enabled. . . . . . . . . . . : No
Autoconfiguration Enabled . . . . : Yes
IPv6 Address. . . . . . . . . . . : 2002:75e6:1f4::75e6:1f4(Preferred)
Default Gateway . . . . . . . . . : 2002:c058:6301::c058:6301
DNS Servers . . . . . . . . . . . : 218.248.241.3218.248.240.180
NetBIOS over Tcpip. . . . . . . . : Disabled

19
Bansilal Ramnath Agarwal Charitable Trust’s
Vishwakarma Institute of Information Technology, Pune-48
(An Autonomous Institute affiliated to Savitribai Phule Pune University)

OUTPUT :

TO TEST LAN CONNECTION :


C:\Users\Sam>ping msec.org.inPinging msec.org.in [69.167.190.8] with 32 bytes of data:
Reply from 69.167.190.8: bytes=32 time=532ms TTL=114
Reply from 69.167.190.8: bytes=32 time=451ms TTL=114
Reply from 69.167.190.8: bytes=32 time=458ms TTL=114
Reply from 69.167.190.8: bytes=32 time=456ms TTL=114
Ping statistics for 69.167.190.8:Packets: Sent = 4, Received = 4, Lost = 0 (0% loss),Approximate
round trip times in milli-seconds:Minimum = 451ms, Maximum = 532ms, Average = 474ms

20

You might also like