`

高级Linux程序设计第三章:进程

阅读更多

 

高级Linux程序设计第一章:入门

http://forfuture1978.iteye.com/blog/649981

高级Linux程序设计第二章:编写良好的Linux软件

http://forfuture1978.iteye.com/blog/652338

 

 

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

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

  • 系统中的进程以树的形式组织,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;

}


 

2
2
分享到:
评论

相关推荐

    高级Linux程序第三章:进程.docx

    总的来说,高级Linux程序设计第三章涵盖了进程管理的各个方面,从基本的进程创建到复杂的进程同步和文件系统操作,这些都是Linux程序员必须掌握的核心技能。通过深入理解和实践这些知识,开发者可以更好地设计和优化...

    Linux高级程序设计中文第三版.pdf

    首先,书中详细讲解了C语言的基础,这是Linux程序设计的主要语言,包括数据类型、控制结构、函数、指针等核心概念。通过对C语言的深入学习,读者可以更好地掌握与操作系统交互的底层机制。 其次,书中的章节详细...

    LINUX高级程序设计

    - **第3章:进程** —— 深入探讨了Linux下的进程管理机制,包括进程创建、调度、控制和通信等方面的知识。 - **第4章:线程** —— 探讨了多线程编程在Linux环境中的应用,涵盖了线程的创建、同步、互斥锁、条件...

    Linux程序设计第三版1-19章全部源码

    《Linux程序设计第三版》是Linux系统编程领域的一本经典著作,它深入浅出地介绍了在Linux环境下进行程序开发所需的基础知识和高级技术。书中的源码覆盖了1到19章,这些章节包含了从基本的系统调用、文件操作到网络...

    Linux 程序设计第三版源代码

    《Linux程序设计第三版》是一本深入探讨Linux系统下程序开发的经典著作,书中涵盖了大量关于C语言编程在Linux环境中的应用。源代码是理解和实践书本理论知识的关键,通过阅读和分析这些代码,读者可以更好地掌握...

    linux 高级程序设计第三版源代码(杨宗德著)

    《Linux高级程序设计》是学习Linux系统编程的重要参考资料,作者杨宗德的第三版源代码提供了丰富的实践案例,有助于读者深入理解Linux环境下的程序开发。本压缩包包含的文件是作者对书中理论知识的实现,涵盖了多种...

    高级linux程序设计

    ### 高级Linux程序设计知识点概述 #### 一、引言 《高级Linux程序设计》是一本专注于Linux平台上高级UNIX编程的技术书籍。该书由Mark Mitchell、Jeffrey Oldham和Alex Samuel共同撰写,并由完美废人(高远)翻译成...

    Linux高级程序设计 第三版 源码(示例代码)(杨宗德)

    《Linux高级程序设计》第三版是由杨宗德编著的一本深入探讨Linux系统下编程的经典教材。这本书旨在帮助读者理解并掌握在Linux环境下进行高效、可靠编程的技术与方法。源码包中的示例代码是为了配合书中的讲解,帮助...

    Linux高级程序设计 第三版 源码(示例代码)(杨宗德).zip

    通过对这些代码的阅读和修改,读者可以更深入地理解Linux程序设计的各种技术和陷阱。 总之,《Linux高级程序设计》第三版的源码集合是一份宝贵的教育资源,无论你是初学者还是经验丰富的开发者,都能从中受益匪浅。...

    linux 高级程序设计第三版源代码

    《Linux高级程序设计》第三版是Linux编程领域的一本经典著作,由杨宗德编写,提供了深入理解Linux系统编程的全面知识。这本书的源代码包含了各个章节的实例,旨在帮助读者通过实践掌握复杂的Linux编程技术。以下是...

    Linux高级程序设计中文第三版.zip

    9. **错误处理与调试**:Linux程序设计中的错误处理是关键,书中会指导如何利用gdb等工具进行调试,以及如何编写健壮的错误处理代码。 10. **性能优化**:Linux高级程序设计可能涵盖如何利用各种Linux工具分析和...

    linux 高级程序设计第三版(杨宗德著)

    《Linux高级程序设计》第三版,由杨宗德编著,是针对Linux系统进行深入编程的一本经典教材。这本书旨在帮助读者理解Linux环境下的高级编程技术,涵盖了从基本的系统调用到复杂的进程通信机制等多个方面。以下是该书...

    Linux程序设计(第四版)中文版和英文版

    《Linux程序设计(第四版)》是一本深入探讨Linux系统编程的经典著作,涵盖了广泛的Linux程序设计主题。本书分为中文版和英文版,旨在满足不同语言背景的读者需求。作者Neil Mathew与Richard合作,结合了丰富的经验...

    linux 程序设计第三版

    《Linux程序设计第三版》是一本深受欢迎的Linux编程指南,专为那些希望在Linux环境下进行系统级和应用级编程的开发者所编写。这本书详细介绍了Linux操作系统的核心概念、API接口以及编程技巧,是Linux程序员的必备...

    linux程序设计第四版高清原版 附带各章源代码

    《Linux程序设计第四版》是一本深受欢迎的编程教材,主要面向那些希望深入理解Linux操作系统下编程技术的读者。本书详细介绍了如何在Linux环境下编写高效、可靠的程序,涵盖了C语言编程、系统调用、进程管理、文件I/...

    Linux程序设计 第四版 英文版 经典linux书籍 819页 pdf Beginning Linux Programming

    ### Linux程序设计第四版知识点概览 #### 一、书籍基本信息 - **书名**:《Beginning Linux Programming》第四版 - **作者**:Neil Matthew 和 Richard Stones - **出版社**:Wiley Publishing, Inc. - **出版日期*...

Global site tag (gtag.js) - Google Analytics