zoukankan      html  css  js  c++  java
  • 高级Linux程序设计第三章:进程

    • 每个进程都有一个唯一的进程号。

    • 每个进程都有一个父进程。

    • 系统中的进程以树的形式组织,init进程(进程号为1)作为根。

      • 进程0是调度进程,没有程序与之对应,是内核的一部分。

      • 进程1是init进程,是在系统启动的阶段由内核启动的,对应/sbin/init程序,是普通的用户进程。

    • 程序中可以通过getpid()得到进程号,通过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;

    }

    • 通过ps命令可以得到系统中运行的所有进程。

    • 通过kill命令可以杀掉某个进程。

    1、创建进程

    1.1、system函数

    • system函数提供了一种在程序中运行一个命令的简单方法。

    #include <stdlib.h>

    int main ()

    {

        int return_value;

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

        return return_value;

    }

    1.2、fork及exec函数

    • 当程序调用fork的时候,则一个完全复制的子程序被创建。

    • 父进程将从fork被调用的地方继续执行下去。

    • 子进程也是从相同的地方运行下去。

    • 父进程中fork函数的返回值是子进程的进程号。

    • 子进程中fork函数的返回值是零。

    #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;

    }

    • exec函数将当前运行的进程替换为另一个程序。

    • exec函数是一组函数:

      • 包含p的函数(execvp, execlp)接收的参数是程序名。

      • 不包含p的函数接收的参数是程序的全路径。

      • 包含v的函数(execv, execvp, execve)以数组的形式接收参数列表。

      • 包含l的函数(execl, execlp, execle)以列举的形式接收参数列表。

      • 包含e的函数(execve, execle)以数组的形式接收环境变量。

    #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;

    }

    2、信号

    • 信号是发送给进程的特殊信息。

    • 当一个进程接收到一个信号的时候,它会立即处理此信号,并不等待完成当前的函数调用甚至当前一行代码。

    • 系统会在特殊情况下向进程发送信号:

      • SIGBUS:总线错误

      • SIGSEGV:段违规

      • SIGFPE:浮点数异常

    • 一个进程可以向另一个进程发送信号。

    • 可以发送SIGTERM和SIGKILL信号来结束一个进程。

    • 信号可以用来向一个进程发送命令。有两种用户自定义信号:SIGUSER1和SIGUSR2

    • sigaction函数设定信号处理方式

      • SIG_DFL表示使用默认的信号处理方式。

      • SIG_IGN表示此信号可以被忽略。

      • 第二个参数是sigaction结构体,其包括一个信号处理函数。

    • 由于信号处理是异步的,因而在信号处理函数中不要调用I/O操作,也不要调用库或者系统的函数。

    • 信号处理函数中应该尽量处理最少的东西。

    • 信号处理函数也可能被另一个信号中断。

    • 如果在信号处理函数中期望对一个全局变量进行操作,此变量应该是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;

    }

    编译上述代码为程序sigusr1

    gcc -o sigusr1 sigusr1.c

    运行程序

    [liuchao@localhost Signal]$ ./sigusr1

    the process ID is 3401

    在另一个终端,用ps命令得到sigusr1的进程号

    [liuchao@localhost ~]$ ps -a

    PID TTY TIME CMD

    3401 pts/1 00:00:00 sigusr1

    3403 pts/3 00:00:00 ps

    向此进程号发送多个sigusr1信号

    [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

    当进程结束后

    [liuchao@localhost Signal]$ ./sigusr1

    the process ID is 3401

    SIGUSR was raised 5 times

    3、结束进程

    • 一个进程可以有两种方式结束:

      • 进程本身调用exit函数,或者其main函数结束。

      • 进程接收到信号后异常结束。

        • ctrl+c发送SIGINT信号

        • kill命令发送SIGTERM信号

        • abort函数发送SIGABRT信号

        • SIGKILL函数会立即结束一个进程,此信号不能被阻止,被程序自己处理。

    • 所有这些信号都可以用kill命令发送

    % kill -KILL pid
    • 用kill函数可以在程序中向一个进程发送信号。

    kill (child_pid, SIGTERM);
    • 有关wait函数:

      • wait函数阻止当前的进程,直到其中一个子进程结束。

      • waitpid函数等待一个指定的子进程结束。

      • wait3函数等待子进程结束并返回子进程的各种资源使用的统计。

      • wait4函数等待特定的子进程结束并返回子进程的各种资源使用的统计。

    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;

    }

    • 所谓僵死的进程指的是一个进程结束了但资源尚未被回收。

    • 父进程有责任在子进程结束的时候用wait函数回收子进程的资源。

    • 在子进程尚未被父进程回收的时候,其在系统中作为僵死进程存在。

    #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>

    • 当一个程序结束的时候,其所有的子进程都被一个特殊的进程(init进程)继承。init进程负责回收所有它继承过来的僵死的子进程。

    • 当一个子进程结束的时候,将对其父进程发送SIGCHID信号。

    • 一个进程可以通过处理SIGCHID信号来回收子进程。

    #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;

    }

  • 相关阅读:
    windows 按时自动化任务
    Linux libusb 安装及简单使用
    Linux 交换eth0和eth1
    I.MX6 GPS JNI HAL register init hacking
    I.MX6 Android mmm convenient to use
    I.MX6 GPS Android HAL Framework 调试
    Android GPS GPSBasics project hacking
    Python windows serial
    【JAVA】别特注意,POI中getLastRowNum() 和getLastCellNum()的区别
    freemarker跳出循环
  • 原文地址:https://www.cnblogs.com/forfuture1978/p/1720774.html
Copyright © 2011-2022 走看看