Notes for Advanced Linux Programming - 3. Processes

3. Processes

  • Each process is identified by its unique process ID
  • Every process has a parent process.
  • Processes are arranged in a tree, with the init process at its root
  • A program can obtain the process ID with getpid() and can obtain the process ID of its parent process with the getppid().

#include <stdio.h>

#include <unistd.h>

int main ()

{

    printf (“The process ID is %d\n”, (int) getpid ());

    printf (“The parent process ID is %d\n”, (int) getppid ());

    return 0;

}

  • The ps command displays the processes that are running on your system.
  • You can kill a running process with the kill command.

3.1 Creating Processes

3.1.1. Using system

  • The system function provides an easy way to execute a command from within a program.

#include <stdlib.h>

int main ()

{

    int return_value;

    return_value = system (“ls -l /”);

    return return_value;

}

3.1.2. Using fork and exec

  • When a program calls fork, a duplicate process, called the child process, is created.
  • The parent process continues executing the program from the point that fork was called.
  • The child process, too, executes the same program from the same place.
  • The return value in the parent process is the process ID of the child.
  • The return value in the child process is zero.

#include <stdio.h>

#include <sys/types.h>

#include <unistd.h>

int main ()

{

    pid_t child_pid;

    printf (“the main program process ID is %d\n”, (int) getpid ());

    child_pid = fork ();

    if (child_pid != 0) {

        printf (“this is the parent process, with id %d\n”, (int) getpid ());

        printf (“the child’s process ID is %d\n”, (int) child_pid);

    }

    else

        printf (“this is the child process, with id %d\n”, (int) getpid ());

    return 0;

}

  • The exec functions replace the program running in a process with another program.
  • Functions that contain the letter p in their names (execvp and execlp) accept a program name.
  • Functions that don’t contain the p must be given the full path.
  • Functions that contain the letter v in their names (execv, execvp, and execve) accept the argument list as a vector.
  • Functions that contain the letter l (execl, execlp, and execle) accept the argument list as a list.
  • Functions that contain the letter e in their names (execve and execle) accept an array of environment variables.

#include <stdio.h>

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

/* Spawn a child process running a new program. PROGRAM is the name of the program to run; the path will be searched for this program. ARG_LIST is a NULL-terminated list of character strings to be passed as the program’s argument list. Returns the process ID of the spawned process. */

int spawn (char* program, char** arg_list)

{

    pid_t child_pid;

    /* Duplicate this process. */

    child_pid = fork ();

    if (child_pid != 0)

    /* This is the parent process. */

        return child_pid;

    else {

        /* Now execute PROGRAM, searching for it in the path. */

        execvp (program, arg_list);

        /* The execvp function returns only if an error occurs. */

        fprintf (stderr, “an error occurred in execvp\n”);

        abort ();

    }

}

int main ()

{

    /* The argument list to pass to the “ls” command. */

    char* arg_list[] = {

        “ls”, /* argv[0], the name of the program. */

        “-l”,

        “/”,

        NULL /* The argument list must end with a NULL. */

    };

    /* Spawn a child process running the “ls” command. Ignore the returned child process ID. */

    spawn (“ls”, arg_list);

    printf (“done with main program\n”);

    return 0;

}

3.2 Signals

  • A signal is a special message sent to a process.
  • When a process receives a signal, it processes the signal immediately, without finishing the current function or even the current line of code
  • The Linux system sends signals to processes in response to specific conditions.
  • SIGBUS (bus error),
  • SIGSEGV (segmentation violation),
  • SIGFPE (floating point exception)
  • A process may also send a signal to another process.
  • End another process by sending it a SIGTERM or SIGKILL signal
  • Send a command to a running program.Two “userdefined” signals are reserved for this purpose: SIGUSR1 and SIGUSR2.
  • The sigaction function can be used to set a signal disposition.
  • SIG_DFL, which specifies the default disposition for the signal.
  • SIG_IGN, which specifies that the signal should be ignored.
  • A pointer to a signal-handler function.
  • Because signals are asynchronous, you should avoid performing any I/O operations or calling most library and system functions from signal handlers.
  • A signal handler should perform the minimum work necessary to respond to the signal.
  • It is possible for a signal handler to be interrupted by the delivery of another signal.
  • If you use a global variable to flag a signal from a signal-handler function, it should be of the special type sig_atomic_t.

#include <stdio.h>

#include <signal.h>

#include <string.h>

#include <sys/types.h>

#include <unistd.h>

sig_atomic_t sigusr1_count = 0;

void handler(int signal_number)

{

++sigusr1_count;

}

int main(int argc, char* argv[])

