The vi editor - Georgia State University

Download Report

Transcript The vi editor - Georgia State University

SYSTEMS PROGRAMMING
SYSTEMS PROGRAMMING
 System
calls is a programmer’s functional
interface to the UNIX kernel.
1.
2.
3.
File management
Process management
Error handling
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>
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!
ERROR HANDLING: PERROR()
 errno=2
 main:
No such file or
directory
 errno=2
 main: No such file or
directory
 main: Error 0
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 (directoryspecific 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.
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
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.
File

FILE MANAGEMENT SYSTEM CALLS
 Manipulates
regular, directory and special files.
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.
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);
}
… }
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);
}
… }}
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)
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.
 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);
}
…
}
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.
 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){
perror ("reverse: "); exit (1);}
}
}
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.
 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);
}
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)
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);
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);
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
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
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.
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
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)
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.
#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; }
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
4000 and 2000, respectively.
int main() {
int flag;
flag = chmod("test.txt",0600);
if (flag == -1)
perror("problem setting mode");
}
PROCESS MANAGEMENT
 Process
is a unique instance of a running or a
runnable program.
 Every process in UNIX has
 code
 data
 a stack
1. Parameters
2. the address of the calling programs
3. the return address, e. g. where to go when the
function returns
4. The automatic (local) variables
 a unique process ID (PID)
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
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
Child process PID=35
running shell
PID 34 running shell,
waiting for a child
exec()
wait()
PID 34 running shell,
awakens
fork()
PID=35 running utility
signal
exit()
PID=35 terminates
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.
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

ORPHAN PROCESS: PPID BECOMES 1
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 0-255.
 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.

EXAMPLE
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
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.

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
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.
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
EXAMPLE
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
EXAMPLE