进程控制---进程程序替换

目录

1.进程程序替换

1.快速见见效果

2.进程替换的原理-fork

3.认识全部接口

1》execl

那么这个除了替换我们的指令之外,还可以替换我们的程序吗?

程序替换,本质不会创建新的进程,只是把代码进程替换了

2》execlp

3》execv

4》execvp

5》execvpe

6》execle

[7》execve 系统调用](#7》execve 系统调用)


我们之前学过指令,这些指令跑起来其实也是一个进程

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-26]$ file /usr/bin/sleep
/usr/bin/sleep: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, BuildID[sha1]=6e225bf5e1fbd30ee61bd7c94e0eccf32cc2b2ab, stripped
[user1@iZ5waahoxw3q2bZ 26-4-26]$ sleep 10000
ls
pwd

但是为什么我们在sleep里面输入指令没有反应呢?

sleep父进程是bash,sleep期间bash在阻塞等待,所以当然没反应。

1.进程程序替换

1.快速见见效果

exec 程序替换的接口

NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file

SYNOPSIS
#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[]);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

execvpe(): _GNU_SOURCE

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-27]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");
    execl("/usr/bin/ls","ls","-l","-a",NULL);
    printf("我的程序运行完毕了");
    return 0;
}

[user1@iZ5waahoxw3q2bZ 26-4-27]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-27]$ ./proc
我的程序要运行了!
total 28
drwxrwxr-x  2 user1 user1 4096 Apr 27 20:25 .
drwxrwxr-x 14 user1 user1 4096 Apr 27 18:09 ..
-rw-rw-r--  1 user1 user1   59 Apr 27 19:40 Makefile
-rwxrwxr-x  1 user1 user1 8544 Apr 27 20:25 proc
-rw-rw-r--  1 user1 user1  300 Apr 27 20:25 proc.c

我们这个程序竟然可以去执行系统中的命令。这种现象叫做程序替换

2.进程替换的原理-fork

进程=内核数据结构+代码和数据

替换原理

用fork创建子进程后执行的是和父进程相同的程序(但有可能执行不同的代码分支),子进程往往要调用⼀种exec函数以执行另⼀个程序。当进程调用⼀种exec函数时,该进程的用户空间代码和数据完全被新程序替换,从新程序的启动例程开始执行。调用exec并不创建新进程,所以调⽤exec前后该进程的id并未改变。

复制代码
int main()
{
    printf("我的程序要运行了!\n");
    execl("/usr/bin/ls","ls","-l","-a",NULL);
    printf("我的程序运行完毕了");
    return 0;
}

代码段是main函数这里的,数据段就是内部定义的变量

进程一旦调用execl,会把指明的新的路径下的新的程序,把其代码重新覆盖式地写到代码中,覆盖式地写到数据段中。而整个替换过程只替换代码和数据。

在程序替换的过程中,并没有创建新的进程,只是:
把当前进程的代码和数据覆盖式地进行替换!

1》一旦程序执行成功,就去执行新代码,原始代码的后半部分,已经不存在了!
2》exec*函数,只有失败返回值,没有成功返回值
exec*系列的函数,不用对返回值进行判断,只要返回,就是失败!

execl

复制代码
RETURN VALUE
       The  exec()  functions  return  only  if an error has occurred.  The return value is -1, and
       errno is set to indicate the error.

3.认识全部接口

NAME
execl, execlp, execle, execv, execvp, execvpe - execute a file

SYNOPSIS
#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[]);

Feature Test Macro Requirements for glibc (see feature_test_macros(7)):

execvpe(): _GNU_SOURCE

1》execl

int execl(const char *path, const char *arg, .../* (char *) NULL */); //"..."可变参数列表

第一个参数:要以路径+程序名
作用: execl("/usr/bin/ls","ls","-a","-l",NULL);
我要执行谁 [user1@iZ5waahoxw3q2bZ 26-4-27]$ ls -a -l

之后的参数:命令行怎么写,你就怎么传。以","为分割符。

我想怎么执行它

把参数一个一个传进来叫做list,list可变参数列表

最后一个参数必须以NULL结尾,表明参数传递成功

