CS469 - Linux and Unix Administration and Networking

Processes and threads:

Processes are started with fork(), which duplicates the calling process in it's entirety, memory pages are made copy on write, so nothing is actually duplicated in memory until those memory pages are written to. Each new process has it's own Process ID, memory, I/O descriptors, etc.

Threads are created with clone() or clone2() which is like fork(), but allows the calling process/thread to determine what aspects of the calling process are either shared or copied -- such as the memory space, FS information, I/O descriptors, signals, etc.

While a process is running it is usually in one of the following states:

State What it represents
R Running: the process is executing in normal user-space.
S Interruptible Sleep: The process has either given up it's time slice or been pre-empted by the kernel into giving up it's time.
D Uninterruptible Sleep: The process is waiting on I/O to complete, usually means it is waiting for a kernel system call to return.
T Stopped: The process execution has been temporarily suspended by a job control signal (SIGSTOP/SIGTSTP) until it is continued (via SIGCONT)
X/Z Dead or Zombie: The process has completed and has either been reaped (X - should never actually be seen) or is waiting to be reaped (Z).

When a process finishes it returns an exit status code (additional information about resource usage during it's execution may also be available) that is to be given to it's parent process via the wait() system call. Until a process has been waited on (i.e. reaped) it will appear as a zombie. If for some reason the parent is not around, the init process (PID 1) inherits the dead process and reaps it.

Viewing processes:

> ps

  • report snapshot of current processes
    -eLf or auxm - View threads

> pstree

  • View processes in a tree like format

> top
> htop

  • View processes continously

Attributes of processes/threads:

  • Memory (+stack, shared memory objects) that is futher divided into:

    • Virtual Size (VSZ) The amount of memory of all memory objects associated with the process, including non-resident (swapped to disk / over-committed non-used pages) pages and shared memory objects.

    • Size - The total size of the heap and stack.

    • Resident Set Size (RSS) - For the most part, the amount of memory that is specifically resident to the particular process (the non-swapped heap + stack.)

  • File-system information (root fs, cwd, umask)

  • I/O Descriptors (files, IPC, network objects)

  • Namespace (hierarchy of mounted filesystems)

  • Signal handlers

  • Credentials (man 7 credentials), consisting of:

    • Process ID / thread group ID
    • Parent Process ID (PPID)
    • Process Group ID (PGID) / Session ID (SID) (for shell job control)
    • User ID (real uid / effective uid / saved set-uid / FS uid)
    • Group ID (real gid / effective gid / saved set-gid / FS gid / supplimental gids)
  • Scheduling priority (niceness, -20(highest) to 19 (lowest))

  • Resource limits (limit/ulimit)

  • Processor affinity

  • Additional Meta information:

    • Start time
    • CPU time used
    • Processor currently running on.
    • Current state: R/S/D/T/X or Z

/proc entries for processes (man 5 proc)

/proc/[0-9]+ Process directories
/proc/self Symlink to the current processes own proc entry.
/proc/*/fd Links to a processes open files.
/proc/*/cwd Link to the processes current working directory.

Signals (man 7 signal):

Signals are like 1/2 bit messages to a process that can have one of the following effects (called dispositions):

  • Terminate the process
  • Be ignored by the process
  • Terminate and dump core (image of the processes memory)
  • Stop the process
  • Continue process execution
  • Turn execution over to a custom handler.

Normal signals number from 1 to 31, and many have default actions, SIGKILL (i.e. terminate with extreme prejudice) and SIGSTOP (stop execution) cannot be caught, blocked or ignored.

> kill [ -signal ] pid [ pid... ]

  • Send a signal to a process or processes.

> killall name

  • Kill processes by name.

> pgrep
> pkill

  • look up or signal processes based on name and other attributes.

Process resource limits:

Each process or set of processes under a specific user ID have a set amount of resource limits that might be modified upward or downward. Some resources can be made unlimited and some can only be modified downward unless you are the super-user:

Normally the resource limits should be modified by the shell builtin limit (c-shells) or ulimit (Bourne shells such as bash.)

Useful limits
core file size Size of core file if program dies and should dump a core file
data seg size Maximum data segment size of a process
scheduling priority Default niceness for programs
file size Maximum size of a file that a process can create/write to
max memory size Maximum amount of real memory a process can consume
open files Maximum number of open files for a process
pipe size Memory size in 512 byte blocks for a pipe queue
stack size Maximum size of a processes stack
cpu time The amount of CPU time a process may consume
max user processes Maximum number of processes this uid may use at any given time
virtual memory Maximum amount of virtual memory a process can consume

> prlimit

  • Print the currently set resource limits

(tcsh) > limit
(bash) > ulimit

  • Print/ Set resource limits.

> nice

  • Start a program at a specific niceness

> renice

  • Set the niceness of a process.

> ionice

  • Get/Set I/O scheduling class and priority
    -c class
    -n level (0-7 for best-effort or realtime)
(3) idle Lowest priority
(2) best-effort Normal (8 levels, 0 being highest, 7 lowest)
(1) realtime Highest (8 levels)

Core files

  • Reading: man 5 core

When a program dies due to an error in the program, such as from a segmentation fault (attempting to access memory not allocated to the process,) it may be allowed to dump an image of its memory to a file, called a core dump, which can then be accessed via a debugger, such as gdb to help debug the cause of the fault. This can be very useful to debug a long-running program where one may wish to run the program in the background (such as a daemon) and not wait in a debugger for it to fail.

The core file will usually be written into the processes current working directory at the time of the program fault.

NOTE: Setuid processes cannot dump core (they create 0 byte corefiles,) unless /proc/sys/fs/suid_dumpable is set. Read man core(5) for other limitations. Be sure that core files generated by privileged processes cannot be access by a normal user as the core file could contain sensitive information, such as hashed passwords, private keys, etc.

Enabling core-dumps:

(tcsh) > limit coredumpsize unlimited
(bash) > ulimit -c unlimited

The above will let processes generate core-dumps for debugging.

A core-dump demo:


#include <stdio.h>
int main(void) {
  char *s = NULL;
  printf("%s\n", s); /* Dereferences a NULL pointer */

> gcc -ggdb -o crash crash.c

> limit coredumpsize unlimited

> ./crash
Segmentation fault (core dumped)

> gdb ./crash core


(gdb) where, etc.