Android中杀进程的几种方法 - killBackgroundProcesses

Posted Jtag特工

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了Android中杀进程的几种方法 - killBackgroundProcesses相关的知识,希望对你有一定的参考价值。

android中杀进程的几种方法 (1) - killBackgroundProcesses

ActivityManager中提供了几种方式来杀进程,比如有forceStopPackage、removeTask、killBackgroundProcesses等。
下面我们就来看看它们的背后都做了些什么。

removeTask

我们之前已经写了一篇《关于Android的浅杀》来介绍这个方法的变迁,大家可以回忆一下找找感觉。
复习一下removeTask的好处是,相对后面几个,它的逻辑要简单得多。

killBackgroundProcesses

ActivityManager中的killBackgroundProcesses

这是目前(截止至API24)唯一官方公开建议使用的方法,其它的都是隐藏的API。
我们先看一张这个API背后调用的简图:

另外有一个被废弃的restartPackage方法,现在只是killBackgroundProcesses的马甲。

2400    @Deprecated
2401    public void restartPackage(String packageName) 
2402        killBackgroundProcesses(packageName);
2403    

调用killBackgroundProcesses需要权限android.Manifest.permission.KILL_BACKGROUND_PROCESSES

按照惯例,这个方法肯定是通过IPC调用到AMS中:

2418    public void killBackgroundProcesses(String packageName) 
2419        try 
2420            ActivityManagerNative.getDefault().killBackgroundProcesses(packageName,
2421                    UserHandle.myUserId());
2422         catch (RemoteException e) 
2423        
2424    

AMS中的killBackgroundProcesses

我们直接来看AMS中的killBackgroundProcesses:

5202    @Override
5203    public void killBackgroundProcesses(final String packageName, int userId) 

首先就是检查KILL_BACKGROUND_PROCESSES的权限:

5204        if (checkCallingPermission(android.Manifest.permission.KILL_BACKGROUND_PROCESSES)
5205                != PackageManager.PERMISSION_GRANTED &&
5206                checkCallingPermission(android.Manifest.permission.RESTART_PACKAGES)
5207                        != PackageManager.PERMISSION_GRANTED) 
5208            String msg = "Permission Denial: killBackgroundProcesses() from pid="
5209                    + Binder.getCallingPid()
5210                    + ", uid=" + Binder.getCallingUid()
5211                    + " requires " + android.Manifest.permission.KILL_BACKGROUND_PROCESSES;
5212            Slog.w(TAG, msg);
5213            throw new SecurityException(msg);
5214        
5215

下面正式开始干活:

5216        userId = handleIncomingUser(Binder.getCallingPid(), Binder.getCallingUid(),
5217                userId, true, ALLOW_FULL_ONLY, "killBackgroundProcesses", null);
5218        long callingId = Binder.clearCallingIdentity();
5219        try 
5220            IPackageManager pm = AppGlobals.getPackageManager();
5221            synchronized(this) 
5222                int appId = -1;
5223                try 
5224                    appId = UserHandle.getAppId(pm.getPackageUid(packageName, 0));
5225                 catch (RemoteException e) 
5226                
5227                if (appId == -1) 
5228                    Slog.w(TAG, "Invalid packageName: " + packageName);
5229                    return;
5230                

真正的逻辑都在killPackageProcessesLocked中。调用进来的时候只有包名和用户ID两个参数,但是killPackageProcessesLocked却搞出来9个参数。

5231                killPackageProcessesLocked(packageName, appId, userId,
5232                        ProcessList.SERVICE_ADJ, false, true, true, false, "kill background");
5233            
5234         finally 
5235            Binder.restoreCallingIdentity(callingId);
5236        
5237    

AMS的killPackageProcessesLocked

