【Framework】startService启动流程

news2024/11/16 3:46:16

前言

启动service有两种方式:startService和bindService。 这一篇先讲startService,读者如果只想看流程图,可以直接跳到总结。

1. ContextImpl

代码路径:frameworks\base\core\java\android\app\ContextImpl.java

1.1 startService

我们在调用startService的时候,实际是调用到ContextImpl的startService

@Override
public ComponentName startService(Intent service) {
    warnIfCallingFromSystemProcess();
    return startServiceCommon(service, false, mUser);
}

1.2 startServiceCommon

startServiceCommon通过ActivityManager获取AMS的接口IActivityManager,将启动service请求发送给AMS

private ComponentName startServiceCommon(Intent service, boolean requireForeground,
                                         UserHandle user) {
    try {
        // 调用AMS的IActivityManager接口启动service
        ComponentName cn = ActivityManager.getService().startService(
                mMainThread.getApplicationThread(), service,
                service.resolveTypeIfNeeded(getContentResolver()), requireForeground,
                getOpPackageName(), getAttributionTag(), user.getIdentifier());
        if (cn != null) {
            if (cn.getPackageName().equals("!")) {
                throw new SecurityException("Not allowed to start service " + service
                        + " without permission " + cn.getClassName());
            } else if (cn.getPackageName().equals("!!")) {
                throw new SecurityException("Unable to start service " + service
                        + ": " + cn.getClassName());
            } else if (cn.getPackageName().equals("?")) {
                throw ServiceStartNotAllowedException.newInstance(requireForeground,
                        "Not allowed to start service " + service + ": " + cn.getClassName());
            }
        }
        return cn;
    } catch (RemoteException e) {
        throw e.rethrowFromSystemServer();
    }
}

2. ActivityManagerService

代码路径:frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

2.1 startService

AMS 的startService 实现如下

final ActiveServices mServices;
@Override
public ComponentName startService(IApplicationThread caller, Intent service ...) throws TransactionTooLargeException {
    synchronized(this) {
        // 通过Binder获取调用方的进程id
        final int callingPid = Binder.getCallingPid();
        // 通过Binder获取调用方的uid
        final int callingUid = Binder.getCallingUid();
        final long origId = Binder.clearCallingIdentity();
        ComponentName res;
        try {
            res = mServices.startServiceLocked(caller, service,
                    resolvedType, callingPid, callingUid,
                    requireForeground, callingPackage, callingFeatureId, userId);
        } finally {
            Binder.restoreCallingIdentity(origId);
        }
        return res;
    }
}

3. ActiveServices

代码路径:frameworks\base\services\core\java\com\android\server\am\ActiveServices.java

3.1 startServiceLocked

ComponentName startServiceLocked(IApplicationThread caller, Intent service ...) throws TransactionTooLargeException {
    return startServiceLocked(caller, service ....);
}

3.2 startServiceLocked

ComponentName startServiceLocked(IApplicationThread caller, Intent service ...) throws TransactionTooLargeException {
    ....
    // 如果客户端尝试启动/连接的是别名,那么我们需要返回
    // 客户端的别名组件名称,而不是“目标”组件名称,
    final ComponentName realResult = startServiceInnerLocked(r, service, callingUid, callingPid, fgRequired, callerFg, allowBackgroundActivityStarts, backgroundActivityStartsToken);
    if (res.aliasComponent != null
            && !realResult.getPackageName().startsWith("!")
            && !realResult.getPackageName().startsWith("?")) {
        return res.aliasComponent;
    } else {
        return realResult;
    }
}

3.3 startServiceInnerLocked

内部有两个startServiceInnerLocked 的重载,这里只分析最后一个:

ComponentName startServiceInnerLocked(ServiceMap smap, Intent service, ServiceRecord r ...) throws TransactionTooLargeException {
    final int uid = r.appInfo.uid;
    final String packageName = r.name.getPackageName();
    final String serviceName = r.name.getClassName();
    mAm.mBatteryStatsService.noteServiceStartRunning(uid, packageName, serviceName);
    // 准备启动service
    String error = bringUpServiceLocked(r, service.getFlags(), callerFg,
            false /* whileRestarting */,
            false /* permissionsReviewRequired */,
            false /* packageFrozen */,
            true /* enqueueOomAdj */);
    mAm.updateOomAdjPendingTargetsLocked(OomAdjuster.OOM_ADJ_REASON_START_SERVICE);
    if (error != null) {
        return new ComponentName("!!", error);
    }
    return r.name;
}

