进程的几种终止方式

(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函数执行时,会调用fork、execve、waitpid等函数。

原型:

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

Linux进程实践(3) --进程终止与exec函数族的更多相关文章

  1. 1.2 Linux中的进程 --- fork、vfork、exec函数族、进程退出方式、守护进程等分析

    fork和vfork分析: 在fork还没有实现copy on write之前,Unix设计者很关心fork之后立即执行exec所造成的地址空间浪费,也就是拷贝进程地址空间时的效率问题,所以引入vfo ...

  2. Linux系统编程——进程替换:exec 函数族

    在 Windows 平台下,我们能够通过双击运行可运行程序,让这个可运行程序成为一个进程.而在 Linux 平台.我们能够通过 ./ 运行,让一个可运行程序成为一个进程. 可是.假设我们本来就执行着一 ...

  3. exec函数族的使用

    作者:王姗姗,华清远见嵌入式学院讲师. exec用被执行的程序完全替换调用它的程序的影像.fork创建一个新的进程就产生了一个新的PID,exec启动一个新程序,替换原有的进程,因此这个新的被exec ...

  4. Linux进程理解与实践(三)进程终止函数和exec函数族的使用

    进程的几种终止方式(Termination) (1)正常退出 从main函数返回[return] 调用exit 调用_exit或者_Exit 最后一个线程从其启动处返回 从最后一个线程调用pthrea ...

  5. Linux进程实践(1) --Linux进程编程概述

    进程 VS. 程序 什么是程序? 程序是完成特定任务的一系列指令集合. 什么是进程? [1]从用户的角度来看:进程是程序的一次执行过程 [2]从操作系统的核心来看:进程是操作系统分配的内存.CPU时间 ...

  6. jprofiler_监控远程linux服务器的JVM进程(实践)

    几天前写了一篇文章,jprofiler_监控远程linux服务器的tomcat进程(实践),介绍了使用jprofiler怎样监控远程linux的tomcat进程,这两天想了想,除了可以监控tomcat ...

  7. Linux常用指令---kill | killall(终止进程)

    kill Linux中的kill命令用来终止指定的进程(terminate a process)的运行,是Linux下进程管理的常用命令.通常,终止一个前台进程可以使用Ctrl+C键,但是,对于一个后 ...

  8. Linux进程控制——exec函数族

    原文:http://www.cnblogs.com/hnrainll/archive/2011/07/23/2114854.html 1.简介 在Linux中,并不存在exec()函数,exec指的是 ...

  9. Linux进程实践(5) --守护进程

    概述 守护进程是在需要在后台长期运行不受终端控制的进程,通常情况下守护进程在系统启动时自动运行,在服务器关闭的时候自动关闭:守护进程的名称通常以d结尾,比如sshd.xinetd.crond.atd等 ...

随机推荐

  1. AnyConnect使用说明(电脑版Windows)

    一.下载客户端 Anyconnect支持Windows.Mac电脑. 二.安装 1.双击打开下载的文件,点“Next”开始安装. 2.选择“I accept …”,再点下一步. 3.点“Install ...

  2. Go语言-通道类型

    通道(Channel)是Go语言中一种非常独特的数据结构.它可用于在不同Goroutine之间传递类型化的数据,并且是并发安全的.相比之下,我们之前介绍的那些数据类型都不是并发安全的.这一点需要特别注 ...

  3. Python3 元组

    Python 的元组与列表类似,不同之处在于元组的元素不能修改. 元组使用小括号,列表使用方括号. 元组创建很简单,只需要在括号中添加元素,并使用逗号隔开即可. 如下实例: tup1 = ('Goog ...

  4. Rails多路径调用相同方法原路返回的方法

    有时候可能有多条path到达同一个method,此时,我们希望在该方法完成后自动转到之前进入的path中去,其实实现起来非常简单,只需要实现如下两个方法: def redirect_back_or(d ...

  5. Programming In Scala笔记-第十七章、Scala中的集合类型

    本章主要介绍Scala中的集合类型,主要包括:Array, ListBuffer, Arraybuffer, Set, Map和Tuple. 一.序列 序列类型的对象中包含多个按顺序排列好的元素,可以 ...

  6. solr多集合配置

    1.1 多SolrCore配置 一个solr工程中可以配置多个SolrCore实例. 分享牛原创(尊重原创 转载对的时候第一行请注明,转载出处来自分享牛http://blog.csdn.net/qq_ ...

  7. 理解性能的奥秘——应用程序中慢,SSMS中快(1)——简介

    本文属于<理解性能的奥秘--应用程序中慢,SSMS中快>系列 在工作中发现有不少类似的现象,有幸看到国外大牛写的一篇文章,由于已经完善得不能再添油加醋,所以决定直接翻译,原文出处:http ...

  8. IMDG产品功能扩展

    开源IMDG通常都提供了SPI或其他接口,供用户自行扩展.以Hazelcast为例,我们可以用一些好玩的小工具增强其查询.Map和后端持久化的功能.这些小工具虽然看起来很小,但功能也非常强大. SQL ...

  9. JVM内存区域划分(JDK6/7/8中的变化)

    前言 Java程序的运行是通过Java虚拟机来实现的.通过类加载器将class字节码文件加载进JVM,然后根据预定的规则执行.Java虚拟机在执行Java程序的过程中会把它所管理的内存划分为若干个不同 ...

  10. iOS开发之UIWebView的常见一些用法

    虽然现在Xcode8已经开始使用WKWebView这个框架进行网页展示,但是UIWebView也有一些常用的方法需要知道,下面就简单展示一下,仅供大家参考 相关知识:1.设置背景透明:2.加载本地HT ...