system的封装代码
#include <stdio.h>
#include <stdlib.h>
#include <errno.h>
#include <sys/wait.h>
#include <dirent.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
char *pmalloc = NULL;
int system_vfork(const char * cmdstring)
{
pid_t pid;
int status;
if(cmdstring == NULL)
{
return (1); //如果cmdstring为空,返回非零值,一般为1
}
if((pid = vfork())<0)
{
status = -1; //fork失败,返回-1
}
else if(pid == 0)
{
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127); // exec执行失败返回127,注意exec只在失败时才返回现在的进程,成功的话现在的进程就不存在啦~~
}
else //父进程
{
while(waitpid(pid, &status, 0) < 0)
{
if(errno != EINTR)
{
status = -1; //如果waitpid被信号中断,则返回-1 EINTR Interrupted system call
break;
}
}
}
return status; //如果waitpid成功,则返回子进程的返回状态
}
/* 封装system系统函数,打印记录错误 */
int INF_System(const char *cmd)
{
int status = -1;
if (NULL == cmd)
{
printf("INF_System error, cmd is NULL");
return -1;
}
status = system_vfork(cmd);
if (-1 == status)
{
printf("INF_System error, status = %d, cmd[%s], errno%d : %s", \
status, cmd, errno, strerror(errno));
return status;
}
if (WIFEXITED(status))
{
if (127 == WEXITSTATUS(status))
{
printf("INF_System error, status = %d, cmd[%s], errno%d : %s", \
WEXITSTATUS(status), cmd, errno, strerror(errno));
return status;
}
}
else if (WIFSIGNALED(status))
{
printf("INF_System error, status = %d, sig = [%d], cmd[%s], errno%d : %s", \
WEXITSTATUS(status), WTERMSIG(status), cmd, errno, strerror(errno));
}
else if (WIFSTOPPED(status))
{
printf("INF_System error, status = %d, sig = [%d], cmd[%s], errno%d : %s", \
WEXITSTATUS(status), WSTOPSIG(status), cmd, errno, strerror(errno));
}
return status;
}
int main()
{
INF_System("ls -l");
return 0;
}
system的封装代码其实就是fork改成了vfork来实现,虽然fork实现了写时拷贝,效率得到很大改善,但如果只是执行shell命令这一块,vfork的效率还是比fork高的。
如果system之前,把SIGCHLD信号行为被设置为SIG_IGN,或者其他行为,而不是SIG_DFL 行为,执行system就会报错,因为waitpid()函数有可能因为找不到子进程而报ECHILD错误。(SIGCHLD 忽略信号 当子进程停止或退出时通知父进程)
**int execl(const char path, const char arg, …)
execl函数特点:
当进程调用一种exec函数时,该进程完全由新程序代换,而新程序则从其main函数开始执行。因为调用exec并不创建新进程,所以前后的进程ID并未改变。exec只是用另一个新程序替换了当前进程的正文、数据、堆和栈段。exec函数·一旦成功,后面的代码是无法执行的。
wait/waitpid(等待子进程中断或结束)
**作用:**使父进程阻塞等待子进程的退出,将子进程的状态切换为死亡态以便于系统释放子进程的资源。
定义函数 pid_t waitpid(pid_t pid,int * status,int options);
函数调用 waitpid(pid, NULL, 0);
函数说明 waitpid()会暂时停止目前进程的执行,直到有信号来到或子进程结束。如果在调用waitpid()时子进程已经结束,则waitpid()会立即返回子进程结束状态值。子进程的结束状态值会由参数status返回,而子进程的进程识别码也会一快返回。如果不在意结束状态值,则参数status可以设成NULL。参数pid为欲等待的子进程识别码,其他数值意义如下:
pid<-1 等待进程组识别码为pid绝对值的任何子进程。
pid=-1 等待任何子进程,相当于wait()。
pid=0 等待进程组识别码与目前进程相同的任何子进程。
pid>0 等待任何子进程识别码为pid的子进程。
返回值 如果执行成功则返回子进程识别码(PID),如果有错误发生则返回-1。失败原因存于errno。
waitpid把子进程的结束状态返回后存于status,有几个宏可判别结束情况:
WIFEXITED(status) 这个宏用来指出子进程是否为正常退出的(通过exit()或者_exit()),如果是,它会返回一个非零值。
WEXITSTATUS(status) 当WIFEXITED返回非零值时,我们可以用这个宏来提取子进程的返回值,如果子进程调用exit(5)退出,WEXITSTATUS(status)就会返回5;如果子进程调用exit(7),WEXITSTATUS(status)就会返回7。请注意,如果进程不是正常退出的,也就是说,WIFEXITED返回0,这个值就毫无意义。
WIFEXITED(status) 如果子进程正常退出,则该宏伟真
WEXITSTATUS(status) 如果子进程正常退出,则宏将获取子进程的退出值
WIFSIGNALED(status)如果子进程是因为信号而结束,则此宏值为真
WIFSTOPPED(status)如果子进程的被信号暂停,且optionWUNTRACED已经被设置时,则此宏值为真
WTERMSIG(status) 如果子进程被杀死,则该宏将获取导致他死亡的信号值
WSTOPSIG(status) 如果WSTOPSIG(status)为真,则该宏将获取导致子进程暂停的信号值
exit/_exit
作用:进程的正常退出
原型:void exit(int status) void _exit(int status)
区别:exit退出时,会自动冲洗标志IO总残留的数据到内核,如果进程注册了“退出处理函数”,(atexit()注册函数),还会自动执行这些函数。_exit会直接退出。