参数中的ServiceRecord 是用于描述Service,结构如下

代码路径:frameworks\base\services\core\java\com\android\server\am\ServiceRecord.java

final class ServiceRecord extends Binder implements ComponentName.WithComponentName {
    final ComponentName name; // service 组件.
    private final ArrayMap<IBinder, ArrayList<ConnectionRecord>> connections
            = new ArrayMap<IBinder, ArrayList<ConnectionRecord>>();
                            // 所有的绑定Service的客户端
}

3.4 bringUpServiceLocked

private String bringUpServiceLocked(ServiceRecord r, int intentFlags ...) throws TransactionTooLargeException {
    if (r.app != null && r.app.getThread() != null) {
         // 调用service#onSatrtCommand周期
         sendServiceArgsLocked(r, execInFg, false);
         return null;
	}
    if (!whileRestarting && mRestartingServices.contains(r)) {
        // 等待重启,不需要做任何事情
        return null;
    }
    ....
    // 描述运行的应用程序进程的信息
    ProcessRecord app;
    if (!isolated) {
        // 获取进程信息
        app = mAm.getProcessRecordLocked(procName, r.appInfo.uid);
        if (app != null) {
            final IApplicationThread thread = app.getThread();
            final int pid = app.getPid();
            final UidRecord uidRecord = app.getUidRecord();
            // 如果运行Service的应用进程存在,就启动Service
            if (thread != null) {
                try {
                    app.addPackage(r.appInfo.packageName, r.appInfo.longVersionCode,
                            mAm.mProcessStats);
                    // 真正启动service的地方
                    realStartServiceLocked(r, app, thread, pid, uidRecord, execInFg,
                            enqueueOomAdj);
                    return null;
                } catch (TransactionTooLargeException e) {
                    ....
                }
            }
        }
    }
    ...
    // App未运行 -- 启动并排队此服务记录,在应用程序启动时执行。
    if (app == null && !permissionsReviewRequired && !packageFrozen) {
        ....
        // 创建进程,其中procName用来描述Service想要在哪个进程上运行,默认是当前进程
        app = mAm.startProcessLocked(procName, r.appInfo, true, intentFlags,
                    hostingRecord, ZYGOTE_POLICY_FLAG_EMPTY, false, isolated);
        if (app == null) {
            // 启动进程失败
            String msg = "Unable to launch app "
                    + r.appInfo.packageName + "/"
                    + r.appInfo.uid + " for service "
                    + r.intent.getIntent() + ": process is bad";
            Slog.w(TAG, msg);
            bringDownServiceLocked(r, enqueueOomAdj);
            return msg;
        }
    }
    if (r.delayedStop) {
        // 嘿,我们已经被要求停止了!
        r.delayedStop = false;
        if (r.startRequested) {
            // 将服务停止
            stopServiceLocked(r, enqueueOomAdj);
        }
    }
    return null;
}

3.5 realStartServiceLocked

该方法会通过IApplicationThread 调用到客户端进程。

/**
 * 请注意,此方法的名称不应与启动的服务概念混淆。
 * 这里的“start”表示在客户端中启动实例,此方法也会被 bindService()调用
 */
