Linux进程实践(3) --进程终止与exec函数族

简介: 进程的几种终止方式(1)正常退出   从main函数返回[return]   调用exit   调用_exit/_Exit(2)异常退出   调用abort   产生SIGABOUT信号   由信号终止  Ctrl+C [SIGINT]   .

进程的几种终止方式

(1)正常退出

   从main函数返回[return]

   调用exit

   调用_exit/_Exit

(2)异常退出

   调用abort   产生SIGABOUT信号

   由信号终止  Ctrl+C [SIGINT]

   ...(并不完全, 如return/pthread_exit等)


测试[exit/_exit]

//尝试查看该程序的打印输出
int main()
{
    cout << "In main, pid = " << getpid();	
    //去掉了endl;
    //原理:与终端关联,stdout为行缓冲,在文件中,为全缓冲;
    //详细信息请参考《UNIX环境高级编程》(第三版)8.5节, P188
    //exit(0);为C库函数,详细解释如下
    _exit(0);
}


由图可知,系统调用_exit直接陷入内核,而C语言库函数是经过一系列的系统清理工作,再调用Linux内核的;

int main()
{
    cout << "In main, pid = " << getpid();
    fflush(stdout);	//增加了刷新缓冲区工作
    _exit(0);
}

小结:exit与_exit区别

   1)_exit是一个系统调用,exit是一个c库函数

   2)exit会执行清除I/O缓存

   3)exit会执行调用终止处理程序 //终止处理程序如下

 

终止处理程序:atexit

#include <stdlib.h>
int atexit(void (*function)(void));
//测试
void exitHandler1(void)
{
    cout << "If exit with exit, the function exitHandler will be called1" << endl;
}
void exitHandler2(void)
{
    cout << "If exit with exit, the function exitHandler will be called2" << endl;
}

int main()
{
    cout << "In main, pid = " << getpid() << endl;
    atexit(exitHandler1);	//注意,先注册的后执行
    atexit(exitHandler2);
    exit(0);
}


异常终止

int main()
{
    cout << "In main, pid = " << getpid() << endl;
    atexit(exitHandler1);
    atexit(exitHandler2);
    abort();
    //exit(0);
}

exec函数族

exec替换进程印象

   在进程的创建上,Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。

   当我们创建了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。

   exec只是用磁盘上的一个新程序替换了当前进程的正文段, 数据段, 堆段和栈段.

 

函数族信息

#include <unistd.h>
int execve(const char *filename, char *const argv[],
                  char *const envp[]);

#include <unistd.h>
extern char **environ;
int execl(const char *path, const char *arg, ...);
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,
           ..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
            char *const envp[]);

说明:

   execl,execlp,execle(都带“l”, 代表list)的参数个数是可变的,参数以必须一个空指针结束。

   execv和execvp的第二个参数是一个字符串数组(“v”代表“vector”,字符串数组必须以NULL结尾),新程序在启动时会把在argv数组中给定的参数传递到main。

   名字最后一个字母是“p”的函数会搜索PATH环境变量去查找新程序的可执行文件。如果可执行文件不在PATH定义的路径上,就必须把包括子目录在内的绝对文件名做为一个参数传递给这些函数;

 

/*总结:l代表可变参数列表,p代表在path环境变量中搜索file文件。envp代表环境变量*/

//示例execlp
int main()
{
    pid_t pid = fork();
    if (pid == 0)
    {
        if (execlp("/bin/pwd", "pwd", NULL) == -1)
            err_exit("execlp pwd error");
    }
    wait(NULL);

    pid = fork();
    if (pid == 0)
    {
        if (execlp("/bin/ls", "ls", "-l", NULL) == -1)
            err_exit("execlp ls -l error");
    }
    wait(NULL);
    cout << "After execlp" << endl;
}
//示例execve
int main()
{
    char *const args[] =
    {
        (char *)"/bin/date",
        (char *)"+%F",
        NULL
    };
    execve("/bin/date",args,NULL);
    cout << "After fork..." << endl;

    return 0;
}
//示例execle
//1:main.cpp
int main()
{
    cout << "In main, pid = " << getpid() << endl;

    char *const environ[] =
    {
        "AA=11",
        "BB=22",
        "CC=33",
        NULL
    };
    execle("./hello","./hello",NULL,environ);	//当environ填为NULL时,则什么都不传递
    cout << "After fork..." << endl;

    return 0;
}
extern char **environ;
int main()
{
    cout << "In hello, pid = " << getpid() << endl;

    cout << "environ:" << endl;
    for (int i = 0; environ[i] != NULL; ++i)
    {
        cout << "\t" << environ[i] << endl;
    }
}
/*
In main, pid = 3572	//PID保持不变
In hello, pid = 3572
environ:
	AA=11
	BB=22
	CC=33
*/

