240 likes | 377 Views
Operating Systems 371-1-1631 Winter Semester 2011. Practical Session 1, System Calls. System Calls. A System Call is an interface between a user application and a service provided by the operating system (or kernel ). These can be roughly grouped into five major categories:
E N D
Operating Systems371-1-1631Winter Semester 2011 Practical Session 1, System Calls
System Calls • A System Callis an interface between a user application and a service provided by the operating system (or kernel). • These can be roughly grouped into five major categories: • Process control (e.g. create/terminate process) • File Management (e.g. read, write) • Device Management (e.g. logically attach a device) • Information Maintenance (e.g. set time or date) • Communications (e.g. send messages)
System Calls - motivation • A process is not supposed to access the kernel. It can’t access the kernel memory or functions. • This is strictly enforced (‘protected mode’) for good reasons: • Can jeopardize other processes running. • Cause physical damage to devices. • Alter system behavior. • The system call mechanism provides a safe mechanism to request specific kernel operations.
System Calls - interface • Calls are usually made with C/C++ library functions: User Application C - Library Kernel System Call getpid() Load arguments,eax _NR_getpid,kenel mode (int 80) Call Sys_Call_table[eax] sys_getpid() return syscall_exit resume_userspace return User-Space Kernel-Space
System Calls – tips • Kernel behavior can be enhanced by altering the system calls themselves: imagine we wish to write a message (or add a log entry) whenever a specific user is opening a file. We can re-write the system call open with our new open function and load it to the kernel (need administrative rights). Now all “open” requests are passed through our function. • We can examine which system calls are made by a program by invoking strace<arguments>.
Process control • Fork • pid_t fork(void); • Fork is used to create a new process. It creates an exact duplicate of the original process. • Once the call is finished, the process and its copy go their separate ways. Subsequent changes to one do not effect the other. • The fork call returns a different value to the original process (parent) and its copy (child): in the child process this value is zero, and in the parent process it is the PID of the child process. • When fork is invoked the parent’s information should be copied to its child – however, this can be wasteful if the child will not need this information (see exec()…). To avoid such situations, Copy On Write (COW) is used.
Copy On Write (COW) • How does Linux manage COW? fork() Parent Process DATA STRUCTURE(task_struct) Child Process DATA STRUCTURE(task_struct) write information RW RO RW protection fault! Copying is expensive. The child process will point to the parent’s pages Well, no other choice but to allocate a new RW copy of each required page
Process control An example: int i = 3472; printf("my process pid is %d\n",getpid()); fork_id=fork(); if (fork_id==0){ i= 6794; printf(“child pid %d, i=%d\n",getpid(),i); } else printf(“parent pid %d, i=%d\n",getpid(),i); return 0; Output:my process pid is 8864 child pid 8865, i=6794 parent pid 8864, i=3472 Program flow: PID = 8864 i = 3472 fork () PID = 8865 fork_id=0i = 6794 fork_id = 8865i=3472
Process control - zombies • When a process ends, the memory and resources associated with it are deallocated. • However, the entry for that process is not removed from its parent process table. • This allows the parent to collect the child’s exit status. • When this data is not collected by the parent the child is called a “zombie”. Such a leak is usually not worrisome in itself, however, it is a good indicator for problems to come.
Process control • Wait • pid_t wait(int *status); • pid_t waitpid(pid_t pid, int *status, int options); • The wait command is used for waiting on child processes whose state changed (the process terminated, for example). • The process calling wait will suspend execution until one of its children (or a specific one) terminates. • Waiting can be done for a specific process, a group of processes or on any arbitrary child with waitpid. • Once the status of a process is collected that process is removed from the process table of the collecting process. • Kernel 2.6.9 and later also introduced waitid(…) which gives finer control.
Process control • exec* • int execv(const char *path, char *const argv[]); • int execvp(const char *file, char *const argv[]); • exec…. • The exec() family of function replaces current process image with a new process image (text, data, bss, stack, etc). • Since no new process is created, PID remains the same. • Exec functions do not return to the calling process unless an error occurred (in which case -1 is returned and errno is set with a special value). • The system call is execve(…)
errno • The <errno.h> header file includes the integer errno variable. • This variable is set by system calls in the event of an error to indicate what went wrong. • errnos value is only relevant when the call returned an error (usually -1). • errno is thread local meaning that setting it in one thread does not affect its value in any other thread. • Code defensively! Use errno often!
Process control – simple shell #define… … int main(int argc, char **argv){ … while(true){ type_prompt(); read_command(command, params); pid=fork(); if (pid<0){ if (errno==EAGAIN) printf(“ERROR can not allocate sufficient memory\n”); coutinue; } if (pid>0) wait(&status); else execvp(command,parmas); }
File management • In POSIX operating systems files are accessed via a file descriptor (Microsoft Windows uses a slightly different object: file handle). • A file descriptor is an integer specifying the index of an entry in the file descriptor table held by each process. • A file descriptor table is held be each process, and contains details of all open files. The following is an example of such a table: • File descriptors can refer to files, directories, sockets and a few more data objects.
File management • Open • int open(const char *pathname, int flags); • int open(const char *pathname, int flags, mode_t mode); • Open returns a file descriptor for a given pathname. • This file descriptor will be used in subsequent system calls (according to the flags and mode) • Flags define the access mode: O_RDONLY (read only), O_WRONLY (write only), O_RDRW (read write). These can be bit-wised or’ed with more creation and status flags such as O_APPEND, O_TRUNC, O_CREAT. • Close • Int close(int fd); • Closes a file descriptor so it no longer refers to a file. • Returns 0 on success or -1 in case of failure (errno is set).
File management • Read • ssize_t read(int fd, void *buf, size_t count); • Attempts to read up tocount bytes from the file descriptor fd, into the buffer buf. • Returns the number of bytes actually read (can be less than requested if read was interrupted by a signal, close to EOF, reading from pipe or terminal). • On error -1 is returned (and errno is set). • Note: The file position advances according to the number of bytes read. • Write • ssize_t write(int fd, const void *buf, size_t count); • Writes up tocount bytes to the file referenced to by fd, from the buffer positioned at buf. • Returns the number of bytes actually wrote, or -1 (and errno) on error.
File management • Dup • int dup(int oldfd); • int dup2(int oldfd, int newfd); • The dup commands create a copy of the file descriptor oldfd. • After a successful dup command is executed the old and new file descriptors may be used interchangeably. • They refer to the same open file descriptions and thus share information such as offset and status. That means that using lseek on one will also affect the other! • They do not share descriptor flags (FD_CLOEXEC). • Dup uses the lowest numbered unused file descriptor, and dup2 uses newfd (closing current newfdif necessary). • Returns the new file descriptor, or -1 in case of an error (and set errno).
Fork – example (1) How many lines of “Hello” will be printed in the following example: int main(int argc, char **argv){ int i; for (i=0; i<10; i++){ fork(); printf(“Hello\n”); } return 0; }
Fork – example (1) How many lines of “Hello” will be printed in the following example: int main(int argc, char **argv){ int i; for (i=0; i<10; i++){ fork(); printf(“Hello\n”); } return 0; } Program flow: Total number of printf calls: i=0 i=1 i=2
Fork – example (2) How many lines of “Hello” will be printed in the following example: int main(int argc, char **argv){ int i; for (i=0; i<10; i++){ printf(“Hello\n”); fork(); } return 0; }
Fork – example (2) How many lines of “Hello” will be printed in the following example: int main(int argc, char **argv){ int i; for (i=0; i<10; i++){ printf(“Hello\n”); fork(); } return 0; } Program flow: Total number of printf calls: i=0 i=1 i=2
Fork – example (3) How many lines of “Hello” will be printed in the following example: int main(int argc, char **argv){ int i; for (i=0; i<10; i++) fork(); printf(“Hello\n”); return 0; }
Fork – example (3) How many lines of “Hello” will be printed in the following example: int main(int argc, char **argv){ int i; for (i=0; i<10; i++) fork(); printf(“Hello\n”); return 0; } Program flow: Total number of printf calls: i=0 i=1 i=2