private void realStartServiceLocked(ServiceRecord r, ProcessRecord app,
        IApplicationThread thread, int pid, UidRecord uidRecord, boolean execInFg,
        boolean enqueueOomAdj) throws RemoteException {
    ....
    bumpServiceExecutingLocked(r, execInFg, "create", null /* oomAdjReason */);
    boolean created = false;
    try {
        // 通知客户端创建service
        thread.scheduleCreateService(r, r.serviceInfo,
                mAm.compatibilityInfoForPackage(r.serviceInfo.applicationInfo),
                app.mState.getReportedProcState());
        r.postNotification();
        created = true;
    } catch (DeadObjectException e) {
        // 应用进程已经死亡
        mAm.appDiedLocked(app, "Died when creating service");
        throw e;
    } finally {
        if (!created) {
            final boolean inDestroying = mDestroyingServices.contains(r);
            serviceDoneExecutingLocked(r, inDestroying, inDestroying, false);
            // 清理
            if (newService) {
                psr.stopService(r);
                r.setProcess(null, null, 0, null);
            }
            // 尝试重启服务
            if (!inDestroying) {
                scheduleServiceRestartLocked(r, false);
            }
        }
    }
    // 调用Service的onBind方法
    requestServiceBindingsLocked(r, execInFg);
    updateServiceClientActivitiesLocked(psr, null, true);
    if (newService && created) {
        psr.addBoundClientUidsOfNewService(r);
    }
    // 如果服务处于已启动状态,并且没有
    // pending参数,然后伪造一个,以便调用onStartCommand()
    if (r.startRequested && r.callStart && r.pendingStarts.size() == 0) {
        r.pendingStarts.add(new ServiceRecord.StartItem(r, false, r.makeNextStartId(),
                null, null, 0));
    }
    // 通知客户端调用onStartCommand 方法
    sendServiceArgsLocked(r, execInFg, true);
    if (r.delayedStop) {
        // 需要停止服务
        r.delayedStop = false;
        if (r.startRequested) {
            if (DEBUG_DELAYED_STARTS) Slog.v(TAG_SERVICE,
                    "Applying delayed stop (from start): " + r);
            stopServiceLocked(r, enqueueOomAdj);
        }
    }
}

3.5.1 bumpServiceExecutingLocked

// 将给定的服务记录提升到执行状态。
private boolean bumpServiceExecutingLocked(ServiceRecord r, boolean fg, String why, String oomAdjReason) {
    boolean timeoutNeeded = true;
    ProcessServiceRecord psr;
    if (r.executeNesting == 0) {
        if (r.app != null) {
            psr = r.app.mServices;
            if (timeoutNeeded && psr.numberOfExecutingServices() == 1) {
                // 启动服务超时任务,即开启ANR计时
                scheduleServiceTimeoutLocked(r.app);
            }
        }
    } else if (r.app != null && fg) {
        psr = r.app.mServices;
        if (!psr.shouldExecServicesFg()) {
            if (timeoutNeeded) {
                scheduleServiceTimeoutLocked(r.app);
            }
        }
    }
    ....
    return oomAdjusted;
}

3.5.2 scheduleServiceTimeoutLocked

这里通过handler延时检查service是否发生ANR,其中

  • SERVICE_TIMEOUT = 20s。
  • SERVICE_BACKGROUND_TIMEOUT = SERVICE_TIMEOUT * 10,即200s。
void scheduleServiceTimeoutLocked(ProcessRecord proc) {
    if (proc.mServices.numberOfExecutingServices() == 0 || proc.getThread() == null) {
        return;
    }
    Message msg = mAm.mHandler.obtainMessage(
            ActivityManagerService.SERVICE_TIMEOUT_MSG);
    msg.obj = proc;
    mAm.mHandler.sendMessageDelayed(msg, proc.mServices.shouldExecServicesFg()
            ? SERVICE_TIMEOUT : SERVICE_BACKGROUND_TIMEOUT);
}

3.5.3 sendServiceArgsLocked

该方法主要设置参数,并传给客户端

private final void sendServiceArgsLocked(ServiceRecord r, boolean execInFg,
        boolean oomAdjusted) throws TransactionTooLargeException {
     ParceledListSlice<ServiceStartArgs> slice = new ParceledListSlice<>(args);
     slice.setInlineCountLimit(4);
     Exception caughtException = null;
     try {
         // 调用客户客户端的scheduleServiceArgs,触发onStartCommand周期
         r.app.getThread().scheduleServiceArgs(r, slice);
     } catch (TransactionTooLargeException e) {
         caughtException = e;
     }
}

4. ApplicationThread

代码路径:frameworks\base\core\java\android\app\ActivityThread.java

4.1 scheduleCreateService