5541    private final boolean killPackageProcessesLocked(String packageName, int appId,
5542            int userId, int minOomAdj, boolean callerWillRestart, boolean allowRestart,
5543            boolean doit, boolean evenPersistent, String reason) 
5544        ArrayList<ProcessRecord> procs = new ArrayList<>();
5545
5546        // Remove all processes this package may have touched: all with the
5547        // same UID (except for the system or root user), and all whose name
5548        // matches the package name.
5549        final int NP = mProcessNames.getMap().size();
5550        for (int ip=0; ip<NP; ip++) 
5551            SparseArray<ProcessRecord> apps = mProcessNames.getMap().valueAt(ip);
5552            final int NA = apps.size();
5553            for (int ia=0; ia<NA; ia++) 
5554                ProcessRecord app = apps.valueAt(ia);
5555                if (app.persistent && !evenPersistent) 
5556                    // we don't kill persistent processes
5557                    continue;
5558                
5559                if (app.removed) 
5560                    if (doit) 
5561                        procs.add(app);
5562                    
5563                    continue;
5564                
5565
5566                // Skip process if it doesn't meet our oom adj requirement.
5567                if (app.setAdj < minOomAdj) 
5568                    continue;
5569                
5570
5571                // If no package is specified, we call all processes under the
5572                // give user id.
5573                if (packageName == null) 
5574                    if (userId != UserHandle.USER_ALL && app.userId != userId) 
5575                        continue;
5576                    
5577                    if (appId >= 0 && UserHandle.getAppId(app.uid) != appId) 
5578                        continue;
5579                    
5580                // Package has been specified, we want to hit all processes
5581                // that match it.  We need to qualify this by the processes
5582                // that are running under the specified app and user ID.
5583                 else 
5584                    final boolean isDep = app.pkgDeps != null
5585                            && app.pkgDeps.contains(packageName);
5586                    if (!isDep && UserHandle.getAppId(app.uid) != appId) 
5587                        continue;
5588                    
5589                    if (userId != UserHandle.USER_ALL && app.userId != userId) 
5590                        continue;
5591                    
5592                    if (!app.pkgList.containsKey(packageName) && !isDep) 
5593                        continue;
5594                    
5595                
5596
5597                // Process has passed all conditions, kill it!
5598                if (!doit) 
5599                    return true;
5600                
5601                app.removed = true;
5602                procs.add(app);
5603            
5604        

前面各种条件都准备好了之后,针对每一个proc去调用removeProcessLocked.

5606        int N = procs.size();
5607        for (int i=0; i<N; i++) 
5608            removeProcessLocked(procs.get(i), callerWillRestart, allowRestart, reason);
5609        
5610        updateOomAdjLocked();
5611        return N > 0;
5612    

AMS之removeProcessLocked(4)

5916    private final boolean removeProcessLocked(ProcessRecord app,
5917            boolean callerWillRestart, boolean allowRestart, String reason) 
5918        final String name = app.processName;
5919        final int uid = app.uid;
...

下面再调用两个参数的removeProcessNameLocked.

5923        removeProcessNameLocked(name, uid);
5924        if (mHeavyWeightProcess == app) 
5925            mHandler.sendMessage(mHandler.obtainMessage(CANCEL_HEAVY_NOTIFICATION_MSG,
5926                    mHeavyWeightProcess.userId, 0));
5927            mHeavyWeightProcess = null;
5928        
5929        boolean needRestart = false;
5930        if (app.pid > 0 && app.pid != MY_PID) 
5931            int pid = app.pid;
5932            synchronized (mPidsSelfLocked) 
5933                mPidsSelfLocked.remove(pid);
5934                mHandler.removeMessages(PROC_START_TIMEOUT_MSG, app);
5935            
5936            mBatteryStatsService.noteProcessFinish(app.processName, app.info.uid);
5937            if (app.isolated) 
5938                mBatteryStatsService.removeIsolatedUid(app.uid, app.info.uid);
5939            
5940            boolean willRestart = false;
5941            if (app.persistent && !app.isolated) 
5942                if (!callerWillRestart) 
5943                    willRestart = true;
5944                 else 
5945                    needRestart = true;
5946                
5947            

前面的该通知的都通知到了,终于可以正式开杀了。

5948            app.kill(reason, true);
5949            handleAppDiedLocked(app, willRestart, allowRestart);
5950            if (willRestart) 
5951                removeLruProcessLocked(app);
5952                addAppLocked(app.info, false, null /* ABI override */);
5953            
5954         else 
5955            mRemovedProcesses.add(app);
5956        
5957
5958        return needRestart;
5959    

ProcessRecord之kill

