UNIX System Calls PDF
UNIX System Calls PDF
A system call is just what its name implies -- a request for the
operating system to do something on behalf of the user's program. The
system calls are functions used in the kernel itself. To the
programmer, the system call appears as a normal C function call.
However since a system call executes code in the kernel, there must be a
mechanism to change the mode of a process from user mode to kernel mode.
The C compiler uses a predefined library of functions (the C library)
that have the names of the system calls. The library functions
typically invoke an instruction that changes the process execution mode
to kernel mode and causes the kernel to start executing code for system
calls. The instruction that causes the mode change is often referred to
as an "operating system trap" which is a software generated interrupt.
The library routines execute in user mode, but the system call interface
is a special case of an interrupt handler. The library functions pass
the kernel a unique number per system call in a machine dependent way --
either as a parameter to the operating system trap, in a particular
register, or on the stack -- and the kernel thus determines the specific
system call the user is invoking. In handling the operating system
trap, the kernel looks up the system call number in a table to find the
address of the appropriate kernel routine that is the entry point for
the system call and to find the number of parameters the system call
expects. The kernel calculates the (user) address of the first
parameter to the system call by adding (or subtracting, depending on the
direction of stack growth) an offset to the user stack pointer,
corresponding to the number of the parameters to the system call.
Finally, it copies the user parameters to the "u area" and call the
appropriate system call routine. After executing the code for the
system call, the kernel determines whether there was an error. If so,
it adjusts register locations in the saved user register context,
typically setting the "carry" bit for the PS (processor status) register
and copying the error number into register 0 location. If there were no
errors in the execution of the system call, the kernel clears the
"carry" bit in the PS register and copies the appropriate return values
from the system call into the locations for registers 0 and 1 in the
saved user register context. When the kernel returns from the operating
system trap to user mode, it returns to the library instruction after
the trap instruction. The library interprets the return values from the
kernel and returns a value to the user program.
UNIX system calls are used to manage the file system, control processes,
and to provide interprocess communication. The UNIX system interface
consists of about 80 system calls (as UNIX evolves this number will
increase). The following table lists about 40 of the more important
system call:
-1-
http://www.di.uevora.pt/~lmr/syscalls.html 1/57
2017-7-26 UNIX System Calls
[NOTE: The system call interface is that aspect of UNIX that has
changed the most since the inception of the UNIX system. Therefore,
when you write a software tool, you should protect that tool by putting
system calls in other subroutines within your program and then calling
only those subroutines. Should the next version of the UNIX system
change the syntax and semantics of the system calls you've used, you
need only change your interface routines.]
-2-
When a system call discovers and error, it returns -1 and stores the
reason the called failed in an external variable named "errno". The
"/usr/include/errno.h" file maps these error numbers to manifest
constants, and it these constants that you should use in your programs.
When you use system calls in your programs, you should check the value
returned by those system calls. Furthermore, when a system call
discovers an error, you should use the "perror()" subroutine to print a
diagnostic message on the standard error file that describes why the
system call failed. The syntax for "perror()" is:
void perror(string)
char string;
"perror()" displays the argument string, a colon, and then the error
message, as directed by "errno", followed by a newline. The output of
"perror()" is displayed on "standard error". Typically, the argument
give to "perror()" is the name of the program that incurred the error,
argv[0]. However, when using subroutines and system calls on files, the
related file name might be passed to "perror()".
There are occasions where you the programmer might wish to maintain more
control over the printing of error messages than "perror()" provides --
such as with a formatted screen where the newline printed by "perror()"
would destroy the formatting. In this case, you can directly access the
same system external (global) variables that "perror()" uses. They are:
-3-
/* errmsg1.c
print all system error messages using "perror()"
*/
http://www.di.uevora.pt/~lmr/syscalls.html 3/57
2017-7-26 UNIX System Calls
*/
#include <stdio.h>
int main()
{
int i;
extern int errno, sys_nerr;
/* errmsg2.c
print all system error messages using the global error message table.
*/
#include <stdio.h>
int main()
{
int i;
extern int sys_nerr;
extern char *sys_errlist[];
-4-
Following are some examples in the use of the most often used system
calls.
The file structure related system calls available in the UNIX system let
you create, open, and close files, read and write files, randomly access
files, alias and remove files, get information about files, check the
accessibility of files, change protections, owner, and group of files,
and control devices. These operations either use a character string
that defines the absolute or relative path name of a file, or a small
integer called a file descriptor that identifies the I/O channel. A
channel is a connection between a process and a file that appears to the
process as an unformatted stream of bytes. The kernel presents and
accepts data from the channel as a process reads and writes that
channel. To a process then, all input and output operations are
synchronous and unbuffered.
When doing I/O, a process specifies the file descriptor for an I/O
channel, a buffer to be filled or emptied, and the maximum size of data
to be transferred. An I/O channel may allow input, output, or both.
Furthermore, each channel has a read/write pointer. Each I/O operation
starts where the last operation finished and advances the pointer by the
number of bytes transferred. A process can access a channel's data
randomly by changing the read/write pointer.
All input and output operations start by opening a file using either the
"creat()" or "open()" system calls. These calls return a file
descriptor that identifies the I/O channel. Recall that file
descriptors 0, 1, and 2 refer to standard input, standard output, and
standard error files respectively, and that file descriptor 0 is a
channel to your terminal's keyboard and file descriptors 1 and 2 are
channels to your terminal's display screen.
creat()
-5-
/* creat.c */
#include <stdio.h>
#include <sys/types.h> /* defines types used by sys/stat.h */
#include <sys/stat.h> /* defines S_IREAD & S_IWRITE */
int main()
{
int fd;
http://www.di.uevora.pt/~lmr/syscalls.html 5/57
2017-7-26 UNIX System Calls
int fd;
fd = creat("datafile.dat", S_IREAD | S_IWRITE);
if (fd == -1)
printf("Error in opening datafile.dat\n");
else
{
printf("datafile.dat opened for read/write access\n");
printf("datafile.dat is currently empty\n");
}
close(fd);
exit (0);
}
-6-
open()
Next is the open() system call. open() lets you open a file for
reading, writing, or reading and writing.
#include <fcntl.h>
Multiple values are combined using the | operator (i.e. bitwise OR).
Note: some combinations are mutually exclusive such as: O_RDONLY |
O_WRONLY and will cause open() to fail. If the O_CREAT flag is used,
then a mode argument is required. The mode argument may be specified in
the same manner as in the creat() system call.
-7-
/* open.c */
int main()
{
int fd;
char buffer[80];
http://www.di.uevora.pt/~lmr/syscalls.html 7/57
2017-7-26 UNIX System Calls
-8-
close()
To close a channel, use the close() system call. The prototype for the
close() system call is:
int close(file_descriptor)
int file_descriptor;
read() write()
The read() system call does all input and the write() system call does
all output. When used together, they provide all the tools necessary to
do input and output sequentially. When used with the lseek() system
call, they provide all the tools necessary to do input and output
randomly.
Both read() and write() take three arguments. Their prototypes are:
There is no limit on transfer_size, but you must make sure it's safe to
copy transfer_size bytes to or from the memory pointed to by
buffer_pointer. A transfer_size of 1 is used to transfer a byte at a
time for so-called "unbuffered" input/output. The most efficient value
for transfer_size is the size of the largest physical record the I/O
channel is likely to have to handle. Therefore, 1K bytes -- the disk
block size -- is the most efficient general-purpose buffer size for a
standard file. However, if you are writing to a terminal, the transfer
is best handled in lines ending with a newline.
For an example using read() and write(), see the above example of
open().
-9-
lseek()
int file_descriptor;
http://www.di.uevora.pt/~lmr/syscalls.html 9/57
2017-7-26 UNIX System Calls
int file_descriptor;
long offset;
int whence;
where file_descriptor identifies the I/O channel and offset and whence
work together to describe how to change the file pointer according to
the following table:
If successful, lseek() returns a long integer that defines the new file
pointer value measured in bytes from the beginning of the file. If
unsuccessful, the file position does not change.
-10-
/* lseek.c */
#include <stdio.h>
#include <fcntl.h>
int main()
{
int fd;
long position;
fd = open("datafile.dat", O_RDONLY);
if ( fd != -1)
{
position = lseek(fd, 0L, 2); /* seek 0 bytes from end-of-file */
if (position != -1)
printf("The length of datafile.dat is %ld bytes.\n", position);
else
perror("lseek error");
}
else
printf("can't open datafile.dat\n");
close(fd);
http://www.di.uevora.pt/~lmr/syscalls.html 10/57
2017-7-26 UNIX System Calls
close(fd);
}
-11-
Many UNIX systems have defined manifest constants for use as the
"whence" argument of lseek(). The definitions can be found in the
"file.h" and/or "unistd.h" include files. For example, the University
of Maryland's HP-9000 UNIX system has the following definitions:
The definitions from unistd.h are the most "portable" across UNIX and
MS-DOS C compilers.
dup()
The dup() system call duplicates an open file descriptor and returns the
new file descriptor. The new file descriptor has the following
properties in common with the original file descriptor:
http://www.di.uevora.pt/~lmr/syscalls.html 11/57
2017-7-26 UNIX System Calls
has the same file pointer -- that is, both file descriptors share one
file pointer.
has the same access mode, whether read, write, or read and write.
int dup(file_descriptor)
int file_descriptor;
-12-
/* dup.c
demonstrate redirection of standard output to a file.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/stat.h>
int main()
{
int fd;
http://www.di.uevora.pt/~lmr/syscalls.html 12/57
2017-7-26 UNIX System Calls
-13-
link()
The UNIX system file structure allows more than one named reference to a
given file, a feature called "aliasing". Making an alias to a file
means that the file has more than one name, but all names of the file
refer to the same data. Since all names refer to the same data,
changing the contents of one file changes the contents of all aliases to
that file. Aliasing a file in the UNIX system amounts to the system
creating a new directory entry that contains the alias file name and
then copying the i-number of a existing file to the i-number position of
this new directory entry. This action is accomplished by the link()
system call. The link() system call links an existing file to a new
file.
where both original_name and alias_name are character strings that name
the existing and new files respectively. link() will fail and no link
will be created if any of the following conditions holds:
http://www.di.uevora.pt/~lmr/syscalls.html 13/57
2017-7-26 UNIX System Calls
/* link.c
*/
#include <stdio.h>
int main()
{
if ((link("foo.old", "foo.new")) == -1)
{
perror(" ");
exit (1); /* return a non-zero exit code on error */
}
exit(0);
}
-14-
unlink()
The opposite of the link() system call is the unlink() system call.
unlink() removes a file by zeroing the i-number part of the file's
directory entry, reducing the link count field in the file's inode by 1,
and releasing the data blocks and the inode if the link count field
becomes zero. unlink() is the only system call for removing a file in
the UNIX system.
int unlink(file_name)
char *file_name;
/* unlink.c
*/
#include <stdio.h>
int main()
http://www.di.uevora.pt/~lmr/syscalls.html 14/57
2017-7-26 UNIX System Calls
int main()
{
if ((unlink("foo.bar")) == -1)
{
perror(" ");
exit (1); /* return a non-zero exit code on error */
}
exit (0);
}
-15-
exec
The UNIX system provides several system calls to create and end program, to
send and receive software interrupts, to allocate memory, and to do other
useful jobs for a process. Four system calls are provided for creating a
process, ending a process, and waiting for a process to complete. These
system calls are fork(), the "exec" family, wait(), and exit().
The UNIX system calls that transform a executable binary file into a process
are the "exec" family of system calls. The prototypes for these calls are:
Unlike the other system calls and subroutines, a successful exec system call
does not return. Instead, control is given to the executable binary file
named as the first argument. When that file is made into a process, that
process replaces the process that executed the exec system call -- a new
process is not created. If an exec call should fail, it will return a -1.
-16-
p user's PATH is searched for command, and command can be a shell program
execl Takes the path name of an executable program (binary file) as its
first argument. The rest of the arguments are a list of command
line arguments to the new program (argv[]). The list is
terminated with a null pointer:
execle Same as execl(), except that the end of the argument list is
followed by a pointer to a null-terminated list of character
pointers that is passed a the environment of the new program
(i.e., the place that getenv() searches for exported shell
variables):
http://www.di.uevora.pt/~lmr/syscalls.html 16/57
2017-7-26 UNIX System Calls
execv("/bin/cat", args);
-17-
execlp Same as execl(), except that the program name doesn't have to be
a full path name, and it can be a shell program instead of an
executable module:
execvp Same as execv(), except that the program name doesn't have to be
a full path name, and it can be a shell program instead of an
executable module:
execvp("cat", args);
When transforming an executable binary file into a process, the UNIX system
preserves some characteristics of the replaced process. Among the items
saved by the exec system call are:
The last of these is the most interesting because the shell uses this feature
to handle input/output redirection.
-18-
fork()
The exec family of system calls transforms an executable binary file into a
process that overlays the process that made the exec system call. The UNIX
system does not create a new process in response to an exec system call. To
create a new process, you must use the fork() system call. The prototype for
the fork() system call is:
int fork()
fork() causes the UNIX system to create a new process, called the "child
process", with a new process ID. The contents of the child process are
identical to the contents of the parent process.
The new process inherits several characteristics of the old process. Among
the characteristics inherited are:
The environment.
All signal settings.
The set user ID and set group ID status.
The time left until an alarm clock signal.
The current working directory and the root directory.
The file creation mask as established with umask().
The child process begins executing and the parent process continues executing
at the return from the fork() system call. This is difficult to understand
at first because you only call fork() once, yet it returns twice -- once per
process. To differentiate which process is which, fork() returns zero in the
child process and non-zero (the child's process ID) in the parent process.
exec routines are usually called after a call to fork(). This combination,
known as a fork/exec, allows a process to create a child to execute a
command, so that the parent doesn't destroy itself through an exec. Most
command interpreters (e.g. the shell) on UNIX use fork and exec.
wait()
You can control the execution of child processes by calling wait() in the
parent. wait() forces the parent to suspend execution until the child is
finished. wait() returns the process ID of a child process that finished.
If the child finishes before the parent gets around to calling wait(), then
when wait() is called by the parent, it will return immediately with the
child's process ID. (It is possible to have more that one child process by
simply calling fork() more than once.). The prototype for the wait() system
call is:
int wait(status)
int *status;
http://www.di.uevora.pt/~lmr/syscalls.html 18/57
2017-7-26 UNIX System Calls
where status is a pointer to an integer where the UNIX system stores the
value returned by the child process. wait() returns the process ID of the
process that ended. wait() fails if any of the following conditions hold:
-19-
If the process ended by calling the exit() system call, the second lowest
byte of status is set to the argument given to exit() and the lowest byte
of status is set to zeroes.
If the process ended because of a signal, the second lowest byte of status
is set to zeroes and the lowest byte of status contains the signal number
that ended the process. If the seventh bit of the lowest byte of status
is set (i.e. status & 0200 == 0200) then the UNIX system produced a core
dump of the process.
exit()
The exit() system call ends a process and returns a value to it parent. The
prototype for the exit() system call is:
void exit(status)
int status;
where status is an integer between 0 and 255. This number is returned to the
parent via wait() as the exit status of the process. By convention, when a
process exits with a status of zero that means it didn't encounter any
problems; when a process exit with a non-zero status that means it did have
problems.
Following are some example programs that demonstrate the use of fork(),
exec(), wait(), and exit():
/* status.c
demonstrates exit() returning a status to wait().
*/
int main()
{
unsigned int status;
Note: since wait() returns the exit status multiplied by 256 (contained in
the upper 8 bits), the status value is shifted right 8 bits (divided by 256)
to obtain the correct value.
-20-
/* myshell.c
This program is a simple command interpreter that uses execlp() to
execute commands typed in by the user.
*/
#include <stdio.h>
#define EVER ;;
int main()
{
int process;
char line[81];
for (EVER)
{
fprintf(stderr, "cmd: ");
if ( gets (line) == (char *) NULL) /* blank line input */
exit (0);
-21-
http://www.di.uevora.pt/~lmr/syscalls.html 20/57
2017-7-26 UNIX System Calls
/* newdir.c
create a new directory, called newdir, using fork() and exec().
*/
#include <stdio.h>
int main()
{
int fd;
if ( fork() != 0)
wait ((int *) 0);
else
{
execl ("/bin/mkdir", "mkdir", "newdir", (char *) NULL);
fprintf (stderr, "exec failed!\n");
exit (1);
}
-22-
http://www.di.uevora.pt/~lmr/syscalls.html 21/57
2017-7-26 UNIX System Calls
Software Interrupts
signal()
The UNIX system provides a facility for sending and receiving software
interrupts, also called SIGNALS. Signals are sent to a process when a
predefined condition happens. The number of signals available is system
dependent. For example, the University's HP-9000 has 31 signals defined.
The signal name is defined in /usr/include/sys/signal.h as a manifest
constant.
1. Ignore the signal. This means that the program will never be informed of
the signal no matter how many times it occurs. The only exception to this
is the SIGKILL signal which can neither be ignored nor caught.
2. A signal can be set to its default state, which means that the process
will be ended when it receives that signal. In addition, if the process
receives any of SIGQUIT, SIGILL, SIGIOT, SIGEMT, SIGFPE, SIGBUS, SIGSEGV, or
SIGSYS, the UNIX system will produce a core image (core dump), if possible,
in the directory where the process was executing when it received the
program-ending signal.
3. Catch the signal. When the signal occurs, the UNIX system will transfer
control to a previously defined subroutine where it can respond to the signal
as is appropriate for the program.
You define how you want to respond to a signal with the signal() system call.
The prototype is:
#include <sys/signal.h>
where signal_name is the name of the signal from signal.h and function is any
of SIG_IGN, meaning that you wish to ignore the signal when it occurs;
SIG_DFL, meaning that you wish the UNIX system to take the default action
when your program receives the signal; or a pointer to a function that
returns an integer. The function is given control when your program receives
the signal, and the signal number is passed as an argument. signal() returns
the previous value of function, and signal() fails if any of the following
conditions hold:
-23-
http://www.di.uevora.pt/~lmr/syscalls.html 22/57
2017-7-26 UNIX System Calls
Once a signal is caught, the UNIX system resets it to its initial state (the
default condition). In general, if you intend for your program to be able to
catch a signal repeatedly, you need to re-arm the signal handling mechanism.
You must do this as soon after receipt of the signal as possible, namely just
after entering the signal handling routine.
You should use signals in your programs to isolate critical sections from
interruption.
The state of all signals is preserved across a fork() system call, but all
caught signals are set to SIG_DFL across an exec system call.
kill()
The UNIX system sends a signal to a process when something happens, such as
typing the interrupt key on a terminal, or attempting to execute an illegal
instruction. Signals are also sent to a process with the kill() system call.
Its prototype is:
even though the process named by process_id is in the system, you cannot
send it a signal because your effective user ID does not match either the
real or effective user ID of process_id.
-24-
http://www.di.uevora.pt/~lmr/syscalls.html 23/57
2017-7-26 UNIX System Calls
alarm()
Every process has an alarm clock stored in its system-data segment. When the
alarm goes off, signal SIGALRM is sent to the calling process. A child
inherits its parent's alarm clock value, but the actual clock isn't shared.
The alarm clock remains set across an exec.
where seconds defines the time after which the UNIX system sends the SIGALRM
signal to the calling process. Each successive call to alarm() nullifies the
previous call, and alarm() returns the number of seconds until that alarm
would have gone off. If seconds has the value 0, the alarm is canceled.
alarm() has no error conditions.
The following is an example program that demonstrates the use of the signal()
and alarm() system calls:
/* timesup.c */
#include <stdio.h>
#include <sys/signal.h>
#define EVER ;;
void main();
int times_up();
void main()
{
signal (SIGALRM, times_up); /* go to the times_up function */
/* when the alarm goes off. */
alarm (10); /* set the alarm for 10 seconds */
int times_up(sig)
int sig; /* value of signal */
{
printf("Caught signal #< %d >n", sig);
printf("Time's up! I'm outta here!!\n");
exit(sig); /* return the signal number */
}
-25-
Interprocess Communication
UNIX System V allows processes to communicate with one another using pipes,
messages, semaphores, and shared memory. This sections describes how to
communicate using pipes.
One way to communicate between two processes is to create a pipeline with the
pipe() system call. pipe() builds the channel, but it is up to you to
connect the standard input of one process to the standard output of the other
process.
Some I/O system calls act differently on pipe file descriptors from the way
they do on ordinary files, and some do nothing at all. Following is a
summary of these actions:
-26-
close Means more on a pipe than it does on a file. Not only does it
free up the file descriptor for reuse, but when the writing file
descriptor is closed it acts as an end-of-file for the reader.
If the read
http://www.di.uevora.pt/~lmr/syscalls.html end file descriptor is closed, a write() on the other 25/57
2017-7-26 UNIX System Calls
fcntl This system call sets or clears the O_NDELAY flag, whose effect
is described under write and read above.
fstat Not very useful on pipes. The size returned is the number of
bytes in the pipe, but this fact is seldom useful. A pipe may be
distinguished by a link count of 0, since a pipe is the only
source of a file descriptor associated with something not linked
into a directory. This distinction might be useful to I/O
routines that want to treat pipes specially.
lseek Not used with pipes. This means that if a pipe contains a
sequence of messages, it isn't possible to look through them for
the message to read next. Like toothpaste in a tube, you have to
get it out to examine it, and then there is no way to put it
back.
Pipes use the buffer cache just as ordinary files do. Therefore, the
benefits of writing and reading pipes in units of a block (usually 512 or
1024 bytes) are just as great. A single write() execution is atomic, so if
512 bytes are written with a single system call, the corresponding read()
will return with 512 bytes (if it requests that many). It will not return
with less than the full block. However, if the writer is not writing
complete blocks, but the reader is trying to read complete blocks, the reader
may keep getting partial blocks anyway.
-27-
/* who_wc.c */
/* demonstrates a one-way pipe between two processes.
This program implements the equivalent of the shell command:
who | wc -l
#include <stdio.h>
int main()
{
int pid_1, /* will be process id of first child - who */
pid_2, /* will be process id of second child - wc */
pfd[2]; /* pipe file descriptor table. */
-28-
IMMMMMMMMMMMMMMMMMM;
: :
ZDDDDDDDDDDDDDDD: who_wc :DDDDDDDDDDDDD?
3 : : 3
3 HMMMMMMMMMMMMMMMMMM< 3
3 3
IMMMMMMMOMMMMMM; IMMMMMMOMMMMMMM;
: : : :
: who :DDDDDDDDDDDDDDDDDDDDDDDDDDDDDDDD> wc -l :
: : pipe channel : :
HMMMMMMMMMMMMMM< HMMMMMMMQMMMMMM<
3
3
ZDDDDDDADDDDDD?
3 3
3 terminal 3
@DDDDDDDDDDDDDY
-29-
File Status
stat() - fstat()
The i-node data structure holds all the information about a file except the
file's name and its contents. Sometimes your programs need to use the
information in the i-node structure to do some job. You can access this
information with the stat() and fstat() system calls. stat() and fstat()
return the information in the i-node for the file named by a string and by a
file descriptor, respectively. The format for the i-node struct returned by
these system calls is defined in /usr/include/sys/stat.h. stat.h uses types
built with the C language typedef construct and defined in the file
/usr/include/sys/types.h,
http://www.di.uevora.pt/~lmr/syscalls.html so it too must be included and must be included 28/57
2017-7-26 UNIX System Calls
#include <sys/types.h>
#include <sys/stat.h>
where file_name names the file as an ASCII string and file_descriptor names
the I/O channel and therefore the file. Both calls returns the file's
specifics in stat_buf. stat() and fstat() fail if any of the following
conditions hold:
-30-
/* stat.h */
struct stat
{
dev_t st_dev; /* The device number containing the i-node */
ino_t st_ino; /* The i-number */
unsigned short st_mode; /* The 16 bit mode */
short st_nlink; /* The link count; 0 for pipes */
ushort st_uid; /* The owner user-ID */
ushort st_gid; /* The group-ID */
dev_t st_rdev; /* For a special file, the device number */
off_t st_size; /* The size of the file; 0 for special files */
time_t st_atime; /* The access time. */
int st_spare1;
time_t st_mtime; /* The modification time. */
int st_spare2;
http://www.di.uevora.pt/~lmr/syscalls.html 29/57
2017-7-26 UNIX System Calls
int st_spare2;
time_t st_ctime; /* The status-change time. */
int st_spare3;
long st_blksize;
long st_blocks;
uint st_remote:1; /* Set if file is remote */
dev_t st_netdev; /* ID of device containing */
/* network special file */
ino_t st_netino; /* Inode number of network special file */
long st_spare4[9];
};
-31-
/* status.c */
/* demonstrates the use of the stat() system call to determine the
status of a file.
*/
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
int main();
http://www.di.uevora.pt/~lmr/syscalls.html 30/57
2017-7-26 UNIX System Calls
{
int isdevice = FALSE;
struct stat stat_buf;
if (argc != 2)
{
printf("Usage: %s filename\n", argv[0]);
exit (1);
}
if ( stat( argv[1], &stat_buf) == ERR)
{
perror("stat");
exit (1);
}
printf("\nFile: %s status:\n\n",argv[1]);
if ((stat_buf.st_mode & S_IFMT) == S_IFDIR)
printf("Directory\n");
else if ((stat_buf.st_mode & S_IFMT) == S_IFBLK)
{
printf("Block special file\n");
isdevice = TRUE;
}
else if ((stat_buf.st_mode & S_IFMT) == S_IFCHR)
{
printf("Character special file\n");
isdevice = TRUE;
}
else if ((stat_buf.st_mode & S_IFMT) == S_IFREG)
printf("Ordinary file\n");
else if ((stat_buf.st_mode & S_IFMT) == S_IFIFO)
printf("FIFO\n");
-32-
if (isdevice)
printf("Device number:%d, %d\n", (stat_buf.st_rdev > 8) & 0377,
stat_buf.st_rdev & 0377);
printf("Resides on device:%d, %d\n", (stat_buf.st_dev > 8) & 0377,
stat_buf.st_dev & 0377);
printf("I-node: %d; Links: %d; Size: %ld\n", stat_buf.st_ino,
stat_buf.st_nlink, stat_buf.st_size);
if ((stat_buf.st_mode & S_ISUID) == S_ISUID)
printf("Set-user-ID\n");
if ((stat_buf.st_mode & S_ISGID) == S_ISGID)
printf("Set-group-ID\n");
if ((stat_buf.st_mode & S_ISVTX) == S_ISVTX)
printf("Sticky-bit set -- save swapped text after use\n");
printf("Permissions: %o\n", stat_buf.st_mode & 0777);
exit (0);
}
access()
To determine if a file
http://www.di.uevora.pt/~lmr/syscalls.html is accessible to a program, the access() system call 31/57
2017-7-26 UNIX System Calls
where file_name is the name of the file to which access permissions given in
access_mode are to be applied. Access modes are often defined as manifest
constants in /usr/include/sys/file.h. The available modes are:
These values may be ORed together to check for mone than one access
permission. The call to access() returns 0 if the program has the given
access permissions, otherwise -1 is returned and errno is set to the reason
for failure. This call is somewhat useful in that it makes checking for a
specific permission easy. However, it only answers the question "do I have
this permission?" It cannot answer the question "what permissions do I
have?"
-33-
The following example program demonstrates the use of the access() system
call to remove a file. Before removing the file, a check is made to make
sure that the file exits and that it is writable (it will not remove a
read-only file).
/* remove.c */
#include <stdio.h>
#include <sys/file.h>
int main();
if (argc != 2)
{
printf("Usage: %s filename\n", argv[0]);
exit (1);
}
http://www.di.uevora.pt/~lmr/syscalls.html 32/57
2017-7-26 UNIX System Calls
}
if (access (argv[1], F_OK) == ERR) /* check that file exists */
{
perror(argv[1]);
exit (1);
}
if (access (argv[1], W_OK) == ERR) /* check for write permission */
{
fprintf(stderr,"File: %s is write protected!\n", argv[1]);
exit (1);
}
if (unlink (argv[1]) == ERR)
{
perror(argv[1]);
exit (1);
}
exit (0);
}
-34-
Directories
System V Directories
#define DIRSIZ 14
struct direct {
ino_t d_ino;
char d_name[DIRSIZ];
};
It should be noted that the name of the file, d_name is NOT guaranteed to be
null-terminated; programs should always be careful of this. Files which have
been deleted will have i-numbers (d_ino) equal to zero; these should in
general be skipped over when reading the directory. A directory is read
simply by opening it (in read-only mode) and reading structures either one at
a time or all at once. The following example program simply opens the
current directory and prints the names of all the files it contains. The
program simulates the ls -a command. Note that the file names are not sorted
like the real ls command would do.
/* my_ls.c
This program simulates the System V style ls -a command. Filenames
are printed as they occur in the directory -- no sorting is done.
*/
#include <stdio.h>
#include <fcntl.h>
#include <sys/types.h>
#include <sys/dir.h>
int main()
{
int fd;
struct direct dir;
-35-
If you need more information about the file such as size or permissions, you
would use the stat() system call to obtain it.
struct direct {
long d_fileno; /* file number of entry */
short d_reclen; /* length of this record */
short d_namlen;
http://www.di.uevora.pt/~lmr/syscalls.html /* length of string in d_name */ 34/57
2017-7-26 UNIX System Calls
Unlike on System V, filenames can be longer than 14 characters and the size
of a directory structure can be variable. Therefore, the read() call can not
be used to read the directory. Instead, Berkely style systems provide a set
of library functions to read directories. These functions are also declared
in the ndir.h include file. They are:
-36-
The following example shows how to perform a Berkeley (or HP) style ls -a
read of a directory. One important note: filenames in the directory
structure are null-terminated in Berkeley style systems -- on System V they
are not.
#include <stdio.h>
#include <sys/types.h>
#include <ndir.h>
main()
{
DIR *dirp;
struct direct *dp;
For more information, type: man directory while logged onto the
University's HP system.
http://www.di.uevora.pt/~lmr/syscalls.html 35/57
2017-7-26 UNIX System Calls
-37-
Time
The UNIX operating system keeps track of the current date and time by storing
the number of seconds that have elasped since midnight January 1, 1970 UTC
(Coordinated Universal Time, also known as Greenwich Mean Time (GMT)). This
date is considered the informal "birthday" of the UNIX operating system. The
time is stored in a signed long integer. (For the curious, assuming a 32 bit
signed long integer, UNIX time will break at 03:14:08 January 19, 2038 UTC.)
In all versions of UNIX, the time() system call may be used to obtain the
time of day. This call is peculiar in that if given the address of a long
integer as an argument, it places the time in that integer and returns it.
If, however, a null pointer is passed, the time of day is just returned.
Several routines are available to convert the long integer returned by time()
into an ASCII date string. With the UNIX operating system, an ASCII date
string is a string as shown below:
/* my_date.c
print the current date and time in a format similar to the output
of the date command.
*/
#include <stdio.h>
#include <time.h> /* may need to be #include <sys/time.h> instead */
int main()
{
http://www.di.uevora.pt/~lmr/syscalls.html 36/57
2017-7-26 UNIX System Calls
{
long now, time();
char *ctime();
time (&now);
printf("It is now %s\n", ctime (&now));
exit (0);
}
-38-
Often you need access to specific information about the current date and
time. The localtime() and gmtime() functions will provide it. They do this
by converting the long integer returned by time() into a data structure
called tm, which is defined in the time.h header file. In fact, this is what
the header file looks like:
struct tm {
int tm_sec; /* seconds after the minute - [0,59] */
int tm_min; /* minutes after the hour - [0,59] */
int tm_hour; /* hours since midnight - [0,23] */
int tm_mday; /* day of the month - [1,31] */
int tm_mon; /* months since January - [0,11] */
int tm_year; /* years since 1900 */
int tm_wday; /* days since Sunday - [0,6] */
int tm_yday; /* days since January 1 - [0,365] */
int tm_isdst; /* daylight savings time flag */
};
As you can see, there is quite a bit of information you can access. The
tm_isdst member is non-zero if Daylight Savings Time is in effect. The
localtime() function returns the time in the local time zone, whereas the
gmtime() function returns the time in the UTC (or GMT) time zone. Both
localtime() and gmtime() take as their argument a pointer to a long integer
that represents the date and time as the number of seconds since January 1,
1970 (such a returned by time() ). The return pointers to a tm structure,
where the converted data is placed. The following example prints the local
date in the familiar mm/dd/yy format:
/* day.c
print date in mm/dd/yy format
*/
#include <stdio.h>
#include <time.h> /* may need to be #include <sys/time.h> instead */
int main()
{
long now, time();
time (&now);
today = localtime (&now);
-39-
Parsing Input
When dealing with input from a command line, the first step is to parse
(break up) the input line into tokens, which are groups of characters that
form syntactic units; examples are words, strings, and special symbols.
Following are some sample programs and functions that demonstrate various
ways to parse an input line:
/* parse.c
Split the input buffer into individual tokens. Tokens are
assumed to be separated by space or tab characters.
A pointer to each token is stored in an array of pointers.
This method is very similar to the argv argument to main().
*/
#include <stdio.h>
#define EVER ;;
#define MAXARG 64
int main()
{
char buf[256];
char *args[MAXARG]; /* accept MAXARG number of tokens */
int num_arg,
lcv;
for (EVER)
{
printf("Enter line: ");
if ((gets(buf)) == (char *) NULL)
{
putchar('\n');
exit(0);
}
num_arg = parse_cmd(buf, args);
printf("Number of tokens = %d\n",num_arg);
for (lcv = 0; lcv < num_arg; lcv++)
puts(args[lcv]);
}
}
http://www.di.uevora.pt/~lmr/syscalls.html 38/57
2017-7-26 UNIX System Calls
-40-
-41-
CURSES
What can curses do? Among the functions to be found in curses are those
that:
- Move the cursor to any point on the screen
- Insert text anywhere on the screen, doing it even in highlight mode
- Divide the screen into rectangular areas called windows
- Manage each window independently, so you can be scrolling one window
while erasing a line in another
- Draw a box around a window using a character of your choice
- Write output to and read input from a terminal screen
- Control the data output and input -- for example, to print output in
bold type or prevent it from echoing (printing back on a screen)
- Draw simple graphics
If these features leave you unimpressed, remember that they are only tools.
When you use these tools in your programs, the results can be spectacular.
The point is -- curses is easy to use and ready to go -- so that you can
concentrate on what you want your program to do. curses will make you
program look sharp.
Where did curses come from? The author of curses in Ken Arnold who wrote the
package while a student at the University of California, Berkeley. At the
same time, Bill Joy was writing his editor program, vi. Ken Arnold credits
Bill Joy with providing the ideas (as well as code) for creating the
capability to generally describe terminals, writing routines to read the
terminal database, and implementing routines for optimal cursor movement.
The original source of information about curses is Ken Arnold's paper
entitled "Screen Updating and Cursor Movement Optimization: A Library
Package".
What makes curses tick? The original version of curses developed by Ken
Arnold incorporated a database known as termcap, or the terminal capabilities
database. In System V Release 2, the termcap database was replaced by the
terminfo data base, and curses was rewritten to incorporate it. Both of
these versions of curses can be used with more than one hundred terminals.
The information in the termcap or terminfo database is used by the curses
routines to determine what sequence of special characters must be sent to a
particular terminal to
http://www.di.uevora.pt/~lmr/syscalls.html cause it to clear the screen, move the cursor up one 40/57
2017-7-26 UNIX System Calls
particular terminal to cause it to clear the screen, move the cursor up one
line, delete a line, etc. It is these databases that make curses truly
terminal independent, since any terminal not already in the database can be
added by a system administrator, and since the structure of both databases
allows users to add their own local additions or modifications for a
particular terminal.
-42-
How to use curses -- the basics: There are a couple of things you have to
know before you can start using the curses library. First, when you compile
a C program that call curses routines, you must specify to the cc command
that the curses library is to be linked in with the program. This is done
with the -lcurses option, which must be specified after all the C program
files. The following is an example cc command line for use on systems that
support the terminfo database:
cc myprog.c -lcurses
Next is an example cc command line for use on systems that support the
termcap database:
Second, all program files that reference curses routines must include the
header file <curses.h> <curses.h> will include the header <stdio.h> so it
is not necessary for your program to include it. It won't hurt anything if
you do -- it just slows down the compilation. Lastly, before you run a
program that uses curses, you must inform curses what type of terminal you
have. You do this by setting the shell variable TERM to the type of terminal
you are using (e.g. a DEC VT100) and exporting the TERM variable into the
environment. This is done in the following manner:
$ TERM=vt100
$ export TERM
This action is usually done for you by your .profile when you log it.
IMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM;
:(0,0) (0, COLS-1):
: :
: :
R : :
O : :
W : . :
: (row,col) :
: :
: :
:
http://www.di.uevora.pt/~lmr/syscalls.html : 41/57
2017-7-26 UNIX System Calls
: :
:(LINES-1,0) (LINES-1, COLS-1):
HMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMMM<
COLUMN
-43-
All programs using the curses library must have the following basic
structure:
#include <curses.h>
main()
{
initscr();
/* main program */
endwin();
}
The initscr() function must be called before any other curses routines. Its
function is to determine the terminal type from the TERM environment variable
and to initialize certain data structures and variables (e.g. LINES and
COLS). The endwin() function should be called prior to program exit to
restore the terminal's original state. Some curses routines change the
terminal's characteristics (e.g. go into raw mode and turn off echoing) and
must be undone before the program exits; otherwise the terminal is left in an
odd state and the user may not know how to change it back. Here is how to
fix the terminal if a curses program leaves it in a "funny" state:
note that you must type a control-j and not the return key, since most likely
NEWLINE mapping will be off and the RETURN key will not work.
-44-
http://www.di.uevora.pt/~lmr/syscalls.html 42/57
2017-7-26 UNIX System Calls
The Current Screen - curscr: curses does not know directly what the terminal
is displaying; it would be even slower to have to query the terminal to find
out what character is being displayed at each location. Instead, curses
keeps and image of what it thinks the screen looks like in a window called
curscr. curscr, like stdscr, is created automatically when you initialize
curses with initscr(). curscr is a WINDOW, and has a screen image the size
of the physical screen. When refresh() is called, it writes the characters
that it is sending to the terminal into their corresponding location in the
screen image of curscr. curscr contains the image of the screen as curses
thinks it was made to look by the last refresh(). refresh() uses the screen
image in curscr to minimize its work. When it goes to refresh a window, it
compares the contents of that window to curscr. refresh() assumes that the
physical screen looks like curscr so it does not output characters that are
the same in curscr and the window that is being refreshed. In this way,
refresh() minimizes the number of characters that it sends to the screen and
save a great deal of time.
The following are a few of the more commonly used curses routines. The list
is not comprehensive:
Terminal Modes: the terminal modes for I/O are usually set after the call to
initscr(). None of the mode setting routines accept parameters.
-45-
I/O Function:
initscr();
addch('e');
refresh();
endwin();
}
x = 3; y = 10;
initscr();
mvaddch(x, y, 'e');
refresh();
endwin();
}
initscr();
addstr("This is a string example.");
refresh();
endwin();
}
x = 3; y = 10;
initscr();
mvaddstr(x, y, "This is the string example.");
refresh();
endwin();
}
-46-
http://www.di.uevora.pt/~lmr/syscalls.html 44/57
2017-7-26 UNIX System Calls
-47-
http://www.di.uevora.pt/~lmr/syscalls.html 45/57
2017-7-26 UNIX System Calls
inch() This function returns the character from under the current
cursor position of the terminals screen, in an integer.
#include <curses.h>
main()
{
int in_char;
initscr();
in_char = inch();
refresh();
endwin();
}
mvinch() This function is used to get the character under the cursor
location specified as x and y coordinates. The value
returned is an integer.
#include <curses.h>
main()
{
int in_char;
initscr();
in_char = mvinch(3, 10);
refresh();
endwin();
}
-48-
http://www.di.uevora.pt/~lmr/syscalls.html 46/57
2017-7-26 UNIX System Calls
clrtobot() This function is used to clear the physical screen from the
current cursor position to the bottom of the screen,
filling it with blank spaces.
#include <curses.h>
main()
{
initscr();
clrtobot();
refresh();
endwin();
}
clrtoeol() This function is used to clear the physical screen from the
current cursor position to the end of the physical screen
line by filling it with blank spaces.
#include <curses.h>
main()
{
initscr();
clrtoeol();
refresh();
endwin();
}
-49-
respects,
http://www.di.uevora.pt/~lmr/syscalls.html it works the same as the delch() function, 47/57
2017-7-26 UNIX System Calls
-50-
main()
http://www.di.uevora.pt/~lmr/syscalls.html 48/57
2017-7-26 UNIX System Calls
main()
{
initscr();
insertln();
refresh();
endwin();
}
initscr();
/* curses function call(s) here */
wrefresh(win);
endwin();
}
-51-
/* sets character
http://www.di.uevora.pt/~lmr/syscalls.html attributes to bold */ 49/57
2017-7-26 UNIX System Calls
-52-
}
http://www.di.uevora.pt/~lmr/syscalls.html 50/57
2017-7-26 UNIX System Calls
box() This function draws a box around the edge of the window.
One of its arguments is the horizontal character and the
other is the vertical character.
#include <curses.h>
main()
{
initscr();
box(stdscr, '-', '*');
/* draws a box around the stdscr */
/* horizontal characters are '-' and vertical characters are '*' */
refresh();
endwin();
}
-53-
WINDOW *newwin(lines, cols, y1, x1) will create a new window. The new
window will have lines lines and cols columns, with the upper left corner
located at (y1,x1). newwin() returns a pointer to WINDOW that points at
the new window structure. The screen image in the new window is filled
with blanks.
WINDOW *subwin(win, lines, cols, y1, x1) will create a sub-window. win is
a pointer to the parent window. The other arguments are the same as in
newwin(), except lines and cols are interpreted relative to the parent's
window and not the terminal screen. A sub-window is a real WINDOW and may
have sub-windows just as easily as the parent window.
delwin(win) will delete the specified window. delwin() calls the system
utility free() to return the space to the pool of available memory. If
the window is a sub-window, delwin() does not free() the space because
that space is still being used by the parent. Deletint a parent does not
free the space occupied by sub-windows. The sub-windows will continue to
occupy space, but their screen images will be undefined. You should take
care to delete windows in the proper order and when needed in order to
maintain good housekeeping of the available memory.
-54-
Window Specific Functions: these functions are some of the functions above
applied to a window. A 'w' is placed before the function name, and the first
argument is a pointer to the window.
Move and Act Function: these functions first move the cursor, then perform
their action. The function names have a 'mv' placed before the corresponding
function above.
http://www.di.uevora.pt/~lmr/syscalls.html 52/57
2017-7-26 UNIX System Calls
mvaddch(y,x,ch) mvwaddch(win,y,x,ch)
mvaddstr(y,x,str) mvwaddstr(win,y,x,ch)
mvdelch(y,x) mvwdelch(win,y,x)
mvdeleteln(y,x) mvwdeleteln(win,y,x)
mvinch(y,x) mvwinch(win,y,x)
mvinsch(y,x,ch) mvwinsch(win,y,x,ch)
mvinsertln(y,x) mvwinsertln(win,y,x)
-55-
/* disptime.c
this program displays the time and refreshes the screen once
every second, so that the screen resembles a digital clock.
*/
#include <curses.h>
#include <time.h>
#include <signal.h>
#define EVER ;;
main()
{
void sig_catch();
long seconds;
static char *title = "The current time is", *convtime, *ctime();
for (EVER)
{
/* get time and convert to ASCII */
time (&seconds);
convtime = ctime (&seconds);
/* display time centered under the title */
mvaddstr (LINES / 2 , (COLS - strlen (convtime)) /2, convtime);
refresh ();
sleep (1);
}
}
void sig_catch()
{
endwin ();
exit (1);
}
-56-
AWK
What is awk?
awk is one of the more unusual UNIX commands. Named after and by its
creators: Aho, Weinberger, and Kernighan, awk combines pattern matching,
comparison making, line decomposition, numberical operations, and C-like
programming features into one program.
pattern1 { action1 }
pattern2 { action2 }
...
patternN { actionN }
awk read each line in the input file(s) one at a time. When a line is read,
each pattern is tested in sequence. Whenever a pattern matches the current
line, the corresponding action is executed. This continues until all the
input has been processed.
awk breaks each input record into fields separated by whitespace (or a
specified delimiter). Within the awk program, fields are designated $1, $2,
etc., and the variable NF is set to the total number of fields in the current
record. The variable $0 stands for the entire record not broken into fields.
-57-
The action associated with BEGIN reads a list of keywords to be indexed from
a file called keywords. Each keyword is used as an index into a table called
KEY, and the corresponding entries in the table are nulled.
The middle action (with no pattern) executes for every input file line. Each
line is checked for the presence of each keyword in the KEY table. The line
number (NR) is appended to the KEY entry for each match.
After all lines are processed, the END action prints each keyword followed by
the lines where it appeared.
Rapid Prototyping
-58-
References
6. Topics in C Programming
Stephen Kochan and Patrick Wood
Hayden Books, 1987
John Strang
O'Reilly & Associates, 1986
-59-
http://www.di.uevora.pt/~lmr/syscalls.html 57/57