Lecture 3
System calls
Lecture
Outline:
System Call Types
There are several types of system calls. Each type solves a specific kind of task:
- Process control
- creating and terminating processes
- loading and executing programs
- getting and setting process attributes
- allocating and freeing memory
- waiting and signaling events
- File management
- creating and deleting files
- opening and closing files
- reading, writing, repositioning
- getting and setting file attributes
- Device management
- requesting and releasing devices
- reading, writing, repositioning
- getting and setting device attributes
- attaching and detaching devices
- Information maintenance
- getting and setting date/time
- getting and setting system data
- getting and setting process, file, or device attributes
- Communications
- create and delete communication connection
- send and receive messages
- transfer status information
- attach and detach remote devices
- Protection
- getting file permissions
- setting file permissions
These tasks will be discussed in upcoming lectures and workshops.
Workshop
Outline
- General idea of system calls
- System calls
open
,close
,read
, andwrite
- System call
sbrk
- System calls in assembly
- System calls in C
Theory
System calls are operations (functions) provided by the operating system kernel, which are available to user applications. They are designed and documented by operating system kernel developers. System calls are typically executed with the help of so-called wrapper functions, which can be conveniently used in user-mode applications (e.g. the glibc library in Linux). System calls allow executing kernel tasks upon user’s requests. Modern operating systems isolate kernel memory from user applications. Therefore, when a user application needs to make a request to the kernel (open a file, create a process, send data to network, etc.), a switch between kernel and user modes is required. This makes system calls much slower than regular function calls. System calls are made in architecture-dependent way, employing specific features of the instruction set architecture. A basic solution for system calls is employing the processor interrupt feature. However, a processor can also provide special instructions for this job. Arguments are passed via registers if they are available, extra arguments are passed via stack. The operating system kernel saves and restores execution state (e.g. registers) when switching between the use and the kernel modes.
System calls in RARS (RISC-V Assembly)
-
open (1024): opens a file with the specified path
Input:
a0
= Null terminated string for the path,a1
= flagsOutput:
a0
= the file descriptor or -1 if an error occurredSupported flags: read-only (0), write-only (1), and write-append (9). The write-only flag creates a file if it does not exist, so it is technically write-create. The write-append flag will start writing at end of an existing file.
-
close (57): closes a file
Input:
a0
= the file descriptor to closeOutput: N/A
-
read (63): reads from a file descriptor into a buffer
Input:
a0
= the file descriptor,a1
= address of the buffer,a2
= maximum length to read.Output:
a0
= the length read or -1 if error. -
write (64): writes to a file from a buffer
Input:
a0
= the file descriptor,a1
= the buffer address,a2
= the length to write.Output:
a0
= the number of characters written. -
sbrk (9): allocates heap memory
Input:
a0
= amount of memory in bytesOutput:
a0
= address to the allocated block
Examples
Writing text to a file:
.data
fout:
.asciz "testout.txt" # filename for output
buffer:
.asciz "The quick brown fox jumps over the lazy dog."
.text
# Open (for writing) a file that does not exist
li a7, 1024 # system call for open file
la a0, fout # output file name
li a1, 1 # Open for writing (flags are 0: read, 1: write)
ecall # open a file (file descriptor returned in a0)
mv s6, a0 # save the file descriptor
# Write to file just opened
li a7, 64 # system call for write to file
mv a0, s6 # file descriptor
la a1, buffer # address of buffer from which to write
li a2, 44 # hardcoded buffer length
ecall # write to file
# Close the file
li a7, 57 # system call for close file
mv a0, s6 # file descriptor to close
ecall # close file
Reading text from a file:
.data
fin:
.asciz "testouts.txt" # filename for input
error:
.asciz "Error: failed to open a file."
buffer:
.space 33
.text
main:
la s0, buffer # address of buffer to which to write
li s1, 32 # buffer size
# Open (for reading) an existing file
li a7, 1024 # system call for open file
la a0, fin # input file name
li a1, 0 # open for reading (flags are 0: read, 1: write)
ecall # open a file (file descriptor returned in a0)
bltz a0, main.error # exit on errord exit
mv s6, a0 # save the file descriptor
main.loop:
# Write to file just opened
li a7, 63 # system call for read from file
mv a0, s6 # file descriptor
mv a1, s0 # address of buffer to which to write
mv a2, s1 # buffer length
ecall # read from a file
bltz a0, main.close # close the file on error and exit
mv t0, a0 # save size read
add t1, s0, a0 # write zero terminator to end of buffer
sb zero, 0(t1) #
# Print the buffer read from a file
li a7, 4
mv a0, s0
ecall
# If bytes read = 32, loop.
beq t0, s1, main.loop
main.close:
# Close the file
li a7, 57 # system call for close file
mv a0, s6 # file descriptor to close
ecall # close file
main.exit:
li a7, 10
ecall
main.error:
li a7, 4
la a0, error
ecall
Allocating memory in the heap:
.macro new_line
li a7, 11
li a0, '\n'
ecall
.end_macro
.macro sbrk(%bytes)
li a7, 9
li a0, %bytes
ecall
.end_macro
.text
# Allocates 16 bytes in the heap
sbrk(16)
li a7, 34
ecall
new_line
# Allocates 32 bytes in the heap
sbrk(32)
li a7, 34
ecall
new_line
# Allocates 64 bytes in the heap
sbrk(64)
li a7, 34
ecall
new_line
System calls in Linux API (C language)
Linux provides the following facilities to execute system calls:
-
POSIX functions that are mapped directly to Linux system calls. For example,
open()
,read()
,write()
, etc. -
glibc functions which are operating-system independent wrappers around system calls. For example,
fopen()
,scanf()
,printf()
, etc. -
syscall()
- special glibc function to perform an indirect system call by number.
API-functions that perform system calls
Functions that are called in user program to execute system calls are defined in special header files. To make them available, a corresponding header must be included into the program source code and then a function can be called.
- The “open” function from POSIX:
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *path, int oflag, ...);
- The
fopen
function from glibc:
#include <stdio.h>
FILE *fopen(const char *pathname, const char *mode);
- The “syscall” function:
#include <unistd.h>
#include <sys/syscall.h> /* For SYS_xxx definitions */
long syscall(long number, ...);
In Ubuntu 20.04 LTS, more system call declarations can be found in the following file:
/usr/src/linux-headers-5.4.0-53/include/linux/syscalls.h
.
Examples
Example 1: Using the printf
glibc function:
#include <stdio.h>
int main () {
printf("Hello World\n");
return 0;
}
Example 2: Using the write
POSIX function:
#include <fcntl.h>
#include <unistd.h>
int main () {
write (1, "Hello World\n", 12);
return 0;
}
Example 3: Using the syscall
function:
#include <unistd.h>
#include <sys/syscall.h>
int main () {
syscall (1, 1, "Hello World\n", 12);
return 0;
}
All the three examples, do the same: they print the “Hello World” message to the console. To compile and run them, the following commands need to be executed:
acos@acos-vm:~$ gcc test.c –o test
acos@acos-vm:~$ ./test
Tasks
System calls in RARS (RISC-V Assembly):
- Write a program that creates a copy of the specified file. Input arguments:
- The name of the source and target files are read from the standard input (use system call 8).
- The buffer to store data being copied is allocated in the heap (use system call 9). The buffer size is specified in standard input.
- Buffers for storing source and target names are also allocated in the heap (their size is 256 bytes).
System calls in C:
-
Read documentation on the read and write system calls. Note descriptors standard numbers for
stdin
,stdout
, andstderr
. Write a program that reads chars (note the&c
notation) fromstdin
, increments them by 1, and writes them tostdout
. Do not forget to include all the headers mentioned in the manual. To closestdin
from the terminal, use the^D
key combination (it is not passed to program, but interpreted by operating system as end of output). -
Read documentation on the open system call. Take notice of flags, which are used to indicate how the file is opened. Flags are bits and can be combined with bitwise OR (
|
). TheO_RDONLY
flag is used to open a file for reading. TheO_WRONLY|O_CREAT|O_TRUNC
combination is used for open a file for writing. The mode parameter is required when creating the file. It specifies file access rights. For example, theS_IRUSR
flag means that user has read permission, theS_IRGRP
means that group has read permission, theS_IROTH
flag means others have read permission. Write a program that reads 100 words fromstdin
and writes them to a file named outfile. Do not forget to close the file. -
Modify the previous program to accept command-line arguments (argc/argv). Pass via command-line arguments the number of words (use sscanf to get an integer from argv[1] and the name of output file (argv[2]).
Homework
TODO
References
- System call (Wikipedia)
- System call open (Wikipedia)
- System call close (Wikipedia)
- System call read (Wikipedia)
- System call write (Wikipedia)
- System call sbrk (Wikipedia)
- The GNU C Library (glibc)
- POSIX (Wikipedia)
- C POSIX library (Wikipedia)