在realStartServiceLocked 方法中会调用到IApplicationThread的scheduleCreateService 接口。对应的实现类是在ActivityThread中,这个时候,调用流程就来到了应用端。

private class ApplicationThread extends IApplicationThread.Stub {
   public final void scheduleCreateService(IBinder token,
           ServiceInfo info, CompatibilityInfo compatInfo, int processState) {
       updateProcessState(processState, false);
       CreateServiceData s = new CreateServiceData();
       s.token = token;
       s.info = info;
       s.compatInfo = compatInfo;
       sendMessage(H.CREATE_SERVICE, s);
   }
}

scheduleCreateService将参数封装在CreateServiceData 对象中,并且将消息发送给H类,实现binder线程切换到主线程

private void sendMessage(int what, Object obj, int arg1, int arg2, boolean async) {
    Message msg = Message.obtain();
    msg.what = what;
    msg.obj = obj;
    msg.arg1 = arg1;
    msg.arg2 = arg2;
    if (async) {
        msg.setAsynchronous(true);
    }
    mH.sendMessage(msg);
}

4.2 scheduleServiceArgs

public final void scheduleServiceArgs(IBinder token, ParceledListSlice args) {
    List<ServiceStartArgs> list = args.getList();
    for (int i = 0; i < list.size(); i++) {
        ServiceStartArgs ssa = list.get(i);
        ServiceArgsData s = new ServiceArgsData();
        s.token = token;
        s.taskRemoved = ssa.taskRemoved;
        s.startId = ssa.startId;
        s.flags = ssa.flags;
        s.args = ssa.args;
        sendMessage(H.SERVICE_ARGS, s);
    }
}

5. ActivityThread

代码路径:frameworks\base\core\java\android\app\ActivityThread.java

5.1 H类

在ActivityThread中有一个名为H的Handler类,对应的handleMessage实现如下

class H extends Handler {
    public void handleMessage(Message msg) {
       case CREATE_SERVICE:
          handleCreateService((CreateServiceData)msg.obj);
          Trace.traceEnd(Trace.TRACE_TAG_ACTIVITY_MANAGER);
          break;
       case SERVICE_ARGS:
          handleServiceArgs((ServiceArgsData)msg.obj);
          break;
    }
}

5.2 handleCreateService

  private void handleCreateService(CreateServiceData data) {
        LoadedApk packageInfo = getPackageInfoNoCheck(
                data.info.applicationInfo, data.compatInfo);
        Service service = null;
        try {
            // 获取application对象
            Application app = packageInfo.makeApplicationInner(false, mInstrumentation);
            // 获取classLoader
            final java.lang.ClassLoader cl;
            if (data.info.splitName != null) {
                cl = packageInfo.getSplitClassLoader(data.info.splitName);
            } else {
                cl = packageInfo.getClassLoader();
            }
            // 通过工厂模式创建Service对象
            service = packageInfo.getAppFactory()
                    .instantiateService(cl, data.info.name, data.intent);
            // 获取context对象
            ContextImpl context = ContextImpl.getImpl(service
                    .createServiceBaseContext(this, packageInfo));
            // service 的resource必须使用app的loader
            context.getResources().addLoaders(
                    app.getResources().getLoaders().toArray(new ResourcesLoader[0]));
            // 将Service赋值给context的outerContext字段
            context.setOuterContext(service);
            // attach方法内部与创建的context,application,activityThread进行关联
            service.attach(context, this, data.info.name, data.token, app,
                    ActivityManager.getService());
            // 调用Service的onCreate方法
            service.onCreate();
            mServicesData.put(data.token, data);
            mServices.put(data.token, service);
            try {
                // 通知AMS,Service成功启动
                ActivityManager.getService().serviceDoneExecuting(
                        data.token, SERVICE_DONE_EXECUTING_ANON, 0, 0);
            } catch (RemoteException e) {
                throw e.rethrowFromSystemServer();
            }
        } catch (Exception e) {
            if (!mInstrumentation.onException(service, e)) {
                throw new RuntimeException(
                    "Unable to create service " + data.info.name
                    + ": " + e.toString(), e);
            }
        }
    }

