创建新进程:
与fork()不同,exec函数不是创建调用进程的子进程,而是创建一个新的进程
取代调用进程自身,新进程会用自己的全部地址空间,覆盖调用进程的空间,
但进程的PID保持不变
int execl(const char*path,const char *arg,...);
功能:创建新进程,PID不变
参数: path:可执行文件的路径
arg:命令行参数内容
execl("./a.out","a.out","123","hello",NULL);//命令行参数以空结束
函数成功不返回,失败返回-1.
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:文件名 //传文件名会通过环境变量表找文件
argv[]:命令行参数指针数组//以NULL结尾
envp[]:环境变量指针数组//以NULL结尾
调用exec函数不仅改变调用进程的地址空间和进程映像,调用进程的一些
属性也发生了变化。
1、任何处于阻塞状态的信号都会消失
2、被设置为捕获的信号会还原为默认操作
3、有关线程属性的设置会还原为缺省值
4、有关进程的统计信息会复位
5、与进程内存相关的任何数据都会丢失,包括内存映射文件
6、标准库在用户空间维护的一切数据结构(如通过atexit或on_exit函数注册的
退出函数)都会丢失
但有些属性会被进程继承下来,比如PID,PPID,实际用户ID和实际组ID、优先级,
以及文件描述符等。
注意如果进程创建成功,exec函数是不会返回的,因为成功的exec调用会以跳转
到新进程的入口地址作为结束,而刚刚运行的代码是不会存在于新进程的地址空
间中的,但如果进程创建失败,exec函数会返回-1.
调用exec函数固然可以创建出新的进程,但是新进程会取代原来的进程。如果既想
创建新进程,同时又希望原来的进程继续存在,则可以考虑fork+exec模式,即在
fork产生的子进程里调用exec函数,新进程取代了子进程,但父进程依然存在。
//新进程的创建exec.c
#include<stdio.h>
#include<stdlib.h>
#include<unistd.h>
int main()
{
printf("当前进程:%d\n",getpid());
/*if(execl("../day11/waitpid","waitpid",NULL) ==-1)
{
perror("execl");
return -1;
}*/
/*
if(execlp("ls","ls","-la",NULL)== -1)
{
perror("execl");
return -1;
}*/
char *envp[] = {"FOOD=GUOBAOROU","AGE=18","NAME=lli",NULL};
if(execle("./new","new","123","hello",NULL,envp) ==-1)
{
perror("execle");
return -1;
}
printf("变身结束的进程:%d\n",getpid());
return 0;
}
//execl
当前进程:92219
父进程:92219
子进程:92220
子进程:92221
子进程:92222
子进程:92223
我是92219父进程,我要回收92224子进程
子进程:92224
92219父进程回收92224子进程
我是92219父进程,我要回收92223子进程
92219父进程回收92223子进程
我是92219父进程,我要回收92222子进程
92219父进程回收92222子进程
我是92219父进程,我要回收92221子进程
92219父进程回收92221子进程
我是92219父进程,我要回收92220子进程
92219父进程回收92220子进程
//execlp
当前进程:92264
总用量 36
drwxrwxr-x 2 tarena tarena 4096 6月 1 11:14 .
drwxrwxr-x 14 tarena tarena 4096 6月 1 10:43 ..
-rwxrwxr-x 1 tarena tarena 8760 6月 1 11:14 execl
-rw-rw-r-- 1 tarena tarena 412 6月 1 11:11 execl.c
-rw------- 1 tarena tarena 12288 6月 1 11:11 .execl.c.swp
//execle
当前进程:92373
PID:92373
命令行参数
new
123
hello
环境变量
FOOD=GUOBAOROU
AGE=18
NAME=ll
//new.c
#include<stdio.h>
#include<unistd.h>
int main(int argc,char *argv[],char *envp[])
{
printf("PID:%d\n",getpid());
printf("命令行参数\n");
for(char **pp = argv;*pp;pp++)
{
printf("%s\n",*pp);
}
printf("环境变量\n");
for(char **pp = envp;*pp;pp++)
{
printf("%s\n",*pp);
}
return 0;
}
//fork+exec
#include<stdio.h>
#include<unistd.h>
#include<sys/wait.h>
int main(void)
{
printf("父进程:%d\n",getpid());
//创建子进程,让子进程变成ls
pid_t pid1 =fork();
if(pid1 == -1)
{
perror("fork");
return -1;
}
if(pid1 == 0)
{
if(execl("/bin/ls","ls","-i","-l","-a",NULL) == -1)
{
perror("execl");
return -1;
}
//return 0;//取代成功之后,后面语句不会执行,失败走if不执行
}
//父进程负责回收子进程僵尸
int status;//输出子进程的终止状态
if(waitpid(-1,&status,0) == -1)
{
perror("waitpid");
return -1;
}
if(WIFEXITED(status))
{
printf("正常终止,退出码%d\n",WEXITSTATUS(status));
}
else
{
printf("异常终止,信号%d",WTERMSIG(status));
}
//再创建子进程,变身new
pid_t pid2 = fork();
if(pid2 == -1)
{
perror("fork");
return 0;
}
if(pid2 == 0)
{
if(execl("./new","new","123",NULL) == -1)
{
perror("execl");
return -1;
}
}
if(waitpid(-1,&status,0) == -1)
{
perror("waitpid");
return -1;
}
if(WIFEXITED(status))
{
printf("正常终止,退出码%d\n",WEXITSTATUS(status));
}
else
{
printf("异常终止,信号%d",WTERMSIG(status));
}
return 0;
}
父进程:92729
总用量 68
2896362 drwxrwxr-x 2 tarena tarena 4096 6月 1 12:57 .
1853125 drwxrwxr-x 14 tarena tarena 4096 6月 1 10:43 ..
2905886 -rwxrwxr-x 1 tarena tarena 8824 6月 1 11:38 execl
2905890 -rw-rw-r-- 1 tarena tarena 601 6月 1 12:21 execl.c
2905889 -rw-rw-r-- 1 tarena tarena 1483 6月 1 12:57 forf_exec.c
2905883 -rw------- 1 tarena tarena 12288 6月 1 12:57 .forf_exec.c.swp
2905887 -rwxrwxr-x 1 tarena tarena 8928 6月 1 12:57 fork_exec
2905888 -rwxrwxr-x 1 tarena tarena 8704 6月 1 12:52 new
2905893 -rw-rw-r-- 1 tarena tarena 347 6月 1 12:52 new.c
正常终止,退出码0
PID:92731
命令行参数
new
123
环境变量
XDG_VTNR=7
LC_PAPER=zh_CN.UTF-8
LC_ADDRESS=zh_CN.UTF-8
XDG_SESSION_ID=c2
XDG_GREETER_DATA_DIR=/var/lib/lightdm-data/tarena
LC_MONETARY=zh_CN.UTF-8
CLUTTER_IM_MODULE=ibus
SESSION=ubuntu
GPG_AGENT_INFO=/home/tarena/.gnupg/S.g
#include<stdlib.h>
int system(const char* command);
功能:执行shell命令
参数:command shell命令行字符串
返回值:成功返回command进程的终止状态,失败返回-1
system函数执行command参数所表示的命令行,并返回命令进程的终止状态
若command参数取NULL,返回非0表示shell可用,返回0表示shell不可用
在system函数内部调用vfork、exec和waitpid等函数
如果调用vfork或waitpid函数出错,则返回-1
如果调用exec函数出错,则在子进程中执行exit(127)
如果都成功,则返回command进程的终止状态(由waitpid的status参数获得)
使用system函数而不用vfork+exec的好处是,system函数针对各种错误和
信号都做了必要的处理,而且system是标准库函数,可跨平台使用
//使用system
#include<stdio.h>
#include<stdlib.h>
int main()
{
int status = system("ls -a");
if(status == -1)
{
perror("system");
return -1;
}
if(WIFSIGNALED(status))
{
printf("异常终止:%d",WTERMSIG(status));
}
else
{
printf("正常终止:%d",WEXITSTATUS(status));
}
status = system("./new 123 hello");
if(status == -1)
{
perror("system");
return -1;
}
if(WIFSIGNALED(status))
{
printf("异常终止:%d",WTERMSIG(status));
}
else
{
printf("正常终止:%d",WEXITSTATUS(status));
}
return 0;
}
. .. execl execl.c forf_exec.c fork_exec new new.c system system.c .system.c.swp
PID:92850
命令行参数
./new
123
hello
环境变量
LIBRARY_PATH=:.:..:.
LESSOPEN=| /usr/bin/lesspipe %s
C_INCLUDE_PATH=:.:../include:.:../include
GNOME_KEYRING_PID=
MAIL=/var/mail/tarena