不想让其影响其他代码,可不可以不把自己的程序进行替换。让父进程安心做自己的,其他进程去执行。

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        sleep(1);
        //child
        execl("/usr/bin/ls","ls","-l","-a",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    //execl("/usr/bin/top","top",NULL);
    printf("我的程序运行完毕了!\n");
    //return 0;
}

[user1@iZ5waahoxw3q2bZ 26-4-28]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc
我的程序要运行了!
total 28
drwxrwxr-x  2 user1 user1 4096 Apr 27 21:06 .
drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 ..
-rw-rw-r--  1 user1 user1   59 Apr 27 20:49 Makefile
-rwxrwxr-x  1 user1 user1 8696 Apr 27 21:06 proc
-rw-rw-r--  1 user1 user1  465 Apr 27 21:06 proc.c
我的程序运行完毕了!

为什么没有影响父进程?
1.进程具有独立性
2.数据、代码发生写时拷贝

exec*接口---属于加载器的范畴

那么这个除了替换我们的指令之外,还可以替换我们的程序吗?

可以的

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc
#include<iostream>

int main()
{
    std::cout<<"hello C++"<<std::endl;
    return 0;
}
[user1@iZ5waahoxw3q2bZ 26-4-28]$ g++ other.cc -o other
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ll
total 36
-rw-rw-r-- 1 user1 user1   59 Apr 27 20:49 Makefile
-rwxrwxr-x 1 user1 user1 9056 Apr 27 21:12 other
-rw-rw-r-- 1 user1 user1   88 Apr 27 21:12 other.cc
-rwxrwxr-x 1 user1 user1 8696 Apr 27 21:06 proc
-rw-rw-r-- 1 user1 user1  465 Apr 27 21:06 proc.c

用proc.c去调用other.cc

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        sleep(1);
        //child
        //execl("/usr/bin/ls","ls","-l","-a",NULL);
        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    //execl("/usr/bin/top","top",NULL);
    printf("我的程序运行完毕了!\n");
    //return 0;
}

运行

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc
我的程序要运行了!
hello C++
我的程序运行完毕了!
程序替换,本质不会创建新的进程,只是把代码进程替换了
复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        printf("I am Child,My Pid Is:%d\n",getpid());
        sleep(1);
        //child
        //execl("/usr/bin/ls","ls","-l","-a",NULL);
        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    //execl("/usr/bin/top","top",NULL);
    printf("我的程序运行完毕了!\n");
    //return 0;
}
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc
#include<iostream>
#include<unistd.h>

int main()
{
    std::cout<<"hello C++,My Pid Is;"<<getpid()<<std::endl;
    return 0;
}

运行

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc
我的程序要运行了!
I am Child,My Pid Is:4500
hello C++,My Pid Is;4500
我的程序运行完毕了!

2》execlp

int execlp(const char *file, const char *arg, .../* (char *) NULL */);
p---path
第一个参数:只需要告诉要执行的文件名--因为execlp会自动在环境变量PATH中查找指定的命令

后面参数:同上,命令行怎么写,就怎么传。

execlp("ls","ls","-l","-a",NULL);

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        printf("I am Child,My Pid Is:%d\n",getpid());
        sleep(1);
        execlp("ls","ls","-l","-a",NULL);

        //child
        //execl("/usr/bin/ls","ls","-l","-a",NULL);
        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    //execl("/usr/bin/top","top",NULL);
    printf("我的程序运行完毕了!\n");
    //return 0;
}

[user1@iZ5waahoxw3q2bZ 26-4-28]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc
我的程序要运行了!
I am Child,My Pid Is:4512
total 44
drwxrwxr-x  2 user1 user1 4096 Apr 27 21:25 .
drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 ..
-rw-rw-r--  1 user1 user1   59 Apr 27 20:49 Makefile
-rwxrwxr-x  1 user1 user1 9160 Apr 27 21:19 other
-rw-rw-r--  1 user1 user1  128 Apr 27 21:19 other.cc
-rwxrwxr-x  1 user1 user1 8848 Apr 27 21:25 proc
-rw-rw-r--  1 user1 user1  603 Apr 27 21:25 proc.c
我的程序运行完毕了!

3》execv

int execv(const char *path, char *const argv[]);
v---vector

第一个参数:同上上 ,要以路径+程序名