5.3 handleServiceArgs

 private void handleServiceArgs(ServiceArgsData data) {
        CreateServiceData createData = mServicesData.get(data.token);
        Service s = mServices.get(data.token);
        if (s != null) {
            try {
                int res;
                if (!data.taskRemoved) {
                    // 调用onStartCommand周期
                    res = s.onStartCommand(data.args, data.flags, data.startId);
                } else {
                    s.onTaskRemoved(data.args);
                    res = Service.START_TASK_REMOVED_COMPLETE;
                }
                try {
                    // 将res(即启动状态)返回给AMS,通知Service已经启动
                    ActivityManager.getService().serviceDoneExecuting(
                            data.token, SERVICE_DONE_EXECUTING_START, data.startId, res);
                } catch (RemoteException e) {
                    throw e.rethrowFromSystemServer();
                }
            }
        }
    }

6. ActivityManagerService

代码路径:frameworks\base\services\core\java\com\android\server\am\ActivityManagerService.java

6.1 serviceDoneExecuting

该方法只调用了ActiveServices的serviceDoneExecuting

final ActiveServices mServices;

public void serviceDoneExecuting(IBinder token, int type, int startId, int res) {
    synchronized(this) {
        mServices.serviceDoneExecutingLocked((ServiceRecord) token, type, startId, res, false);
    }
}

7. ActiveServices

代码路径:frameworks\base\services\core\java\com\android\server\am\ActiveServices.java

7.1 serviceDoneExecutingLocked

传入的type是SERVICE_DONE_EXECUTING_ANON

    void serviceDoneExecutingLocked(ServiceRecord r, int type, int startId, int res,
            boolean enqueueOomAdj) {
        boolean inDestroying = mDestroyingServices.contains(r);
        if (r != null) {
            // service 已启动,即已调用onStartCommand周期
            if (type == ActivityThread.SERVICE_DONE_EXECUTING_START) {
                // 判断onStartCommand的返回值
                r.callStart = true;
                switch (res) {
                    case Service.START_STICKY_COMPATIBILITY:
                    case Service.START_STICKY: { 
                        // 服务如果被杀,会被系统重新启动
                        r.findDeliveredStart(startId, false, true);
                        // 如果被杀,不要停止服务
                        r.stopIfKilled = false;
                        break;
                    }
                    case Service.START_NOT_STICKY: {
                        // 服务被杀,不会被系统启动
                        r.findDeliveredStart(startId, false, true);
                        if (r.getLastStartId() == startId) {
                            r.stopIfKilled = true;
                        }
                        break;
                    }
                    case Service.START_REDELIVER_INTENT: {
                        // 将保留此项目,但是不重新启动Service,直到调用stop方法。
                        ServiceRecord.StartItem si = r.findDeliveredStart(startId, false, false);
                        if (si != null) {
                            si.deliveryCount = 0;
                            si.doneExecutingCount++;
                            r.stopIfKilled = true;
                        }
                        break;
                    }
                    case Service.START_TASK_REMOVED_COMPLETE: {
                        r.findDeliveredStart(startId, true, true);
                        break;
                    }
                }
            }
            .... 
            final long origId = Binder.clearCallingIdentity();
            // 调用重载方法
            serviceDoneExecutingLocked(r, inDestroying, inDestroying, enqueueOomAdj);
            Binder.restoreCallingIdentity(origId);
        }
    }

7.2 serviceDoneExecutingLocked

private void serviceDoneExecutingLocked(ServiceRecord r, boolean inDestroying,
            boolean finishing, boolean enqueueOomAdj) {
      ....
      if (psr.numberOfExecutingServices() == 0) {
	    // 移除启动超时的任务
	    mAm.mHandler.removeMessages(ActivityManagerService.SERVICE_TIMEOUT_MSG, r.app);
      } else if (r.executeFg) {
          // 需要重新评估应用是否仍需要位于前台。
         for (int i = psr.numberOfExecutingServices() - 1; i >= 0; i--) {
             if (psr.getExecutingServiceAt(i).executeFg) {
                 psr.setExecServicesFg(true);
                 break;
             }
        }
    }
    // 如果service处于回收状态,就移除记录,并清空binding的列表
    if (inDestroying) {
        mDestroyingServices.remove(r);
        r.bindings.clear();
    }
    if (finishing) {
        // 应用没有声明persistent属性
        if (r.app != null && !r.app.isPersistent()) {
            stopServiceAndUpdateAllowlistManagerLocked(r);
        }
        r.setProcess(null, null, 0, null);
    }
}

