BEST代写-线上编程学术专家

Best代写-最专业靠谱代写IT | CS | 留学生作业 | 编程代写Java | Python |C/C++ | PHP | Matlab | Assignment Project Homework代写

操作系统代写 | COMP0019 Alternative assessment

操作系统代写 | COMP0019 Alternative assessment

这个作业是调式并查看内存中的程序

COMP0019 Alternative assessment

TURN OVER
WeensyOS process. Suppose the C function prototype for this new WeensyOS system  
call is as follows:  
int debug_get_long(int processid, long *targetaddr, long *result);  
where:  
processid is the WeensyOS process ID of the target process within whose virtual  
address space an 8-byte value should be retrieved;  
targetaddr is the virtual address within the target process’s virtual address space  
from which an 8-byte value should be retrieved;  
result is a pointer to a long in the calling process’s virtual address space, where  
the system call will store the requested value retrieved from the virtual address space  
of the target process;  
the int return value indicates whether the system call has completed successfully  
or with an error.  
Design debug get long() for the WeensyOS kernel. Give your design in C code,  
and comment your code thoroughly, explaining each significant step taken in the code.  
Assume that there is already a symbolic constant SYS DEBUG GET LONG that can be  
used in the relevant switch statement in the WeensyOS kernel source to dispatch to  
your code when an application invokes the debug get long() system call. Provide  
the code for the system call starting with the relevant case statement. (You may find it  
useful to recall how you added support for fork() to the WeensyOS kernel in CW4.)  
Hints: You will need to refamiliarize yourself with how process and kernel page ta-  
bles work in WeensyOS to solve this problem. For full marks, you will need to include  
error-handling code that returns an error and avoids crashing the kernel if the call to  
debug get long() provides an invalidtargetaddr and processid, or an in-  
valid combination of the two.  
We will mark your answer by evaluating the correctness and completeness of your design  
as expressed in code. This is a design exercise and not a programming task: we won’t  
try to compile and run your code, or test it. We will not deduct marks for minor syntax  
errors. Don’t try to compile and run your code as you work on your solution; we are only  
asking you to write out the code to make your design concrete, as a way of demonstrating  
your understanding of virtual memory and how WeensyOS supports it.  
[Question 1: Total 20 marks]  
COMP0019  
2
CONTINUED
Recall the notion of a file pointer in the Linux/UNIX file input/output (I/O) system calls,  
which tracks the current byte offset within a currently open Linux/UNIX file—where in  
the bytes of the file to read or write next, when the next read() or write() system  
call is issued. As discussed in 0019, when a process has a file descriptor for a currently  
open file, modern Linux and UNIX OSes track the file pointer for that file using state held  
in the kernel’s memory.  
Note: throughout this question, we are referring to the UNIX file I/O system calls, such  
as read() and write(), and not the C library’s buffered standard I/O file I/O calls,  
such as fread() and fwrite().  
By contrast, in an early version of UNIX, the various UNIX file I/O system calls main-  
tained the file pointer for a currently open file in user-level memory within the process’s  
virtual address space. When the file pointer advanced (after a read() or write() sys-  
tem call on the file, using the exact same API for these calls as in modern UNIX), the  
kernel would update the file’s file pointer value in the user-level memory of the process  
that called read() or write().  
Note that in all versions of UNIX (the early version discussed above and modern ones),  
output to a terminal always appends to the end of the terminal, regardless of the file  
pointer value for the open file for that terminal. Terminals were originally teletypes, one-  
character-at-time physical printers onto paper, and modern terminals are windows in a  
windowing system; in both cases, each successive write() always adds to the end of  
the terminal’s output. When a shell is interactive (when it accepts commands from the  
user interactively), its output goes to a terminal, and thus interactive shells always append  
their output to the end of the terminal, regardless of the file pointer value for the open file  
for that terminal.  
Suppose you run a fully implemented sh0019 shell (that fully solves 0019 CW5) ac-  
cording to the following scenario:  
You start an interactive command-line sh0019 shell. At its command prompt, you  
run a second instance of your sh0019 shell and have it run the commands in the  
above shell script by typing the command:  
sh0019 -q script > outfile  
a. Suppose you run sh0019 in the above scenario on the aforementioned early version  
of UNIX. Assume that sh0019 compiles and runs on this early version of UNIX,  
but experiences the early UNIX version’s different file pointer implementation de-  
scribed above.  
COMP0019  
3
TURN OVER
On this early version of UNIX that maintains the file pointer in memory within a  
process’s address space, what output results (and in what file)? Justify your answer  
by describing the sequence of process creation, program execution, and file I/O  
system calls that occurs, and how the file pointer influences the output.  
[10 marks]  
b. What output does the same command give (and in what file) when run on a modern  
version of Linux, which maintains file pointers as described in the 0019 lectures and  
in CS:APP/3e? Justify your answer by explaining what is different about modern  
Linux’s implementation of UNIX file I/O operations, and how this implementation  
difference gives rise to the output.  
[10 marks]  
[Question 2: Total 20 marks]  
COMP0019  
4
CONTINUED  
3
. C Compilation and x86-64 Assembly  
Consider the below disassembly of the compiled x86-64 object code for the C function  
mystery(), as output by objdump -S:  
Disassembly of section .text:  
0000000000000000 <mystery>:  
0
1
4
6
8
a:  
c:  
f:  
1:  
4:  
7:  
a:  
c:  
d:  
:
:
:
:
:
55  
pushq %rbp  
48 89 e5  
85 d2  
7e 14  
89 d0  
31 c9  
8b 14 8e  
01 d2  
01 14 8f  
48 ff c1  
48 39 c8  
75 f0  
5d  
movq  
%rsp,%rbp  
testl %edx,%edx upmpff ꢁxꢂeerr apaꢂ0  
jle  
1c <mystery+0x1c>  
movl  
xorl  
movl  
addl  
addl  
incq  
cmpq  
jne  
%edx,%eax  
%ecx,%ecx  
(%rsi,%rcx,4),%edx ꢅrv ꢄefꢆꢇf(gn  
%edx,%edx  
%edx,(%rdi,%rcx,4)  
%rcx  
%rcx,%rax  
c <mystery+0xc>  
eo ꢀxoo00000000…i.i=0  
gꢁꢁꢄ)or ꢁx  
1
1
1
1
1
1
cxꢀ i  
ꢂrrpgigfnfff=ꢃꢃ ꢄgx  
popq  
retq  
%rbp  
c3  
a. How many arguments does the C function mystery() take, and what evidence in  
the disassembly supports this conclusion?  
,,fofꢈvrꢂv e,gꢄip ꢈoꢄifeoꢄꢄef,ꢄꢁf,ꢄꢁd) ꢁd)  
[2 marks]  
b. Below is a skeleton for the C code that when compiled yields the above assembly  
code. In your answers document, write out the skeleton and fill in the missing code  
(where there are blanks in the skeleton) that matches the above assembly code (2  
marks per blank):  
void mystery(___n_t_[_,]_,_n_t__,]_,_og__g_c____________________________)  
{
eft _o  
____ i;  
=
=c  
for (i = 0; i _____; i++)  
[
]f]ꢉ2  
a[i] += ________________________;  
}
[8 marks]  
[Question 3 Total: 10 marks]  
COMP0019  
5
TURN OVER  
4
. Concurrency with pthreads on x86-64  
a. Consider the following C code, which starts four pthreads, each of which increments  
each element of an array of unsigneds ten million times, and uses C11 atomics  
for synchronization. Include files have been omitted in the interest of brevity.  
void *threadfunc(void *arg)  
{
_Atomic unsigned **x = (_Atomic unsigned **) arg;  
for (int i = 0; i != 10000000; i++) {  
for (int j = 0; j < 10; j++) {  
atomic_fetch_add(x[j], 1);  
}
}
}
int main()  
{
pthread_t th[4];  
_
_
Atomic unsigned n[10];  
Atomic unsigned *pn[10];  
for (int i = 0; i < 10; i++) {  
n[i] = ATOMIC_VAR_INIT(0);  
pn[i] = &(n[i]);  
}
for (int i = 0; i < 4; i++) {  
pthread_create(&th[i], NULL, threadfunc, (void *) pn);  
}
for (int i = 0; i != 4; i++) {  
pthread_join(th[i], NULL);  
}
for (int i = 0; i < 10; i++) {  
printf(“%u\n”, n[i]);  
}
}
Rewrite the above code to instead use spin locks for synchronization. You may  
not place a spin lock outside the outer for() loop in threadfunc(), as doing  
so reduces concurrency among threads too much. Subject to that prohibition, you  
must place spin lock(s) so as to ensure correct synchronization, and to minimize the  
overhead of locking.  
Omit include files. Use the spin lock API discussed in 0019 lecture, summarized  
below for your reference. You need not provide code for the implementation of  
spin_lock() or spin_unlock().  
/*  
allocate and initialize spin lock */  
COMP0019  
6
CONTINUED  
atomic_flag spinlock = ATOMIC_FLAG_INIT;  
int spin_lock(atomic_flag *lock); // acquire spin lock  
int spin_unlock(atomic_flag *lock); // release spin lock  
[6 marks]  
b. How does the C compiler enforce atomicity on the x86-64 CPU instructions that  
implement C11 atomic operations?  
[2 marks]  
c. Which version of the code discussed in this exam question requires more C11 atomic  
operations for correct synchronization: the one in the exam question paper, which  
directly invokes C11 atomics, or your answer that uses spin locks? Justify your  
answer by referring to the relevant aspects of these two versions of the code.  
[2 marks]  
[Question 4 Total: 10 marks]  
COMP0019
bestdaixie