[OSy] Fwd: Re: OS Assignment 4: System Calls

Petr Tůma petr.tuma at d3s.mff.cuni.cz
Wed Dec 12 11:16:31 CET 2018


Forwarding in case it helps other people ... PT


-------- Forwarded Message --------
Subject: Re: OS Assignment 4: System Calls
Date: Tue, 11 Dec 2018 09:38:36 +0100
From: Petr Tůma <petr.tuma at d3s.mff.cuni.cz>
To: Immo Hellwig <immo.hellwig at stud.uni-goettingen.de>

Hi Immo,

>>> 2. Blocking a process:
>>> 
>>> I can't find a systemcall/function in thread.c for that. How I block a process?
>> 
>> I do not see this point in the assignment, can you please point me to what you need ? In general, a
>> thread goes to sleep by calling thread_suspend, is that what you had in mind ?
> *I can't see the syscall thread_suspend. thread_usleep is time limited (not blocked as specifically demanded
> in the task), also there is no syscall to wake the thread up again. So I thought we have to implement it
> over some sort of mutex-locking and that's the point where i am stuck at the moment.*

Can you please point me to the assignment where blocking through this syscall is specifically demanded ? The
only place I see blocking mentioned is in the description of getc, but for that it is better to block on the
kernel side (that is, your userspace implementation of getc should simply do a syscall to sys_getc, and your
kernel implementation of sys_getc should block if there is no character available).

>> The exit function in userspace should completely terminate the executing process, including killing its
>> other threads. This is something the current sys_exit function does not do, so you should I think extend
>> it.
> *    The sys_exit function is not mentioned in the task description. Is that also part of the Ex.Ass.4? The 
> task description only asks for asks for an implementation of "void exit (int retval)", that can be found in 
> thread.c and also focuses only on the user space functions. *

I think I know what you're asking. Let me explain:

- The task is to implement userspace functions, such as getc, putc, or exit, as described in the text. These
functions should be implemented by the librt library, you will find their headers in files such as
user/librt/stdio.h or user/librt/thread.h.

- Most of the userspace functions, however, cannot be implemented without calling the kernel (for example,
since kernel is the only part of your system that can access devices, including keyboard and display (printer),
your getc and putc userspace functions must call the kernel, the same goes for the exit function because it is
the kernel that manages threads and therefore only the kernel can terminate them).

- Usually, the kernel functions called from the userspace functions are named "sys_something". So, when you
look into kernel/exc/syscall.c, you will see that it contains functions such as sys_getc, sys_putc, and also
sys_exit. These are the functions that are intended to be called by the userspace functions such as getc, putc,
or exit.

- To call a kernel function from a userspace function, you need to use the syscall processor instruction. To do
that, use the syscall macro from the user/librt/syscall.h file. You will notice that the file also defines
constants such as SYS_PUTC, SYS_GETC, or SYS_EXIT. These constants represent values that the syscall mechanism
uses to tell the kernel which sys_something function to call.

So in the case of the exit function, the code you are expected to implement goes something like this. First,
you need to find the userspace implementation of the exit function - the signature is in the
user/librt/thread.h file, the body of the function is in the user/librt/thread.c file. When you look at the
body, you will see that it already uses the SYSCALL1 macro to call the system, with the identifier of the
requested function set to SYS_EXIT, and the single argument set to retval. So your work in userspace is pretty
much done.

Next, you should look into the kernel/exc/syscall.c file to see what it does when called with the SYS_EXIT
identifier. You will see that the syscall function in that file uses the identifier as an index to the
syscall_table table, and that table says that for SYS_EXIT, the sys_exit function should be called. That is a
kernel function that happens to already be implemented in the same file.

Finally, when you look at the sys_exit function, you will see that it simply sets the process return value, as
it should, and then calls thread_finish, to terminate the calling thread. This is less than the assignment
requires, you are also supposed to terminate other threads the process might have - so here you should add code
that will terminate the other threads of the same process before calling thread_finish on itself.

A small note. The fact that the userspace getc function should use the SYS_GETC syscall to call the sys_getc
kernel function is simply a sensible design choice. You could implement things in many other ways - but this is
what we expected, so the sources already contain some of the SYS constants and sys functions, often with empty
bodies.

>> The point here is that some functions - in particular malloc and free - should not be implemented simply
>> by making a syscall, which would implement their complete functionality. Instead, for example malloc and
>> free should only need to make a syscall when they allocate pages using vma_map and vma_unmap. Most of the
>> time, malloc and free should work in previously allocated pages and therefore not make a syscall at all.
> *So the methods in stdio.c are excluded from this?*

Well, putc and getc simply have to call the kernel, there is no way around this. So that part is simple.

What is more difficult to decide are the puts and printf functions. Both receive a pointer to a string as their
argument, and printf may also receive a variable number of additional arguments. If you would try to call
kernel and do most of the work in there, then with puts you would have to include some checking whether the
passed address of the string is correct, and with printf you would have to come up with a way to pass a
variable number of arguments, which is something that the syscall macro is not designed to do now.

So with these two functions, it is maybe better to do the iteration over the string (and other things that need
to be done) in userspace, and only call the kernel to do print individual characters (so, printf in userspace
would repeatedly call putc, which would call sys_putc in kernel, and so on).

By the way, doing printf might be a bit tough if you are not familiar with C. Looking at the implementation of
printk in kernel/lib/print.c may help.

>> Looking into syscall.c, I see that the default implementation of sys_getc is empty, so you need to add
>> that. I think the waiting can be done inside the sys_getc implementation in the kernel too.
> *I didn't delete the syscall.obj file in the kernel yet, because i thought we don't have to implement that 
> as well. Do we? Its not mentioned in the task description.*

I think you will have to - but the skeleton is already there, you just need to fill in the function bodies,
which are sometimes very simple (for example putc will be just one line, calling printer_putchar).

> The function doesn't print anything ( what makes debugging really hard). I know its not the actual implementation, but that doesn't work as well, so I tried this simplified approach. the syscall.obj file is not deleted, so kernel side cant be the reason.
> 
> size_t printf (const char *format, ...)
> {
>         SYSCALL1(SYS_PUTSTR,(unative_t) format);
>         return 0;
> }

When you look at the definition of sys_putstr, you will see that it has two arguments - string pointer and
size. You set only one, possibly the other register happens to contain zero, so it prints nothing.

I have tried this, which works:

size_t printf (const char *format, ...)
{
     char c;
     while ((c = *(format ++)) != 0) {
         SYSCALL1 (SYS_PUTC, (unative_t) c);
     }
     return 0;
}

Petr




More information about the NSWI004 mailing list