Linux编程基础 3.2:进程管理-2

exec函数族

使用 fork() 函数创建的子进程,其中包含的程序代码完全相同,只能根据 fork() 函数的返回值,执行不同的代码分支。

exec 函数族的功能是:根据指定的文件名或路径找到可执行文件,用该文件取代调用该函数的进程中的程序,再用该文件的 main() 函数开始执行文件的内容。

调用 exec 函数族时不创建新进程,因此进程的 pid 不会改变。exec 只是用新程序中的数据替换了进程中的代码段、数据段以及堆和栈中的数据。

exec 调用成功时没有返回值。

调用 fork()exec 函数族后,进程与其中的数据变化情况如下:

exec 函数族中包含6个函数,它们包含在系统库 unistd.h,分别为:

#include <unistd.h>
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 execve(const char *path, char * const argv[], char * const envp[]);

函数名及参数说明:

  • 函数第一个参数为 file 或 path,其区别:
    • 当参数是 path,传入的为路径名;
    • 当参数是 file,传入的可执行文件名。若传入的数据中包含 / ,就将其视为路径名;否则系统会根据进程的环境变量 PATH ,在它指定的各个目录中搜素传入的可执行文件。
  • 可以将 exec 函数族分为 execlexecv 两类:
    • • execl 类:函数将以列举的形式传入参数,参数列表会将执行第一个文件用到的参数逐一列举,由于参数列表的长度不定,所以要用哨兵NULL表示列举结束;
    • • execv 类:函数将以参数向量表传递参数,char * argv[] 的形式传递文件执行时使用的参数,数组中最后一个参数为NULL。
  • 如果没有参数 char * const envp[],则采用默认环境变量;如果有,则用传入的参数替换默认环境变量

实际上,只有 execve() 是真正的系统调用,其他5个函数最终都调用 execve()

案例 6-3(书P127):

#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>

int main(){
    pid_t pid = fork();

    if( pid == -1){//子进程创建失败
        perror("fork error");
        exit(1);
    }
    else if(pid > 0){//父进程执行printf
        printf("parent porcess: pid = %d\n",getegid());
    }
    else if(pid == 0){//子进程执行如下
        printf("child process: pid = %d\n",getpid());

        //1.execl()
        //execl("/bin/ls","-a","-l","6-3.c",NULL);

        //2.execlp()
        //execlp("ls","-a","-l","6-3.c",NULL);

        //3.execvp()
        char *arg[]={"-a","-l","6-3.c",NULL};
        execvp("ls",arg);

        perror("error exec\n");
        printf("child process : pid = %d\n",getpid());
    }

    return 0;
}

 execl()execlp()execvp() 调用 ls 命令,执行结果是相同的,下面只贴出一个

 exec 函数族在调用成功时不会产生返回值,不过由于各种原因(调用的文件不存在、环境变量错误等),导致调用失败的概率比较大,所以可以在调用 exeec 函数后通过 perror() 函数打印错误信息,获取函数的调用结果。

进程退出

Linux 系统中进程的退出通过 exit() 函数实现。exit() 函数存在于系统库函数 stdilb.h(之前学C应该也用到了),形式如下:

#include <stdlib.h>
void exit(int status);

参数说明:

  • status:表示进程的退出状态,0表示正常退出,非0表示异常退出,一般用-1或1表示;
  • 为了可读性,标准C定义了两个宏:EXIT_SUCCESS和EXIT_FAILURE,分别表示正常退出和非正常退出。

Linux系统中有一个与 exit() 函数非常相似的函数:_exit()

_exit() 定义在 unistd.h 中,形式如下:

#include <unistd.h>
void _exit(int status);

两者区别:

  • _exit():系统会无条件停止操作,终止进程并清除进程所用内存空间及进程在内核中的各种数据结构;
  • exit():对 _exit() 进行了包装,在调用 _exit() 之前先检查文件的打开情况,将缓冲区中的内容写回文件(除了这两个操作外也会干别的,但是这里没学过),相对来说 exit 比 _exit 更为安全。

特殊进程

  1. 孤儿进程:父进程负责回收子进程,如果父进程在子进程退出之前退出,子进程就会变成孤儿进程,此时 init 进程将代替父进程完成子进程的回收工作;
  2. 僵尸进程:调用 exit 函数后,该进程不会马上消失,而是留下一个称为僵尸进程的数据结构。它几乎放弃进程退出前占用的所有内存,既没有可执行代码也不能被调度,只是在进程列表中保留一个位置,记载进程的退出状态等信息供父进程回收。若父进程没有回收子进程的代码,子进程将会一直处于僵尸态。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值