基本检查:
if (cmdstring == NULL) { return status; }
检查输入的命令字符串是否为空,如果为空则直接返回。
- 第一次
vfork
: - 第一次
vfork
:创建子进程。 - 第二次
vfork
:在子进程中创建孙子进程。 - 孙子进程:获取文件描述符限制,关闭所有非标准输入输出的文件描述符,执行命令。
- 子进程:退出,使孙子进程成为孤儿进程,由
init
进程接管。 - 父进程:等待子进程退出,并检查状态。
父进程调用
vfork
创建子进程:- 父进程调用
vfork
创建子进程。 - 子进程是父进程的副本,共享父进程的地址空间。
- 父进程调用
子进程调用
vfork
创建孙子进程:- 子进程调用
vfork
创建孙子进程。 - 孙子进程是子进程的副本,共享子进程的地址空间。
- 子进程调用
孙子进程执行命令:
- 孙子进程调用
getrlimit
获取文件描述符限制。 - 孙子进程关闭多余的文件描述符。
- 孙子进程调用
execl
执行/bin/sh
命令。 - 如果
execl
成功,孙子进程的内存空间被替换,孙子进程执行新的命令。 - 如果
execl
失败,孙子进程调用_exit(127)
退出。
- 孙子进程调用
子进程退出:
- 子进程在创建孙子进程后立即调用
_exit(0)
退出。
- 子进程在创建孙子进程后立即调用
父进程等待子进程结束:
- 父进程调用
waitpid
等待子进程结束,并处理EINTR
错误。
- 父进程调用
孙子进程的父进程:在孙子进程创建后,子进程立即调用
_exit(0)
退出。此时,孙子进程的父进程(子进程)已经终止,但孙子进程仍然存在。孙子进程在子进程退出后,会被操作系统接管,成为孤儿进程。孤儿进程会被
init
进程(进程ID为1)收养,init
进程会负责处理孤儿进程的退出状态。获取文件描述符限制:
getrlimit(RLIMIT_FSIZE, &rl)
获取当前进程的文件描述符限制。rl.rlim_cur
是当前文件描述符的软限制。
关闭文件描述符:
- 从 3 开始关闭文件描述符,因为文件描述符 0、1 和 2 通常被用于标准输入、标准输出和标准错误。
close(i)
关闭文件描述符i
。- 文件描述符 0、1 和 2 分别对应标准输入(stdin)、标准输出(stdout)和标准错误(stderr),这些是进程的基本输入输出通道,通常不应该被关闭。从 3 开始关闭文件描述符,确保不会影响这些基本的输入输出通道。
类似System()的不阻塞进程调用的实现
类似System()的不阻塞进程调用的实现
在一些情况下,我们希望在代码中创建系统调用的同时,让该进程独立于执行它的软件运行。常规的system( )系统调用会在执行代码的时候阻塞并返回命令的返回值。因此,在工程中我们重新实现了一个执行命令的函数 exec_cmd
,通过两次 vfork
来创建子进程和孙子进程,以确保孙子进程在独立的环境中执行命令。
代码实现
OSI_INT32 exec_cmd(const char *cmdstring)
{
pid_t pid;
pid_t ppid;
int status=0;
int i;
int fdlimit = 1024;
struct rlimit rl;
/* 基本检查 */
if (cmdstring == NULL) {
return status;
}
/* 第一次vfork */
/* fork子进程与父进程共享内存,内存受限,使用vfork */
if ((pid = vfork()) < 0) {
status = -1;
} else if (pid == 0) {
/* 第二次vfork */
if ((ppid = vfork()) < 0) {
status = -1;
} else if (ppid == 0) {
/* 孙子进程 */
/* 获取fd限制 */
if (getrlimit(RLIMIT_FSIZE, &rl) == -1) {
OSI_Debug(DLEVEL_ERROR, "getrlimit fd limit error!");
} else {
fdlimit = rl.rlim_cur;
}
/* 关闭除了0,1,2以外的其他文件描述符 */
for (i=3; i<fdlimit; i++) {
close (i);
}
/* 执行命令 */
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127);
}
/* ust exit in child, then the grand child is free(father is 1(init)) */
/* 强制退出 */
_exit(0);
} else {
/* 等待子进程退出 */
while (waitpid(pid, &status, 0) < 0)
{
if (errno == EINTR)
{
continue;
}
status = -1;
break;
}
}
/* 返回执行结果 */
return status;
}
if ((pid = vfork()) < 0) {
status = -1;
} else if (pid == 0) {
/* 子进程 */
if ((ppid = vfork()) < 0) {
status = OSI_ERROR;
} else if (ppid == 0) {
/* 孙子进程 */
if (getrlimit(RLIMIT_FSIZE, &rl) == -1) {
printf( "getrlimit fd limit error!");
} else {
fdlimit = rl.rlim_cur;
}
for (i = 3; i < fdlimit; i++) {
close(i);
}
execl("/bin/sh", "sh", "-c", cmdstring, (char *)0);
_exit(127);
}
_exit(0);
} else {
/* 父进程 */
while (waitpid(pid, &status, 0) < 0) {
if (errno == EINTR) {
continue;
}
status = -1;
break;
}
}