private static final Singleton<IActivityManager> IActivityManagerSingleton =
new Singleton<IActivityManager>() {
@Override
protected IActivityManager create() {
final IBinder b = ServiceManager.getService(Context.ACTIVITY_SERVICE);
final IActivityManager am = IActivityManager.Stub.asInterface(b);
return am;
}
};
public final class Libcore {
private Libcore() { }
public static final Os rawOs = new Linux();
public static volatile Os os = new BlockGuardOs(rawOs);
...
}
public final class Linux implements Os {
Linux() { }
public native String getenv(String name);
public native String[] environ();
...
}
JNIEXPORT jint JNICALL
UNIXProcess_forkAndExec(JNIEnv *env,
jobject process,
jbyteArray prog,
jbyteArray argBlock, jint argc,
jbyteArray envBlock, jint envc,
jbyteArray dir,
jintArray std_fds,
jboolean redirectErrorStream)
{
...
// 上面设置环境变量,重定向输入输出流
// startChild关键函数启动子进程
resultPid = startChild(c);
assert(resultPid != 0);
if (resultPid < 0) {
throwIOException(env, errno, START_CHILD_SYSTEM_CALL " failed");
goto Catch;
}
restartableClose(fail[1]); fail[1] = -1; /* See: WhyCantJohnnyExec */
switch (readFully(fail[0], &errnum, sizeof(errnum))) {
case 0: break; /* Exec succeeded */
case sizeof(errnum):
waitpid(resultPid, NULL, 0);
throwIOException(env, errnum, "Exec failed");
goto Catch;
default:
throwIOException(env, errno, "Read failed");
goto Catch;
}
fds[0] = (in [1] != -1) ? in [1] : -1;
fds[1] = (out[0] != -1) ? out[0] : -1;
fds[2] = (err[0] != -1) ? err[0] : -1;
Finally:
#if START_CHILD_USE_CLONE
free(c->clone_stack);
#endif
/* Always clean up the child's side of the pipes */
closeSafely(in [0]);
closeSafely(out[1]);
closeSafely(err[1]);
/* Always clean up fail descriptors */
closeSafely(fail[0]);
closeSafely(fail[1]);
releaseBytes(env, prog, pprog);
releaseBytes(env, argBlock, pargBlock);
releaseBytes(env, envBlock, penvBlock);
releaseBytes(env, dir, c->pdir);
free(c->argv);
free(c->envv);
free(c);
if (fds != NULL)
(*env)->ReleaseIntArrayElements(env, std_fds, fds, 0);
return resultPid;
Catch:
/* Clean up the parent's side of the pipes in case of failure only */
closeSafely(in [1]);
closeSafely(out[0]);
closeSafely(err[0]);
goto Finally;
}
最关键函数 startChild 继续跟踪
static pid_t
startChild(ChildStuff *c) {
#if START_CHILD_USE_CLONE
#define START_CHILD_CLONE_STACK_SIZE (64 * 1024)
/*
* See clone(2).
* Instead of worrying about which direction the stack grows, just
* allocate twice as much and start the stack in the middle.
*/
if ((c->clone_stack = malloc(2 * START_CHILD_CLONE_STACK_SIZE)) == NULL)
/* errno will be set to ENOMEM */
return -1;
return clone(childProcess,
c->clone_stack + START_CHILD_CLONE_STACK_SIZE,
CLONE_VFORK | CLONE_VM | SIGCHLD, c);
#else
#if START_CHILD_USE_VFORK
/*
* We separate the call to vfork into a separate function to make
* very sure to keep stack of child from corrupting stack of parent,
* as suggested by the scary gcc warning:
* warning: variable 'foo' might be clobbered by 'longjmp' or 'vfork'
*/
volatile pid_t resultPid = vfork();
#else
/*
* From Solaris fork(2): In Solaris 10, a call to fork() is
* identical to a call to fork1(); only the calling thread is
* replicated in the child process. This is the POSIX-specified
* behavior for fork().
*/
pid_t resultPid = fork();
#endif
if (resultPid == 0)
// 子进程处理对应命令
childProcess(c);
assert(resultPid != 0); /* childProcess never returns */
return resultPid;
#endif /* ! START_CHILD_USE_CLONE */
}
static int
childProcess(void *arg)
{
const ChildStuff* p = (const ChildStuff*) arg;
/* Close the parent sides of the pipes.
Closing pipe fds here is redundant, since closeDescriptors()
would do it anyways, but a little paranoia is a good thing. */
if ((closeSafely(p->in[1]) == -1) ||
(closeSafely(p->out[0]) == -1) ||
(closeSafely(p->err[0]) == -1) ||
(closeSafely(p->fail[0]) == -1))
goto WhyCantJohnnyExec;
/* Give the child sides of the pipes the right fileno's. */
/* Note: it is possible for in[0] == 0 */
if ((moveDescriptor(p->in[0] != -1 ? p->in[0] : p->fds[0],
STDIN_FILENO) == -1) ||
(moveDescriptor(p->out[1]!= -1 ? p->out[1] : p->fds[1],
STDOUT_FILENO) == -1))
goto WhyCantJohnnyExec;
if (p->redirectErrorStream) {
if ((closeSafely(p->err[1]) == -1) ||
(restartableDup2(STDOUT_FILENO, STDERR_FILENO) == -1))
goto WhyCantJohnnyExec;
} else {
if (moveDescriptor(p->err[1] != -1 ? p->err[1] : p->fds[2],
STDERR_FILENO) == -1)
goto WhyCantJohnnyExec;
}
if (moveDescriptor(p->fail[1], FAIL_FILENO) == -1)
goto WhyCantJohnnyExec;
/* close everything */
if (closeDescriptors() == 0) { /* failed, close the old way */
int max_fd = (int)sysconf(_SC_OPEN_MAX);
int fd;
for (fd = FAIL_FILENO + 1; fd < max_fd; fd++)
if (restartableClose(fd) == -1 && errno != EBADF)
goto WhyCantJohnnyExec;
}
/* change to the new working directory */
if (p->pdir != NULL && chdir(p->pdir) < 0)
goto WhyCantJohnnyExec;
if (fcntl(FAIL_FILENO, F_SETFD, FD_CLOEXEC) == -1)
goto WhyCantJohnnyExec;
// 最终调用 JDK_execvpe 执行命令
JDK_execvpe(p->argv[0], p->argv, p->envv);
WhyCantJohnnyExec:
/* We used to go to an awful lot of trouble to predict whether the
* child would fail, but there is no reliable way to predict the
* success of an operation without *trying* it, and there's no way
* to try a chdir or exec in the parent. Instead, all we need is a
* way to communicate any failure back to the parent. Easy; we just
* send the errno back to the parent over a pipe in case of failure.
* The tricky thing is, how do we communicate the *success* of exec?
* We use FD_CLOEXEC together with the fact that a read() on a pipe
* yields EOF when the write ends (we have two of them!) are closed.
*/
{
int errnum = errno;
restartableWrite(FAIL_FILENO, &errnum, sizeof(errnum));
}
restartableClose(FAIL_FILENO);
_exit(-1);
return 0; /* Suppress warning "no return value from function" */
}
static void
JDK_execvpe(const char *file,
const char *argv[],
const char *const envp[])
{
if (envp == NULL || (char **) envp == environ) {
execvp(file, (char **) argv);
return;
}
if (*file == '\0') {
errno = ENOENT;
return;
}
if (strchr(file, '/') != NULL) {
execve_with_shell_fallback(file, argv, envp);
} else {
/* We must search PATH (parent's, not child's) */
char expanded_file[PATH_MAX];
int filelen = strlen(file);
int sticky_errno = 0;
const char * const * dirs;
for (dirs = parentPathv; *dirs; dirs++) {
const char * dir = *dirs;
int dirlen = strlen(dir);
if (filelen + dirlen + 1 >= PATH_MAX) {
errno = ENAMETOOLONG;
continue;
}
memcpy(expanded_file, dir, dirlen);
memcpy(expanded_file + dirlen, file, filelen);
expanded_file[dirlen + filelen] = '\0';
execve_with_shell_fallback(expanded_file, argv, envp);
/* There are 3 responses to various classes of errno:
* return immediately, continue (especially for ENOENT),
* or continue with "sticky" errno.
*
* From exec(3):
*
* If permission is denied for a file (the attempted
* execve returned EACCES), these functions will continue
* searching the rest of the search path. If no other
* file is found, however, they will return with the
* global variable errno set to EACCES.
*/
switch (errno) {
case EACCES:
sticky_errno = errno;
/* FALLTHRU */
case ENOENT:
case ENOTDIR:
#ifdef ELOOP
case ELOOP:
#endif
#ifdef ESTALE
case ESTALE:
#endif
#ifdef ENODEV
case ENODEV:
#endif
#ifdef ETIMEDOUT
case ETIMEDOUT:
#endif
break; /* Try other directories in PATH */
default:
return;
}
}
if (sticky_errno != 0)
errno = sticky_errno;
}
}
static void
execve_with_shell_fallback(const char *file,
const char *argv[],
const char *const envp[])
{
#if START_CHILD_USE_CLONE || START_CHILD_USE_VFORK
/* shared address space; be very careful. */
execve(file, (char **) argv, (char **) envp);
if (errno == ENOEXEC)
execve_as_traditional_shell_script(file, argv, envp);
#else
/* unshared address space; we can mutate environ. */
environ = (char **) envp;
execvp(file, (char **) argv);
#endif
}