前言
fork创建一个子进程的一般目的有哪些呢? (1)一个父进程希望复制自己,使父、子进程同时执行不同的代码段。这在网络进程中是常见的—父进程等待客户端的服务请求。当这种请求到达时,父进程调用fork,使子进程处理此请求。父进程则继续等待下一个服务请求到达。 (2)一个进程要执行不同的程序。这对shell是最常见的情况,这种情况下,子进程从fork返回后立即调用exec。
上一节我们详细介绍了第一点,接下来我们介绍下第二点。 我们用fork函数创建新进程后,经常在新进程调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序。因为调用exec函数并不创建新进程,所以前后进程的ID并没有发生改变。
API介绍
一、exec族:execl, execlp, execle, execv, execvp, execvpe
功能:在调用进程内部执行一个可执行文件,可执行文件既是可以是二进制文件,也可以是任何Linux下可执行的脚本文件。 函数原型: #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函数族的函数执行成功后不会返回,调用失败时,会设置errno并返回-1,然后从原程序的调用点接着往下执行。 参数说明: path:可执行文件的路径名字 arg:可执行程序所带的参数,第一个参数为可执行文件名字(没有带路径),且arg必须以NULL结束 argv[]:可执行文件所带参数指针数组 file:如果参数file中包含/,则就将其视为路径名,否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助: l:使用参数列表 p:使用文件名,并从PATH环境进行寻找可执行文件 v:应先构造一个指向各参数的指针数组,然后该数组的地址作为这些函数的参数 e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量。
带有l的exec族函数,包括execl, execlp, execle,要求将新程序的每个命令行参数都说明为一个单独的参数,这种参数表以空指针结尾。注意第一个命令行参数即argv[0]为可执行文件名字。
以execl函数为例子来说明:
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("before exec!\n");
if(execl("./pro","pro","hello","world",NULL)==-1)
{
printf("exec failed!\n");
perror("why");
}
printf("code end!\n");
return 0;
}
#include <stdio.h>
int main(int argc,char *argv[])
{
int i=0;
printf("argc:%d\n",argc);
printf("argv:\n");
for(i=0;i<argc;i++)
{
printf("%s\n",*argv);
argv++;
}
return 0;
}
如果该路径不存在pro可执行文件,调用失败,会设置errno并返回-1,通过perror函数打印错误原因。同时程序也会往下运行,打印字符串”code end!”。
before exec!
exec failed!
why: No such file or directory
code end!
如果调用成功,程序不会往下运行,不打印字符串”code end!”,直接执行pro,输出命令行参数信息。
before exec!
argc:3
argv:
pro
hello
world
如何将当前路径添加到环境变量? 查看PATH环境变量。
Ubuntu@Embed_Learn:~/learn/process$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games
通过pwd指令获取当前路径。
Ubuntu@Embed_Learn:~/learn/process$ pwd
/home/Ubuntu/learn/process
通过添加当前路径到PATH环境变量。
Ubuntu@Embed_Learn:~/learn/process$ export PATH=$PATH:/home/Ubuntu/learn/process
查看当前路径添加到PATH环境变量是否成功。
Ubuntu@Embed_Learn:~/learn/process$ echo $PATH
/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/home/Ubuntu/learn/process
带有p的exec族函数,包括execlp, execvp, execvpe,如果参数file中包含‘/’,则就将其视为路径名,否则就按PATH环境变量,在它所指定的各目录中搜寻可执行文件。
以execlp函数为例子来说明:
#include <unistd.h>
#include <stdio.h>
int main()
{
printf("before exec!\n");
if(execlp("pro","pro","hello","world",NULL)==-1)
{
printf("exec failed!\n");
perror("why");
}
printf("code end!\n");
return 0;
}
#include <stdio.h>
int main(int argc,char *argv[])
{
int i=0;
printf("argc:%d\n",argc);
printf("argv:\n");
for(i=0;i<argc;i++)
{
printf("%s\n",*argv);
argv++;
}
return 0;
}
实验结果与execl函数一样,区别在于execlp函数中的file参数不需要带有’/’,只需要将当前路径添加到环境变量PATH即可,如果没有添加的话,会调用失败。除此之外,还可以运行常见指令可执行文件,比如cp、pwd等。 带有v的exec族函数,包括execv, execvp, execvpe,参数argv为可执行文件所带参数指针数组。
以execv函数为例子来说明:
#include <unistd.h>
#include <stdio.h>
int main()
{
char *argv[4]={"pro","hello","world",NULL};
printf("before exec!\n");
if(execv("./pro",argv)==-1)
{
printf("exec failed!\n");
perror("why");
}
printf("code end!\n");
return 0;
}
#include <stdio.h>
int main(int argc,char *argv[])
{
int i=0;
printf("argc:%d\n",argc);
printf("argv:\n");
for(i=0;i<argc;i++)
{
printf("%s\n",*argv);
argv++;
}
return 0;
}
介绍完exec族函数,我们结合fork函数来进行应用。
例程:写一段程序,当父进程检测到输入为1的时候,创建子进程把配置文件的字段值修改掉。
#include <sys/types.h>
#include <unistd.h>
#include <stdio.h>
int main()
{
int data=0;
pid_t pid;
while(1)
{
scanf("%d",&data);
if(data==1)
{
pid=fork();
if(pid==0)
{
if(execl("./doconfig","doconfig","LENG","100",NULL)==-1)
{
printf("exec failed!\n");
perror("why");
}
}
}
else
{
printf("do nothing!\n");
}
}
return 0;
}
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
int main(int argc,char** argv){
int fd;
char* find_str=malloc(128);
char* value=malloc(128);
if(argc!=3)
{
printf("arguments errors!\n");
return -1;
}
strcpy(find_str,argv[1]);
strcpy(value,argv[2]);
fd=open("config",O_RDWR);
if(fd==-1)
{
printf("no config file\n");
return -1;
}
int size=lseek(fd,0,SEEK_END);
lseek(fd,0,SEEK_SET);
char *read_buf=(char*)malloc(sizeof(char)*size+1);
int n_read=read(fd,read_buf,size);
if(n_read==-1)
printf("read fail");
char* str_start=strstr(read_buf,find_str);
if(str_start==NULL)
{
printf("no designative argument!\n");
return -1;
}
str_start+=strlen(find_str)+1;
while(1)
{
if(*str_start=='\n')
break;
*str_start=*value;
str_start++;
value++;
}
printf("read_count:%d context:\n%s\n",n_read,read_buf);
lseek(fd,0,SEEK_SET);
int n_write=write(fd, read_buf, strlen(read_buf));
if(n_write==-1)
printf("write fail!\n");
close(fd);
return 0;
}
SPEED=5
LENG=125
SCORE=75
LEVEL=95
二、system函数
功能:执行一条shell命令 函数原型: #include <stdlib.h> int system(const char *command); 返回:运行成功,则返回进程的状态值。当sh不能执行时,返回127。运行失败,返回-1。 参数说明: command:shell命令字符串
在命令行执行文件的方式 1、./pro 2、sh -c ./pro
例程:通过system函数把配置文件的字段值修改掉。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
int main()
{
int status=0;
status=system("./doconfig LENG 125");
printf("system status:%d\n",WEXITSTATUS(status));
return 0;
}
三、popen函数
功能:执行一条shell命令,并获取执行结果 函数原型: #include <stdio.h> FILE *popen(const char *command, const char *type); int pclose(FILE *stream); 返回: FILE型指针 参数说明: command:shell命令字符串 type:”r”—读 “w”—写
例程:编写一段代码,通过popen函数把配置文件的字段值修改掉,并获取结果存储到ret数组。
#include <unistd.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
int main()
{
FILE* fp;
char ret[1024];
memset(ret,'\0',1024);
fp=popen("./doconfig LENG 125","r");
int nread=fread(ret,1,1024,fp);
printf("nread:%d bytes ret:%s\n",nread,ret);
pclose(fp);
return 0;
}
popen函数与system函数区别在于popen函数可以获取到执行结果,而system函数不可以。
|