543    void kill(String reason, boolean noisy) 
544        if (!killedByAm) 
545            Trace.traceBegin(Trace.TRACE_TAG_ACTIVITY_MANAGER, "kill");
546            if (noisy) 
547                Slog.i(TAG, "Killing " + toShortString() + " (adj " + setAdj + "): " + reason);
548            
549            EventLog.writeEvent(EventLogTags.AM_KILL, userId, pid, processName, setAdj, reason);
550            Process.killProcessQuiet(pid);
551            Process.killProcessGroup(info.uid, pid);
552            if (!persistent) 
553                killed = true;
554                killedByAm = true;
555            
556            Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
557        
558    

Process之killProcessQuiet

非常省事儿,发给一个SIGNAL_KILL出去就是了。

1068    public static final void killProcessQuiet(int pid) 
1069        sendSignalQuiet(pid, SIGNAL_KILL);
1070    

Process之killProcessGroup

这个直接用native写的。

public static final native int killProcessGroup(int uid, int pid);

android_util_Process.cpp之android_os_Process_killProcessGroup

JNI函数只是一个简单地对libprocessgroup中的killProcessGroup的封装

1035jint android_os_Process_killProcessGroup(JNIEnv* env, jobject clazz, jint uid, jint pid)
1036
1037    return killProcessGroup(uid, pid, SIGKILL);
1038

processgroup.cpp之killProcessGroup

这是我们的逻辑头一次走进/system/core/中,位于/system/core/libprocessgroup/processgroup.cpp

252int killProcessGroup(uid_t uid, int initialPid, int signal)
253
254    int processes;
255    const int sleep_us = 5 * 1000;  // 5ms
256    int64_t startTime = android::uptimeMillis();
257    int retry = 40;
258
259    while ((processes = killProcessGroupOnce(uid, initialPid, signal)) > 0) 
260        SLOGV("killed %d processes for processgroup %d\\n", processes, initialPid);
261        if (retry > 0) 
262            usleep(sleep_us);
263            --retry;
264         else 
265            SLOGE("failed to kill %d processes for processgroup %d\\n",
266                    processes, initialPid);
267            break;
268        
269    
270
271    SLOGV("Killed process group uid %d pid %d in %" PRId64 "ms, %d procs remain", uid, initialPid,
272            android::uptimeMillis()-startTime, processes);
273
274    if (processes == 0) 
275        return removeProcessGroup(uid, initialPid);
276     else 
277        return -1;
278    
279

killProcessGroupOnce

216static int killProcessGroupOnce(uid_t uid, int initialPid, int signal)
217
218    int processes = 0;
219    struct ctx ctx;
220    pid_t pid;
221
222    ctx.initialized = false;
223
224    while ((pid = getOneAppProcess(uid, initialPid, &ctx)) >= 0) 
225        processes++;
226        if (pid == 0) 
227            // Should never happen...  but if it does, trying to kill this
228            // will boomerang right back and kill us!  Let's not let that happen.
229            SLOGW("Yikes, we've been told to kill pid 0!  How about we don't do that.");
230            continue;
231        
232        if (pid != initialPid) 
233            // We want to be noisy about killing processes so we can understand
234            // what is going on in the log; however, don't be noisy about the base
235            // process, since that it something we always kill, and we have already
236            // logged elsewhere about killing it.
237            SLOGI("Killing pid %d in uid %d as part of process group %d", pid, uid, initialPid);
238        
239        int ret = kill(pid, signal);
240        if (ret == -1) 
241            SLOGW("failed to kill pid %d: %s", pid, strerror(errno));
242        
243    
244
245    if (ctx.initialized) 
246        close(ctx.fd);
247    
248
249    return processes;
250

getOneAppProcess

我们再跟一下这个有趣的函数:getOneAppProcess

112static pid_t getOneAppProcess(uid_t uid, int appProcessPid, struct ctx *ctx)
113
114    if (!ctx->initialized) 
115        int ret = initCtx(uid, appProcessPid, ctx);
116        if (ret < 0) 
117            return ret;
118        
119    
120
121    char *eptr;
122    while ((eptr = (char *)memchr(ctx->buf_ptr, '\\n', ctx->buf_len)) == NULL) 
123        int ret = refillBuffer(ctx);
124        if (ret == 0) 
125            return -ERANGE;
126        
127        if (ret < 0) 
128            return ret;
129        
130    
131
132    *eptr = '\\0';
133    char *pid_eptr = NULL;
134    errno = 0;
135    long pid = strtol(ctx->buf_ptr, &pid_eptr, 10);
136    if (errno != 0) 
137        return -errno;
138    
139    if (pid_eptr != eptr) 
140        return -EINVAL;
141    
142
143    ctx->buf_len -= (eptr - ctx->buf_ptr) + 1;
144    ctx->buf_ptr = eptr + 1;
145
146    return (pid_t)pid;
147

