106 Matching Annotations
  1. Apr 2025
    1. Arm 系统内存管理单元 (SMMU) 架构是用于 Arm 设备的标准且得到良好支持的 IOMMU,可同时实现隔离和直接分配。

      SMMU是一个增强的IOMMU,它的一部分运行在EL1,由主机内核控制,负责IOMMU中的辅助管理任务,另外一部分运行在EL2,由pKVM Hypervisor控制,负责IOMMU中的页面管理任务。

    2. 长期以来,在运行客户机时,KVM 会在启用第 2 阶段转换的情况下运行;而在运行主机 Linux 内核时,KVM 会在停用第 2 阶段转换的情况下运行。此架构允许来自主机第 1 阶段 MMU 的内存访问通过第 2 阶段 MMU,从而允许从主机无限制地访问客户机内存页面。

      原生的KVM虽然也支持两级页表映射,但仅在运行guest VM时才会启用第2阶段转换;而在运行host OS时会停用第2阶段转换,这使得host OS有能力无限制的访问到guest VM的内存页面。

    1. 可使用as将u8类型转char 之所以不支持其他整数类型,是因为其他整数类型的值可能无法转换为char(即不在UTF-8编码表范围的整数值)

      在rust中,char类型的有效字符对应的 unicode 码位的范围是 U+0000 到 U+10FFFF,并且去除了 UTF-16 的部分代理区间(U+D800 到 U+DFFF),所以u8类型的整数全在这个有效范围内,而u16/u32类型的整数可能不在这个有效范围内。

    2. 可使用as将char转为各种整数类型,目标类型小于4字节时,将从高位截断

      注意:这里是将字符对应的 unicode 码位 (code points) 转换为整数类型,而不是基于 UTF-8 编码进行转换。

    3. 在存储char类型数据时,会将其转换为UTF-8编码的数据(即Unicode代码点)进行存储。

      注意:这里的描述有问题,单个char类型在存储时,保存的是对应的 unicode 码位 (即 unicode 代码点), 所以固定占用4个字节;只有存储字符串时,才会以 UTF-8 编码进行存储,此时字符串内的单个 char 类型占用 1~4 个字节。

    1. 一个程序遵循Copyleft,我们首先声明它是有版权的;然后,我们给它加上发布条款,这个条款就是一个法律声明,它赋予所有人有使用、修改和重新发布程序的代码及其衍生作品的权利,但要求在这个过程中保持发布规则不变。

      即软件必须一直是自由的。

    1. 自由地分发该软件修改后的拷贝。

      修改后分发的软件可以不再是自由软件(即不继续使用自由软件许可);注意如果获取的自由软件是基于Copyleft许可的,则修改后的软件必须继续使用Copyleft许可,即必须仍然是自由软件。

    1. // 需注意,下面的数值都加上了类型后缀。 // 这是因为在调用方法的时候,需要知道值的 // 所属类型才能找到这种类型具有的方法

      如果没有指定类型后缀,也不会作为i32/f64处理,例如:<br /> println!("{}", 3.pow(3));<br /> help: you must specify a concrete type for this numeric value, like i32

    1. 在Rust中,可以在表达式结尾加上分号;来将表达式转换为【语句】。

      例如:在 "3+4;" 这个语句中,"3+4" 是一个表达式,计算的结果7是这个表达式的返回值,而语句 "3+4;" 本身没有返回值。

  2. Nov 2022
    1. 用于声明或定义的代码都是语句。例如let声明变量、fn定义函数、struct声明结构体等。

      这种就是“单纯的”语句,并且不一定需要尾部加分号。

    2. let x = if true { println!("true"); 33 // 分支的最后一条代码计算结果赋值给x,不能分号结尾 } else { println!("false"); 44 // 分支的最后一条代码计算结果赋值给x,不能分号结尾 }; // 这个结尾分号表示let语句的结尾分号

      if结构是一个表达式,所以它有返回值。在if结构这个表达式中也可以包含语句,例如 "println!("true");"。

      注意:33和44的尾部不能加分号,如果加分号,就变成了语句,最后的返回值就变为空()。

  3. May 2022
    1. xxx_defconfig文件是平台厂商提供的一份默认配置。

      内核编译时,先用xxx_defconfig文件和我们定义的次.config文件合并生成一个临时的xxx_defconfig文件,然后用此xxx_defconfig文件中的设置值和Kconfig文件中的默认值生成一个主.config文件。

      最后makefile根据主.config文件中的配置值编译内核。

  4. Mar 2022
    1. 标准IO函数(如fread,fwrite等)会在内存中建立缓冲,该函数刷新内存缓冲,将内容写入内核缓冲,要想将其真正写入磁盘,还需要调用fsync。(即先调用fflush然后再调用fsync,否则不会起作用)。fflush以指定的文件流描述符为参数(对应以fopen等函数打开的文件流),仅仅是把上层缓冲区中的数据刷新到内核缓冲区就返回,因此相对于fsync而言不是很安全,还需要再调用一下fsync来把数据真正写入硬盘。

      fopen -> fwrite(to local buffer) -> fflush(to kernel buffer) -> fsync(to disk) -> fclose

      open -> write(to kernel buffer) -> fsync(to disk) -> close

  5. Jan 2022
    1. printf with a "%zd" format expects an argument of the signed type that corresponds to the unsigned type size_t. Standard C doesn't provide a name for this type or a good way to determine what it is. If size_t is a typedef for unsigned long, for example, then "%zd" expects an argument of type long, but that's not a portable assumption.

      期望将unsigned type的size_t变量转换为signed type的变量。

  6. Dec 2021
    1. SecurityContext描述系统资源的安全上下文,SELinux的安全访问策略就是在安全上下文的基础上实现的;

      seapp_contexts:用于声明APP进程和所创建数据目录的安全上下文。

      file_contexts:用于声明文件的安全上下文。

      service_contexts:用于声明 java service 的安全上下文。

      property_contexts:用于声明属性的安全上下文。

      hwservice_contexts:用于声明 HIDL service 的安全上下文。

    2. SecurityPolicy描述系统资源的安全访问策略,系统启动时init进程负责把策略文件加载到内核的LSM模块中;

      各种te文件。

    1. neverallow { domain -kernel -init -recovery } block_device:blk_file { open read write };

      除了domain为kernel、init、recovery外,其它domain都不允许对block_device有open、read、write的权限。

    2. 安全上下文

      seapp_contexts:用于声明APP进程和所创建数据目录的安全上下文。

      file_contexts:用于声明文件的安全上下文。

      service_contexts:用于声明 java service 的安全上下文。

      property_contexts:用于声明属性的安全上下文。

      hwservice_contexts:用于声明 HIDL service 的安全上下文。

  7. Sep 2021
    1. 单词边界

      注意:这里的单词是由字母、数字、下划线组成,所以对于字符串 "|bc| #|a1_|# |34|" ,这里 "|" 对应的才是单词边界。

  8. Aug 2021
    1. 一般对静态变量的传递为拷贝,对动态变量的传递为引用。

      静态变量:不可变类型变量。

      动态变量:可变类型变量。

    1. 实例变量指的是在任意类方法内部,以“self.变量名”的方式定义的变量,其特点是只作用于调用方法的对象。另外,实例变量只能通过对象名访问,无法通过类名访问。

      由于实例变量是在某个类方法的内部被定义的,所以只有类对象调用该类方法时,才会定义这个实例变量,也才能通过该类对象访问此实例变量。

    1. 2、在调用基类的方法时,需要加上基类的类名前缀,且需要带上 self 参数变量。

      在类外,无法通过子类对象调用父类方法,会报错: son.Father.getName(self)

  9. Jul 2021
    1. _foo: 以单下划线开头的表示的是 protected 类型的变量,即保护类型只能允许其本身与子类进行访问,不能用于 from module import * __foo: 双下划线的表示的是私有类型(private)的变量, 只能是允许这个类本身进行访问了。

      protected变量只允许在本类及其子类内部进行访问,不允许在类外进行访问。

      private变量只允许在本类内部进行访问,不允许在子类及类外进行访问。

    2. Python不允许实例化的类访问私有数据,但你可以使用 object._className__attrName( 对象名._类名__私有属性名 )访问属性

      对于私有的类变量:className._className__attrName

      对于私有的实例变量:object._className__attrName

    3. 实例变量:在类的声明中,属性是用变量来表示的。这种变量就称为实例变量,是在类声明的内部但是在类的其他成员方法之外声明的。

      实例变量(实例属性):定义在类体中,某个方法内,self.变量名 = 变量值。

    4. 局部变量:定义在方法中的变量,只作用于当前实例的类。

      局部变量:定义在类体中,某个方法内,变量名 = 变量值。

    5. 类变量:类变量在整个实例化的对象中是公用的。类变量定义在类中且在函数体之外。类变量通常不作为实例变量使用。

      类变量(类属性):定义在类体中,所有方法之外,相当于C++中类的静态成员变量,为整个类对象共有。

  10. May 2021
    1. try: 正常的操作 ...................... except ExceptionType, Argument: 你可以在这输出 Argument 的值...

      注意,这里的Argument实际上是ExceptionType的实例。

    2. try-finally 语句无论是否发生异常都将执行最后的代码。

      注意,即使没有异常发生,finally子句中的代码都会被执行。

  11. Apr 2021
    1. 如果在函数内部调用 locals(),返回的是所有能在该函数里访问的命名。 如果在函数内部调用 globals(),返回的是所有在该函数里能访问的全局名字。 两个函数的返回类型都是字典。所以名字们能用 keys() 函数摘取。

      调用locals()时,返回的内容不包括全局名字。

  12. Feb 2021
    1. #!/usr/bin/python # -*- coding: UTF-8 -*- a = [1,2,3] b = a if len(a) != 0 else "" print(b) c=[] d = c if len(c) != 0 else "c 是一个空列表" print(d)

      这里的if简单条件判断相当于:

      if len(a) != 0:<br> b = a<br> else<br> b = ""

      注意:此处if和else后面没有加冒号。

    1. vars(obj) 返回一个object的name space。用dictionary表示

      dir(math):返回一个列表<br> ['__doc__', '__file__', '__name__', '__package__', 'acos', 'acosh', 'asin', 'asinh', 'atan', 'atan2', 'atanh', 'ceil', 'copysign', 'cos', 'cosh', 'degrees', 'e', 'erf', 'erfc', 'exp', 'expm1', 'fabs', 'factorial', 'floor', 'fmod', 'frexp', 'fsum', 'gamma', 'hypot', 'isinf', 'isnan', 'ldexp', 'lgamma', 'log', 'log10', 'log1p', 'modf', 'pi', 'pow', 'radians', 'sin', 'sinh', 'sqrt', 'tan', 'tanh', 'trunc']

      vars(math):返回一个字典<br> {'pow': , 'fsum': , 'cosh': , 'ldexp': , 'hypot': , 'acosh': , 'tan': , 'asin': , 'isnan': , 'log': , 'fabs': , 'floor': , 'atanh': , 'modf': , 'sqrt': , '__package__': None, 'frexp': , 'degrees': , 'lgamma': , 'log10': , '__doc__': 'This module is always available. It provides access to the\nmathematical functions defined by the C standard.', 'asinh': , 'fmod': , 'atan': , 'factorial': , '__file__': '/usr/local/python-2.7.17/lib/python2.7/lib-dynload/math.so', 'copysign': , 'expm1': , 'ceil': , 'isinf': , 'sinh': , '__name__': 'math', 'trunc': , 'cos': , 'pi': 3.141592653589793, 'e': 2.718281828459045, 'tanh': , 'radians': , 'sin': , 'atan2': , 'erf': , 'erfc': , 'exp': , 'acos': , 'log1p': , 'gamma': }

    2. from modname import name1[, name2[, ... nameN]]

      例如:<br> print __name__<br> from math import __name__<br> print __name__

      结果:<br> __main__<br> math

    3. 变量是拥有匹配对象的名字(标识符)。命名空间是一个包含了变量名称们(键)和它们各自相应的对象们(值)的字典。

      例如:

      a=5

      b=5

      则变量a和b都是int类型对象5的名字(标识符)。

  13. Jan 2021
  14. Nov 2020
    1. 一个生产者可能对应着多个消费者,生产者向队列中插入一条数据之后发出signal,然后各个消费者线程的pthread_cond_wait获取mutex后返回,当然,这里只有一个线程获取到了mutex,然后进行处理,其它线程会pending在这里,处理线程处理完毕之后释放mutex,刚才等待的线程中有一个获取mutex,如果这里用if,就会在当前队列为空的状态下继续往下处理,这显然是不合理的。

      当第一个消费者读取消息后,消息链将为空,第一个消费者释放mutex,第二个消费者抢占mutex,然后退出pthread_cond_wait(),如果用if语句,则第二个消费者将退出if语句继续往下读取空的消息链,这将报错,如果用while语句,则第二个消费者退出pthread_cond_wait()后,会检查消息链是否为空,为空则重新进入pthread_cond_wait(),阻塞并等待条件变量的到来,同时释放mutex。

    2. void enqueue_msg(struct msg *mp) { pthread_mutex_lock(&qlock); mp->m_next = workq; workq = mp; pthread_mutex_unlock(&qlock); pthread_cond_signal(&qready); }

      enqueue_msg是生产者,它会将新生成的msg插入到workq的前面,然后将workq的指向修改为新生成的msg。

    3. void process_msg(void) { struct msg *mp; for (;;) { pthread_mutex_lock(&qlock); while (workq == NULL) pthread_cond_wait(&qready, &qlock); mp = workq; workq = mp->m_next; pthread_mutex_unlock(&qlock); /* now process the message mp */ } }

      process_msg是消费者,它会先获取workq指向的msg,然后将workq的指向修改为下一个要处理的msg,最后回头处理已获取的msg。

    1. mmc rpmb read-block <rpmb device> <address> <blocks count> <output file> [key file]

      从RPMB设备的指定地址读取一个数据块(256bytes),Host可以指定是否需要用AuthKey验证MAC。

    2. A向B请求服务(比如说登录某个网站),A将密码hash化传给B。但是在这中间,E抓取到该hash值。此后,E冒充A向B发送同样的hash值来获取服务。

      在重放攻击中,E无法通过抓取的hash逆推出A的密码。

    3. Write protect was designed to protect against data corruption or erasure (whether malicious or unintentional). Once write protect is set, a host can’t erase or write to the specified protected area. However, unlike password lock, data can still be read from this area.

      Write Protect确保未经授权的host无法执行写操作,但是可以执行读操作。

    4. Password lock was the first security feature integrated into the eMMC spec; previously it had been implemented in legacy SD cards. The password lock feature is designed to protect the contents of the user area from any type of access (read, write, or erase).

      Password Lock确保未经授权的host无法执行读写操作。

  15. Oct 2020
    1. 不定长参数 * 输出一般都是元组的结构形式, ** 双星输出的都是字典形式的 结构,在传值的中也要确保值得准确匹配才行,不然会报错。

      注意:不同于字典,传值时用key=value,而不是key:value。

    2. python 中一切都是对象,严格意义我们不能说值传递还是引用传递,我们应该说传不可变对象和传可变对象。

      无论是不可变类型传递还是可变类型传递,在将实参传给形参时,实参和形参的id都一致;但是在不可变类型传递中,当形参发生变化时,形参的id也将发生变化,所以不会影响不同id的实参;而在可变类型传递中,当形参发生变化时,形参的id可能并没有变化,所以可能会影响相同id的实参。

    3. 在 python 中,类型属于对象,变量是没有类型的: a=[1,2,3] a="Runoob" 以上代码中,[1,2,3] 是 List 类型,"Runoob" 是 String 类型,而变量 a 是没有类型,她仅仅是一个对象的引用(一个指针),可以是 List 类型对象,也可以指向 String 类型对象。

      [1,2,3]属于list类型,是一个list类型对象;"Runoob"属于string类型,是一个string类型对象。

      变量a没有所属的类型,仅仅是一个对象的引用(指针),可以引用一个list类型对象,也可以引用一个string类型对象。

    1. if (!generateKeymasterKey(keymaster, auth, appId, &kmKey)) return false; if (!writeStringToFile(kmKey, dir + "/" + kFn_keymaster_key_blob)) return false;

      生成用于加密unencrypted key的kmKey,存储至/data/unencrypted/temp/keymaster_key_blob。

    2. if (!auth.usesKeymaster()) { return kStretch_none; } else if (auth.secret.empty()) { return kStretch_nopassword; }

      由于auth.token为空值,auth.secret为空值,因此auth.usesKeymaster()为true,所以最后返回kStretch_nopassword。

    3. if (!writeStringToFile(encryptedKey, dir + "/" + kFn_encrypted_key)) return false;

      将用kmKey加密后的内容存储至文件/data/unencrypted/temp/encrypted_key。

    4. if (stretchingNeedsSalt(stretching)) { if (ReadRandomBytes(SALT_BYTES, salt) != OK) { LOG(ERROR) << "Random read failed"; return false; } if (!writeStringToFile(salt, dir + "/" + kFn_salt)) return false; }

      写入salt(从设备/dev/urandom读取的16bytes的随机字节数据流)至文件/data/unencrypted/temp/salt。

    5. if (!createSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;

      写入secdiscardable(从设备/dev/urandom读取的16384bytes的随机字节数据流)至文件/data/unencrypted/temp/secdiscardable,然后计算其hash并存储至变量secdiscardable_hash。

    6. if (!readSecdiscardable(dir + "/" + kFn_secdiscardable, &secdiscardable_hash)) return false;

      读取文件/data/unencrypted/key/secdiscardable的内容,并返回其hash。

    7. if (!readRandomBytesOrLog(SECDISCARDABLE_BYTES, &secdiscardable)) return false;

      从设备/dev/urandom读取16384bytes的随机字节数据流存储至变量secdiscardable。

    1. 锁卡的目的:一些运营商会要求控制某一类卡的使用,从而保护自己的利益(运营商定制机)

      运营商定制机只能使用定制运营商的SIM卡。

    1. SHA512_Init(&c); SHA512_Update(&c, key_ref1, SHA512_DIGEST_LENGTH); unsigned char key_ref2[SHA512_DIGEST_LENGTH]; SHA512_Final(key_ref2, &c);

      对key_ref1计算一次SHA512得到64bytes的key_ref2。

    2. SHA512_Init(&c); SHA512_Update(&c, key, length); unsigned char key_ref1[SHA512_DIGEST_LENGTH]; SHA512_Final(key_ref1, &c);

      对密钥计算一次SHA512得到64bytes的key_ref1。

    3. LOG(DEBUG) << "Added key " << key_id << " (" << ref << ") to keyring " << device_keyring << " in process " << getpid();

      01-07 16:55:08.542 662 662 D vold : Added key 504049461 (ext4:1fc143592fab9ead) to keyring 471845337 in process 662

      01-07 16:55:08.542 662 662 D vold : Added key 124396701 (f2fs:1fc143592fab9ead) to keyring 471845337 in process 662

      01-07 16:55:08.542 662 662 D vold : Added key 39885111 (fscrypt:1fc143592fab9ead) to keyring 471845337 in process 662

      注意:这里1fc143592fab9ead就是要返回的raw key reference(即密钥的hash)。

    1. // Map user ids to key references std::map<userid_t, std::string> s_de_key_raw_refs; std::map<userid_t, std::string> s_ce_key_raw_refs; // TODO abolish this map, per b/26948053 std::map<userid_t, KeyBuffer> s_ce_keys;

      s_de_key_raw_refs保存各个用户DE密钥的raw key reference。

      s_ce_key_raw_refs保存各个用户CE密钥的raw key reference。

      s_ce_keys保存各个用户CE密钥的明文。

    2. // DE_n key auto system_de_path = android::vold::BuildDataSystemDePath(user_id); auto misc_de_path = android::vold::BuildDataMiscDePath(user_id); auto vendor_de_path = android::vold::BuildDataVendorDePath(user_id); auto user_de_path = android::vold::BuildDataUserDePath(volume_uuid, user_id);

      system_de_path为/data/system_de/0

      misc_de_path为/data/misc_de/0

      vendor_de_path为/data/vendor_de/0

      user_de_path为/data/user_de/0

    3. // DE_sys key auto system_legacy_path = android::vold::BuildDataSystemLegacyPath(user_id); auto misc_legacy_path = android::vold::BuildDataMiscLegacyPath(user_id); auto profiles_de_path = android::vold::BuildDataProfilesDePath(user_id);

      system_legacy_path为/data/system/users/0

      misc_legacy_path为/data/misc/users/0

      profiles_de_path为/data/misc/profiles/cur/0

    4. static std::vector<std::string> get_ce_key_paths(const std::string& directory_path)

      此函数用于获得路径directory_path下首字母为'c'的子目录的集合。

    5. if (entry->d_type != DT_DIR || entry->d_name[0] != 'c') { LOG(DEBUG) << "Skipping non-key " << entry->d_name; continue; }

      不是目录或者目录名首字母不为'c'则continue。

      01-07 16:55:08.952 662 662 D vold : Skipping non-key .

      01-07 16:55:08.952 662 662 D vold : Skipping non-key ..

    6. if (entry->d_type != DT_DIR || !is_numeric(entry->d_name)) { LOG(DEBUG) << "Skipping non-de-key " << entry->d_name; continue; }

      不是目录或者目录名不为数字则continue。

    1. // Represents the information needed to decrypt a disk encryption key. // If "token" is nonempty, it is passed in as a required Gatekeeper auth token. // If "token" and "secret" are nonempty, "secret" is appended to the application-specific // binary needed to unlock. // If only "secret" is nonempty, it is used to decrypt in a non-Keymaster process. class KeyAuthentication { public: KeyAuthentication(const std::string& t, const std::string& s) : token{t}, secret{s} {}; bool usesKeymaster() const { return !token.empty() || secret.empty(); }; const std::string token; const std::string secret; };

      表示用于解密磁盘加密密钥所需的信息。如果"token"是非空的,则将其作为Gatekeeper所需的身份验证令牌传递。如果"token"和"secret"为非空值,则"secret"将附加到所要解锁的应用程序的二进制文件中。如果只有"secret"是非空的,则在非Keymaster进程中将其用于解密。

  16. Sep 2020
    1. Time 模块包含了以下内置函数,既有时间处理的,也有转换时间格式的:

      时间戳:time.time()

      时间戳-->时间元组:time.localtime()、time.gmtime()、

      时间戳-->字符串:time.ctime()、

      时间元组-->字符串:time.asctime()、time.strftime()、

      字符串-->时间元组:time.strptime()、

      时间元组-->时间戳:time.mktime()、

    1. const std::string device_key_dir = std::string() + DATA_MNT_POINT + fscrypt_unencrypted_folder; const std::string device_key_path = device_key_dir + "/key"; const std::string device_key_temp = device_key_dir + "/temp";

      device_key_dir为/data/unencrypted

      device_key_path为/data/unencrypted/key

      device_key_temp为/data/unencrypted/temp

    1. // Parse the arguments: exec [SECLABEL [UID [GID]*] --] COMMAND ARGS... // SECLABEL can be a - to denote default std::size_t command_arg = 1; for (std::size_t i = 1; i < args.size(); ++i) { if (args[i] == "--") { command_arg = i + 1; break; } }

      SECLABEL可以取"-"表示默认值。

      args:exec [SECLABEL [UID [GID]*] --] COMMAND ARGS...

      args[0]:exec。

      for循环是用于获得COMMAND的索引,注意:如果遍历一遍后没有发现"--",则command_arg等于1,即COMMAND为args[1]。

    2. std::size_t nr_supp_gids = command_arg - 1 /* -- */ - 4 /* exec SECLABEL UID GID */;

      nr_supp_gids是补充的GID的个数。

      注意:command_arg既可以表示COMMAND的索引,也可以表示COMMAND前面元素的个数。

    1. 三、锁卡流程解析   1.首先modem侧检测SIM卡的配置信息并与之作比对,若匹配则继续加载SIM卡,若不匹配,则上报加锁信息   2.RIL层检测到modem上报的加锁信息,然后发送给framework层,最终在AP层监测到事件:   3.AP层显示出锁卡界面,要求用户输入解锁码进行解锁

      成功解锁后,手机就可以使用非定制运营商的SIM卡了。

      borneo的cricket定制机第一次开机后会在引导界面会要求用户插入SIM卡,如果用户插入的是cricket的SIM卡,则可以继续往下;但如果插入的是非cricket的SIM卡,则会弹出mycricket app,要求用户输入解锁码,用户需要联网获取正确的解锁码解锁后,才可以继续往下。

    2. 锁卡即SIMLock,当手机开机启动或者插入SIM卡时,手机modem侧预置在NV项中的配置信息会与SIM卡中的信息做比对,检测是否匹配。若匹配,则SIM卡可以正常使用。若不匹配,则SIM卡相关功能均无法正常使用,例如拨打电话、发送短信及上网等;或者是只能注册2G网,不能注册4G。

      SIM:Subscriber Identity Module,用户识别模块。

      SIMLock锁定的是手机使用SIM卡的功能,而不是SIM卡本身。

    3. SIMLock锁和图案锁,数字密码锁,PIN码锁,PUK锁一样,是Keyguard模块中的一种锁。

      SIMLock锁用于锁定/解锁手机使用SIM卡的功能。

      图案锁和数字密码锁用于锁定/解锁屏幕。

      PIN码锁和PUK锁用于锁定/解锁SIM卡。

    1. 值可以取任何数据类型,但键必须是不可变的,如字符串,数字或元组。

      因为元组一旦被定义就不能被修改,所以可以取元组作为键。

    1. >>> a=(1,2,3,4,5,6) >>> c=a[1:4]+a[5] # 报错, a[5] 被当成了整型 Traceback (most recent call last): File "<stdin>", line 1, in <module> TypeError: can only concatenate tuple (not "int") to tuple

      注意:只能将元组连接元组,由于a[5]返回的是单个元素,所以与元组连接会报错。

    1. list_2d = [ [0 for i in range(5)] for i in range(5)]

      [0 for i in range(5)]生成包含5个0的列表[0, 0, 0, 0, 0]。

      [ [0, 0, 0, 0, 0] for i in range(5) ]生成包含5个列表[0, 0, 0, 0, 0]的列表[ [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0], [0, 0, 0, 0, 0] ]

      更简单的实现是:list_2d = [ [0]*5 ]*5

  17. Aug 2020