1. Systems Calls
In an operating system, a process runs in two different modes; kernel mode and user mode, denoted by Kmode and Umode for short. While in Umode, a process has very limited privileges. It can not do anything that requires special privileges. Privileged operations must be done in Kmode. System call, or syscall for short, is a mechanism which allows a process to enter Kmode to perform operations not allowed in Umode. Operations such as fork child process, exec to change execution image, even termination must all be done in Kernel. In this chapter, we shall discuss syscalls for file operations in Unix/Linux.
2. System Call Man Pages
In Unix and most versions of Linux, the online manual (man) pages are maintained in the /usr/man/ directory (Goldt et al. 1995; Kerrisk 2010, 2017). In Ubuntu Linux, they are in the /usr/share/man directory. All the syscall man pages are listed in the man2 subdirectory. The sh command man 2 NAME displays the man pages of the syscall NAME. For example,
man 2 stat : display man pages of stat(), fstat() and lstat() syscalls
man 2 open: display man pages of open() syscall
man 2 read: display man pages of read() syscall, etc.
Many syscalls require specific included header files, which are listed in the SYNOPSIS part of the man pages. Without proper header files, the C compiler may generate many warnings due to mismatches in syscall function name types. Some syscalls may also require specific data structures as parameters, which must be present as described in the man pages.
3. System Calls for File Operations
Syscalls must be issued from a program. Their usage is just like ordinary function calls. Each syscall is a library function, which assembles the syscall parameters and ultimately issues a syscall to the OS kernel.
int syscall(int a, int b, int c, int d);
where the first parameter a is the syscall number, and b, c, d are parameters to the corresponding kernel function. In Intel x86 based Linux, syscalls are implemented by the INT 0x80 assembly instruction, which causes the CPU to switch from User mode to Kernel mode. The kernel’s syscall handler routes the call to a corresponding kernel function based on the syscall number. When the process finishes executing the kernel function, it returns to User mode with the desired results. A return value >=0 means SUCCESS, -1 means FAILED. In case of failure, an errno variable (in errno.h) records the error number, which can be mapped to a string describing the error reason. The following example shows how to use some of simple syscalls.
Example C8.1. mkdir, chdir, getcwd, syscalls.
/************ C8.1.c file ************/
#include <stdio.h>
#include <errno.h> int main()
{
char buf[256], *s;
int r;
r = mkdir(“newdir”, 0766); // mkdir syscall
if (r < 0)
printf(“errno=%d : %s\n”, errno, strerror(errno));
r = chdir(“newdir”); // cd into newdir
s = getcwd(buf, 256); // get CWD string into buf[ ]
printf(“CWD = %s\n”, s);
}
The program issues a mkdir() syscall to make a new directory. The mkdir() syscall requires a pathname and a permission (0766 in octal). If newdir does not exist, the syscall would succeed with a 0 return value. If we run the program more than once, it should fail on the second or any successive runs with a return value -1 since the directory already exists. In that case, the program would print the message
errno=17 : File exists
In addition to mkdir(), the program also illustrates the usage of chdir() and getcwd() syscalls.
EXERCISE: Modify the C8.1 program to make many directories in one run, e.g.
mymkdir dirl dir2 dir3, … dirn
Hint: write main() as main(int argc, char *argv[ ])
Simple System Calls: The following lists some of the simple syscalls for file operations. The reader is encouraged to write C programs to use and test them.
access : check user permissions for a file.
int access(char *pathname, int mode);
chdir : change directory
int chdir(const char *path);
chmod : change permissions of a file
int chmod(char *path, mode_t mode);
chown : change owner of file
int chown(char *name, int uid, int gid);
chroot : change (logical) root directory to pathname
int chroot(char *pathname);
getcwd : get absolute pathname of CWD
char *getcwd(char *buf, int size);
mkdir : create a directory
int mkdir(char *pathname, mode t mode);
rmdir : remove a directory (must be empty)
int rmdir(char *pathname);
link : hard link new filename to old filename
int link(char *oldpath, char *newpath);
unlink : decrement file’s link count; delete file if link count reaches 0
int unlink(char *pathname);
symlink : create a symbolic link for a file
int symlink(char *oldpath, char *newpath);
rename : change the name of a file
int rename(char *oldpath, char *newpath);
utime : change access and modification times of file
int utime(char *pathname, struct utimebuf *time)
The following syscalls require superuser privilege.
mount : attach a file system to a mounting point directory
int mount(char *specialfile, char *mountDir);
umount : detach a mounted file system
int umount(char *dir);
mknod : make special files
int mknod(char *path, int mode, int device);
Source: Wang K.C. (2018), Systems Programming in Unix/Linux, Springer; 1st ed. 2018 edition.