removeProcessGroup

回头我们再看看removeProcessGroup,还要把对应的目录删掉。

149static int removeProcessGroup(uid_t uid, int pid)
150
151    int ret;
152    char path[PROCESSGROUP_MAX_PATH_LEN] = 0;
153
154    convertUidPidToPath(path, sizeof(path), uid, pid);
155    ret = rmdir(path);
156
157    convertUidToPath(path, sizeof(path), uid);
158    rmdir(path);
159
160    return ret;
161

AMS之removeProcessLocked(2)

5872    private final ProcessRecord removeProcessNameLocked(final String name, final int uid) 
5873        ProcessRecord old = mProcessNames.remove(name, uid);
5874        if (old != null) 
5875            old.uidRecord.numProcs--;
5876            if (old.uidRecord.numProcs == 0) 
5877                // No more processes using this uid, tell clients it is gone.
5878                if (DEBUG_UID_OBSERVERS) Slog.i(TAG_UID_OBSERVERS,
5879                        "No more processes in " + old.uidRecord);
5880                enqueueUidChangeLocked(old.uidRecord, true);
5881                mActiveUids.remove(uid);
5882            
5883            old.uidRecord = null;
5884        
5885        mIsolatedProcesses.remove(uid);
5886        return old;
5887    

AMS之enqueueUidChangeLocked

18855    private final void enqueueUidChangeLocked(UidRecord uidRec, boolean gone) 
18856        if (uidRec.pendingChange == null) 
18857            if (mPendingUidChanges.size() == 0) 
...
18860                mUiHandler.obtainMessage(DISPATCH_UIDS_CHANGED_MSG).sendToTarget();
18861            
18862            final int NA = mAvailUidChanges.size();
18863            if (NA > 0) 
18864                uidRec.pendingChange = mAvailUidChanges.remove(NA-1);
...
18867             else 
18868                uidRec.pendingChange = new UidRecord.ChangeItem();
...
18871            
18872            uidRec.pendingChange.uidRecord = uidRec;
18873            uidRec.pendingChange.uid = uidRec.uid;
18874            mPendingUidChanges.add(uidRec.pendingChange);
18875        
18876        uidRec.pendingChange.gone = gone;
18877        uidRec.pendingChange.processState = uidRec.setProcState;
18878    

DISPATCH_UIDS_CHANGED_MSG消息在Handler中是如何处理的呢?我们查AMS中的UiHandler:

1611            case DISPATCH_UIDS_CHANGED_MSG: 
1612                dispatchUidsChanged();
1613             break;

AMS之dispatchUidsChanged

3787    private void dispatchUidsChanged() 
3788        int N;
3789        synchronized (this) 
3790            N = mPendingUidChanges.size();
3791            if (mActiveUidChanges.length < N) 
3792                mActiveUidChanges = new UidRecord.ChangeItem[N];
3793            
3794            for (int i=0; i<N; i++) 
3795                final UidRecord.ChangeItem change = mPendingUidChanges.get(i);
3796                mActiveUidChanges[i] = change;
3797                change.uidRecord.pendingChange = null;
3798                change.uidRecord = null;
3799            
3800            mPendingUidChanges.clear();
...
3803        
3804
3805        if (mLocalPowerManager != null) 
3806            for (int j=0; j<N; j++) 
3807                UidRecord.ChangeItem item = mActiveUidChanges[j];
3808                if (item.gone) 
3809                    mLocalPowerManager.uidGone(item.uid);
3810                 else 
3811                    mLocalPowerManager.updateUidProcState(item.uid, item.processState);
3812                
3813            
3814        
3815
3816        int i = mUidObservers.beginBroadcast();
3817        while (i > 0) 
3818            i--;
3819            final IUidObserver observer = mUidObservers.getBroadcastItem(i);
3820            if (observer != null) 
3821                try 
3822                    for (int j=0; j<N; j++) 
3823                        UidRecord.ChangeItem item = mActiveUidChanges[j];
3824                        if (item.gone) 
...
3827                            observer.onUidGone(item.uid);
3828                         else 
...
3832                            observer.onUidStateChanged(item.uid, item.processState);
3833                        
3834                    
3835                 catch (RemoteException e) 
3836                
3837            
3838        
3839        mUidObservers.finishBroadcast();
3840
3841        synchronized (this) 
3842            for (int j=0; j<N; j++) 
3843                mAvailUidChanges.add(mActiveUidChanges[j]);
3844            
3845        
3846    

