基本概念:
用fork函数创建新的子进程后,子进程往往要调用一种exec函数以执行另一个程序。当进程调用一种exec函数时,该进程执行的程序完全替换为新程序,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用磁盘上的一个新程序替换了当前进程的正文段、数据段、堆段和栈段。
系统调用:
EXECVE(2) Linux Programmer's Manual EXECVE(2)
NAME
execve - execute program
SYNOPSIS
#include <unistd.h>
int execve(const char *filename, char *const argv[],
char *const envp[]);
其他函数族:
EXEC(3) Linux Programmer's Manual EXEC(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[]);
在执行exec后,进程ID没有改变。但新程序从调用进程继承了下列属性:
1.进程ID和父进程ID
2.实际用户ID和实际组ID
3.附属组ID
4.进程组ID
5.会话ID
6.控制终端
7.闹钟尚预留时间
8.当前工作目录
9.根目录
10.文件模式创建屏蔽字
11.文件锁
12.进程信号屏蔽
13.未处理信号
14.资源限制
15.nice值
16.tms_utime、tms_stime、tms_cutime、tms_cstime值
例子:
被执行的新程序:
------ gcc printf.c -o printf -----
#include <stdio.h>
#include <unistd.h>
int main()
{
while (1) {
printf("hello world\n");
sleep(1);
}
return 0;
}
主程序:
----- gcc execve.c -----
#include <sys/types.h>
#include <sys/wait.h>
#include <stdlib.h>
void Perror(const char *s)
{
perror(s);
exit(EXIT_FAILURE);
}
int main()
{
pid_t pid;
if ((pid = fork()) < 0) {
} else if (pid == 0) { /* first child */
if ((pid = fork()) < 0) {
Perror("fork error");
} else if (pid > 0) {
exit(0); /* parent from second fork == first child */
}
sleep(2);
printf("second child pid = %ld, parent pid = %ld\n",
(long)getpid(), (long)getppid());
if (execve("printf", NULL, NULL) == -1) /* 程序的路径要绝对路径或者相对路径 */
Perror("execl error");
exit(0);
}
if (waitpid(pid, NULL, 0) != pid) /* wait for first child */
Perror("waitpid error");
printf("parent for first child exit\n");
return 0;
}
运行:
./a.out
运行结果:
ps:更好的办法是用system函数,参考文章:system函数详解
参考:《unix环境高级编程》·第3版
End;