Android init.rc整理

2023-10-31

AIL概述

init.rc由AIL语言编写而成。可以参考system/core/init/README.md来学习AIL语法相关知识。不同Android版本关于AIL的说明存在一些细微差异,但基本语法和总的思路是不变的。往往我们可以先查看对应的system/core/init/README.md来了解这些差异。 以下是我们参考Android11下的system/core/init/README.md,对AIL的学习总结。

  • Android Init Language由五大类语句组成:Actions, Commands, Services, Options, Imports
  • 换行为语句分隔符。空格为词分隔符。反斜杠转义符可用于插入不做分隔的空格符。双引号用于包括不需要空格分隔的词。行尾反斜杠用于多行衔接为一行。
  • 行首以 # 开始用于注释。
  • 使用${var}可进行变量扩展。
  • 一段内容往往以Actions 或者Services开头,并以Commands或者Options结尾。
    Commands 或者Options用于修饰Actions 或者Services。(阅读时注意)
  • Service有唯一名称,如果已经定义过一个相同的Service,后定义的被忽略,且log中记录了此异常。

Init .rc 文件

  • AIL语言用于扩展名为rc的文件中。
  • 一般设备系统中多个路径下都存在rc文件。一般规范的路径为:/{system,vendor,odm}/etc/init/*.rc。
  • 根目录/init.rc为主rc文件,它是rc文件被加载的入口。它通过Imports语句将其他各个路径下的rc文件都加载进来。
  • 不支持第一阶段进行mout机制的Legacy设备(legacy是什么意思?与uefi有什么区别),原先是可以在mount_all的时候进行导入rc文件,但这种方式已被弃用,并且在Android Q(Android10)之后是不允许的。
rc文件目录规范:
  1. /system/etc/init/ 用于核心系统功能,例如SurfaceFlinger、MediaService和logd等。
  2. /vendor/etc/init/ 用于芯片供应商的一些核心操作或者守护进程功能。
  3. /odm/etc/init/ 用于设备制造厂商的功能,比如传感器和其他外围设备所需的操作或者守护进程。
  • 服务对应的二进制程序如果放在system|vendor|odm分区下,相应的定义服务的rc文件也需要在相同分区的etc/init/目录下。一般我们开发的时候rc和二进制程序是在相同的工程目录下,在Android.mk 中通过 LOCAL_INIT_RC宏定义指定rc文件能将其编译到对应分区的etc/init/目录下。(Android.bp 是通过配置init_rc)。
//logcatd开发的工程目录在system/core/logcat
//logcatd  logcatd.rc都在此工程目录下
//通过Android.bp指定init_rc 

//@system/core/logcat/Android.bp
sh_binary {
    name: "logpersist.start",
    src: "logpersist",
    init_rc: ["logcatd.rc"],
    required: ["logcatd"],
    symlinks: [
        "logpersist.stop",
        "logpersist.cat",
    ],
//最终编译出来的logcatd.rc就位于system/etc/init/logcatd.rc
$ cd out/target/product/${TARGET_PRODUCT}
$ find -name logcatd.rc
./system/etc/init/logcatd.rc

说明: 将二进制程序相关的服务配置到它独立对应的rc文件中相比之前那样在一个统一的rc文件中全部定义进程的服务更好。这种方法确保该二进制程序对应的Service是唯一的。并且更加直观感受到服务配置和二进制程序是完整的整体,理解该二进制程序就是在init时候初始化的。同时也避免出现多个服务绑定一个程序,或者重复服务的定义。


语法详解

Services

Service指的是需要在初始化就启动的服务,通过定义还可以让他们在退出的时候自动重启。格式如下:

service <name> <pathname> [ <argument> ]*
   <option>
   <option>
   ...

Options

Option 是修饰service的属性,它用于定义service何时启动以及怎样启动。

  • capabilities [ <capability>\* ] 表示执行该service的能力,这些就是Linux MAN定义的,但需要去掉"CAP_"前缀。如果没有增加capabilities修饰,则表示该服务没有任何capabilities。

      ###example1, BLOCK_SUSPEND:
      service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service.ranchu
          class hal
          user audioserver
          # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
          group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
          #有能力block系统休眠
          capabilities BLOCK_SUSPEND
          ioprio rt 4
          task_profiles ProcessCapacityHigh HighPerformance
          onrestart restart audioserver
          
      ###example2,NET_ADMIN NET_RAW:
      service ril-daemon /vendor/bin/hw/libgoldfish-rild
          class main
          user radio
          group radio cache inet misc audio log readproc wakelock
          #NET_ADMIN:执行和网络相关的一系列操作,例如接口配置,防火墙配置等等
          #NET_RAW: 使用RAW and PACKET socket接字绑定到任何地址进行透明代理
          capabilities BLOCK_SUSPEND NET_ADMIN NET_RAW
      
      ###example3,SYS_BOOT:
      service charger /system/bin/charger
          class charger
          seclabel u:r:charger:s0
          user system
          group system wakelock input
          #有能力进行reboot或者调用kexec_load
          capabilities SYS_BOOT
          file /dev/kmsg w
          file /sys/fs/pstore/console-ramoops-0 r
          file /sys/fs/pstore/console-ramoops r
          file /proc/last_kmsg r
    
  • class <name> [ <name>\* ] 用于指定service的类名,相同的类名可以在action中配置成一起启动或者一起停止。 一个service至少有一个class,如果没有指定 class name, 默认class 为"default"。当然也可以指定多个class name, 多个class name便于service分组管理。 TODO:关于core,main,late_start具体启动时间我们在后面章节再继续详细说明。

      ###example1, animation 
      service vendor.gralloc-3-0 /vendor/bin/hw/android.hardware.graphics.allocator@3.0-service
          interface android.hardware.graphics.allocator@3.0::IAllocator default
          #`animation` calss是指可以进行开机动画或者关机动画的服务组。
          #这些服务往往很早就运行或者在关机的最后阶段运行的
          #因此需要注意他们不能去打开/data下的文件,应该要保证/data分区即便卸载了也能正常运行。
          class hal animation
          interface android.hardware.graphics.allocator@3.0::IAllocator default
          user system
          group graphics drmrpc
          capabilities SYS_NICE
          onrestart restart surfaceflinger
          
      ###example2, core
      service shutdownanim /system/bin/bootanimation shutdown
          #core 一般是早启动,最晚结束的一些系统核心程序
          class core
          user graphics
          group graphics audio
          disabled
          oneshot
      	
      
      ###example3, main
      service wpa_supplicant /vendor/bin/hw/wpa_supplicant \
          /vendor/etc/wifi/wpa_config.txt
      #   we will start as root and wpa_supplicant will switch to user wifi
      #   after setting up the capabilities required for WEXT
      #   user wifi
      #   group wifi inet keystore
          interface android.hardware.wifi.supplicant@1.0::ISupplicant default
          interface android.hardware.wifi.supplicant@1.1::ISupplicant default
          interface android.hardware.wifi.supplicant@1.2::ISupplicant default
          interface android.hardware.wifi.supplicant@1.3::ISupplicant default
          #class main 一般为framework层进程
          class main
          socket wpa_wlan0 dgram 660 wifi wifi
          disabled
          oneshot
      
      
      ###example4, late_start
      service bugreport /system/bin/dumpstate -d -p -B -z \
              -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
          #late_start: 一般在初始化最后阶段才需要启动的程序
          class late_start
          disabled
          oneshot
          keycodes 114 115 116
    
  • console [<console>] 用于指定该服务输出 是否要打印到dmesg。 默认输出到/dev/console, 而/dev/console可以通过设置androidboot.console来指定。

#@*.rc
service recovery /sbin/recovery
    console
    seclabel u:r:recovery:s0
#@BoardConfig_*.mk
 BOARD_KERNEL_CMDLINE := androidboot.wificountrycode=CN androidboot.hardware=rk30board androidboot.console=ttyFIQ0 firmware_class.path=/vendor/etc/firmware init=/init rootwait ro init=/init

#@device 可以查看console相关属性:
$ getprop |grep conso                                                                                                                                                               
[init.svc.console]: [running]
[ro.boot.console]: [ttyFIQ0]
[ro.boottime.console]: [3672952642]
[ro.consoleable]: [1]

  • critical用于说明关键服务,当该服务4分钟内或者在开机完成前退出超过4次,将会导致设备进入bootloader模式。

      service ueventd /sbin/ueventd
          #指明是关键服务
          critical
          seclabel u:r:ueventd:s0
    
  • disabled 表明该服务无法自动通过class启动,必须通过name或者interface name来启动。

  • enter_namespace <type> <path> 很少用到,Android11 rockchip未查到使用。输入路径的namespace。network type可以指定为"net". 只能设置一个type。

  • file <path> <type> 打开文件,并将它的fd传递给进程,C 层可使用android_get_control_file获取到fd。 type类为:“r”, “w” ,“rw”。

      service charger /system/bin/charger
          class charger
          seclabel u:r:charger:s0
          user system
          group system wakelock input
          capabilities SYS_BOOT
          file /dev/kmsg w
          file /sys/fs/pstore/console-ramoops-0 r
          file /sys/fs/pstore/console-ramoops r
          file /proc/last_kmsg r
    
  • group <groupname> [ <groupname>\* ] 设置用户组, 默认为root组。

  • interface <interface name> <instance name>关联HIDL 接口,名称必须是完整的,这样才能方便hwservicemanager快速启动这些HIDL服务。可关联多个HIDL 接口。

      service wpa_supplicant /vendor/bin/hw/wpa_supplicant -Dnl80211 -iwlan0 -c/vendor/etc/wifi/wpa_supplicant.conf -g@android:wpa_wlan0
          interface android.hardware.wifi.supplicant@1.0::ISupplicant default
          interface android.hardware.wifi.supplicant@1.1::ISupplicant default
          interface android.hardware.wifi.supplicant@1.2::ISupplicant default
          interface android.hardware.wifi.supplicant@1.3::ISupplicant default
          socket wpa_wlan0 dgram 660 wifi wifi
          group system wifi inet
          oneshot
          disabled
    
  • ioprio <class> <priority> 通过SYS_ioprio_set syscall设置此service的IO优先级和IO优先级类。类为“rt”、“be”或“idle”。优先级为0-7。

      service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service.ranchu
          class hal
          user audioserver
          # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
          group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
          capabilities BLOCK_SUSPEND
          ioprio rt 4
          task_profiles ProcessCapacityHigh HighPerformance
          onrestart restart audioserver
    
  • keycodes <keycode> [ <keycode>\* ] 设置keycode,当同时触发这些按键事件时,服务将启动。常用于bugreport service。

      # bugreport is triggered by holding down volume down, volume up and power
      service bugreport /system/bin/dumpstate -d -p -B -z \
              -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
          class late_start
          disabled
          oneshot
          keycodes 114 115 116
    

也可以通过一个propertry来定义keycodes, propertry的属性格式为"keycode1,keycode2,keycode3"或者"none"。由于keycodes在很早就初始化了,使用的property必须为PRODUCT_DEFAULT_PROPERTY_OVERRIDES 。
例如:

# bugreport is triggered by holding down volume down, volume up and power
service bugreport /system/bin/dumpstate -d -p -B -z \
        -o /data/user_de/0/com.android.shell/files/bugreports/bugreport
    class late_start
    disabled
    oneshot
    #ro.keycodes.bugreport 设置为"114,115,116"。 若设置为"none"则表示该服务不会响应keycodes
    keycodes ${ro.keycodes.bugreport:-none}
  • memcg.limit_in_bytes <value> 设置子进程的内存最小值, memcg.limit_percent <value> 设置子进程的最小内存占比(物理内存)。较少用到。

  • memcg.limit_property <value> 使用一个property来定义 memcg.limit_in_bytes.设置后将覆盖memcg.limit_in_bytes 以及memcg.limit_percent 中设置的值。较少用到。

  • memcg.soft_limit_in_bytes <value>设置子进程的运行内存最小值。较少用到。

  • memcg.swappiness <value> swappiness是Linux内核参数,控制交换出运行时内存的相对权重。swappiness参数值可设置范围在0到100之间。 低参数值会让内核尽量少用交换,更高参数值会使内核更多的去使用交换空间。默认值为60。 也较少使用到。

$ cat /proc/sys/vm/swappiness    
100
  • namespace <pid|mnt> namespace 是 Linux 内核用来隔离内核资源的方式。通过 namespace 可以让一些进程只能看到与自己相关的一部分资源,而另外一些进程也只能看到与它们自己相关的资源,这两拨进程根本就感觉不到对方的存在。详细可参考Pid Namespace 原理与源码分析

1、进程所属的 PID namespace 在它创建的时候就确定了,不能更改,所以调用 unshare 和 nsenter 等命令后,原进程还是属于老的 PID namespace,新 fork 出来的进程才属于新的 PID namespace;
2、PID namespace 可以嵌套;
3、PID namespace 中的 init 进程。当一个进程的父进程退出后,该进程就变成了孤儿进程。孤儿进程会被当前 PID namespace 中 PID 为 1 的进程接管,而不是被最外层的系统级别的 init 进程接管。

  • oneshot 如果服务退出则不要自动重启服务。

  • onrestart 当该service重启时,运行命令。

      service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service.ranchu
          class hal
          user audioserver
          # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
          group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
          capabilities BLOCK_SUSPEND
          ioprio rt 4
          task_profiles ProcessCapacityHigh HighPerformance
          onrestart restart audioserver
    
  • oom_score_adjust <value> 自定义oom_score,等同于优先级,优先级越高值越小(-1000 ~1000)。分值越小越不会被kill。 详细参考linux内核的oom score是咋算出来的

#某个系统应用:
$ cat /proc/${process_id}/oom_score_adj                                                                                                                                                    
-800

#adb shell优先级最高
$ cat  /proc/self/oom_score_adj 
-1000 

  • override一般是odm service使用,用于覆盖之前system或者vendor已经定义的服务配置。如果rc中存在多个override标识的相同类名服务,则最后一个被解析的服务才生效。可以通过imports分析,避免出错。

  • priority <priority>设置service进程的优先级。范围为-20~19. 默认为0. 实际是通过setpriority()函数设置。

  • reboot_on_failure <target> 当该服务启动失败或者运行中异常退出时,执行重启。 参数 其实就是设置property sys.powerctl 的值。格式一般为"reboot,reason"。

$ setprop sys.powerctl reboot,test_service_abnormal_exit
##system reboot
$ getprop |grep reasason
[persist.sys.boot.reason]: [reboot,test_service_abnormal_exit]
[ro.boot.bootreason]: [reboot,test_service_abnormal_exit]
[sys.boot.reason]: [reboot,test_service_abnormal_exit]  
  • restart_period <seconds> 设置后non-oneshot 服务将以此周期重启服务。比如restart_period 3600 表示该服务将会每隔1小时启动。

  • rlimit <resource> <cur> <max> 对当前服务以及子进程生效。等同于setrlimit命令。rootfs,ueventd, adbd等,默认为init.

      service syslogd /system/xbin/syslog.sh
          class main
          oneshot
          seclabel u:r:netd:s0
    
  • setenv <name> <value> 在启动的进程中设置环境变量

  • shutdown <shutdown_behavior> 指定当关机时service进程行为。如果没有具体说明,关机时,使用SIGTERM和SIGKILL关闭service。关机期间,“critical” 标识的service不会被关闭直到关机超时。“critical” 标识的service在关机开始时,如果没处于运行状态,则它还会被启动。

  • sigstop 发送SIGSTO给该服务。用于调试。

  • socket <name> <type> <perm> [ <user> [ <group> [ <seclabel> ] ] ]创建一个名为/dev/socket/name的UNIX域socket,并将其fd传递给service程序。type必须为“dgram”、“stream”或“seqpacket”。type可以加后缀“+passcred”,启用SO_PASSCRE。用户和组默认值为0。”seclabel’指的是selinux context. 进程中C层可以使用 libcutils android_get_control_socket()来获取这个fd。

  • stdio_to_kmsg将stdout和stderr重定向到/dev/kmsg_debug。对于那些启动较早或者未使用android log的程序来说,调试就方便多了。仅在 /dev/kmsg_debug 启用时生效。(userdebug/eng生效,user不生效)。而stdin还是使用的console。

  • task_profiles <profile> [ <profile>\* ]设置任务profiles。这是为了取代writepid来设置当前service的profiles。 ( 早期是通过writepid <file> [ <file>\* ], 将service 的pid写入文件,以支持 cgroup的使用。详细可参考LINUX CGROUP总结)

      service vendor.audio-hal /vendor/bin/hw/android.hardware.audio.service.ranchu
          class hal
          user audioserver
          # media gid needed for /dev/fm (radio) and for /data/misc/media (tee)
          group audio camera drmrpc inet media mediadrm net_bt net_bt_admin net_bw_acct wakelock context_hub
          capabilities BLOCK_SUSPEND
          ioprio rt 4
          task_profiles ProcessCapacityHigh HighPerformance
          onrestart restart audioserver
    
  • timeout_period <seconds> 超过这个时间kill掉service。oneshot service不会被重启,其他服务会自动重启。与restart_period配合使用可以创建周期运行的service。

  • updatable标识服务可以在之后的启动顺序中通过APEX被覆盖(通过“override” option)。携带了updatable标识的service如果在全部的APEX激活前启动,则这个程序会被推迟到所有APEX激活后运行。没有携带updatable标识的service是不能被APEX覆盖。

  • user <username> 指定user,默认为root。早期要获取Linux capabilities,进程需要以root运行并请求capabilities,之后再修改成它期望的运行uid。通过fs_config有一种新的机制来替换早期的那种实现,现在允许厂商将程序的Linux capabilities添加到文件系统中。这个新的机制在http://source.android.com/devices/tech/config/filesystem.html中有具体的描述,支持不需要通过以root用户运行的身份来指定这些capabilities。在Android O(8.0),进程可以在rc中通过“capabilities” option 直接请求capabilities。

  • writepid <file> [ <file>\* ] 将子进程的pid写入文件中,以支持cgroup/cpuset的使用。如果指定的不是/dev/cpuset/,但是property ‘ro.cpuset.default’ 中设置了非空的cpuset名称 (比如: ‘/foreground’),那么pid会被写入/dev/cpuset/cpuset_name/tasks。 此option仅适用于子进程,如果要设置当前service的task profiles, 就需要使用task_profiles.

Actions

Actions 是一系列commands的集合,同时包含一个trigger来决定是否要执行这一系列的命令。当触发器被触发时,Action就被加入执行队列(已在队列中则忽略)。每个队列中的Action是按顺序执行和移除的,而Action中的命令也是按顺序执行的。而其他的一些操作包括设备创建销毁,property的设置以及进程的重启是穿插在各个命令执行之间的。格式如下:

on <trigger> [&& <trigger>]*
   <command>
   <command>
   <command>

同时被触发的Action将按照他们在rc文件中定义的先后顺序加入到执行队列中。例如:

on boot
   setprop a 1
   setprop b 2

on boot && property:test=true
   setprop c 1
   setprop d 2

on boot
   setprop e 1
   setprop f 2

如果当前启机且test属性为true,则执行顺序如下:

setprop a 1
setprop b 2
setprop c 1
setprop d 2
setprop e 1
setprop f 2

Triggers

Triggers 是一系列明确的事件,用于触发action操作。触发器分为事件触发器和属性触发器。

事件触发器是由“trigger”命令或init程序中的QueueEventTrigger()函数触发的。它们一般采用简单字符串的形式,例如“boot”或“late init”。

属性触发器是由指定的Property更改时触发的。Property变更为某个指定的value,格式为property:<name>=<value>. Property只要有变更就触发,格式为property:<name>=\*

一个Action可以有多个属性触发器,但只能有一个事件触发器。

例如:

on boot && property:a=b 表示在开机并且 property:a=b时触发action。
on property:a=b && property:c=d 表示以下三种情况下触发action:

   1. 开机并且property a=b并且property c=d.
   2. property:c=d 并且a 的值变更为b, 
   3. property:a=b 并且c 的值变更为d

Commands

  • bootchart [start|stop] 启动或者停止Android开机性能分析工具 Bootchart,这个命令一般直接在default init.rc中使用,前提是“ /data/bootchart/enabled ”文件必须要存在(否则仍然无法启动bootchart)。

  • chmod <octal-mode> <path> 等同linux chmod命令,修改文件访问权限。

  • chown <owner> <group> <path>等同linux chown命令,修改文件所有者以及文件组。

  • class_start <serviceclass> 启动名为且未运行的service。

  • class_start_post_data <serviceclass>class_start一样,也是用于启动服务,不同的是class_start_post_data针对在data mount之后启动service。仅仅适用于FDE(Full Disk Encryption)设备, FDE详细可参考Android FDE 加密过程

  • class_stop <serviceclass> 停止并禁用名为且正在运行的service.

  • class_reset <serviceclass>停止名为且正在运行的service,但不禁用他们,他们仍然可以通过class_start重新启动。

  • class_reset_post_data <serviceclass> 类似class_reset,但是仅针对那些在data mount后才启动的服务。仅适用于FDE设备。

  • class_restart <serviceclass> 重启名为的service.

  • copy <src> <dst>拷贝文件,特别适用于数据量比较大的文件。src文件不能是链接路径,也不可以是777权限的文件。dst 如果文件不存在,则创建这个文件,权限默认为0600。dst文件如果已存在是正常的常规文件并且已经存在,内容将被覆盖。

  • domainname <name> 设置domian name.

  • enable <servicename> 启用service。如果该服务此时需要启动则就会被启动。例如:bootloader需要启动一个服务时会设置一系列的属性,表示一个服务是否应该启动了。

      on property:ro.boot.myfancyhardware=1
          enable my_fancy_service_for_my_fancy_hardware
    
  • exec [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ] fock一个进程来运行这些命令。init进程将挂起(不执行其他命令),直到当前进程退出。

      on boot
          exec -- /system/bin/init.eth0.sh
    
  • exec_background [ <seclabel> [ <user> [ <group>\* ] ] ] -- <command> [ <argument>\* ] fock进程后台运行这些命令。init继续处理其他命令。

  • exec_start <service> 运行指定的service,且停止执行其他命令直到启动该service成功后再执行其他命令。类似于exec,但是此处参数是一个定义好的服务,而不是直接将命令和参数加入。

  • export <name> <value> 设置全局环境变量。所有再此之后运行的程序都将继承此环境变量。

  • hostname <name>设置hostname。

  • ifup <interface> up网络接口

  • insmod [-f] <path> [<options>] 安装kernel模块,-f:强制安装模块,即使运行的kernel版本与要安装的kernel版本不一致。

  • interface_start <name> 查找并启动HIDL或者AIDL 接口. name格式为 <hidl_interface>/<instance>或者aidl/<interface> .例如:android.hardware.secure_element@1.1::ISecureElement/eSE1 或者
    aidl/aidl_lazy_test_1.
    注意:这些命令仅作用于interface service指定的interface,而不是接口服务选项指定的接口,也不是在运行时注册的interface。

  • interface_restart <name> 查找并重启HIDL或者AIDL 接口.

  • interface_stop <name>查找并停止HIDL或者AIDL 接口.

  • load_system_props 该命令已无效

  • load_persist_props 当data分区被加密时,加载persistent 属性, 默认的 init.rc中会用到该命令。

  • loglevel <level> 设置init的log 等级0~7,虽然跟kernel log等级是对应的,但是该选项仅仅作用于init, kerkel log需要通过 write 命令写入 /proc/sys/kernel/printk来进行调整。

  • mark_post_data标识该触发器是在data分区挂载后运行的。

  • mkdir <path> [<mode>] [<owner>] [<group>] [encryption=<action>] [key=<key>]创建目录,可以指定mode,owner和group. 如果不指定则按权限为755,root用户和root组创建。如果指定了,但目录已存在,则目录的权限和用户,组信息将被更新。

action 可以指定为:

  • None: 不需要进行加密操作,目录根据上层目录加密算法进行处理。
  • Require: 需要加密,如果加密失败则终止开机进程。
  • Attempt: 尝试加密,加密失败仍可继续。
  • DeleteIfNecessary: 递归处理:如果存在子目录,但当前策略的加密算法不匹配,则删除子目录。

key 可以指定为:

  • ref: 使用systemwide DE key
  • per_boot_ref: 使用每次开机都更新的key
  • mount_all [ <fstab> ] [--<option>] 对给出的 fstab 表调用 fs_mgr_mount_all 接口,option为“early” 或者 “late”. option如果是--early则init将会跳过“latemount”标识的的item,并触发fs encryption state 事件。option如果是--late则init就只mount “latemount”标识的的item. 如果不携带[–]则mount的是fstab 表中全部item。 如果fstab 也没有指定,那么就以fstab.${ro.boot.fstab_suffix}, fstab.${ro.hardware} ,fstab.${ro.hardware.platform} 按顺序扫描处理。

      on fs
          mount_all /vendor/etc/fstab.${ro.hardware} --early
      
      on late-fs
          # Start services for bootanim
          start servicemanager
          start hwcomposer-2-1
          start gralloc-2-0
          start surfaceflinger
          start bootanim
      
          exec_start wait_for_keymaster
          # Mount RW partitions which need run fsck
          mount_all /vendor/etc/fstab.${ro.hardware} --late
    
:/vendor/etc $ cat fstab.rk30board                                                                                                                                                       
# Android fstab file.
#<src>  <mnt_point> <type> <mnt_flags and options> <fs_mgr_flags>
# The filesystem that contains the filesystem checker binary (typically /system) cannot
# specify MF_CHECK, and must come before any filesystems that do specify MF_CHECK

/dev/block/by-name/system                          /                          ext4      ro,barrier=1                                                      wait,avb,slotselect
/dev/block/by-name/cache                           /cache                    ext4       noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard                wait,check
/dev/block/by-name/metadata                        /mnt/vendor/metadata       ext4       noatime,nodiratime,nosuid,nodev,noauto_da_alloc,discard                wait
/dev/block/by-name/misc                            /misc                      emmc       defaults                                                           defaults
/dev/block/by-name/frp                             /frp                       emmc       defaults                                                           defaults
/dev/block/by-name/parameter                        /parameter                 emmc      defaults                                                            defaults
/dev/block/by-name/baseparamer                    /baseparamer                emmc        defaults                                                            defaults
/dev/block/by-name/resource	                     /resource	                  emmc	     defaults				                              defaults
/devices/platform/*usb*                               auto                     vfat       defaults                                                        voldmanaged=usb:auto
/dev/block/zram0                                     none                      swap       defaults                                                        zramsize=50%
# For sdmmc
/devices/platform/fe320000.dwmmc/mmc_host*           auto                      auto           defaults                                                  voldmanaged=sdcard1:auto
# Full disk encryption has less effect on rk3399, so default to enable this.
/dev/block/by-name/userdata                         /data                      f2fs      noatime,nodiratime,nosuid,nodev,discard,inline_xattr,fsync_mode=strict     wait,check,notrim,forceencrypt=/cache/key_file,quota,reservedsize=128M

  • mount <type> <device> <dir> [ <flag>\* ] [<options>] mount名为 处于dir 目录的设备, flag包括"ro", “rw”, “remount”, “noatime”, …, options包括 “barrier=1”, “noauto_da_alloc”, “discard”, … 。

  • parse_apex_configs 从已挂载的APEXes中解析配置文件。apexd通过设置apexd.status为ready通知mount事件.(仅通知一次)

  • restart <service>stop再start service,如果service正在restart,则不重复处理。

  • restorecon <path> [ <path>\* ]将目录指定为file_contexts中定义context。如果文件是init.rc创建的,则不需要调用此命令,创建的时候已经标记正确。

     	restorecon /vendor/bin/tee-supplicant
    
  • restorecon_recursive <path> [ <path>\* ]递归处理目录下全部路径,将目录指定为file_contexts中定义context。

          restorecon_recursive /mnt/vendor/metadata/tee/
    
  • rm <path> 对path调用unlink(2) 。 你可能想直接使用 exec --rm,但这个exec --rm的前提是系统部分已完成挂载。

  • rmdir <path> 对path调用rmdir(2)。

  • readahead <file|dir> [--fully]对给定的file或者给定的目录下的file调用readahead(2)。--fully 读取全部的文件内容。详细可参考Linux内核的文件预读(readahead)

  • setprop <name> <value>设置Properties value。

  • setrlimit <resource> <cur> <max> 设置资源使用限制,linux下每种资源都有相关的软硬限制,软限制是内核强加给相应资源的限制值,硬限制是软限制的最大值。详细可参考linux下getrlimit()与setrlimit()函数说明及使用, 此命令对所有之后启动的应用生效。该命令一般在init的早期使用,从而能全局生效。resource最好能按照缩写指定,例如‘cpu’, ‘rtio’, ‘RLIM_CPU’, ‘RLIM_RTIO’等,当然也可以指定为resource枚举对应的int值。cur 和 max 可以设置为 ‘unlimited’ 或者 ‘-1’ 表示没有限制。

  • start <service>启动一个未运行的服务,注意不是同步的。因此假如这个service给其他service提供了什么communication channel, 只是简单的先使用该命令start 这个服务并不能保证其他服务请求channel时这些channel已创建。如果要确保这个先后顺序还需要用户使用其他机制进行保证。

  • stop <service>如果该服务正在运行,则停止它。

  • swapon_all [ <fstab> ]对给定的fstab调用fs_mgr_swapon_all函数。如果fstab未指定,将会直接顺序处理/odm/etc, /vendor/etc,或者根目录下的 fstab.${ro.boot.fstab_suffix}, fstab.${ro.hardware} , fstab.${ro.hardware.platform}表文件。

  • symlink <target> <path> 创建symbolic链接。

      symlink /system/etc /etc 
    
  • sysclktz <minutes_west_of_gmt>设置时区,0表示GMT

  • trigger <event> 触发一个事件,用于另一个action插入执行。

  • umount <path> unmunt 文件系统

  • umount_all [ <fstab> ]对给定的fstab调用fs_mgr_umount_all函数,如果fstab未指定,将会直接顺序处理/odm/etc, /vendor/etc,或者根目录下的 fstab.${ro.boot.fstab_suffix}, fstab.${ro.hardware} , fstab.${ro.hardware.platform}表文件。

  • verity_update_state <mount-point> 内部实现是设置dm-verity的状态,并且设置partition.*.verified的属性. 用于adb remount,因为fs_mgr 不能直接设置它。

      on early-boot
          # Update dm-verity state and set partition.*.verified properties
          verity_update_state
    
  • wait <path> [ <timeout> ] 等待指定路径创建,如果路径已存在则返回并继续下一个命令。可以设置超时时间,以秒为单位,float类型,默认值为5s。

  • wait_for_prop <name> <value>等待property被设置为某个value值,如果当前property已经为value则直接继续下条命令。

  • write <path> <content> 打开一个文件,并将content写入。如果该文件不存在,将会新创建该文件,如果该文件存在,则直接写入内容,原先的内容可能被覆盖。

Imports

  • import <path>导入一个新的rc文件内容,如果path是个目录,则会导入该目录下所有rc文件内容,仅导入当前目录,不支持递归,不能导入子目录下的rc文件。

import关键字并不是一个命令,它遵循以下规则,总共三种情况:

  1. 开机时,加载根目录下的/init.rc 或者加载 `ro.boot.init_rc` 中指定的脚本。
  2. 在加载/init.rc之后mount {system,vendor,odm}的时候就加载对应的 /{system,vendor,odm}/etc/init/ 下的rc文件。
  3. (已失效) 在mount_all时加载全部的 /{system,vendor,odm}/etc/init/ 下的rc文件或者特殊定义的rc文件。这个情况在Android Q之后已经不适用。

由于legacy设备的历史原因,以及为了向下兼容legacy设备(不是严格保证),当前导入rc文件的顺序仍然有些复杂。只有两点是可以保证一个命令比另一个命令先运行:

1) 将一个需要先运行的命令放入到一个会先触发的action中。
2) 将两个命令都放入同一个action,并把先运行command排前。

尽管有些复杂,实际的第一阶段rc加载的顺序为:

1. /init.rc 先被解析加载,同时在它内部import的rc文件会被递归加载。
2. /system/etc/init/下的rc文件按名字的字母排序排序后加载进来,加载一个的同时递归加载import进来的rc文件。
3. 再重复2继续处理r /vendor/etc/init 之后  /odm/etc/init 的rc文件。

以下伪代码可以更清楚地解释这个顺序:

fn Import(file)
  Parse(file)
  for (import : file.imports)
    Import(import)

Import(/init.rc)
Directories = [/system/etc/init, /vendor/etc/init, /odm/etc/init]
for (directory : Directories)
  files = <Alphabetical order of directory's contents>
  for (file : files)
    Import(file)

init相关的Properties

  • init.svc.<name> init使用该属性表示某个name的service状态。value包括:“stopped”, “stopping”, “running”, “restarting”.
  • dev.mnt.blk.<mount_point>一个mount分区实际存储设备(block device)的base name。mount分区路径如果存在"/“则用”."来替换。如果就是根目录分区,则直接用"root"代替。主要用于动态分区。Init中有一个机制可以跟踪mounts并异步更新Android属性。“mount_point”一般为root、system、data或vendor。详细请参考Android10 动态分区介绍
:/ $ getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [sda]
[dev.mnt.blk.firmware]: [sde]
[dev.mnt.blk.metadata]: [sde]
[dev.mnt.blk.persist]: [sda]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.vendor]: [dm-1]
:/ $ getprop | grep dev.mnt.blk
[dev.mnt.blk.data]: [dm-4]
[dev.mnt.blk.metadata]: [sda]
[dev.mnt.blk.mnt.scratch]: [sda]
[dev.mnt.blk.mnt.vendor.persist]: [sdf]
[dev.mnt.blk.product]: [dm-2]
[dev.mnt.blk.root]: [dm-0]
[dev.mnt.blk.system_ext]: [dm-3]
[dev.mnt.blk.vendor]: [dm-1]
[dev.mnt.blk.vendor.firmware_mnt]: [sda]
  • ctl.[<target>_]<command> 以ctl打头的这个格式的属性能够触发init的响应,且该属性是只用于set而不用于read的。<target>_是可选的,一般为一个service的option,只能指定一个option。<commands>只能为start restart stop 中的一个。这个属性的value就是对应的service名称。 如果option是interface那么它的value就是一个service提供的interface,而不是这个service名称。
    AndroidQ版本引入了LAZY HAL概念,以支持低性能的Android设备,Lazy hal可以使hal服务在使用的时候开启,而当不使用时,所有client都注销服务,关闭hal服务,因此,这个功能可以有效地提高Android设备的性能和降低功耗。如果服务是 lazy HALs,需要oneshot on。详细可参考Camera_service中打开Lazy_Hal功能
    //就会运行logd的start option。
    SetProperty("ctl.start", "logd")
    //就会启动一个提供了 `aidl aidl_lazy_test_1`接口 的service 的start option. 		
    SetProperty("ctl.interface_start", "aidl/aidl_lazy_test_1")
    //按oneshort启动,如果进程挂掉不再重启。
    SetProperty("ctl.oneshot_one_start", "logd") 
    // 进程挂掉后还要重新启动。
    SetProperty("ctl.oneshot_off_start", "logd")
    //发送SIGSTO给该服务。用于调试。
    SetProperty("ctl.sigstop_on_start", "logd")
    SetProperty("ctl.sigstop_off_start", "logd")

Boot timing

init在属性中也记录了一些开机时间相关的信息(以ns为单位,1秒=1000毫秒,1毫秒=1000微秒,1微秒=1000纳秒):

  • ro.boottime.init 开机后时钟运行时长 (2,098,647,130ns = 2s)
  • ro.boottime.init.first_stage 开机的第一阶段耗时多少
  • ro.boottime.init.selinux 在运行SELinux阶段耗时多少
  • ro.boottime.init.cold_boot_waitinit等待 ueventd coldboot解析结束
getprop |grep ro.boottime.init                                                                                                                                                      
[ro.boottime.init]: [2098647130]
[ro.boottime.init.cold_boot_wait]: [146]
[ro.boottime.init.first_stage]: [89287676]
[ro.boottime.init.fsck.cache]: [46]
[ro.boottime.init.fsck.data]: [18]
[ro.boottime.init.fsck.metadata]: [62]
[ro.boottime.init.fsck.scratch]: [43]
[ro.boottime.init.mount.cache]: [2]
[ro.boottime.init.mount.data]: [54]
[ro.boottime.init.mount.metadata]: [6]
[ro.boottime.init.mount.scratch]: [2]
[ro.boottime.init.mount_all.early]: [199]
[ro.boottime.init.mount_all.late]: [148]
[ro.boottime.init.selinux]: [145286473]

  • ro.boottime.<service-name> 某个服务第一次启动的时间(以ns为单位)
    //可以比较各个服务启动的先后顺序
:/ $ getprop |grep boottime                                                                                                                                                              
[ro.boottime.adbd]: [11866422925]
[ro.boottime.audioserver]: [3573729716]
[ro.boottime.auth_pem]: [11533756891]
[ro.boottime.bootanim]: [4405600758]
[ro.boottime.cameraserver]: [11539368558]
[ro.boottime.console]: [3561321923]
[ro.boottime.drm]: [11546434767]
[ro.boottime.gatekeeperd]: [11776180082]
[ro.boottime.generate_cert]: [3353394194]
[ro.boottime.health-hal-2-0]: [3520695378]
[ro.boottime.healthd]: [3452477163]
[ro.boottime.hidl_memory]: [3450825162]
[ro.boottime.hwservicemanager]: [3104810753]
...

相关调试工具介绍

Bootcharting

Android11的init支持使用"bootcharting"来获取log相关的文件。相关工具可参考http://www.bootchart.org/.

  • 模拟器上可以直接带上-bootchart timeout参数来启动bootchart的方式启动模拟器。
  • 真实设备中使用 adb shell 'touch /data/bootchart/enabled'来启用bootchart。当调试完成后需要手动清除 /data/bootchart/enabled文。

log文件写入了/data/bootchart/, 使用如下命令可以自动打包这些文件并获取这些文件。

sudo apt-get install pybootchartgui
# grab-bootchart.sh uses $ANDROID_SERIAL.
$ANDROID_BUILD_TOP/system/core/init/grab-bootchart.sh

还需要注意的一点是:这些log中会按照init运行时间为0,进行记录的。如果想要知道相比于开机时钟运行时间,则再看一下init是什么时候启动的,增加这个时间,就能知道了。

Comparing two bootcharts

使用compare-bootcharts.py可以便捷地比较选择的进程的开始时间和结束时间。前面提到的grab-bootchart.sh会在主机的/tmp/android-bootchart下创建一个bootchart.tgz。如果将两次抓的log解压到主机两个不同的目录,就能对比两份数据。例如:

Usage: system/core/init/compare-bootcharts.py base-bootchart-dir exp-bootchart-dir

process: baseline experiment (delta) - Unit is ms (a jiffy is 10 ms on the system)
------------------------------------
/init: 50 40 (-10)
/system/bin/surfaceflinger: 4320 4470 (+150)
/system/bin/bootanimation: 6980 6990 (+10)
zygote64: 10410 10640 (+230)
zygote: 10410 10640 (+230)
system_server: 15350 15150 (-200)
bootanimation ends at: 33790 31230 (-2560)

Systrace

Systrace (http://developer.android.com/tools/help/systrace.html) 用于获取userdebug/eng设备启动期间的性能分析报告.

如下例子是wm am策略的trance event:

$ANDROID_BUILD_TOP/external/chromium-trace/systrace.py \
      wm am --boot

该命令会让设备重启,等设备重启且启动阶段已完成后,主机敲下" Ctrl+C",trace 报告就能从设备中获取并以trace.html形式保存到主机上。
注意:trace事件是在persistent properties 加载之后才进行记录的,如果trace事件在此之前就发生了,是不会被记录的,比如vold, surfaceflinger, and servicemanager就受到这个限制的影响,因为他们是在persistent properties 加载之前就启动了。Zygote 初始化,以及是从Zygote孵化的进程就不会受到此限制的影响。


如何调试init

当一个service由init启动,它有可能执行execv() 失败,导致服务没有成功启动。但一般来说,这个情况不是典型的,有可能是链接的时候出错了。Android中的链接会将它的log打印到logdstderr,所以如果出现错误,是能够在logcat中看到错误信息进行排查的。如果出错的时候,还无法访问logcat,那么可以使用stdio_to_kmsg 选项将 stderr 输出到kmsg ,这样通过串口就能查看。

init service就应该要通过init启动,不建议使用其它方式来启动service进程,因为init提供了一系列难以手动复制的方式来创建service的环境(用户、组、安全标签、功能等)。

另外,如果想要从一开始就调试service,可以使用sigstop选项。此选项将在调用exec之前就立即向service发送SIGSTOP信号。这是一扇窗户

开发者可以在继续使用SIGCONT服务之前附加调试器、strace等。这使得开发者能够在继续服务之前准备好debugger, strace等调试环境。sigstop甚至还可以通过ctl.sigstop_on_start, ctl.sigstop_off_start进行动态控制。
以下是动态调试logd的例子:

 @终端1:   
  stop logd
   setprop ctl.sigstop_on logd
   start logd
   ps -e | grep logd
   > logd          4343     1   18156   1684 do_signal_stop 538280 T init
   gdbclient.py -p 4343
   b main
   c
   c
   c
   > Breakpoint 1, main (argc=1, argv=0x7ff8c9a488) at system/core/logd/main.cpp:427

以下是在另一个终端中使用strace进行操作:

@终端2:
stop logd
setprop ctl.sigstop_on logd
start logd
ps -e | grep logd
> logd          4343     1   18156   1684 do_signal_stop 538280 T init
strace -p 4343

(From a different shell)
kill -SIGCONT 4343

> strace runs

主机端init脚本验证

init脚本的校验是在编译阶段进行的。尤其是以下情况:

1. action,service,import格式校验。
2. 所有命令都是有效的关键字,并且参数值在正确的范围内。
3. 所有service的option都是有效的,这比解析comment更加严格,属于完全解析。例如要求uid和gid是必须要指明的。

也有一部分解析工作是在运行时处理的,因此以下这几种情况编译期间是不做检查的:

1. 编译期间不检查command的参数的有效性,例如,不检查文件路径是否确实存在,SELinux是否允许操作,或者UID和GID是否已指定。
2. 编译期间不检查service是否存在或者是否已定义SELinux domain。
3. 编译期间不检查service是否在其他init脚本中已经被定义。

开机早期启动顺序

开机早期启动顺序包括三个阶段:第一阶段初始化、SELinux设置和第二阶段初始化。
第一阶段初始化主要是mount system的最基本设置。尤其包括挂载/dev, /proc,挂载 ‘early mount’ 分区(包括系统代码的那些分区,比如:system 和vendor)以及给带有虚拟硬盘的设备将system.img挂载到根目录。
需要注意的是,在Android Q, system.img总是包含TARGET_ROOT_OUT,并且总是在第一阶段阶段初始化结束后挂载到根目录下。Android Q 也会需使用动态分区,因此也会需要带虚拟硬盘来启动Android.

根据设备配置,第一阶段初始化有以下三种不同情况:

1. system-as-root 设备, 第一阶段初始化是/system/bin/init的一部分,/init链接指向/system/bin/init以实现向后兼容。这些设备不需要挂载system.img的事情,因为根据这个定义,它已经被内核挂载为rootfs。
2. 对于带有虚拟硬盘的设备,第一阶段初始化是位于虚拟硬盘/init的静态可执行文件。设备会先加载system.img到/system目录下。然后以root身份将/system移到根目录。操作完成后,虚拟硬盘中的内容会被释放。
3. 对于使用recovery作为虚拟硬盘的设备,第一阶段init包含在recovery ramdisk中位于/init的共享init中。这些设备首先以root身份将内容切换到/first_stage_ramdisk,并在环境中删除掉recovery 组件,之后就和2一样的操作。请注意:之所以android 是以正常启机的方式启动而不是按recovery mode启动取决于内核是否使用了androidboot.force_normal_boot=1命令。

第一阶段初始化完成后,使用“selinux_setup”参数执行/system/bin/init。这个阶段是选择性地编译SELinux并将其加载到系统中。selinux.cpp包含了有关该过程细节的更多信息。

最后一个阶段,第二阶段解析结束一完成,它将使用“second_stage”参数再次执行/system/bin/init。此时,init的主要阶段将运行,并通过init rc文件继续后续的开机流程。

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

Android init.rc整理 的相关文章

  • SharedPreferences - java.lang.Boolean 无法转换为 java.lang.String

    我对因不应该抛出的异常而感到困惑 错误如下 java lang ClassCastException java lang Boolean cannot be cast to java lang String 但是 我的代码不会尝试将布尔值转
  • 在 Android 10/Q 上运行的 Android ACTIVITY_RECOGNITION 权限 SDK 28 (SDK 29)

    我的 Android 应用程序以 SDK 28 为目标 并连接到 Google Fit 以上传数据并读取其他一些数据 该应用程序使用 HistoryAPI 读取 com google step count delta 数据 本文档声称 如果
  • 错误:链接引用失败。 -> 排队作业

    我正在使用 Kotlin 学习 Android Material Design 一切都很顺利 直到我尝试使用 android support design widget FloatingActionButton 当我重建项目时 我收到以下错
  • onScale 和 Canvas - 缩放图像后如何调整原点?

    我有一个非常简单的测试应用程序 带有自定义组件MyView java https github com afarber android newbie blob master TestScroll src de afarber testscr
  • 有没有办法获取 Android 扩展文件中特定文件的 Url?

    我正在构建一个 PhoneGap 应用程序 其中包含大型音频和视频文件 在 Android 中 媒体文件应位于扩展文件中 以使应用程序大小保持在 Google Play 50 MB 限制以下 我目前正在考虑两种播放视频文件的方法 将所需的视
  • WebView 与 Chrome 自定义选项卡

    我正在构建一个应用程序 在详细活动中我必须显示一个网页 我本来打算使用 WebView 但后来我看到了 Chrome Custom Tab 你们认为最好实施什么 为什么 如果您只想显示某个页面 那么我建议您使用 chrome 自定义选项卡
  • Android 无法查找支持版本 27.0.0 的窗口

    更新后supportVersion to 27 0 0仅在 Android 5 0 2 上 应用程序会因以下堆栈跟踪而崩溃 W WindowManager Failed looking up window java lang Illegal
  • 每当调用 startactivityforresult 时 Android 就会终止我的应用程序

    好吧 在我的应用程序中 我使用 Android 的默认相机和图库 startActivityforResult 为 Intent i new Intent android intent action PICK MediaStore Imag
  • Android 拖放 ACTION_DRAG_ENDED 未触发

    我真的很花时间解决这个问题 到目前为止找不到任何有相关经验的朋友 这是我发布第一个应用程序之前的最后一个功能 所以在结局即将到来的情况下陷入困境让我发疯 如果我将对象放在可接受的区域中 我的拖放效果会非常好 但是 如果拖放到其他地方 我不会
  • Android:如何在双 SIM 卡手机中获取两张 SIM 卡的 SIM ID

    我想获取双 SIM 卡手机中两个 SIM 卡的 SIM 序列号 ID 有什么方法可以获取它们吗 提前致谢 import java lang reflect Method import android content Context impo
  • 如何以编程方式启用小米应用程序的自动启动

    我想知道小米是否可以提供任何应用程序的后台服务 我的应用程序中有需要始终在后台运行的服务 在除小米之外的所有设备中都工作正常 如何以编程方式完成 也适用于小米 oppo vivo 和 oneplus 手机 try Intent intent
  • 删除对象时删除嵌套字段中的索引

    我仍在使用 Firebase 这次我有一个与删除对象相关的问题 我有如下结构 users UsErId1 name Jack email email protected cdn cgi l email protection UsErId2
  • Android 中表与游标的并集

    我正在尝试将两个具有相同字段的表合并起来 通过内容提供程序 创建一个用于创建 ListView 的游标 Override public Cursor query Uri uri String projection String select
  • 如何从MediaCodec获取解码格式?

    我正在与MediaCodec 我用它来解码 mp4 video MediaCodec 将视频解码为YUV格式 但我需要得到RGBA 一切都很好 但我发现有几种可能的格式 例如YUV420 YUV422等等 因此 据我所知 要进行转换 我需要
  • webView.loadUrl 显示空白屏幕

    我必须加载这些通过使用下面的代码 但最终显示空白屏幕作为输出 您可以检查url代码之间的链接位于last line WebPagerLoader java public class WebPageLoader extends Activit
  • Android wifi的信号强度[重复]

    这个问题在这里已经有答案了 可能的重复 Android 如何监控WiFi信号强度 https stackoverflow com questions 1206891 android how to monitor wifi signal st
  • 对话框片段中的 onActivityResult

    我正在从对话框片段中拍照 我还需要类似的东西startActivityForResult takePictureIntent actionCode Override public void onActivityResult int requ
  • 是否可以用 C# 为 Android 编写应用程序?

    我们都知道Android运行Dalvik VM程序 通常开发人员用 Java 编写程序并将其编译为 Dalvik 字节码 我想知道是否有可能创建一个可以接受 C 代码并将其编译为 Dalvik 字节码的编译器 嗯 这是一种选择 或者您可以在
  • 加载 highchart 时 Android 错误膨胀类

    我正在尝试加载highcharts via Dialog 下面是我的代码 Gradle implementation com highsoft highcharts highcharts 9 0 1 XML
  • 在edittext android中插入imageview

    我想将 imageview 放在 edittext 中 可能吗 我检查了 evernote 应用程序 它能够将照片放在编辑文本部分 我想让我的应用程序完全相同 我如何才能将从图库中选择的图像视图放入编辑文本中 我首先尝试将 imagevie

随机推荐

  • 面试系列之线程篇

    线程和进程 进程是资源分配和调度的最小独立单元 线程是CPU调度的基本单元 一个进程可以包含多个线程 多个线程共享该进程的资源 线程可以看作是轻量级的进程 进程间通信的方式 volatile synchronized wait notify
  • 字符串学习&总结(感觉主要是总结模板)

    目录 前言 一 哈希 导读 HASH模板 哈希 双哈希 hash应用 hash牛逼克拉斯 0 核心操作 求子串哈希值 1 字符串匹配 2 允许k次失配的字符串匹配 3 最长回文子串 hash操作简单 可解决的问题有点多啊 nice 4 最长
  • 深入理解 RPC : 基于 Python 自建分布式高并发 RPC 服务

    RPC Remote Procedure Call 服务 也即远程过程调用 在互联网企业技术架构中占据了举足轻重的地位 尤其在当下微服务化逐步成为大中型分布式系统架构的主流背景下 RPC 更扮演了重要角色 Google 开源了 gRPC F
  • Grpc demo java 实现

    环境 JDK8 Maven3 6 3 我的 Grpc java demo https github com 999bug grpc java 记得star 搭建步骤 1 利用代码编译器创建maven 项目 2 添加依赖坐标
  • python自动化之models 进阶操作二

    PUBLIC METHODS THAT ALTER ATTRIBUTES AND RETURN A NEW QUERYSET def all self 获取所有的数据对象 def filter self args kwargs 条件查询 条
  • hive--分组排序函数

    分组排序 最主要的区别就是如果两个分数相同 排名是否同列以及排名是否相同 这个方法仅在mysql8 0以后 hive或其他数据库支持 直接看图 原始表 原表如上 想要的结果如下 从图中可以发现 row number函数 如果并列但名次反而不
  • (四十四)数据字典-树状treeview树的实现

    数据字典 treeview树的实现 什么是数据字典 顾名思义数据字典 数据 字典 字典是用来查询东西的 所以数据字典就是描述数据信息的集合 数据字典是一种通用的程序设计方法 程序中有很多主体 每个主体的都有很多属性 每种属性都有很多取值并且
  • Python中的变量与常量

    目录标题 一 变量 1 什么是变量 2 python代码中变量的展示和练习 3 用画图的形式展示变量 变量值与内存之间的关系 二 常量 1 什么是常量 2 python中的常量 一 变量 1 什么是变量 程序运行过程中 值会发生变化的量 也
  • xshell连接centos7虚拟机时显示SSH服务器拒绝了密码

    xshell连接centos7虚拟机时显示SSH服务器拒绝了密码 这个问题可能有以下几个原因 1 SSH服务未启动或未安装 在CentOS上安装SSH服务 可以使用以下命令 sudo yum install openssh server 然
  • xp服务器文档在哪里设置密码,xp ftp服务器设置密码

    xp ftp服务器设置密码 内容精选 换一换 登录Windows操作系统的弹性云服务器时 需使用密码方式登录 因此 用户需先根据创建弹性云服务器时使用的密钥文件 获取该弹性云服务器初始安装时系统生成的管理员密码 Administrator帐
  • Visual Studio2019中源文件 <bits/stdc++.h>无法打开解决方法

    在Visual Studio2019中创建stdc h文件 将下面代码保存文件当中 C includes used for precompiling C Copyright C 2003 2015 Free Software Foundat
  • MySQL介绍,SQL入门及表结构分析

    数据库分类 关系型数据库 SQL 通过表与表之间 行与列之间的关系去存储数据 如MySQL Oracle 两者本质都是DBMS 数据库管理系统 非关系型数据库 No SQL意为Not only SQL 通过对象自身属性去存储 如Redis
  • git cherry-pick使用总结

    第一次在csdn发文章 还没找到节奏 请多多指教 这次给大家介绍一下Git中常用的cherry pick cherry pick的作用 将现有的某个提交应用到当前分支上 git cherry pick edit n m parent num
  • QComboBox类的使用,下拉列表框的触发:activated与currentIndexChanged的区别

    一 介绍 QcomboBox属于继承自QWidget 给用户提供一种呈现选项列表的方式 作用 使其占最小的控件 列举最多的选项供用户选择 二 触发条件 当前用户点击所选的具体列表项 两种触发方式 1 void currentIndexCha
  • Redis学习笔记五:redis主从复制

    通常使用redis时 如果redis存储的是一些非常重要的数据 那么只配置一台服务器的redis是有风险的 以为如果主服务器宕机 影响到正常业务之外 最终要的是数据的丢失 因此我们常常会配置多台redis做集群 除了防止主机宕机 我们还可以
  • 【AI视野·今日CV 计算机视觉论文速览 第166期】Mon, 28 Oct 2019

    AI视野 今日CS CV 计算机视觉论文速览 Mon 28 Oct 2019 Totally 47 papers 上期速览 更多精彩请移步主页 Interesting 联合显著性检测 提出了一种从单张图像中检测出具有相似语义属性的物体显著性
  • [转]一文看懂区块链架构设计 - 从概念到底层技术

    前言 区块链作为一种架构设计的实现 与基础语言或平台等差别较大 区块链是加密货币背后的技术 是当下与VR虚拟现实等比肩的热门技术之一 本身不是新技术 类似Ajax 可以说它是一种技术架构 所以我们从架构设计的角度谈谈区块链的技术实现 无论你
  • 广西人才网实习信息爬取与数据库存储实战

    广西人才网实习信息爬取与数据库存储实战 https www gxrc com 大家好 我是W 项目介绍 本项目为CrawlSpider结合MySQL MongoDB爬取求职网站信息的项目 目标是将网站指定分类下的招聘信息 包括 职位名称 公
  • Jupyter Notebook导入和删除虚拟环境

    在Jupyter Notebook中加载虚拟环境 比如现在我创建了一个虚拟环境名为pytorch 那么首先我先进入虚拟环境 activate pytorch Linux下需要使用source activate 然后运行 conda inst
  • Android init.rc整理

    AIL概述 init rc由AIL语言编写而成 可以参考system core init README md来学习AIL语法相关知识 不同Android版本关于AIL的说明存在一些细微差异 但基本语法和总的思路是不变的 往往我们可以先查看对