在进程的创建上Unix采用了一个独特的方法,它将进程创建与加载一个新进程映象分离。这样的好处是有更多的余地对两种操作进行管理。当我们创建
了一个进程之后,通常将子进程替换成新的进程映象,这可以用exec系列的函数来进行。当然,exec系列的函数也可以将当前进程替换掉。
包含头文件
功能
用exec函数可以把当前进程替换为一个新进程。
原型
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[]);
参数
path
参数表示你要启动程序的名称包括路径名
arg
参数表示启动程序所带的参数
返回值
成功,没有返回值----因为进程替换,他已经去执行别的进程,如果非要说,不妨说是替换进程的返回值,一个正常退出的程序的返回值为 0
失败,返回-1
可分为两组
execl
,execlp
,execle
(都带l
,可以将l
理解为list
)的参数个数是可变的,参数以一个空指针结束。
execv
、execvp
和execvpe
的第二个参数是一个字符串数组(可以理解为vector
)。
新程序在启动时会把在argv
数组中给定的参数传递到main
(不管是不是进程替换,main
函数都是一个程序的入口函数)
关于函数的一些解释
名字含字母“p”的函数会搜索PATH环境变量去查找新程序的可执行文件。如果可执行文件不在PATH定义的路径上,就必须把包括子目录在内的绝对文件名做为一个参数传递给这些函数。
名字最后一个字母为"e"的函数可以自设环境变量。
关于这一些列函数的实现
这些函数通常都是用execve实现的,这是一种约定俗成的做法,并不是非这样不可。
int execve(const char *filename, char *const argv[], char *const envp[]);
ID
和父进程ID
(pid
, ppid
)ID
和实际组ID
(ruid
, rgid
)ID
(sgid
)ID
umask
关于 文件描述符的close-on-exec
属性
文件描述符如果存在close-on-exec
标记的话,那么打开的文件描述符会被关闭。
如果可执行程序文件存在SUID
和SGID
位的话,那么有效用户ID
和组ID
(euid
, gid
)会发生变化
关于信号处理方式
程序启动的时候,所有的信号处理方式都是默认的。
对fork
来说,因为子进程和父进程的地址空间是一样的,所以信号处理方式保留了下来。
接下来进行exec
,会将所有设置成为捕捉的信号都修改成为默认处理,而原来已经设置成为忽略的信号就不发生改变。
示例
我们写一个简单的小程序,演示一下exec
函数的用法,我们拿 execle
举例吧
/**
* filename instanceOfExec.cpp
* author zbq
* date 2019.05.15
* */
#include
#include
#include
#include
#include
#include
static const char * env_init[] = {"USER=root", "PATH=/root/study/apue", NULL};
int main(void)
{
pid_t pid = 0;
pid = fork();
if(pid < 0)
{
std::cerr << "fork() faild " << std::endl;
exit(1);
}
else if(pid == 0)
{
int ret_exec = execle("/usr/bin/echo", "echo", "hahaha, I'm from exec!!!", (char*)0);
if(ret_exec < 0)
{
std::cerr << "execle() faild -_-|" << std::endl;
exit(-1);
}
exit(0);
}
else
{
pid_t ret_wait = waitpid(pid, nullptr, 0);
if(ret_wait < 0 || ret_wait != pid)
{
std::cerr << "waitpid() faild" << std::endl;
exit(2);
}
}
return 0;
}
我们编译之:
g++ instanceOfExec.cpp -std=c++11
运行之:
./a.out
我们成功打印出了hahaha, I'm from exec!!!
,这和在bash
上输入 echo "hahaha, I'm from exec!!!"
是一样的。
我们用strace
命令来看一下这个过程是怎么样的
execve("./a.out", ["./a.out"], [/* 24 vars */]) = 0
brk(NULL) = 0x12f2000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45ce000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=48797, ...}) = 0
mmap(NULL, 48797, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7f83d45c2000
close(3) = 0
open("/lib64/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \262\5\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=991616, ...}) = 0
mmap(NULL, 3171168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d40a7000
mprotect(0x7f83d4190000, 2093056, PROT_NONE) = 0
mmap(0x7f83d438f000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe8000) = 0x7f83d438f000
mmap(0x7f83d4399000, 82784, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f83d4399000
close(3) = 0
open("/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20S\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1137024, ...}) = 0
mmap(NULL, 3150120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d3da5000
mprotect(0x7f83d3ea6000, 2093056, PROT_NONE) = 0
mmap(0x7f83d40a5000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x100000) = 0x7f83d40a5000
close(3) = 0
open("/lib64/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220*\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=88776, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45c1000
mmap(NULL, 2184192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d3b8f000
mprotect(0x7f83d3ba4000, 2093056, PROT_NONE) = 0
mmap(0x7f83d3da3000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7f83d3da3000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340$\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2151672, ...}) = 0
mmap(NULL, 3981792, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7f83d37c2000
mprotect(0x7f83d3984000, 2097152, PROT_NONE) = 0
mmap(0x7f83d3b84000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c2000) = 0x7f83d3b84000
mmap(0x7f83d3b8a000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7f83d3b8a000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45c0000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45be000
arch_prctl(ARCH_SET_FS, 0x7f83d45be740) = 0
mprotect(0x7f83d3b84000, 16384, PROT_READ) = 0
mprotect(0x7f83d3da3000, 4096, PROT_READ) = 0
mprotect(0x7f83d40a5000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7f83d45bd000
mprotect(0x7f83d438f000, 32768, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7f83d45cf000, 4096, PROT_READ) = 0
munmap(0x7f83d45c2000, 48797) = 0
clone(child_stack=0, flags=CLONE_CHILD_CLEARTID|CLONE_CHILD_SETTID|SIGCHLD, child_tidptr=0x7f83d45bea10) = 4573
wait4(4573, hahaha, I'm from exec!!!
NULL, 0, NULL) = 4573
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4573, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0) = ?
+++ exited with 0 +++
在51
,52
行我们看到fork
函数是调用clone
函数创建4573
号子进程的,然后子进程替换,父进程等待4573
号进程退出,当子进程退出的时候,操作系统会给父进程发送SIGCHILD
信号,父进程检测到该信号,就知道有子进程退出了。
功能
system
函数调用/bin/sh -c command
执行特定的命令,阻塞当前进程直到command
命令执行完毕
原型
int system(const char *command);
返回值
如果无法启动shell
运行命令,system
将返回127
;
出现不能执行system
调用的其他错误时,返回-1
;
如果system
能够顺利执行,返回那个命令的退出码。
system
函数执行时,会调用fork
、execve
、waitpid
等函数。
示例
/**
* filename testSystem.cpp
* author zbq
* date 2019.05.15
* */
#include
#include
#include
#include
int main(void)
{
system("/usr/bin/echo \"hahaha, I'm from system function!\"");
return 0;
}
我们编译之:
g++ testSystem.cpp
运行之:
./a.out
得到结果:
我们利用strace
命令来看看system
函数如何运作起来的
execve("./a.out", ["./a.out"], [/* 24 vars */]) = 0
brk(NULL) = 0x1c20000
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc4a002000
access("/etc/ld.so.preload", R_OK) = -1 ENOENT (No such file or directory)
open("/etc/ld.so.cache", O_RDONLY|O_CLOEXEC) = 3
fstat(3, {st_mode=S_IFREG|0644, st_size=48797, ...}) = 0
mmap(NULL, 48797, PROT_READ, MAP_PRIVATE, 3, 0) = 0x7fdc49ff6000
close(3) = 0
open("/lib64/libstdc++.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0 \262\5\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=991616, ...}) = 0
mmap(NULL, 3171168, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc49adb000
mprotect(0x7fdc49bc4000, 2093056, PROT_NONE) = 0
mmap(0x7fdc49dc3000, 40960, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0xe8000) = 0x7fdc49dc3000
mmap(0x7fdc49dcd000, 82784, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdc49dcd000
close(3) = 0
open("/lib64/libm.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\20S\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=1137024, ...}) = 0
mmap(NULL, 3150120, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc497d9000
mprotect(0x7fdc498da000, 2093056, PROT_NONE) = 0
mmap(0x7fdc49ad9000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x100000) = 0x7fdc49ad9000
close(3) = 0
open("/lib64/libgcc_s.so.1", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\0\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\220*\0\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=88776, ...}) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff5000
mmap(NULL, 2184192, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc495c3000
mprotect(0x7fdc495d8000, 2093056, PROT_NONE) = 0
mmap(0x7fdc497d7000, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x14000) = 0x7fdc497d7000
close(3) = 0
open("/lib64/libc.so.6", O_RDONLY|O_CLOEXEC) = 3
read(3, "\177ELF\2\1\1\3\0\0\0\0\0\0\0\0\3\0>\0\1\0\0\0\340$\2\0\0\0\0\0"..., 832) = 832
fstat(3, {st_mode=S_IFREG|0755, st_size=2151672, ...}) = 0
mmap(NULL, 3981792, PROT_READ|PROT_EXEC, MAP_PRIVATE|MAP_DENYWRITE, 3, 0) = 0x7fdc491f6000
mprotect(0x7fdc493b8000, 2097152, PROT_NONE) = 0
mmap(0x7fdc495b8000, 24576, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_DENYWRITE, 3, 0x1c2000) = 0x7fdc495b8000
mmap(0x7fdc495be000, 16864, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_FIXED|MAP_ANONYMOUS, -1, 0) = 0x7fdc495be000
close(3) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff4000
mmap(NULL, 8192, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff2000
arch_prctl(ARCH_SET_FS, 0x7fdc49ff2740) = 0
mprotect(0x7fdc495b8000, 16384, PROT_READ) = 0
mprotect(0x7fdc497d7000, 4096, PROT_READ) = 0
mprotect(0x7fdc49ad9000, 4096, PROT_READ) = 0
mmap(NULL, 4096, PROT_READ|PROT_WRITE, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0) = 0x7fdc49ff1000
mprotect(0x7fdc49dc3000, 32768, PROT_READ) = 0
mprotect(0x600000, 4096, PROT_READ) = 0
mprotect(0x7fdc4a003000, 4096, PROT_READ) = 0
munmap(0x7fdc49ff6000, 48797) = 0
rt_sigaction(SIGINT, {SIG_IGN, [], SA_RESTORER, 0x7fdc4922c280}, {SIG_DFL, [], 0}, 8) = 0
rt_sigaction(SIGQUIT, {SIG_IGN, [], SA_RESTORER, 0x7fdc4922c280}, {SIG_DFL, [], 0}, 8) = 0
rt_sigprocmask(SIG_BLOCK, [CHLD], [], 8) = 0
clone(child_stack=0, flags=CLONE_PARENT_SETTID|SIGCHLD, parent_tidptr=0x7fff17e54140) = 4553
wait4(4553, hahaha, I'm from system function!
[{WIFEXITED(s) && WEXITSTATUS(s) == 0}], 0, NULL) = 4553
rt_sigaction(SIGINT, {SIG_DFL, [], SA_RESTORER, 0x7fdc4922c280}, NULL, 8) = 0
rt_sigaction(SIGQUIT, {SIG_DFL, [], SA_RESTORER, 0x7fdc4922c280}, NULL, 8) = 0
rt_sigprocmask(SIG_SETMASK, [], NULL, 8) = 0
--- SIGCHLD {si_signo=SIGCHLD, si_code=CLD_EXITED, si_pid=4553, si_uid=0, si_status=0, si_utime=0, si_stime=0} ---
exit_group(0) = ?
+++ exited with 0 +++
我们注意看一下54
和55
行,先调用 clone
函数,之后调用 wait4
函数等待4553
号进程退出,其实fork
函数底层也是用clone
函数实现的。