From 224a954b163d92827ece85d4bc4f3b62cff3b31f Mon Sep 17 00:00:00 2001 From: roger2015 Date: Fri, 14 Mar 2025 10:02:25 +0800 Subject: [PATCH] fix process group --- .../server/am/ActivityManagerService.java | 33 ++++ aosp/system/core/libprocessgroup/Android.bp | 105 +++++++++++++ .../include/processgroup/shell_utils.h | 28 ++++ .../core/libprocessgroup/processgroup.cpp | 102 +++++++++++- .../core/libprocessgroup/shell_utils.cpp | 145 ++++++++++++++++++ 5 files changed, 410 insertions(+), 3 deletions(-) create mode 100644 aosp/system/core/libprocessgroup/Android.bp create mode 100644 aosp/system/core/libprocessgroup/include/processgroup/shell_utils.h create mode 100644 aosp/system/core/libprocessgroup/shell_utils.cpp diff --git a/aosp/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java b/aosp/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java index 2988c2de5..9cfe4782d 100644 --- a/aosp/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java +++ b/aosp/frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java @@ -504,6 +504,7 @@ import java.io.FileOutputStream; import java.io.IOException; import java.io.InputStreamReader; import java.io.PrintWriter; +import java.io.BufferedReader; import java.util.ArrayList; import java.util.Arrays; import java.util.Collections; @@ -4414,6 +4415,7 @@ public class ActivityManagerService extends IActivityManager.Stub (packageName == null ? ("stop user " + userId) : ("stop " + packageName)) + " due to " + reasonString); } + killRelativeProcess(appId); if (mServices.bringDownDisabledPackageServicesLocked( packageName, null /* filterByClasses */, userId, evenPersistent, true, doit)) { @@ -20874,4 +20876,35 @@ public class ActivityManagerService extends IActivityManager.Stub } mOomAdjuster.mCachedAppOptimizer.binderError(debugPid, app, code, flags, err); } + + private String formatAppUid(int uid) { + StringBuffer sb = new StringBuffer(); + if (uid >= Process.FIRST_APPLICATION_UID && uid <= Process.LAST_APPLICATION_UID) { + sb.append('u'); + sb.append(UserHandle.getUserId(uid)); + sb.append("_a"); + sb.append(UserHandle.getAppId(uid) - Process.FIRST_APPLICATION_UID); + } else { + sb.append("incorrect"); + } + return sb.toString(); + } + + private void killRelativeProcess(int uid) { + final String appUid = formatAppUid(uid); + try (BufferedReader br = new BufferedReader(new InputStreamReader( + Runtime.getRuntime().exec("pgrep -u " + appUid).getInputStream()))) { + String pid; + while ((pid = br.readLine()) != null) { + try { + Slog.i(TAG, "kill relative pid: " + pid); + Process.killProcessQuiet(Integer.parseInt(pid)); + } catch (NumberFormatException e) { + Slog.w(TAG, e.toString()); + } + } + } catch (Exception e) { + Slog.w(TAG, e.toString(), e.fillInStackTrace()); + } + } } diff --git a/aosp/system/core/libprocessgroup/Android.bp b/aosp/system/core/libprocessgroup/Android.bp new file mode 100644 index 000000000..cea5bb40e --- /dev/null +++ b/aosp/system/core/libprocessgroup/Android.bp @@ -0,0 +1,105 @@ +package { + default_applicable_licenses: ["Android-Apache-2.0"], +} + +cc_defaults { + name: "libprocessgroup_defaults", + cpp_std: "gnu++20", + cflags: [ + "-Wall", + "-Werror", + "-Wexit-time-destructors", + "-Wno-unused-parameter", + "-Wno-unused-function", + ], +} + +cc_library_headers { + name: "libprocessgroup_headers", + vendor_available: true, + product_available: true, + ramdisk_available: true, + vendor_ramdisk_available: true, + recovery_available: true, + host_supported: true, + native_bridge_supported: true, + export_include_dirs: ["include"], + target: { + linux_bionic: { + enabled: true, + }, + windows: { + enabled: true, + }, + }, + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + min_sdk_version: "29", +} + +cc_library { + srcs: [ + "cgroup_map.cpp", + "shell_utils.cpp", + "processgroup.cpp", + "sched_policy.cpp", + "task_profiles.cpp", + ], + name: "libprocessgroup", + host_supported: true, + native_bridge_supported: true, + ramdisk_available: true, + vendor_ramdisk_available: true, + recovery_available: true, + vendor_available: true, + product_available: true, + vndk: { + enabled: true, + support_system_process: true, + }, + shared_libs: [ + "libbase", + "libcgrouprc", + ], + static_libs: [ + "libjsoncpp", + ], + // for cutils/android_filesystem_config.h + header_libs: [ + "libcutils_headers", + "libprocessgroup_headers", + ], + export_include_dirs: ["include"], + export_header_lib_headers: [ + "libprocessgroup_headers", + ], + defaults: ["libprocessgroup_defaults"], + apex_available: [ + "//apex_available:platform", + "//apex_available:anyapex", + ], + min_sdk_version: "29", +} + +cc_test { + name: "task_profiles_test", + host_supported: true, + defaults: ["libprocessgroup_defaults"], + srcs: [ + "task_profiles_test.cpp", + ], + header_libs: [ + "libcutils_headers", + "libprocessgroup_headers", + ], + shared_libs: [ + "libbase", + "libcgrouprc", + "libprocessgroup", + ], + static_libs: [ + "libgmock", + ], +} diff --git a/aosp/system/core/libprocessgroup/include/processgroup/shell_utils.h b/aosp/system/core/libprocessgroup/include/processgroup/shell_utils.h new file mode 100644 index 000000000..123bcd5ed --- /dev/null +++ b/aosp/system/core/libprocessgroup/include/processgroup/shell_utils.h @@ -0,0 +1,28 @@ +/* + * Copyright (C) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#ifndef _SHELL_UTILS_H_ +#define _SHELL_UTILS_H_ + +#include + +// Runs |shell_command| in the system shell. +// Stores the stdout of the command in |shell_stdout| if given. +// Returns the shell exit status. +int RunShellCommand(const std::string& shell_command, + std::string* shell_stdout=nullptr); + +#endif diff --git a/aosp/system/core/libprocessgroup/processgroup.cpp b/aosp/system/core/libprocessgroup/processgroup.cpp index b926d9aa7..bea68ae56 100644 --- a/aosp/system/core/libprocessgroup/processgroup.cpp +++ b/aosp/system/core/libprocessgroup/processgroup.cpp @@ -46,6 +46,7 @@ #include #include #include +#include #include using android::base::GetBoolProperty; @@ -58,6 +59,15 @@ using namespace std::chrono_literals; #define PROCESSGROUP_CGROUP_PROCS_FILE "cgroup.procs" #define PROCESSGROUP_CGROUP_KILL_FILE "cgroup.kill" #define PROCESSGROUP_CGROUP_EVENTS_FILE "cgroup.events" +#define DISABLE_CGROUP 1 + +static constexpr int FIRST_APPLICATION_UID = 10000; + +enum { + RET_SUCCESS, + RET_FAILED, + RET_IGNORE +}; bool CgroupsAvailable() { static bool cgroups_available = access("/proc/cgroups", F_OK) == 0; @@ -281,6 +291,7 @@ static bool RemoveEmptyUidCgroups(const std::string& uid_path) { } void removeAllEmptyProcessGroups() { +#if !DISABLE_CGROUP LOG(VERBOSE) << "removeAllEmptyProcessGroups()"; std::vector cgroups; @@ -320,6 +331,7 @@ void removeAllEmptyProcessGroups() { } } } +#endif } /** @@ -633,12 +645,81 @@ static int KillProcessGroup( return ret; } +int killProcessGroupInternal(uid_t uid, int initialPid, int signal) { + // ignore killing init process. + if (abs(initialPid) <= 1) { + LOG(WARNING) << "Yikes, we've been told to kill pid " << abs(initialPid) << "! How about we don't do that?"; + return RET_IGNORE; + } + + if (uid < FIRST_APPLICATION_UID) { + // We do not judge errno == ESRCH. When init kills the system process, + // there is a case that kill() fails with errno == ESRCH when system process + // is pulled up and killed in the same time. kill() again after a while will success. + if (kill(-initialPid, signal)) { + return RET_FAILED; + } + return RET_SUCCESS; + } + + std::string output; + // pgrep -P finds out pids whose ppid is + // There is no case that output contains 0. + // We don't need to check if pid == 0, before we kill it. + std::string cmd = "pgrep -P " + std::to_string(initialPid); + int result = RunShellCommand(cmd, &output); + if (result || output.empty()) { + if (kill(initialPid, signal) == 0 || errno == ESRCH) { + return RET_SUCCESS; + } + return RET_FAILED; + } + + std::string pid; + std::string pids; + std::istringstream iss(output); + bool isAllSuccess = true; + while (iss >> pid) { + isAllSuccess &= (kill(std::stoi(pid), signal) == 0 || errno == ESRCH); + pids += pid + " "; + } + // If failed to kill child process, then try to kill this process group again. + if (!isAllSuccess) { + return RET_FAILED; + } + if (kill(initialPid, signal) && errno != ESRCH) { + return RET_FAILED; + } + LOG(INFO) << "killProcessGroup uid=" << uid << ", pids=" << pids; + return RET_SUCCESS; +} + + int killProcessGroup(uid_t uid, pid_t initialPid, int signal) { +#if DISABLE_CGROUP + int sleepTimeMs = 5; + while(killProcessGroupInternal(uid, initialPid, signal) == RET_FAILED) { + // kill 5ms kill 10ms kill 20ms kill 40ms kill 80ms kill + if (sleepTimeMs > 80) { + PLOG(WARNING) << "Failed to kill(-" << initialPid << ", " << signal << "), uid: " << uid + << " errno: " << errno; + return -1; + } + std::this_thread::sleep_for(std::chrono::milliseconds(sleepTimeMs)); + sleepTimeMs *= 2; + } + return 0; +#else return KillProcessGroup(uid, initialPid, signal); +#endif } int killProcessGroupOnce(uid_t uid, pid_t initialPid, int signal) { +#if DISABLE_CGROUP + return killProcessGroupInternal(uid, initialPid, signal); +#else return KillProcessGroup(uid, initialPid, signal, true); +#endif } static int createProcessGroupInternal(uid_t uid, pid_t initialPid, std::string cgroup, @@ -689,6 +770,9 @@ static int createProcessGroupInternal(uid_t uid, pid_t initialPid, std::string c } int createProcessGroup(uid_t uid, pid_t initialPid, bool memControl) { +#if DISABLE_CGROUP + return 0; +#else CHECK_GE(uid, 0); CHECK_GT(initialPid, 0); @@ -711,8 +795,10 @@ int createProcessGroup(uid_t uid, pid_t initialPid, bool memControl) { std::string cgroup; CgroupGetControllerPath(CGROUPV2_HIERARCHY_NAME, &cgroup); return createProcessGroupInternal(uid, initialPid, cgroup, true); +#endif } +#if 0 static bool SetProcessGroupValue(pid_t tid, const std::string& attr_name, int64_t value) { if (!isMemoryCgroupSupported()) { LOG(ERROR) << "Memcg is not mounted."; @@ -731,20 +817,30 @@ static bool SetProcessGroupValue(pid_t tid, const std::string& attr_name, int64_ } return true; } +#endif bool setProcessGroupSwappiness(uid_t, pid_t pid, int swappiness) { - aosp_hack_p(false); +#if DISABLE_CGROUP + return false; +#else return SetProcessGroupValue(pid, "MemSwappiness", swappiness); +#endif } bool setProcessGroupSoftLimit(uid_t, pid_t pid, int64_t soft_limit_in_bytes) { - aosp_hack_p(false); +#if DISABLE_CGROUP + return false; +#else return SetProcessGroupValue(pid, "MemSoftLimit", soft_limit_in_bytes); +#endif } bool setProcessGroupLimit(uid_t, pid_t pid, int64_t limit_in_bytes) { - aosp_hack_p(false); +#if DISABLE_CGROUP + return false; +#else return SetProcessGroupValue(pid, "MemLimit", limit_in_bytes); +#endif } bool getAttributePathForTask(const std::string& attr_name, pid_t tid, std::string* path) { diff --git a/aosp/system/core/libprocessgroup/shell_utils.cpp b/aosp/system/core/libprocessgroup/shell_utils.cpp new file mode 100644 index 000000000..21ddafd16 --- /dev/null +++ b/aosp/system/core/libprocessgroup/shell_utils.cpp @@ -0,0 +1,145 @@ +/* + * Copyright (C) 2016, The Android Open Source Project + * + * Licensed under the Apache License, Version 2.0 (the "License"); + * you may not use this file except in compliance with the License. + * You may obtain a copy of the License at + * + * http://www.apache.org/licenses/LICENSE-2.0 + * + * Unless required by applicable law or agreed to in writing, software + * distributed under the License is distributed on an "AS IS" BASIS, + * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. + * See the License for the specific language governing permissions and + * limitations under the License. + */ + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +using android::base::unique_fd; + +#ifdef __ANDROID__ +const char kShellPath[] = "/system/bin/sh"; +#else +const char kShellPath[] = "/bin/sh"; +#endif + +const int kShellTimeoutMs = 30 * 1000; +const int kMillisecondsPerSecond = 1000; +const int kNanosecondsPerMillisecond = 1000 * 1000; + +// Represents some arbitrary, non-decreasing time in milliseconds. +int64_t GetCurrentTimeMs() { + struct timespec ts; + clock_gettime(CLOCK_MONOTONIC, &ts); + return (int64_t{ts.tv_sec} * kMillisecondsPerSecond) + + (ts.tv_nsec / kNanosecondsPerMillisecond); +} + +int RunShellCommand(const std::string& shell_command, std::string* output) { + int fds[2]; + if (pipe2(fds, O_NONBLOCK) != 0) { + LOG(FATAL) << "Failed to create pipe"; + } + unique_fd read_fd(fds[0]); + unique_fd write_fd(fds[1]); + fcntl(read_fd.get(), F_SETFL, O_CLOEXEC | O_NONBLOCK); + + const pid_t child_pid = fork(); + if (child_pid == -1) { + LOG(FATAL) << "Failed to fork child for shell command: " << shell_command; + } + + if (child_pid == 0) { // We are in the child process. + close(0); // Don't want to read anything in this process. + dup2(write_fd.get(), 1); // Replace existing stdout with the pipe. + read_fd.reset(-1); + write_fd.reset(-1); + // Note that we're keeping parent stderr. + execl(kShellPath, "sh", "-c", shell_command.c_str(), nullptr); + LOG(FATAL) << "exec() of child failed " << strerror(errno); + } + + // We are in the parent process. + write_fd.reset(-1); // Close this or we never get HUP from child. + struct pollfd shell_output; + memset(&shell_output, 0, sizeof(shell_output)); + shell_output.fd = read_fd.get(); + shell_output.events = POLLIN; + + ssize_t nread; + char buf[512]; + int64_t start_time_ms = GetCurrentTimeMs(); + while (GetCurrentTimeMs() - start_time_ms < kShellTimeoutMs) { + int64_t time_left_ms = kShellTimeoutMs - (GetCurrentTimeMs() - start_time_ms); + poll(&shell_output, 1, (time_left_ms < 0) ? 0 : time_left_ms); + // Blindly read from this file descriptor until there is no data available. + do { + nread = TEMP_FAILURE_RETRY(read(shell_output.fd, buf, sizeof(buf))); + if (output && nread > 0) { + output->append(buf, nread); + } + } while (nread > 0); + + // We're done if the child process has closed its stdout. + if (shell_output.revents & POLLHUP) { + break; + } + } + + // Reap our child's exit status. + int wait_status = 0; + int waitpid_ret = 0; + start_time_ms = GetCurrentTimeMs(); + auto NeedToWaitForChild = [child_pid, &wait_status, &waitpid_ret]() { + if (waitpid_ret == 0) { + waitpid_ret = waitpid(child_pid, &wait_status, WNOHANG); + if (waitpid_ret == -1) { + LOG(ERROR) << "waitpid() returned -1 on error(" << errno << "): " + << strerror(errno); + } + } + return waitpid_ret == 0; + }; + + start_time_ms = GetCurrentTimeMs(); + while (NeedToWaitForChild() && GetCurrentTimeMs() - start_time_ms < 1000) { + usleep(1000); + } + + // Child still hasn't died. Send our child the big hammer. + if (waitpid_ret != child_pid) { + int kill_ret = kill(child_pid, SIGKILL); + // Allow kill to fail with ESRCH, since it indicated that the child may + // have already died. + if (kill_ret != 0 && errno != ESRCH) { + LOG(ERROR) << "Failed to send signal to child: " << strerror(errno); + } + + // Wait for the child to die after receiving that signal. + start_time_ms = GetCurrentTimeMs(); + while (NeedToWaitForChild() && GetCurrentTimeMs() - start_time_ms < 1000) { + usleep(1000); + } + } + + if (waitpid_ret == child_pid && WIFEXITED(wait_status)) { + return WEXITSTATUS(wait_status); + } + + LOG(ERROR) << "Shell command timed out."; + return -1; +} \ No newline at end of file -- Gitee