Download presentation
Presentation is loading. Please wait.
1
Systems Programming
2
Systems Programming System calls is a programmer’s functional interface to the UNIX kernel. File management Process management Error handling
3
Error Handling: errno Most system calls are capable of failing.
It if happens, system call returns a value of -1. -1 gives no clue why the error occurred. Each process has a global variable errno that holds the numeric code of the last system-call error. (Initially, it 0). “/usr/include/sys/errno.h” #define EPERM 1 /*Operation not permitted*/ #define ENOENT 2 /*No such file or directory*/ #define EIO 5 /* I/O error */ #define EMFILE 24 /* Too many open files */ #include <errno.h>
4
Error Handling: perror()
void perror(char *str) Displays str : description of the last system call error. No error: “Error 0”. errno should be manually reset to 0 after system call failure!
5
Error Handling: perror()
errno=2 main: No such file or directory main: Error 0
6
Unix File System Unix files are organized by hierarchy of labels commonly known as directory structure. Regular files: sequence of bytes that generally corresponds to code or data. Directory files: stored in a special format and form the backbone of the file system (directory- specific system calls). Special files correspond to peripherals, such as printers, disks, pipes and sockets. File 0 byte A UNIX file is a linear sequence of bytes.
8
File Descriptor File descriptor is a non-negative integer returned by open() or creat(), it is used in subsequent I/0 system calls on the file. File descriptors are numbered subsequently. By convention, 0 – stdin, 1 – stdout, 2 – strerr. Each descriptor has its private set of properties: a file pointer (offset within file; is changed by read/write/lseek) flag indicating if the file descriptor File 0 byte should automatically be closed if the process execs flag indicating if all of the output to the file should be appended to the end of file.
9
File Management System Calls
Manipulates regular, directory and special files.
11
Opening a File: open() int open(char* fileName, int mode[, int perm])
fileName: absolute or relative pathname mode(|): O_RDONLY, O_WRONLY, O_RDWR O_APPEND: file pointer is at the end of the file before each write() O_CREAT: if file doesn’t exist, create it with process’s effective UID as owner ID. O_EXCL: if O_CREAT and file exists, then open() fails. O_NONBLOCK: pipes O_TRUNC: if file exists, it is truncated to length 0. perm: permission (octal number) of created file . Returns lowest unopened nonnegative file descriptor, or -1 if fails.
12
Creating a File #include<fcntl.h> #include<stdio.h>
#include<stdlib.h> void main(){ int fd = open (“reverse.c”, O_CREAT | O_RDWR, 0600); if (fd == -1) { perror ("reverse: "); exit (1); } … }
13
Opening an Existing File
#include<fcntl.h> #include<stdio.h> #include<stdlib.h> void main(int argc,char *argv[]){ if (argc>1) { int fd = open (argv[1], O_RDONLY); if (fd == -1) { perror ("reverse: "); exit (1); } … }}
14
Creating a File: creat()
int creat(char* fileName, mode_t perm) open(fileName, O_CREAT|O_WRONLY|O_TRUNC,perm) <sys/stat.h> S_IRUSR: owner read permit S_IWUSR: owner write permit S_IXUSR: owner execute permit S_IRGRP: group read permit S_IWGRP: group write permit S_IROTH: others read permit S_IWOTH: owners write permit S_IXOTH: others execute permit open(“myfile”, O_CREAT, S_IRUSR | S_IXOTH)
15
Reading from a Regular File: read()
size_t read(int fd, void* buf, size_t count) Copies up to count bytes from the file referenced by fd into the buffer buf. Updates current file position. If successful, returns number of bytes that it read; otherwise, -1. If it was attempted after the last byte has already been read, returns 0 (EOF). Low-level input: no formatting capabilities of scanf. Faster than scanf.
16
Assume, fd points to some file.
int charsRead; char buffer [4096]; while (1) { charsRead = read (fd, buffer, 4096); if (charsRead == 0) break; /* EOF */ if (charsRead == -1) { perror ("reverse: "); exit (1); } …
17
Writing to a Regular File: write()
size_t write(int fd, void* buf, size_t count) Copies up to count bytes from the buffer buf to the file referenced by fd. If O_APPEND was set for fd, file position is set to the end of the file before each write. Updates current file position. If successful, returns number of bytes that were written; otherwise, -1. If returned number is less than count, then the disk probably filled up. Low-level output: no formatting capabilities.
18
Assume, fd and out are two file descriptors.
int charsRead, charsWritten; char buffer [4096]; while (1) { charsRead = read (fd, buffer, 4096); if (charsRead == 0) break; /* EOF */ if (charsRead == -1) { perror ("reverse: "); exit (1);} charsWritten=write(out,buffer,charsRead); if (charsWritten!=charsRead){ }
19
Moving in a File: lseek()
off_t lseek(int fd, off_t offset, int mode) Changes current file position in the file referenced by fd. offset: long integer mode describes how to interpret the offset. <stdio.h> or <unistd.h>: offset is relative to the beginning of the file (SEEK_SET), or to the current file position (SEEK_CUR), or to the end (SEEK_END) Fails if you try to move before the start of the file. Returns current file position, or -1 if fails.
20
lseek (fd, lines[i], SEEK_SET); charsRead = read (fd, buffer,
Assume, fd is a file descriptor, lines[] is an array that holds starting positions of each line in the file. for (i = 0; i <10; i--) { int charsRead; char buffer [4096]; lseek (fd, lines[i], SEEK_SET); charsRead = read (fd, buffer, lines[i+1] - lines[i]); write (1, buffer, charsRead); }
21
Moving in a File: lseek()
Find a current location in the file referenced by fd: lseek (fd, 0, SEEK_CUR); If you move past the end of file and perform write(), Unix automatically extends the size of the file and treats intermediate area as NULLS (0) Unix does not allocate disk area for intermediate space!! (so called “sparse” files)
23
Closing a File: close()
int close(int fd) Closing a file descriptor releases any record locks on the file. All open files are automatically closed by the kernel when a process terminates. Returns 0, or -1 if fails. close(fd);
24
Deleting a File: unlink()
int unlink(const char* fileName) Removes the hard link from the name fileName to its file. If it is the last link to the file, the file’s resources are deallocated. If any process’s file descriptors are currently associated with the file, the file is removed only after all of its file descriptors are closed. So executable can unlink itself during execution and still continue to completion. Returns 0 or -1, if fails. unlink(tmpfd);
25
Obtaining File Information: stat()
int stat(const char* name, struct stat* buf) int fstat(int fd, struct stat* buf)int lstat(const char* name, struct stat* buf) Fills the buffer buf with information about file name lstat() : returns information about the symbolic link itself Returns 0 or -1, if fails
26
stat Structure “/usr/include/sys/stat.h” st_dev the device number
st_ino the inode number st_mode the permission flags st_uid the user ID st_gid the group ID st_size the file size st_atime the last access time st_mtime the last modification time st_ctime the last status change time
27
Macros in /usr/include/sys/stat.h
S_ISDIR(st_mode) true if directory S_ISCHR(st_mode) true if file is a character special device S_ISBLK(st_mode) true if file is a block special device S_ISREG(st_mode) true if a regular file S_ISFIFO(st_mode) true if a pipe The time fields may be decoded with the standard C library asctime() and localtime() subroutines.
29
Reading Directory Information: getdents()
int getdents(int fd, struct dirent* buf, int structSize) Reads the directory file fd from its current position and fills structure buf with the next entry. <dirent.h>: struct dirent { long d_ino; /* inode number */ off_t d_off; /* offset to next dirent */ unsigned short d_reclen; /*length of the dirent */ char d_name [NAME_MAX+1]; /*filename+’\0’*/ } Returns the length of directory if successful, 0 if the last entry has already been read, and -1 if fails
31
Directory Manipulation: opendir()
DIR * opendir (const char *dirname) Opens and returns a directory stream of directory dirname, or NULL, if fails. struct dirent: information about directory entries. char d_name[] is the file name+’\0’. ino_t d_fileno is the file serial number. unsigned char d_namlen is the length of the file name. unsigned char d_type is the type of the file: DT_UNKNOWN, DT_REG (regular file),DT_DIR (directory), DT_FIFO (named pipe), DT_SOCK (local-domain socket), DT_CHR (character device), DT_BLK (block device)
32
Directory Manipulation: readdir() and closedir()
struct dirent * readdir (DIR *dirstream) Reads the next entry from the directory. Returns a pointer to a structure containing information about the file. This structure is statically allocated and can be rewritten by a subsequent call. If there are no more entries or an error is detected, returns a null pointer. int closedir (DIR *dirstream) Closes the directory stream dirstream. Returns 0 or -1 if fails.
33
#include <stddef. h> #include <stdio
#include <stddef.h> #include <stdio.h> #include <sys/types.h> #include <dirent.h> int main (void){ DIR *dp= opendir ("./"); struct dirent *ep; if (dp != NULL){ while (ep = readdir (dp)) puts (ep->d_name); (void) closedir (dp); } else perror ("Couldn't open"); return 0; }
34
Changing File’s Permissions: chmod()
int chmod (const char* fileName, int mode) int fchmod (int fd, int mode) Changes the mode of fileName to mode (specified as octal) Set user ID and set group ID flags have values and 2000, respectively. int main() { int flag; flag = chmod("test.txt",0600); if (flag == -1) perror("problem setting mode"); }
35
Process Management Process is a unique instance of a running or a runnable program. Every process in UNIX has code data a stack Parameters the address of the calling programs the return address, e. g. where to go when the function returns The automatic (local) variables a unique process ID (PID)
36
Init Process When UNIX starts (boots), there is only one process, called init, with PID = 1. The only way to create a new process is to duplicate an existing process. So init is the ancestor of all subsequent processes. Initially, init duplicates several times and each child process replaces its code with the code of the executable getty which is responsible for user logins. init PID =1 Child getty PID=4 Child getty PID =5
38
Parent and Child Processes
It is common for a parent process to suspend itself until one of its child processes terminates. For example, execution in the foreground: Parent process PID 34 running shell fork() Child process PID=35 running shell PID 34 running shell, waiting for a child exec() wait() PID=35 running utility exit() signal PID 34 running shell, awakens PID=35 terminates
40
Creating a New Process: fork()
pit_t fork(void) Causes a process to duplicate The child process inherits a copy of its parent's code, data, stack, open file descriptors, and signal tables The only difference is in the PID and parent process ID (PPID) If fork() succeeds, it returns the PID of the child to the parent process and 0 to the child process. If fork() fails, it returns a -1 to the parent process and the child process is not created.
43
Getting PID and PPID: getpid() and getppid()
pid_t getpid(void) Returns the PID pid_t getppid(void) Returns the PPID They always succeed The PPID value for process with PID=1 is 1
45
Orphan Process: PPID becomes 1
46
Terminating a Process: exit()
void exit(int status) Closes all of a process' file descriptors, deallocates its code, data, and stack; then terminates the process. When a child process terminates, it sends its parent a SIGCHLD signal and waits for its termination code (status) to be accepted. Only lower 8 bits of status are used; so value is A process which is waiting for its parent process to accept its return code is called a zombie process. A parent accepts a child's termination code by executing wait(). The kernel ensures that all children of terminating process are adopted by init init always accepts its children termination codes.
47
Example
48
Zombie Processes A process cannot leave the system until parent process accepts its termination code If parent process is dead; init adopts process and accepts code If the parent process is alive but is unwilling to accept the child's termination code (never executes wait()), the child process will remain a zombie process. Zombie processes do not take up system resources: No data, code, stack But use an entry in the system's fixed-size process table
50
Waiting for a Child: wait()
pid_t wait(int *status) Causes a process to suspend until one of its child processes terminates. A successful call returns the PID of the terminated child process, places its status code into status: If the rightmost byte is zero, the leftmost byte contains the low 8 bits of the value returned by the child's exit() or return() call Otherwise, the rightmost 7 bits are Signal Number that caused the child to terminate and the last bit is 1 if the child core is dumped. No children: wait() returns immediately with -1 Has zombies: wait() returns immediately with the status of one of the zombies.
53
Differentiating a Process: exec family
With the exec family, a process replaces its current code data stack PID and PPID stay the same Only the code that the process is executing changes
54
Differentiating a Process: exec family
Use the absolute or relative name of executable: int execl(const char* path, const char* arg0, ...,const char* argn, NULL) int execv(const char* path, const char* argv[ ]) Use $PATH variable to find the executable: int execlp(const char* path, const char* arg0, ...,const char* argn, NULL) int execvp(const char* path, const char* argv[ ]) Replaces the calling process’ code, data and stack by those of the executable whose pathname is in path.
55
Differentiating a Process: exec family
execl() and execlp(): invoke executable with string arguments pointed by arg1,.. argn. arg0 should be the name of the executable itself the list of arguments should terminate with NULL. execv() and execvp(): invoke executable with string arguments pointed by argv[1],.. argv[n] argv[n+1] is NULL argv[0] should be the name of the executable itself. If executable is not found, -1 is returned Otherwise the calling process replaces its code, data, stack with executable’s,starts executing the new code
56
Example
57
Changing Directories: chdir()
Every process has a current working directory which is used when processing a relative pathname A child process inherits the current working directory from its parent int chdir(const char* pathname) Sets a process' current working directory to pathname The process must have execute permission from the directory to succeed Returns -1 if fails Otherwise, 0
58
Example
Similar presentations
© 2024 SlidePlayer.com. Inc.
All rights reserved.