第二个参数: 提供一个命令行参数表,其实就是一个指针数组!
char *const argv[] = {
(char*const)"ls",
(char*const)"-l",
(char*const)"-a",
NULL
};
execv("/usr/bin/ls",argv);

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        printf("I am Child,My Pid Is:%d\n",getpid());
        sleep(1);

        char *const argv[] = {
            (char*const)"ls",
            (char*const)"-l",
            (char*const)"-a",
            NULL
        };
        execv("/usr/bin/ls",argv);
        
        
        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    printf("我的程序运行完毕了!\n");
}

运行

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc
我的程序要运行了!
I am Child,My Pid Is:4557
total 44
drwxrwxr-x  2 user1 user1 4096 Apr 27 21:32 .
drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 ..
-rw-rw-r--  1 user1 user1   59 Apr 27 20:49 Makefile
-rwxrwxr-x  1 user1 user1 9160 Apr 27 21:19 other
-rw-rw-r--  1 user1 user1  128 Apr 27 21:19 other.cc
-rwxrwxr-x  1 user1 user1 8848 Apr 27 21:32 proc
-rw-rw-r--  1 user1 user1  808 Apr 27 21:32 proc.c
我的程序运行完毕了!

所以我们原来系统的argv也是类似的方式去传的。父进程通过exec传的。

4》execvp

int execvp(const char *file, char *const argv[]);
第一个参数:同,只需要告诉要执行的文件名

第二个参数:同,提供一个命令行参数表。
char *const argv[] = {
(char*const)"ls",
(char*const)"-l",
(char*const)"-a",
NULL
};
execvp(argv[0],argv);

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        printf("I am Child,My Pid Is:%d\n",getpid());
        sleep(1);

        char *const argv[] = {
            (char*const)"ls",
            (char*const)"-l",
            (char*const)"-a",
            NULL
        };
        execvp(argv[0],argv);
        
        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    printf("我的程序运行完毕了!\n");
}

运行

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc
我的程序要运行了!
I am Child,My Pid Is:4576
total 44
drwxrwxr-x  2 user1 user1 4096 Apr 27 21:40 .
drwxrwxr-x 15 user1 user1 4096 Apr 27 20:49 ..
-rw-rw-r--  1 user1 user1   59 Apr 27 20:49 Makefile
-rwxrwxr-x  1 user1 user1 9160 Apr 27 21:19 other
-rw-rw-r--  1 user1 user1  128 Apr 27 21:19 other.cc
-rwxrwxr-x  1 user1 user1 8848 Apr 27 21:40 proc
-rw-rw-r--  1 user1 user1  850 Apr 27 21:40 proc.c
我的程序运行完毕了!

5》execvpe

int execvpe(const char *file, char *const argv[],char *const envp[]);

第一个参数:同,只需要告诉要执行的文件名

第二个参数:同,提供一个命令行参数表。

第三个参数,环境变量
char *const argv[] = {
(char *const)"other",
(char *const)"-a",
(char *const)"-b",
(char *const)"-c",
(char *const)"-d",
NULL
};

char *const env[]= {
(char *const)"MYVAL=123456789",
NULL
};

execvpe("./other",argv,env);

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat other.cc
#include<iostream>
#include<cstdio>
#include<unistd.h>

int main(int argc,char *argv[],char *env[])
{
    std::cout<<"hello C++,My Pid Is;"<<getpid()<<std::endl;
    
    for(int i=0;i<argc;i++)
    {
        printf("argv[%d]:%s\n",i,argv[i]);
    }

    printf("\n");

    for(int i=0;env[i];i++)
    {
        printf("env[%d]:%s\n",i,env[i]);
    }

    return 0;
}
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        printf("I am Child,My Pid Is:%d\n",getpid());
        sleep(1);

        char *const argv[] = {
            (char *const)"other",
            (char *const)"-a",
            (char *const)"-b",
            (char *const)"-c",
            (char *const)"-d",
            NULL
        };

        char *const env[]= {
            (char *const)"MYVAL=123456789",
            NULL
        };

        execvpe("./other",argv,env);
        
        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    printf("我的程序运行完毕了!\n");
}

运行

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ make
gcc -o proc proc.c 
[user1@iZ5waahoxw3q2bZ 26-4-28]$ ./proc
我的程序要运行了!
I am Child,My Pid Is:4615
hello C++,My Pid Is;4615
argv[0]:other
argv[1]:-a
argv[2]:-b
argv[3]:-c
argv[4]:-d