//示例: execve 与 execlp
int main()
{
    pid_t pid = fork();
    if (pid == -1)
        err_exit("fork error");
    else if (pid == 0)
    {
        //示例execve
        char *const args[] =
        {
            "echoall",
            "myarg1",
            "MY ARG2",
            NULL
        };
        char *const env[] =
        {
            "USER=unknown",
            "PATH=/tmp",
            NULL
        };

        execve("./echoall",args,env);
    }
    wait(NULL);

    pid = fork();
    if (pid == -1)
        err_exit("fork error");
    else if (pid == 0)
    {
        //示例execlp
        execlp("./echoall", "echoall", "only one arg", NULL);
    }
    wait(NULL);

    return 0;
}

//echoall
int main(int argc, char *argv[])
{
    for (int i = 0; i < argc; ++i)
        printf("argv[%d]: %s\t", i , argv[i]);
    printf("\n");

    for (char **ptr = environ; *ptr != NULL; ++ ptr)
        printf("%s\n", *ptr);

    exit(0);
}

System系统调用

  system()函数调用“/bin/sh -c command”执行特定的命令,阻塞当前进程直到command命令执行完毕,system函数执行时,会调用forkexecvewaitpid等函数。

原型:

int system(const char *command);

返回值:

    如果无法启动shell运行命令,system将返回127;出现不能执行system调用的其他错误时返回-1。如果system能够顺利执行,返回那个命令的退出码。

//示例
int main()
{
    system("ls -la");

    return 0;
}

自己动手写system

int mySystem(const char *command)
{
    if (command == NULL)
    {
        errno = EAGAIN;
        return -1;
    }
    pid_t pid = fork();
    if (pid == -1)
    {
        perror("fork");
        exit(-1);
    }
    else if (pid == 0)
    {
        execl("/bin/sh","sh","-c",command,NULL);
        exit(127);
    }

    int status;
    waitpid(pid,&status,0);
    //wait(&status);

    return WEXITSTATUS(status);
}

int main()
{
    mySystem("ls -la");

    return 0;
}

目录
相关文章
|
11天前
|
缓存 监控 Linux
linux进程管理万字详解!!!
本文档介绍了Linux系统中进程管理、系统负载监控、内存监控和磁盘监控的基本概念和常用命令。主要内容包括: 1. **进程管理**: - **进程介绍**:程序与进程的关系、进程的生命周期、查看进程号和父进程号的方法。 - **进程监控命令**:`ps`、`pstree`、`pidof`、`top`、`htop`、`lsof`等命令的使用方法和案例。 - **进程管理命令**:控制信号、`kill`、`pkill`、`killall`、前台和后台运行、`screen`、`nohup`等命令的使用方法和案例。
39 4
linux进程管理万字详解!!!
|
2天前
|
存储 运维 监控
深入Linux基础:文件系统与进程管理详解
深入Linux基础:文件系统与进程管理详解
33 8
|
6天前
|
关系型数据库 MySQL Linux
Linux环境下MySQL数据库自动定时备份实践
数据库备份是确保数据安全的重要措施。在Linux环境下,实现MySQL数据库的自动定时备份可以通过多种方式完成。本文将介绍如何使用`cron`定时任务和`mysqldump`工具来实现MySQL数据库的每日自动备份。
19 3
|
11天前
|
算法 Linux 定位技术
Linux内核中的进程调度算法解析####
【10月更文挑战第29天】 本文深入剖析了Linux操作系统的心脏——内核中至关重要的组成部分之一,即进程调度机制。不同于传统的摘要概述,我们将通过一段引人入胜的故事线来揭开进程调度算法的神秘面纱,展现其背后的精妙设计与复杂逻辑,让读者仿佛跟随一位虚拟的“进程侦探”,一步步探索Linux如何高效、公平地管理众多进程,确保系统资源的最优分配与利用。 ####
39 4
|
12天前
|
缓存 负载均衡 算法
Linux内核中的进程调度算法解析####
本文深入探讨了Linux操作系统核心组件之一——进程调度器,着重分析了其采用的CFS(完全公平调度器)算法。不同于传统摘要对研究背景、方法、结果和结论的概述,本文摘要将直接揭示CFS算法的核心优势及其在现代多核处理器环境下如何实现高效、公平的资源分配,同时简要提及该算法如何优化系统响应时间和吞吐量,为读者快速构建对Linux进程调度机制的认知框架。 ####
|
13天前
|
消息中间件 存储 Linux
|
20天前
|
运维 Linux
Linux查找占用的端口,并杀死进程的简单方法
通过上述步骤和命令,您能够迅速识别并根据实际情况管理Linux系统中占用特定端口的进程。为了获得更全面的服务器管理技巧和解决方案,提供了丰富的资源和专业服务,是您提升运维技能的理想选择。
21 1
|
25天前
|
监控 Linux 云计算
Linux操作系统在云计算环境中的实践与优化###
【10月更文挑战第16天】 本文探讨了Linux操作系统在云计算环境中的应用实践,重点分析了其在稳定性、安全性和高效性方面的优势。通过具体案例,阐述了Linux如何支持虚拟化技术、实现资源高效分配以及与其他开源技术的无缝集成。文章还提供了针对Linux系统在云计算中的优化建议,包括内核参数调整、文件系统选择和性能监控工具的应用,旨在帮助读者更好地理解和应用Linux于云计算场景。 ###
31 3
|
9天前
|
缓存 监控 Linux