exec族函数函数的作用:
我们用fork函数创建新进程后,经常会在新进程中调用exec函数去执行另外一个程序。当进程调用exec函数时,该进程被完全替换为新程序(在exec都后面的代码不会被得到执行)。因为调用exec函数并不创建新进程,所以前后进程的ID并没有改变。
exec族函数功能:
在调用进程内部执行一个可执行文件。可执行文件既可以是二进制文件,也可以是任何Linux下可执行的脚本文件。
函数族:
exec函数族分别是:execl, execlp, execle, execv, execvp, execvpe
函数原型:
#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,然后从原程序的调用点接着往下执行。(errno这个值可以通过perror打印出来)
参数说明:
- path:可执行文件的路径名字
- arg:可执行程序所带的参数,第一个参数为可执行文件名字,没有带路径且arg必须以NULL结束,例如:execl("./bin/echoarg",“echoarg”,“abc”,NULL) echoarg是可执行文件名,abc是第一个参数,最后必须以NULL结尾。
- file:如果参数file中包含/,则就将其视为路径名,否则就按 PATH环境变量,在它所指定的各目录中搜寻可执行文件。
exec族函数参数极难记忆和分辨,函数名中的字符会给我们一些帮助:
- l : 使用参数列表
- p:使用文件名,并从PATH环境进行寻找可执行文件
- v:应先构造一个指向各参数的指针数组,然后将该数组的地址作为这些函数的参数。
- e:多了envp[]数组,使用新的环境变量代替调用进程的环境变量
字符串转整型:
#include <stdlib.h>
int atoi(const char *nptr);
long atol(const char *nptr);
long long atoll(const char *nptr);
示例代码:
****execl代码****
#include<stdio.h>
#include <unistd.h>
int main()
{printf("before execl");if(execl("./number","number","12","13",NULL)==-1){printf("execl fail\n");perror("because");}printf("after execl");//因为execl函数调用成功所以它之后的代码就不再执行。return 0;
}****number可执行文件代码:****
#include<stdio.h>
#include <stdlib.h>
int main(int agrc,char*argv[3])
{printf("参数和为:%d\n",atoi(argv[1])+atoi(argv[2]));return 0;
}可以通过whereis ls查看ls可执行程序的位置:
上述程序中就可以替换为下列代码——执行ls即可
execl("ls路径","ls",NULL);
想用ls -l就可以在ls后面加参数即可:
execl("ls路径","ls","-l",NULL);获取系统服务时间指令:date
同样的道理可以用whereis date查看date可执行程序的路径
上述程序中就可以替换为下列代码——执行date即可
execl("date路径","date",NULL);
execl和execlp的区别在于:
exaclp函数带p,所以能通过环境变量PATH查找到可执行文件ps,当可执行文件的文件路径中带有 / 这种路径符号,我们将其视为路径名(就是按照这个路径去找可执行文件),否则就将其视为环境变量(path变量的作用是可以让我们在没有这个应用的路径下面打开我们需要打开的应用,前提是这个应用得在环境变量里面配置了路径),在linux系统中可以用 echo $PATH 查看当前的环境变量,那些冒号是分隔符。如果用execlp就可以写为execlp(“date”,“date”,NULL);直接写date就好。
环境变量和pwd显示的路径不一样,只有将当前路径加入到环境变量的时候,环境变量里面才会出现当前路径,也就意味着在任何路径下都可以访问该路径下的可执行文件。
PATH是什么?
如何修改环境变量?
在linux环境中可以通过以下代码将路径加入到环境变量:
方法一:
export PATH=$PATH:当前路径
$PATH表示当前环境变量
//配置完后可以通过echo $PATH查看配置结果。
生效方法:立即生效
有效期限:临时改变,只能在当前的终端窗口中有效,当前窗口关闭后就会恢复原有的path配置
用户局限:仅对当前用户方法二:
通过修改.bashrc文件:
vim ~/.bashrc
//在最后一行添上:
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:(有以下两种)
1、关闭当前终端窗口,重新打开一个新终端窗口就能生效
2、输入“source ~/.bashrc”命令,立即生效
有效期限:永久有效
用户局限:仅对当前用户方法三:
通过修改profile文件:
vim /etc/profile
/export PATH //找到设置PATH的行,添加
export PATH=/usr/local/mongodb/bin:$PATH
生效方法:系统重启
有效期限:永久有效
用户局限:对所有用户方法四:
通过修改environment文件:
vim /etc/environment
在PATH=”/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/games:/usr/local/games”中加入“:/usr/local/mongodb/bin”
生效方法:系统重启
有效期限:永久有效
用户局限:对所有用户
带v不带l的一类exec函数:
其实就是将execl可执行程序里面的餐数放到指针数组里面,然后用数组的首地址代替可执行程序名字和程序的参数。
示例:
char* canshu[]={"number","12","13","NULL"};
execv("./number",canshu)
exec配合fork使用:
实现功能当父进程检测到输入为1的时候后,创建子进程把配置文件的字段修改掉。
***********************
以下是存在父子进程的程序
通过调用现有的可执行程序
修改目标文件的内容,他这个
要修改文件要加上绝对路径
************************
#include<stdio.h>
#include <sys/types.h>
#include <unistd.h>
#include<stdlib.h>
#include <sys/wait.h>
int main()
{pid_t pid;pid_t fpid,returnpid;int status;int input=0;int execlreturn=0;while(1){printf("请输入数字:\n");scanf("%d",&input);if(input==1){fpid=fork();if(fpid>0){returnpid=waitpid(fpid,&status,0);if(WIFEXITED(status)){printf("子进程正常退出,退出参数是:%d\n",WEXITSTATUS(status));}}if(fpid==0){execlreturn=execl("/home/fhn/linuxfile/changfile","changfile","/home/fhn/linuxfile/test.txt",NULL); if(execlreturn==-1){printf("exec fail\n");perror("execfail");}}}else{printf("do nothing\n");}}return 0;
}
system函数:
#include <stdlib.h>
int system(const char *command);函数说明:
system()会调用fork()产生子进程,由子进程来调用/bin/sh-c string来执行参数string字符串所代表的命令
此命令执行完后随即返回原调用的进程。在调用system()期间SIGCHLD 信号会被暂时搁置,SIGINT和SIGQUIT 信号则会被忽略。sh -c就相当于./ 就是为了执行后面的指令string返回值:
如果fork()失败 返回-1:出现错误如果exec()失败,表示不能执行Shell,返回值相当于Shell执行了exit(127)如果执行成功则返回子Shell的终止状态如果system()在调用/bin/sh时失败则返回127,其他失败原因返回-1。若参数string为空指针(NULL),则返回非零值。如果system()调用成功则最后会返回执行shell命令后的返回值,但是此返回值也有可能为 system()调用/bin/sh失败所返回的127,因此最好能再检查errno 来确认执行成功。system调用结束后还会返回原程序继续执行system下面的代码,而exec族函数不会。我的理解:
在system函数调用成功时返回进程的状态值
当因shell不能执行时也就是system()在调
用/bin/sh时失败时返回127,其他失败情况
返回-1,命令string为空指针(NULL)system
函数的返回值很简单明了,只有0和1。返回1,
表明系统的命令处理程序,即/bin/sh是可用的。
相反,如果命令处理程序不可用,则返回0。 在
判断返回值时最好能再检查errno来确认执行成功使用例子:
system("/home/fhn/linuxfile/changfile /home/fhn/linuxfile/test.txt");
popen函数和system函数区别?
popen函数:
#include <stdio.h>
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
函数说明:
- popen()会调用fork()产生子进程,然后从子进程中调用/bin/sh -c来执行参数command的指令。
- 参数type可使用“r”代表读取,“w”代表写入。依照此type值,popen()会建立管道连到子进程的标准输出设备(比如说:ps指令,所以可以用“”r“”根据返回的指针读取子进程的标准输出内容)或标准输入设备,然后返回一个文件指针。随后进程便可利用此文件指针来读取子进程的输出设备或是写入到子进程的标准输入设备中。
- 此外,所有使用文件指针(FILE*)操作的函数也都可以使用,除了fclose()以外。
- 如果 type 为 r,那么调用进程读进 command 的标准输出。如果 type 为 w,那么调用进程写到 command 的标准输入。
- popen比sysytem的好处是可以通过管道获取运行结果
返回值:
若成功则返回文件指针,否则返回NULL,错误原因存于errno中。
注意:
popen()会继承环境变量,通过环境变量可能会造成系统安全的问题。
为什么要用popen函数:
#include<stdio.h>
#include <stdlib.h>
int main()
{system("ps");return 0;
}这几行代码的运行结果如下:
fhn@ubuntu:~/jincheng$ ./popen PID TTY TIME CMD20157 pts/3 00:00:03 bash24755 pts/3 00:00:00 popen24756 pts/3 00:00:00 sh24757 pts/3 00:00:00 ps如果想要把它运行的结果放到文件或者字符串中去
就要用到popen函数将结果流入到文件中去,如下面代码:
#include<stdio.h>
#include <stdlib.h>
#include <unistd.h>
int main()
{char* buf;FILE* fd;int n_read;buf=(char*)malloc(1024);fd=popen("ps","r");n_read=fread(buf,1,1024,fd);pclose(fd);printf("管道输出:%s",buf);return 0;
}
以下是输出结果:fhn@ubuntu:~/jincheng$ ./popen
管道输出: PID TTY TIME CMD20157 pts/3 00:00:03 bash24865 pts/3 00:00:00 popen24866 pts/3 00:00:00 sh24867 pts/3 00:00:00 ps如果有将信息流入到内存而不打印则屏幕上面没有显示。
注意popen用的是fread、fwrite而不是read和write,因为read返回的是文件描述符不符合,fread返回的是文件指针