[CSE] . COMP3231/9201/3891/9283 Operating Systems 2009/S1 [UNSW]

Tutorial Week 3

Questions and Answers

System Calls

Q1: Why must kernel programmers be especially careful when implementing system calls?

System calls with poor argument checking or implementation can result in a malicious or buggy program crashing, or taking-over the machine.

Q2: Why is recursion or large arrays of local variables avoided by kernel programmers?

The kernel stack is usually a limited resource. A stack overflow crashes the entire machine.

Q3: How does the 'C' function calling convention relate to the system call interface between the application and the kernel? At minimum, what additional information is required beyond that passed to the system-call wrapper function?

The 'C' function calling convention must always be preserved after any system-call wrapper function completes. The preservation of preserved registers (e.g., s0-s8, ra) can be done in the wrapper function itself or by the kernel (the latter case is what's done in OS/161).

The interface between the system-call wrapper function and the kernel can be defined to provide additional information beyond that passed to the wrapper function. At minimum, the wrapper function must add the system call number to the arguments passed to the wrapper function. It's usually added by set an agreed-to register to the value of the system call number.

Q4: In the example given in lectures, the library procedure read invoked the read system call. Is it essential that both have the same name? If not, which name is important?

System calls do not really have names, other than in a documentation sense. When the library procedure read traps to the kernel, it puts the number of the system call in a register or on the stack. This number is used to index into a table. There is really no name used anywhere. On the other hand, the name of the library procedure is very important, since that is what appears in the program.

Q5: To a programmer, a system call looks like any other call to a library procedure. Is it important that a programmer know which library procedures result in system calls? Under what circumstances and why?

As far as program logic is concerned it does not matter whether a call to a library procedure results in a system call. But if performance is an issue, if a task can be accomplished without a system call the program will run faster. Every system call involves overhead time in switching from the user context to the kernel context. Furthermore, on a multiuser system the operating system may schedule another process to run when a system call completes, further slowing the progress in real time of a calling process.

Processes and Threads

Q6: In the three-state process model, what do each of the three states signify? What transitions are possible between each of the states, and what causes a process (or thread) to undertake such a transition?

The three states are: Running , the process is currently being executed on the CPU; Ready, the process is ready to execute, but has not yet been selected for execution by the dispatcher; and Blocked where the process is not runnable as it is waiting for some event prior to continuing execution.

Possible transitions are Running to Ready, Ready to Running, Running to Blocked, and Blocked to Ready.

Events that cause transitions:

  • Running to Ready: timeslice expired, yield, or higher priority process becomes ready.
  • Ready to Running: Dispatcher chose the next thread to run.
  • Running to Blocked: A requested resource (file, disk block, printer, mutex) is unavailable, so the process is blocked waiting for the resource to become available.
  • Blocked to Ready: a resource has become available, so all processes blocked waiting for the resource now become ready to continue execution.


Q7: The following segment of code is similar (but much simpler) to the main task that the daemon inetd performs. It accepts connections on a socket and forks a process to handle the connection.

This is not guaranteed to be compilable. Use the man command if you want to investigate what all the system calls are doing.

0001 xxx(int socket){
0002 
0003        while ((fd = accept(socket, NULL, NULL)) >= 0) {
0004                switch((pid = fork())) {
0005                case -1:
0006                        syslog(LOG_WARN, "%s cannot create process: %s",
0007				progname, sys_error(errno));
0008                        continue;
0009                case 0:         
0010                        close(0);
0011                        close(1);
0012                        dup(fd);
0013                        dup(fd);
0014                        execl("/usr/sbin/handle_connection", 
0015				"handle_connection", NULL);
0016                        syslog(LOG_WARN, "%s cannot exec handle_connection\ 
0017				helper : %s", progname, sys_error(errno));
0018                        _exit(0);
0019                default: 
0020                        waitpid(pid, &status, 0); 
0021                        if (WIFEXITED(status) && WIFEXITSTATUS(status) == 0)
0022                                continue;
0023                        syslog(LOG_WARN, "handle_connection failed:\
0024				exit status +%d\n", status);
0025                }
0026        }
0027 }

  • Identify which lines of code are executed by the parent process.
  • Identify which lines of code are invoked by the child process.
  • Under what circumstances does the child terminate?

The parent process can execute 0001 - 0004 (fork()), 0005 - 0008 (if the fork() fails) whereupon it continues to wait for a connection on the socket, and 0019 - 0026 if the fork() succeeds. By calling waitpid(), the parent suspends execution until the child terminates. It exits the while loop when the accept() fails (see "man exec" for failure modes). The child returns at line 004, and then executes 0009 - 0015, only executing 0016 - 0018 if the execl() fails. The child terminates when the program /usr/sbin/handle_connection terminates (assuming the exec was successful), but otherwise if the exec fails.

Q8: A web server is constructed such that it is multithreaded. If the only way to read from a file is a normal blocking read system call, do you think user-level threads or kernel-level threads are being used for the web server? Why?

A worker thread within the web server will block when it has to read a Web page from the disk. If user-level threads are being used, this action will block the entire process, destroying the value of multithreading. Thus it is essential that kernel threads are used to permit some threads to block without affecting the others.

Q9: Compare reading a file using a single-threaded file server and a multithreaded file server. Within the file server, it takes 15 msec to get a request for work and do all the necessary processing, assuming the required block is in the main memory disk block cache. A disk operation is required for one third of the requests, which takes an additional 75 msec during which the thread sleeps. How many requests/sec can a server handled if it is single threaded? If it is multithreaded?

In the single-threaded case, the cache hits take 15 msec and cache misses take 90 msec. The weighted average is 2/3 × 15 + 1/3 × 90. Thus the mean request takes 40 msec and the server can do 25 per second. For a multithreaded server, all the waiting for the disk is overlapped, so every request takes 15 msec, and the server can handle 66 2/3 requests per second.


Page last modified: 10:06pm on Friday, 20th of March, 2009

Print Version

CRICOS Provider Number: 00098G