env[0]:MYVAL=123456789
我的程序运行完毕了!

但是发现只剩我们传递的了,老的没有了。

所以execvpe第三个参数如果传环境变量,要求被替换的子进程使用全新的!

如果想以新增的方式交给子进程,该怎么做呢?

1.直接putenv

2.exec*e,putenv();environ;

1.替换成这个 execvp("./other",argv);

发现是可以的。为什么不传反而有呢?证明函数内部自己传了。即便不传参,也能获取这个环境变量
environ

2.putenv谁调用它,就在谁的环境变量表里面新增环境变量

SYNOPSIS
#include <stdlib.h>
int putenv(char *string);
新增环境变量

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

char *newenv=(char*)"MYVAL=6666666";

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        printf("I am Child,My Pid Is:%d\n",getpid());
        sleep(1);

        char *const argv[] = {
            (char *const)"other",
            (char *const)"-a",
            (char *const)"-b",
            (char *const)"-c",
            (char *const)"-d",
            NULL
        };

        char *const env[]= {
            (char *const)"MYVAL=123456789",
            NULL
        };

        putenv(newenv);

        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    printf("我的程序运行完毕了!\n");
}

但如果就是想用execvpe呢?

复制代码
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat Makefile
proc:proc.c
	gcc -o $@ $^ -std=c99 
.PHONY:clean
clean:
	rm -f proc
[user1@iZ5waahoxw3q2bZ 26-4-28]$ cat proc.c
#include<stdio.h>
#include<stdlib.h>
#include<errno.h>
#include<string.h>
#include<stdlib.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/wait.h>

char *const addenv[]= {
    (char *const)"MYVAL=123456789",
    NULL
};

int main()
{
    printf("我的程序要运行了!\n");

    if(fork() == 0)
    {
        printf("I am Child,My Pid Is:%d\n",getpid());
        sleep(1);

        char *const argv[] = {
            (char *const)"other",
            (char *const)"-a",
            (char *const)"-b",
            (char *const)"-c",
            (char *const)"-d",
            NULL
        };

        for(int i=0;addenv[i];i++)
        {
            putenv(addenv[i]);
        }
        extern char **environ;
        execvpe("./other",argv,environ);

        execl("./other","other",NULL);
        exit(1);
    }
    
    waitpid(-1,NULL,0); 
    printf("我的程序运行完毕了!\n");
}

6》execle

int execle(const char *path, const char *arg, .../*, (char *) NULL, char * const envp[] */);

除7外其他6个是语言封装!

7》execve 系统调用

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

  • 参数

    • filename:可执行文件的路径(不支持 PATH 搜索)。

    • argv:传递给新程序的命令行参数数组,以 NULL 结尾。

    • envp:传递给新程序的环境变量数组,以 NULL 结尾。

  • 成功时:不会返回(当前进程被新程序替换)。

  • 失败时 :返回 -1,并设置 errno

感谢你的观看,期待我们下次再见!

相关推荐
小夏子_riotous2 小时前
Docker学习路径——10、Docker Compose 一站式编排:从入门到生产级部署
linux·运维·服务器·docker·容器·centos·云计算
zhangrelay2 小时前
三分钟云课实践速通--概率统计--python版
linux·开发语言·笔记·python·学习·ubuntu
云栖梦泽2 小时前
Linux内核与驱动:GPIO设备树与SPI设备树的区别
linux·运维·c++·嵌入式硬件
三品吉他手会点灯2 小时前
STM32 VSCode 开发-C语言程序运行后,终端中文乱码
c语言·ide·笔记·vscode·stm32·学习·编辑器
HalvmånEver2 小时前
MySQL表的查询(二)
linux·数据库·学习·mysql
断问天2 小时前
Faq:Fedora44 Kernel升级后WIFI和声卡都不能使用了
linux·运维·服务器
彷徨而立2 小时前
【C/C++】在头文件中定义全局变量的方法
c语言·开发语言·c++
旧故新长2 小时前
部署自动发卡网站的问题和解决方案
linux·docker
我不是懒洋洋2 小时前
手写一个跳表:从原理到Redis级实现
c语言