AMS之handleAppDiedLocked

最后我们再看一下handleAppDiedLocked,杀了之后,埋的事情也还是要管的。

4564    private final void handleAppDiedLocked(ProcessRecord app,
4565            boolean restarting, boolean allowRestart) 
4566        int pid = app.pid;

后面大家可以看到这个清理用了180多行的代码,而这还不是全部。

4567        boolean kept = cleanUpApplicationRecordLocked(app, restarting, allowRestart, -1);

mLruProcesses也是要清理的。

4568        if (!kept && !restarting) 
4569            removeLruProcessLocked(app);
4570            if (pid > 0) 
4571                ProcessList.remove(pid);
4572            
4573        
4574
4575        if (mProfileProc == app) 
4576            clearProfilerLocked();
4577        

每个Activity负责去做自己的清理,这部分暂时先不分析了,以后用到了再说。

4579        // Remove this application's activities from active lists.
4580        boolean hasVisibleActivities = mStackSupervisor.handleAppDiedLocked(app);
4581
4582        app.activities.clear();
4583
4584        if (app.instrumentationClass != null) 
4585            Slog.w(TAG, "Crash of app " + app.processName
4586                  + " running instrumentation " + app.instrumentationClass);
4587            Bundle info = new Bundle();
4588            info.putString("shortMsg", "Process crashed.");
4589            finishInstrumentationLocked(app, Activity.RESULT_CANCELED, info);
4590        
4591
4592        if (!restarting && hasVisibleActivities && !mStackSupervisor.resumeTopActivitiesLocked()) 
4593            // If there was nothing to resume, and we are not already
4594            // restarting this process, but there is a visible activity that
4595            // is hosted by the process...  then make sure all visible
4596            // activities are running, taking care of restarting this
4597            // process.
4598            mStackSupervisor.ensureActivitiesVisibleLocked(null, 0);
4599        
4600    

AMS之removeLruProcessLocked

不光remove LRU,如果没杀掉的话,这里还会再杀一次。

2811    final void removeLruProcessLocked(ProcessRecord app) 
2812        int lrui = mLruProcesses.lastIndexOf(app);
2813        if (lrui >= 0) 
2814            if (!app.killed) 
2815                Slog.wtfStack(TAG, "Removing process that hasn't been killed: " + app);
2816                Process.killProcessQuiet(app.pid);
2817                killProcessGroup(app.info.uid, app.pid);
2818            
2819            if (lrui <= mLruProcessActivityStart) 
2820                mLruProcessActivityStart--;
2821            
2822            if (lrui <= mLruProcessServiceStart) 
2823                mLruProcessServiceStart--;
2824            
2825            mLruProcesses.remove(lrui);
2826        
2827    

AMS之cleanUpApplicationRecordLocked

这段就不细解释了,原文贴出来的原因是希望大家都体会到,埋葬还有这么多事情要做的。