7.3 stopServiceAndUpdateAllowlistManagerLocked

private void stopServiceAndUpdateAllowlistManagerLocked(ServiceRecord service) {
    final ProcessServiceRecord psr = service.app.mServices;
    // 将记录设置为服务停止,注意该方法不会停止服务,只做记录
    psr.stopService(service);
    psr.updateBoundClientUids();
    if (service.allowlistManager) {
        updateAllowlistManagerLocked(psr);
    }
}

总结

对应的uml图如下

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/707903.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

SHA-256算法及示例

1. 引言 SHA-256&#xff08;安全哈希算法&#xff0c;FIPS 182-2&#xff09;是密码学哈希函数&#xff0c;其摘要长度为256位。SHA-256为keyless哈希函数&#xff0c;即为MDC&#xff08;Manipulation Detection Code&#xff09;。【MAC消息认证码有key&#xff0c;不是key…

【数据库】外键的作用

前言 说到外键&#xff0c;一般就会牵扯出约束。不谈约束&#xff0c;起始外键就是一个普通的字段&#xff08;Column&#xff09;&#xff0c;起到一个关联的作用。 先把约束放一边&#xff0c;看看外键有哪些作用。 建立表中记录的一对一的关系 学生表&#xff1a; 手机表…

C++引用计数

文章目录 1. 什么是引用计数2. 引用计数的实现3. 示例代码 1. 什么是引用计数 引用计数&#xff08;reference count&#xff09;的核心思想是使用一个计数器来标识当前指针指向的对象被多少类的对象所使用&#xff08;即记录指针指向对象被引用的次数&#xff09;。它允许有多…

Linux--查找文件指令:find

语法&#xff1a; find pathname -name 文件名 示例&#xff1a; 从根目录下开始查找名字中带file.txt文件的路径

C++思维导图以及作业

定义一个命名空间Myspace&#xff0c;包含以下函数&#xff1a;将一个字符串中的所有单词进行反转&#xff0c;并输出反转后的结果。例如&#xff0c;输入字符串为"Hello World"&#xff0c;输出结果为"olleH dlroW"&#xff0c;并在主函数内测试该函数。 …

战略书籍排行榜前五名

战略管理对企业的重要性不言而喻。有效的战略管理可以帮助企业确定未来的方向和目标、制定长期发展规划、提高企业的竞争力和获得市场份额。本文推荐的这5本优秀的战略管理类书籍&#xff0c;每一本都有其独特的思想和实践价值&#xff0c;值得企业管理者借鉴和学习。 战略书籍…

Leetcode---351周赛

周赛题目 2748. 美丽下标对的数目 2749. 得到整数零需要执行的最少操作数 2750. 将数组划分成若干好子数组的方式 2751. 机器人碰撞 一、美丽下标对的数目 这题没什么好说的&#xff0c;按照题目的要求直接暴力求解&#xff0c;代码如下 bool gcd(int x,int y){if(x1||y1)…

vue父子组件之间相互控制传值,子组件使用$parent直接控制父组件的值

父子组件之间相互控制传值&#xff0c;子组件控制父组件的值 需求概述 父组件在提交表单后&#xff0c;弹框进行提示&#xff0c;子组件是一个弹框。 vue版本 v2.x 实现原理 在父组件内建立控制器isShowModal&#xff0c;使用v-if来控制子组件的显示与隐藏。在子组件通过…

DAY39——动态规划part2

1.考虑障碍在起点和终点的特殊状况&#xff0c;可直接返回0 2.判断是否存在障碍物&#xff1a;初始化时需要设置障碍物后的坐标为0

常见存储引擎

TiKV 简介 TiKV 是一个分布式事务型的键值数据库&#xff0c;提供了满足 ACID 约束的分布式事务接口&#xff0c;并且通过Raft协议保证了多副本数据一致性以及高可用。TiKV 作为 TiDB 的存储层&#xff0c;为用户写入 TiDB 的数据提供了持久化以及读写服务&#xff0c;同时还存…

