A few LINUX system functions
Here are some notes on a few LINUX system functions. In each case,
there is more to say that what is mentioned here. A good place to
find more information is linux.die.net.
You may also want to read about signals and errno, among other topics.
getpid() and getppid()
Prototypes:
- pid_t getpid(void); /* pid_t is a typedef for a long int */
- pid_t getppid(void);
Libraries:
#include <sys/types.h>
#include <unistd.h>
Purpose:
- getpid() returns the process id of the calling process.
- getppid() returns the process id of the parent of the calling process.
Notes:
- These are always successful.
- LINUX processes can be organized into process groups and a process
group has a process group id. There are related functions such as
setpgid(), getpgid(), etc. Process groups are organized into
sessions, and a sesson has a session id. There are related
functions such as setsid() and getsid().
fork()
Prototype: pid_t fork(void)
Library:
#include <sys/types.h>
#include <unistd.h>
Purpose: fork() creates a new process (a child of the calling process)
by duplicating the calling process (the parent). The entire address
space is duplicated: code, variables, file descriptors, etc., except for
a few items (some of which are listed below).
Notes:
- The child has its own process id.
- The child's parent process ID is the same as the parent's process ID.
- The child's set of pending signals is initially empty.
- The child does not inherit timers or memory locks from the parent.
- The counters of resources use and the CPU time counter for the child
are reeset to 0.
- Execution in both processes continues at the next line after the call
to fork().
- The return code is -1 if an error occurs. Otherwise each process has
a return code: 0 in the child and the pid of the child process in the
parent.
- Thus by inspecting the return value, the parent and child can identify
which is which.
pipe()
Prototype: int pipe(int pipefd[2]);
Library:
#include <unistd.h>
Purpose: pipe() creates a data channel for interprocess communication.
The array pipefd is used to return two file descriptors for the two ends
of the pipe: pipefd[0] is the read end and pipefd[1] is the write end.
Data written to the write end is buffered until it is read from the read
end.
Notes:
- The return value is -1 if an error occurs or 0 on success.
- This is often used with fork(); first use pipe() and then fork() and
the parent and child processes can then communicate through the pipe.
Each will close the file descriptor it does not intend to use. Do not
attempt to use one pipe for both directions. To have two-way
communication, we need two pipes.
- A common default size for the buffer is 64 kilobytes, but this
is not guaranteed.
wait() and waitpid()
Prototypes:
- pid_t wait(int * status);
- pid_t waitpid(pid_t pid, int * status, int options);
Libraries:
#include <sys/types.h>
#include <sys/wait.h>
Purpose: These are used to cause the calling process to wait until a
child process changes state. Such a change might be: the child
terminated, the child was stopped by a signal, or the child was resumed
by a signal.
waitpid() waits until the child process specified by pid has changed
state. The default (options = 0) is to wait until the child terminates.
Here options has many values. (We combine various predefined constants
with OR.) If pid is -1, the wait is for any child at all. The return
value is the PID of the child whose state has changed, or -1 upon error.
If options is not null, it is used to return information about the child.
For instance, WIFEXITED returns a nonzero value if the child terminated,
and WEXITSTATUS returns the exit status of the child.
wait() waits until one child process has terminated. It is equivalent to
waitpid(-1, &status, 0). The return value is the PID of the terminated
child, or -1 upon error.
Notes:
- If a child process terminates and the parent is not waiting for this
event, the child is called a "zombie process". It remains in existence
(a resource leak) until the parent does wait on it or until the kill
command is used, etc.
- If a parent process terminates before its child, the child becomes an
"orphan process" and becomes a child of init().
exit()
Prototype:
void _exit(int status); /* Notice the _ in front of the name. */
void _Exit(int status); /* synonym */
Library:
#include <stdlib>
Purpose: exit() terminates the calling process (after any open file
descriptors are closed).
Notes:
- Any children of the process are inherited by process 1 (init()).
- The process's parent is sent a SIGCHLD signal.
- The value of status is returned to the process's parent as the
process's exit status.
- As exit() does not return, there is no return value.
clone()
Prototype: int clone(int (* fn)(void *), void * child_stack, int flags, void * arg);
Library:
<sched.h>
Purpose: clone() creates a LINUX thread of the calling process.
Notes:
- The calling process and the new thread share the memory space, the
table of file descriptors and the table of signal handlers (and
perhaps more).
- When the new thread begins, it executes the function (*fn)(arg).
(Notice that fn is actually a pointer to a function.)
- The thread needs its own stack space. The calling program must request
memory for the thread's stack space before calling clone() and pass the
address of that memory as the child_stack argument.
- If the call to clone() is not successful, no thread is created and the
return value will be -1.
- When the function (*fn) terminates, the thread terminates, and the
return value of (*fn) will be the exit status of the thread.
- clone() has many options which can be specified by setting the value of the
argument flags. (One does the latter by combining various predefined constants
with OR.)
- A LINUX thread has a thread id. (See gettid()).)
gettid()
Prototype: pid_t gettid();
Library:
<sys/types.h>
Purpose: gettid() returns the thread id of the calling process.
Notes:
- This is always successful.
- If a process has only one thread, its process id and thread id
have the same value.
- This refers to LINUX threads rather than to POSIX threads.
time()
Prototype: time_t time(time_t * t); /* time_t is a typedef for
(probably long unsigned) int */
Library:
#include <sys/types.h>
#include <time.h>
Purpose: time() returns the time as the number of seconds since a
reference time called the "Epoch", 1970/01/01 at 00:00:00. If t is
not null, then *t also contains the value.
Notes:
- On error, the value -1 is returned.
- This does not include leap seconds.
- If you want the time of day, use the LINUX command-line "date"
command. You could execute this from within a program using system(),
direct its output to a file, open and read the file and extract the
time of day and close the file.
sleep()
Prototype: unsigned int sleep(unsigned int seconds);
Library:
#include <unistd.h>
Purpose: sleep() makes the calling process sleep (idle, waiting, doing
nothing) until the requested number of seconds have elapsed and then
returns 0.
init()
This refers to the first process started as the system loads. It is
the ancestor, one way or another, of all processes. Its pid is
typically 1. Obviously, we do not ever need to call it.
system()
Prototype: int system(const char * command);
Library:
#include <stdlib.h>
Purpose: system() executes a shell command (that is, something we might
type at the command line) and and returns to the calling program after the
command has been executed. If there is an error, the return code is -1.
Otherwise the return code is the return status of the command (which is
more or less the value the command uses as an argument of exit). (Here
the command is typically a string constant as in "ls -al".)
Notes:
- The system() function is part of the standard C language, although its
implementation depends on the operating system.
- While the command is being executed, the SIGCHLD signal is blocked and
the SIGINT and SIGQUIT signals are ignored.
exec() family of functions
Prototypes:
- int execl(const char * path, const char * arg, ...);
- int execlp(const char * name, const char * arg, ...);
- int execv(const char * path, char * const argv[]);
- int execvp(const char * name, char * const argv[]);
Library:
#include <unistd.h>
Purpose: the exec() functions transform the current process into a new
process by executing the function specified in the argument name (which
may also contain a path). The other arguments are passed to the
function. This is sometimes called an "overlay".
The differences between the various versions follow patterns:
- The exec functions whose names end with 'p' will look for the executable
file "name" along the usual search path in LINUX (as specified by
the PATH variable).
- The exec functions whose names include 'v' have as their second argument
an array of strings which will be passed to the new function as command-line
arguments. The array must be terminated by a pointer = '\0'.
- The exec functions whose names do not include 'v' have as their second
and later arguments a succession of strings which will be passed to the
new function as command-line arguments.
Notes:
- The exec() functions are not technically part of standard C; they are
mainly found in UNIX and LINUX. Windows systems may have functions by
the same names, but the library names may vary.
- The exec functions do not return at all unless there is an error
(such as not finding the specified executable file). If there is an
error, the value returned is -1.
- There are several more exec() functions as well, not covered here.
- It is important to understand that if the call to execlp() succeeds,
we are no longer executing the calling program's code: we have left
it and are executing another program.
- These are all front ends or wrappers for an underlying function called
execve().
dup()
Prototype: int dup(int oldfd);
Library:
#include <unistd.h>
Purpose: dup() creates a copy of the file descriptor "oldfd". On error,
the return value is -1. On success, the number of the new file
descriptor is returned.
Notes:
- This modifies the file descriptor table. The argument oldfd is an
integer, a subscript into the file descriptor table.
- The lowest-numbered unused entry in the table is used for the new
descriptor.
- After the call, we have two file descriptors that can be used
interchangeably.
- It may be useful to remember that the first three entries in the
table are for standard input (0), standard output (1) and
standard error (2).
daemon()
Prototype: int daemon(int nochdir, int noclose);
Library:
#include <unistd.h>
Purpose: daemon() is used when we want to make a program run in the
background as a system daemon. (For instance, there may be daemons to
do logging or network connections.)
Notes:
- If nochdir is 0, the calling process's current working directing is
changed to the root directory. Otherwise, it is not changed.
- If noclose is 0, the streams standard input, standard output and
standard error are redirected to /dev/null. Otherwise, they are not
affected.
- daemon() calls fork(). If the fork succeeds, the parent process
calls exit() and the child (an orphan process) survives, adopted as
a child by init().
- If an error occurs, the return value is -1. On success the return
value is 0.