{

    printf("the process ID is %d\n", (int)getpid());

    struct sigaction sa;

    memset(&sa, 0, sizeof(sa));

    sa.sa_handler = &handler;

    sigaction(SIGUSR1, &sa, NULL);

    int i = 0;

    while(i < 100)

    {

        sleep(1);

        i++;

    }

    printf("SIGUSR was raised %d times\n", sigusr1_count);

    return 0;

}

  • Compile the above code to program sigusr1

gcc -o sigusr1 sigusr1.c

  • Run the program

[liuchao@localhost Signal]$ ./sigusr1

the process ID is 3401

  • From another terminal, use ps to see the pid of sigusr1

[liuchao@localhost ~]$ ps -a

PID TTY TIME CMD

3401 pts/1 00:00:00 sigusr1

3403 pts/3 00:00:00 ps

  • Send many sigusr1 signals to the process id.

[liuchao@localhost ~]$ kill -s SIGUSR1 3401

[liuchao@localhost ~]$ kill -s SIGUSR1 3401

[liuchao@localhost ~]$ kill -s SIGUSR1 3401

[liuchao@localhost ~]$ kill -s SIGUSR1 3401

[liuchao@localhost ~]$ kill -s SIGUSR1 3401

  • After the process finish.

[liuchao@localhost Signal]$ ./sigusr1

the process ID is 3401

SIGUSR was raised 5 times

3.3 Process Termination

  • A process terminates in one of two ways
  • The executing program calls the exit function, or the program’s main function returns.
  • A process may also terminate abnormally, in response to a signal.
  • SIGINT for ctrl + C
  • SIGTERM for kill command
  • SIGABRT for abort function
  • SIGKILL ends a process immediately and cannot be blocked or handled by a program.
  • Any of these signals can be sent using the kill command

% kill -KILL pid

  • To send a signal from a program, use the kill function.

kill (child_pid, SIGTERM);

  • wait blocks the calling process until one of its child processes exits (or an error occurs).
  • The waitpid function can be used to wait for a specific child process to exit instead of any child process.
  • The wait3 function returns CPU usage statistics about the exiting child process
  • wait4 function allows you to specify additional options about which processes to wait for.

int main ()

{

    int child_status;

    /* The argument list to pass to the “ls” command. */

    char* arg_list[] = {

        “ls”, /* argv[0], the name of the program. */

        “-l”,

        “/”,

        NULL /* The argument list must end with a NULL. */

    };

    /* Spawn a child process running the “ls” command. Ignore the returned child process ID. */

    spawn (“ls”, arg_list);

    /* Wait for the child process to complete. */

    wait (&child_status);

    if (WIFEXITED (child_status))

        printf (“the child process exited normally, with exit code %d\n”, WEXITSTATUS (child_status));

    else

        printf (“the child process exited abnormally\n”);

    return 0;

}

  • A zombie process is a process that has terminated but has not been cleaned up yet.
  • It is the responsibility of the parent process to clean up its zombie children with wait function.
  • If the parent does not clean up its children, they stay around in the system as zombie processes.

#include <stdlib.h>

#include <sys/types.h>

#include <unistd.h>

int main ()

{

    pid_t child_pid;

    /* Create a child process. */

    child_pid = fork ();

    if (child_pid > 0) {

        /* This is the parent process. Sleep for a minute. */

       sleep (60);

    }

    else {

        /* This is the child process. Exit immediately. */

        exit (0);

    }

    return 0;

}

  • % ps -e -o pid,ppid,stat,cmd

3824 2888 S+ ./zombie

3825 3824 Z+ [zombie] <defunct>

  • When a program exits, its children are inherited by a special process, the init program which automatically cleans up any zombie child processes that it inherits.
  • When a child process terminates, Linux sends the parent process the SIGCHLD signal.
  • An easy way to clean up child processes is by handling SIGCHLD.

#include <signal.h>

#include <string.h>

#include <sys/types.h>

#include <sys/wait.h>

sig_atomic_t child_exit_status;

void clean_up_child_process (int signal_number)

{

    /* Clean up the child process. */

    int status;

    wait (&status);

    /* Store its exit status in a global variable. */

    child_exit_status = status;

}

int main ()

{

    /* Handle SIGCHLD by calling clean_up_child_process. */

    struct sigaction sigchld_action;

    memset (&sigchld_action, 0, sizeof (sigchld_action));

    sigchld_action.sa_handler = &clean_up_child_process;

    sigaction (SIGCHLD, &sigchld_action, NULL);

    /* Now do things, including forking a child process. */

    /* ... */

    return 0;

}

posted @ 2010-02-12 10:48  刘超觉先  阅读(911)  评论(0编辑  收藏  举报