15504    private final boolean cleanUpApplicationRecordLocked(ProcessRecord app,
15505            boolean restarting, boolean allowRestart, int index) 
15506        if (index >= 0) 
15507            removeLruProcessLocked(app);
15508            ProcessList.remove(app.pid);
15509        
15510
15511        mProcessesToGc.remove(app);
15512        mPendingPssProcesses.remove(app);
15513
15514        // Dismiss any open dialogs.
15515        if (app.crashDialog != null && !app.forceCrashReport) 
15516            app.crashDialog.dismiss();
15517            app.crashDialog = null;
15518        
15519        if (app.anrDialog != null) 
15520            app.anrDialog.dismiss();
15521            app.anrDialog = null;
15522        
15523        if (app.waitDialog != null) 
15524            app.waitDialog.dismiss();
15525            app.waitDialog = null;
15526        
15527
15528        app.crashing = false;
15529        app.notResponding = false;
15530
15531        app.resetPackageList(mProcessStats);
15532        app.unlinkDeathRecipient();
15533        app.makeInactive(mProcessStats);
15534        app.waitingToKill = null;
15535        app.forcingToForeground = null;
15536        updateProcessForegroundLocked(app, false, false);
15537        app.foregroundActivities = false;
15538        app.hasShownUi = false;
15539        app.treatLikeActivity = false;
15540        app.hasAboveClient = false;
15541        app.hasClientActivities = false;
15542
15543        mServices.killServicesLocked(app, allowRestart);
15544
15545        boolean restart = false;
15546
15547        // Remove published content providers.
15548        for (int i = app.pubProviders.size() - 1; i >= 0; i--) 
15549            ContentProviderRecord cpr = app.pubProviders.valueAt(i);
15550            final boolean always = app.bad || !allowRestart;
15551            boolean inLaunching = removeDyingProviderLocked(app, cpr, always);
15552            if ((inLaunching || always) && cpr.hasConnectionOrHandle()) 
15553                // We left the provider in the launching list, need to
15554                // restart it.
15555                restart = true;
15556            
15557
15558            cpr.provider = null;
15559            cpr.proc = null;
15560        
15561        app.pubProviders.clear();
15562
15563        // Take care of any launching providers waiting for this process.
15564        if (cleanupAppInLaunchingProvidersLocked(app, false)) 
15565            restart = true;
15566        
15567
15568        // Unregister from connected content providers.
15569        if (!app.conProviders.isEmpty()) 
15570            for (int i = app.conProviders.size() - 1; i >= 0; i--) 
15571                ContentProviderConnection conn = app.conProviders.get(i);
15572                conn.provider.connections.remove(conn);
15573                stopAssociationLocked(app.uid, app.processName, conn.provider.uid,
15574                        conn.provider.name);
15575            
15576            app.conProviders.clear();
15577        
15578
15579        // At this point there may be remaining entries in mLaunchingProviders
15580        // where we were the only one waiting, so they are no longer of use.
15581        // Look for these and clean up if found.
15582        // XXX Commented out for now.  Trying to figure out a way to reproduce
15583        // the actual situation to identify what is actually going on.
15584        if (false) 
15585            for (int i = mLaunchingProviders.size() - 1; i >= 0; i--) 
15586                ContentProviderRecord cpr = mLaunchingProviders.get(i);
15587                if (cpr.connections.size() <= 0 && !cpr.hasExternalProcessHandles()) 
15588                    synchronized (cpr) 
15589                        cpr.launchingApp = null;
15590                        cpr.notifyAll();
15591                    
15592                
15593            
15594        
15595
15596        skipCurrentReceiverLocked(app);
15597
15598        // Unregister any receivers.
15599        for (int i = app.receivers.size() - 1; i >= 0; i--) 
15600            removeReceiverLocked(app.receivers.valueAt(i));
15601        
15602        app.receivers.clear();
15603
15604        // If the app is undergoing backup, tell the backup manager about it
15605        if (mBackupTarget != null && app.pid == mBackupTarget.app.pid) 
15606            if (DEBUG_BACKUP || DEBUG_CLEANUP) Slog.d(TAG_CLEANUP, "App "
15607                    + mBackupTarget.appInfo + " died during backup");
15608            try 
15609                IBackupManager bm = IBackupManager.Stub.asInterface(
15610                        ServiceManager.getService(Context.BACKUP_SERVICE));
15611                bm.agentDisconnected(app.info.packageName);
15612             catch (RemoteException e) 
15613                // can't happen; backup manager is local
15614            
15615        
15616
15617        for (int i = mPendingProcessChanges.size() - 1; i >= 0; i--) 
15618            ProcessChangeItem item = mPendingProcessChanges.get(i);
15619            if (item.pid == app.pid) 
15620                mPendingProcessChanges.remove(i);
15621                mAvailProcessChanges.add(item);
15622            
15623        
15624        mUiHandler.obtainMessage(DISPATCH_PROCESS_DIED, app.pid, app.info.uid, Android 进程间通信的几种实现方式

Android 进程间通信的几种实现方式

View 的几种状态

Android中实现IPC的几种方式详细分析及比较

查看某个进程运行时间的几种方法

Eclipse启动后“未响应”或加载慢的几种情况