Spring BeanFactory FactoryBean的区别?

文章目录 前言一、BeanFactory二、FactoryBean 前言 面试中被问到过Spring BeanFactory FactoryBean的区别&#xff1f;当时没答上来&#xff0c;感觉这就是一个文字游戏&#xff0c;后面仔细的了解了一下&#xff0c;分享给大家。 一、BeanFactory 在 Spring 中最核心的就是…

GDB 断点管理

1、b 设置断点 usage 1: b 函数名 usage 2: b 文件名:行号 2、rb 函数名关键字 &#xff1a; 所有带有这个关键字的函数名都设置为断点 (gdb) rb dkauth Breakpoint 7 at 0x34187ae0: file /home/jintao/cvf/apps/cvf/services/dkauth/src/dkauth.c, line 58. void dkauth_…

Python之花舞盛宴:探索列表与元组之美与妙用

前言 在Python编程世界中&#xff0c;列表和元组是两个最常用的数据结构之一。无论是初学者还是经验丰富的开发者&#xff0c;对于这两个数据类型的掌握都至关重要。 列表和元组都是用于存储多个值的容器&#xff0c;但它们在性质和特性上有所不同。列表是可变的&#xff0c;…

VMIC-pci-5565反射内存的优势

优势&#xff1a; &#xff08;1&#xff09;实现远程互连的能力 随着仿真实验复杂度的提高&#xff0c;需要多楼宇多试验室间设备的远程互连&#xff0c;通过单模光纤及光纤HUB将远距离的试验室设备进行连接&#xff0c;单模光纤支持的传输距离可达20km。对于距离300m以内的试…

免费搭建一个有脾气的聊天机器人,1行Python代码就够了!

大家好&#xff0c;这里是程序员晚枫。 之前在小破站&#xff1a;Python自动化办公社区给大家免费分享了用Python制作一个wx机器人&#xff0c;1行代码人人可用&#xff0c;很多人还想要免费的智能聊天功能。 今天终于开发出来了&#xff0c;让我们一起看一下&#xff0c;如何…

第四课—大学英语四六级备考—听力专项

Key Words 1.monarch n.君主政治 非常抱歉误解了您的问题。以下是关于"monarch"这两个意义的常见用法、造句和固定搭配的例子&#xff1a; 1. Monarch&#xff08;君主&#xff09;&#xff1a; - 造句&#xff1a; - The monarch of the country made an…

爆文:硬件工程师如何零基础入门?

大家好&#xff0c;我是记得诚。 最近老是有读者问我学习路线&#xff0c;之前其实写过一篇文章&#xff1a; 硬件工程师如何零基础入门&#xff1f;&#xff08;点击阅读&#xff09; 觉得很有必要再发一遍&#xff0c;这篇文章&#xff0c;在全网也算爆文了。 在CSDN有65…

SpringBoot项目中PageHelper给子查询做分页

目录 1.起因 2.sql改进 3.改造pagehelper 4.新的问题 方案一 方案二 方案三(终极) 5.附上pagehelper官方使用指南 1.起因 项目中有个查询非常慢,查询第一页都很慢, sql为一张大表A left join了许多张基础数据表(小表), select * from A left join b on xxx left join…

安全、环保的随身“数据库”——体验希捷锦系列移动硬盘

移动硬盘不属于那种技术更新换代快、差异化明显的产品&#xff0c;因此消费者在选购时关注的点无外乎容量、价格、外观、传输速度以及数据安全等方面。希捷新推出的锦系列移动硬盘&#xff0c;就聚焦数据安全&#xff0c;同时在选材上贯彻了环保理念&#xff0c;设计方面也值得…

Linux限制用户使用特权-限制su

Linux下&#xff0c;允许普通用户通过su命令切换到root身份进行操作。 相对于直接使用root登录操作&#xff0c;这钟方法要安全些。但仍存在安全隐患。 缩小特权使用用户的范围&#xff0c;无疑是更安全的做法。 Linux提供了这样的设置。 编辑/etc/pam.d/su文件&#xff0c…