61阅读

肛交的过程技巧详解-有车一族必备技能 详解换轮胎全过程

发布时间:2017-10-19 所属栏目:汽车维修技能全程图解

一 : 有车一族必备技能 详解换轮胎全过程

  [61阅读 用车]  随着科学技术的进步,越来越多的高科技运用到了汽车上。然而无论汽车多么先进,最终还是要靠轮胎将动力传递到地面上。车辆的性能不断提高,作为汽车上唯一接触地面的部分,轮胎的工作环境可谓非常恶劣。平时我们开车除了会发生刮蹭事故外,轮胎恐怕是最容易出现问题的部件。所以今天我们就通过图片来讲解一下换轮胎的过程。

61阅读

61阅读

  一种最极端的情况莫过于爆胎了,关于爆胎的问题我们已经讨论过很久了,最重要的就是在发生爆胎时不要惊慌,千万不能急踩刹车,而应该缓慢的减速,把车辆停在尽量不会影响交通的地方,下车之后要做的第一件事就是先设立三角警告标志。

61阅读

61阅读

61阅读

61阅读

  目前市面上多数汽车的备胎都装在后备箱地板下方,一般情况下随车工具也是放在这里,平时各位可能会忽略备胎,在这里提醒大家别忘了定时检查备胎状况以及随车工具是否齐全,免得关键时刻掉链子!

61阅读

61阅读

61阅读

61阅读

61阅读

  拿出备胎和随车工具后,先把轮胎的螺丝依次拧松,但是不要完全拧下来,而是要留下最后的几扣,然后再用千斤顶把车顶起来。

  看着一吨多重的汽车慢慢在自己的手里升起来确实很有成就感,但是可别升过头了,车轮稍微离地就可以了,免得之后装轮胎的时候费劲,给自己添麻烦。另外出于安全考虑,别忘了车升起来之后先把备胎塞到车底。

61阅读

61阅读

61阅读

61阅读

  我认为安装备胎是整个换台过程中最令人头疼的问题,光是让备胎老老实实的和车轴对其就已经很让人头疼了,所以刚才说了千斤顶不要升得太高,否则此时就会很累了。如果有人帮忙扶着车轮就很方便了,但如果只有自己一个人的话就最好采用下图这样的姿势,用双脚顶住轮胎。

61阅读

  装备胎时还要注意一点,那就是不要一口气把一个螺丝拧紧,而是要按照对角线的顺序轮流拧紧。比如说我们展示的这辆车有四颗螺丝,我们先拧左上角螺丝,拧几下之后换右下角,然后是另外的两个,拧的圈数最好相同,这样可以保证所有螺丝受力均匀。

61阅读

61阅读 61阅读

61阅读 61阅读

  拧过几轮之后,当感觉手的力量已经基本拧不动的时候就可以把千斤顶移走了,就像开始拆卸轮胎时那样,装备胎也要靠身体重量把所有螺丝拧紧,同样按照对角线的顺序。

61阅读

61阅读

  确定所有螺丝拧紧之后,备胎就算是换好了……

  总的来说换轮胎这个工作确实是个小小的力气活,当然也是需要一定技巧的,每位经常开车、喜爱开车的人都应该练习一下换轮胎的过程,我相信很多开了好几年车的朋友未必亲自动手换过轮胎,如果有机会熟悉一下过程,这样在遇到爆胎等事故时才能做到临危不乱,胸有成竹。另外还要提醒大家,每次在给车辆做保养的时候不要忘记检查备胎胎压,不要等到用备胎的时候发现胎压不足,那就比较麻烦了……

61阅读

  最后不要忘了把50米以外的三角警示牌拿回来...(文/图 61阅读 罗浩 摄影/61阅读 孟庆嘉)

二 : Android的init过程详解(一)

Android的init过程(二):初始化语言(init.rc)解析

本文使用的软件版本

Android:4.2.2

Linux内核:3.1.10

    本文及后续几篇文章将对Android的初始化(init)过程进行详细地、剥丝抽茧式地分析,并且在其中穿插了大量的知识,希望对读者了解Android的启动过程又所帮助。(www.61k.com]本章主要介绍了与硬件相关初始化文件名的确定以及属性服务的原理和实现。

    Android本质上就是一个基于Linux内核的操作系统。与Ubuntu Linux、Fedora Linux类似。只是Android在应用层专门为移动设备添加了一些特有的支持。既然Android是Linux内核的系统,那么基本的启动过程也应符合Linux的规则。如果研究过其他Linux系统应该了解,一个完整的Linux系统首先会将一个Linux内核装载到内存,也就是编译Linux内核源代码生成的bzImage文件,对于为Android优化的Linux内核源代码会生成zImage文件。该文件就是Linux内核的二进制版本。由于zImage在内核空间运行,而我们平常使用的软件都是在应用空间运行(关于内核空间和应用空间的详细描述,可以参考《Android深度探索(卷1):HAL与驱动开发》一书的内容,在后续的各卷中将会对Android的整体体系进行全方位的剖析)。内核空间和应用空间是不能直接通过内存地址级别访问的,所以就需要建立某种通讯机制。

    目前Linux有很多通讯机制可以在用户空间和内核空间之间交互,例如设备驱动文件(位于/dev目录中)、内存文件(/proc、/sys目录等)。了解Linux的同学都应该知道Linux的重要特征之一就是一切都是以文件的形式存在的,例如,一个设备通常与一个或多个设备文件对应。这些与内核空间交互的文件都在用户空间,所以在Linux内核装载完,需要首先建立这些文件所在的目录。而完成这些工作的程序就是本文要介绍的init。Init是一个命令行程序。其主要工作之一就是建立这些与内核空间交互的文件所在的目录。当Linux内核加载完后,要做的第一件事就是调用init程序,也就是说,init是用户空间执行的第一个程序。

在分析init的核心代码之前,还需要初步了解init除了建立一些目录外,还做了如下的工作

1. 初始化属性

2. 处理配置文件的命令(主要是init.rc文件),包括处理各种Action。

3. 性能分析(使用bootchart工具)。

4. 无限循环执行command(启动其他的进程)。

    尽管init完成的工作不算很多,不过代码还是非常复杂的。Init程序并不是由一个源代码文件组成的,而是由一组源代码文件的目标文件链接而成的。这些文件位于如下的目录。

<Android源代码本目录>/system/core/init

    其中init.c是init的主文件,现在打开该文件,看看其中的内容。由于init是命令行程序,所以分析init.c首先应从main函数开始,现在好到main函数,代码如下:

int main(int argc, char **argv) {  int fd_count = 0;  struct pollfd ufds[4];  char *tmpdev;  char* debuggable;  char tmp[32];  int property_set_fd_init = 0;  int signal_fd_init = 0;  int keychord_fd_init = 0;  bool is_charger = false;  if (!strcmp(basename(argv[0]), "ueventd"))  return ueventd_main(argc, argv);  if (!strcmp(basename(argv[0]), "watchdogd"))  return watchdogd_main(argc, argv);  /* clear the umask */  umask(0);  // 下面的代码开始建立各种用户空间的目录,如/dev、/proc、/sys等  mkdir("/dev", 0755);  mkdir("/proc", 0755);  mkdir("/sys", 0755);  mount("tmpfs", "/dev", "tmpfs", MS_NOSUID, "mode=0755");  mkdir("/dev/pts", 0755);  mkdir("/dev/socket", 0755);  mount("devpts", "/dev/pts", "devpts", 0, NULL);  mount("proc", "/proc", "proc", 0, NULL);  mount("sysfs", "/sys", "sysfs", 0, NULL);  /* 检测/dev/.booting文件是否可读写和创建*/  close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));  open_devnull_stdio();  klog_init();  // 初始化属性  property_init();  get_hardware_name(hardware, &revision);  // 处理内核命令行  process_kernel_cmdline();  … …  is_charger = !strcmp(bootmode, "charger");  INFO("property init\n");  if (!is_charger)  property_load_boot_defaults();  INFO("reading config file\n");  // 分析/init.rc文件的内容  init_parse_config_file("/init.rc");  … …// 执行初始化文件中的动作  action_for_each_trigger("init", action_add_queue_tail);  // 在charger模式下略过mount文件系统的工作  if (!is_charger) {  action_for_each_trigger("early-fs", action_add_queue_tail);  action_for_each_trigger("fs", action_add_queue_tail);  action_for_each_trigger("post-fs", action_add_queue_tail);  action_for_each_trigger("post-fs-data", action_add_queue_tail);  }  queue_builtin_action(property_service_init_action, "property_service_init");  queue_builtin_action(signal_init_action, "signal_init");  queue_builtin_action(check_startup_action, "check_startup");  if (is_charger) {  action_for_each_trigger("charger", action_add_queue_tail);  } else {  action_for_each_trigger("early-boot", action_add_queue_tail);  action_for_each_trigger("boot", action_add_queue_tail);  }  /* run all property triggers based on current state of the properties */  queue_builtin_action(queue_property_triggers_action, "queue_property_triggers"); #if BOOTCHART  queue_builtin_action(bootchart_init_action, "bootchart_init"); #endif  // 进入无限循环,建立init的子进程(init是所有进程的父进程)  for(;;) {  int nr, i, timeout = -1;  // 执行命令(子进程对应的命令)  execute_one_command();  restart_processes();  if (!property_set_fd_init && get_property_set_fd() > 0) {  ufds[fd_count].fd = get_property_set_fd();  ufds[fd_count].events = POLLIN;  ufds[fd_count].revents = 0;  fd_count++;  property_set_fd_init = 1;  }  if (!signal_fd_init && get_signal_fd() > 0) {  ufds[fd_count].fd = get_signal_fd();  ufds[fd_count].events = POLLIN;  ufds[fd_count].revents = 0;  fd_count++;  signal_fd_init = 1;  }  if (!keychord_fd_init && get_keychord_fd() > 0) {  ufds[fd_count].fd = get_keychord_fd();  ufds[fd_count].events = POLLIN;  ufds[fd_count].revents = 0;  fd_count++;  keychord_fd_init = 1;  }  if (process_needs_restart) {  timeout = (process_needs_restart - gettime()) * 1000;  if (timeout < 0)  timeout = 0;  }  if (!action_queue_empty() || cur_action)  timeout = 0; // bootchart是一个性能统计工具,用于搜集硬件和系统的信息,并将其写入磁盘,以便其 // 他程序使用 #if BOOTCHART  if (bootchart_count > 0) {  if (timeout < 0 || timeout > BOOTCHART_POLLING_MS)  timeout = BOOTCHART_POLLING_MS;  if (bootchart_step() < 0 || --bootchart_count == 0) {  bootchart_finish();  bootchart_count = 0;  }  } #endif  // 等待下一个命令的提交  nr = poll(ufds, fd_count, timeout);  if (nr <= 0)  continue;  for (i = 0; i < fd_count; i++) {  if (ufds[i].revents == POLLIN) {  if (ufds[i].fd == get_property_set_fd())  handle_property_set_fd();  else if (ufds[i].fd == get_keychord_fd())  handle_keychord();  else if (ufds[i].fd == get_signal_fd())  handle_signal();  }  }  }  return 0; }

扩展:android编译过程详解 / android 扫描过程详解 / android 启动过程详解

    我们可以看到main函数是非常复杂的,不过我们也不需要每条语句都弄得非常清楚(因为这样弄是非常困难的),通常只需要了解init的主线即可。其实从init的main函数可以看出。Init实际上就分为如下两部分。

1. 初始化(包括建立/dev、/proc等目录、初始化属性、执行init.rc等初始化文件中的action等)。

2. 使用for循环无限循环建立子进程。

    第一项工作很好理解。而第二项工作是init中的核心。在Linux系统中init是一切应用空间进程的父进程。所以我们平常在Linux终端执行的命令,并建立进程。实际上都是在这个无限的for循环中完成的。也就是说,在Linux终端执行ps –e 命令后,看到的所有除了init外的其他进程,都是由init负责创建的。而且init也会常驻内容。当然,如果init挂了,Linux系统基本上就崩溃了。

   由于init比较复杂,所以本文只分析其中的一部分,在后续文章中将详细分析init的各个核心组成部分。

     对于main函数最开始完成的建立目录的工作比较简单,这部分也没什么可以分析的。就是调用了一些普通的API(mkdir)建立一些目录。现在说一些题外话,由于Android的底层源代码(包括init)实际上是属于Linux应用编程领域,所以要想充分理解Android源代码,除了Linux的基本结构要了解外,Linux应用层的API需要熟悉。为了满足这些读者的需要,后续我会写一些关于Linux应用编程的文章。Ok,现在言归正传,接下来分析一个比较重要的部分:配置文件的解析。

     这里的配置文件主要指init.rc。读者可以进到Android的shell,会看到根目录有一个init.rc文件。该文件是只读的,即使有了root权限,可以修改该文件也没有。因为我们在根目录看到的文件只是内存文件的镜像。也就是说,android启动后,会将init.rc文件装载到内存。而修改init.rc文件的内容实际上只是修改内存中的init.rc文件的内容。一旦重启android,init.rc文件的内容又会恢复到最初的装载。想彻底修改init.rc文件内容的唯一方式是修改Android的ROM中的内核镜像(boot.img)。其实boot.img名曰内核镜像,不过该文件除了包含完整的Linux内核文件(zImage)外,还包括另外一个镜像文件(ramdisk.img)。ramdisk.img就包含了init.rc文件和init命令。所以只有修改ramdisk.img文件中的init.rc文件,并且重新打包boot.img文件,并刷机,才能彻底修改init.rc文件。如果读者有Android源代码,编译后,就会看到out目录中的相关子目录会生成一个root目录,该目录实际上就是ramdisk.img解压后的内容。会看到有init命令和init.rc文件。在后续的文章中将会讨论具体如何修改init.rc文件,如何刷机。不过这些内容与本文关系不大,所以不做详细的讨论。

现在回到main函数,在创建完目录后,会看到执行了如下3个函数。

   property_init();
   get_hardware_name(hardware, &revision);
   process_kernel_cmdline();

    其中property_init主要是为属性分配一些存储空间,该函数并不是核心。不过当我们查看init.rc文件时会发现该文件开始部分用一些import语句导入了其他的配置文件,例如,/init.usb.rc。大多数配置文件都直接使用了确定的文件名,只有如下的代码使用了一个变量(${ro.hardware})执行了配置文件名的一部分。那么这个变量值是从哪获得的呢?

import /init.${ro.hardware}.rc

    首先要了解init.${ro.hardware}.rc配置文件的内容通常与当前的硬件有关。现在我们先来关注get_hardware_name函数,代码如下:

void get_hardware_name(char *hardware, unsigned int *revision) {  char data[1024];  int fd, n;  char *x, *hw, *rev;  /* 如果hardware已经有值了,说明hardware通过内核命令行提供,直接返回 */  if (hardware[0])  return;  // 打开/proc/cpuinfo文件  fd = open("/proc/cpuinfo", O_RDONLY);  if (fd < 0) return;  // 读取/proc/cpuinfo文件的内容  n = read(fd, data, 1023);  close(fd);  if (n < 0) return;  data[n] = 0;  // 从/proc/cpuinfo文件中获取Hardware字段的值  hw = strstr(data, "\nHardware");  rev = strstr(data, "\nRevision");  // 成功获取Hardware字段的值  if (hw) {  x = strstr(hw, ": ");  if (x) {  x += 2;  n = 0;  while (*x && *x != '\n') {  if (!isspace(*x))  // 将Hardware字段的值都转换为小写,并更新hardware参数的值  // hardware也就是在init.c文件中定义的hardware数组  hardware[n++] = tolower(*x);  x++;  if (n == 31) break;  }  hardware[n] = 0;  }  }  if (rev) {  x = strstr(rev, ": ");  if (x) {  *revision = strtoul(x + 2, 0, 16);  }  } }

       从get_hardware_name方法的代码可以得知,该方法主要用于确定hardware和revision的变量的值。Revision这里先不讨论,只要研究hardware。获取hardware的来源是从Linux内核命令行或/proc/cpuinfo文件中的内容。Linux内核命令行暂且先不讨论(因为很少传递该值),先看看/proc/cpuinfo,该文件是虚拟文件(内存文件),执行cat /proc/cpuinfo命令会看到该文件中的内容,如图1所示。在白框中就是Hardware字段的值。由于该设备是Nexus 7,所以值为grouper。如果程序就到此位置,那么与硬件有关的配置文件名是init.grouper.rc。有Nexus 7的读者会看到在根目录下确实有一个init.grouper.rc文件。说明Nexus 7的原生ROM并没有在其他的地方设置配置文件名,所以配置文件名就是从/proc/cpuinfo文件的Hardware字段中取的值。

扩展:android编译过程详解 / android 扫描过程详解 / android 启动过程详解

init Android的init过程详解(一)

                                                 图1

现在来看在get_hardware_name函数后面调用的process_kernel_cmdline函数,代码如下:

static void process_kernel_cmdline(void) {  /* don't expose the raw commandline to nonpriv processes */  chmod("/proc/cmdline", 0440);  // 导入内核命令行参数  import_kernel_cmdline(0, import_kernel_nv);  if (qemu[0])  import_kernel_cmdline(1, import_kernel_nv);  // 用属性值设置内核变量  export_kernel_boot_props(); }

      在process_kernel_cmdline函数中除了使用import_kernel_cmdline函数导入内核变量外,主要的功能就是调用export_kernel_boot_props函数通过属性设置内核变量,例如,通过ro.boot.hardware属性设置hardware变量,也就是说可以通过ro.boot.hardware属性值可以修改get_hardware_name函数中从/proc/cpuinfo文件中得到的hardware字段值。下面看一下export_kernel_boot_props函数的代码。

static void export_kernel_boot_props(void) {  char tmp[PROP_VALUE_MAX];  const char *pval;  unsigned i;  struct {  const char *src_prop;  const char *dest_prop;  const char *def_val;  } prop_map[] = {  { "ro.boot.serialno", "ro.serialno", "", },  { "ro.boot.mode", "ro.bootmode", "unknown", },  { "ro.boot.baseband", "ro.baseband", "unknown", },  { "ro.boot.bootloader", "ro.bootloader", "unknown", },  };  // 通过内核的属性设置应用层配置文件的属性  for (i = 0; i < ARRAY_SIZE(prop_map); i++) {  pval = property_get(prop_map[i].src_prop);  property_set(prop_map[i].dest_prop, pval ?: prop_map[i].def_val);  }  // 根据ro.boot.console属性的值设置console变量  pval = property_get("ro.boot.console");  if (pval)  strlcpy(console, pval, sizeof(console));  /* save a copy for init's usage during boot */  strlcpy(bootmode, property_get("ro.bootmode"), sizeof(bootmode));  /* if this was given on kernel command line, override what we read  * before (e.g. from /proc/cpuinfo), if anything */  // 获取ro.boot.hardware属性的值  pval = property_get("ro.boot.hardware");  if (pval)
// 这里通过ro.boot.hardware属性再次改变hardware变量的值 strlcpy(hardware, pval, sizeof(hardware)); // 利用hardware变量的值设置设置ro.hardware属性
// 这个属性就是前面提到的设置初始化文件名的属性,实际上是通过hardware变量设置的
property_set("ro.hardware", hardware); snprintf(tmp, PROP_VALUE_MAX, "%d", revision); property_set("ro.revision", tmp); /* TODO: these are obsolete. We should delete them */ if (!strcmp(bootmode,"factory")) property_set("ro.factorytest", "1"); else if (!strcmp(bootmode,"factory2")) property_set("ro.factorytest", "2"); else property_set("ro.factorytest", "0"); }

     从export_kernel_boot_props函数的代码可以看出,该函数实际上就是来回设置一些属性值,并且利用某些属性值修改console、hardware等变量。其中hardware变量(就是一个长度为32的字符数组)在get_hardware_name函数中已经从/proc/cpuinfo文件中获得过一次值了,在export_kernel_boot_props函数中又通过ro.boot.hardware属性设置了一次值,不过在Nexus 7中并没有设置该属性,所以hardware的值仍为grouper。最后用hardware变量设置ro.hardware属性,所以最后的初始化文件名为init.grouper.rc。

     这里还有一个问题,前面多次提到属性或属性文件,那么这些属性文件指的是什么呢?是init.rc?当然不是。实际上这些属性文件是一些列位于不同目录,系统依次读取的配置文件。

属性服务(Property Service)

在研究这些配置文件之前应先了解init是如何处理这些属性的。编写过Windows本地应用的读者都应了解,在windows中有一个注册表机制,在注册表中提供了大量的属性。在Linux中也有类似的机制,这就是属性服务。init在启动的过程中会启动属性服务(Socket服务),并且在内存中建立一块存储区域,用来存储这些属性。当读取这些属性时,直接从这一内存区域读取,如果修改属性值,需要通过Socket连接属性服务完成。在init.c文件中的一个action函数中调用了start_property_service函数来启动属性服务,action是init.rc及其类似文件中的一种执行机制,由于内容比较多,所以关于init.rc文件中的执行机制将在下一篇文章中详细讨论。

    现在顺藤摸瓜,找到start_property_service函数,该函数在Property_service.c文件中,该文件与init.c文件中同一个目录。

void start_property_service(void) {  int fd;  // 装载不同的属性文件  load_properties_from_file(PROP_PATH_SYSTEM_BUILD);  load_properties_from_file(PROP_PATH_SYSTEM_DEFAULT);  load_override_properties();  /* Read persistent properties after all default values have been loaded. */  load_persistent_properties();  // 创建socket服务(属性服务)  fd = create_socket(PROP_SERVICE_NAME, SOCK_STREAM, 0666, 0, 0);  if(fd < 0) return;  fcntl(fd, F_SETFD, FD_CLOEXEC);  fcntl(fd, F_SETFL, O_NONBLOCK);  // 开始服务监听  listen(fd, 8);  property_set_fd = fd; }

扩展:android编译过程详解 / android 扫描过程详解 / android 启动过程详解

     现在我们已经知道属性服务的启动方式了,那么在start_property_service函数中还涉及到如下两个宏。

PROP_PATH_SYSTEM_BUILD

PROP_PATH_SYSTEM_DEFAULT

     这两个宏都是系统预定义的属性文件名的路径。为了获取这些宏的定义,我们先进行另外一个函数的分析。

     在前面读取属性值时使用过一个property_get函数,该函数在Property_service.c中实现,代码如下:

const char* property_get(const char *name) {  prop_info *pi;  if(strlen(name) >= PROP_NAME_MAX) return 0;  pi = (prop_info*) __system_property_find(name);  if(pi != 0) {  return pi->value;  } else {  return 0;  } }

     可以看到,在property_get函数中调用了一个核心函数__system_property_find,该函数真正实现了获取属性值的功能。该函数属于bionic的一个library,在system_properties.c文件中实现,读者可以在如下的目录找到该文件。

<Android源代码根目录>/bionic/libc/bionic

__system_property_find函数的代码如下:

const prop_info *__system_property_find(const char *name) {  // 获取属性存储内存区域的首地址  prop_area *pa = __system_property_area__;  unsigned count = pa->count;  unsigned *toc = pa->toc;  unsigned len = strlen(name);  prop_info *pi;  while(count--) {  unsigned entry = *toc++;  if(TOC_NAME_LEN(entry) != len) continue;  pi = TOC_TO_INFO(pa, entry);  if(memcmp(name, pi->name, len)) continue;  return pi;  }  return 0; }

     从__system_property_find函数的代码很容易看出,第一行使用了一个__system_property_area__变量,该变量是全局的。在前面分析main函数时涉及到一个property_init函数,该函数调用了init_property_area函数,该函数用于初始化属性内存区域,也就是__system_property_area__变量。

static int init_property_area(void) {  prop_area *pa;  if(pa_info_array)  return -1;  if(init_workspace(&pa_workspace, PA_SIZE))  return -1;  fcntl(pa_workspace.fd, F_SETFD, FD_CLOEXEC);  pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);  pa = pa_workspace.data;  memset(pa, 0, PA_SIZE);  pa->magic = PROP_AREA_MAGIC;  pa->version = PROP_AREA_VERSION;  /* 初始化属性内存区域,属性服务会使用该区域 */  __system_property_area__ = pa;  property_area_inited = 1;  return 0; }

     在前面涉及到的system_properties.c文件对应的头文件system_properties.h中定义了前面提到的两个表示属性文件路径的宏,其实还有另外两个表示路径的宏,一共4个属性文件。system_properties.h文件可以在<Android源代码根目录>/bionic/libc/include/sys目录中找到。这4个宏定义如下:

#define PROP_PATH_RAMDISK_DEFAULT "/default.prop" #define PROP_PATH_SYSTEM_BUILD "/system/build.prop" #define PROP_PATH_SYSTEM_DEFAULT "/system/default.prop" #define PROP_PATH_LOCAL_OVERRIDE "/data/local.prop"

      现在读者可以进入Android设备的相应目录,通常可以找到上述4个文件,如一般会在根目录,会发现一个default.prop文件,cat default.prop会看到该文件的内容。而属性服务就是装载所有这4个属性文件中的所有属性以及使用property_set设置的属性。在Android设备的终端可以直接使用getprop命令从属性服务获取所有的属性值。如图2所示。getprop命令还可以直接根属性名还获取具体的属性值,例如,getprop ro.build.product。

init Android的init过程详解(一)

                                                              图2

     如果读者感兴趣,可以看一下getprop是如何通过属性服务读写属性的。getprop命令的源代码文件是getprop.c。读者可以在<Android源代码根目录>/system/core/toolbox目录中找到该文件。实际上,getprop获取属性值也是通过property_get函数完成的。在前面分析过该函数,实际上调用了__system_property_find函数从__system_property_area__变量指定的内存区域获取相应的属性值。

     此外在system_properties.c文件中还有如下两个函数用于通过属性服务修改或添加某个属性的值。

static int send_prop_msg(prop_msg *msg) {  struct pollfd pollfds[1];  struct sockaddr_un addr;  socklen_t alen;  size_t namelen;  int s;  int r;  int result = -1;  // 创建用于连接属性服务的socket  s = socket(AF_LOCAL, SOCK_STREAM, 0);  if(s < 0) {  return result;  }  memset(&addr, 0, sizeof(addr));  // property_service_socket是Socket设备文件名称  namelen = strlen(property_service_socket);  strlcpy(addr.sun_path, property_service_socket, sizeof addr.sun_path);  addr.sun_family = AF_LOCAL;  alen = namelen + offsetof(struct sockaddr_un, sun_path) + 1;  if(TEMP_FAILURE_RETRY(connect(s, (struct sockaddr *) &addr, alen)) < 0) {  close(s);  return result;  }  r = TEMP_FAILURE_RETRY(send(s, msg, sizeof(prop_msg), 0));  if(r == sizeof(prop_msg)) {  pollfds[0].fd = s;  pollfds[0].events = 0;  r = TEMP_FAILURE_RETRY(poll(pollfds, 1, 250 /* ms */));  if (r == 1 && (pollfds[0].revents & POLLHUP) != 0) {  result = 0;  } else {  result = 0;  }  }  close(s);  return result; } // 用户可以直接调用该函数设置属性值 int __system_property_set(const char *key, const char *value) {  int err;  int tries = 0;  int update_seen = 0;  prop_msg msg;  if(key == 0) return -1;  if(value == 0) value = "";  if(strlen(key) >= PROP_NAME_MAX) return -1;  if(strlen(value) >= PROP_VALUE_MAX) return -1;  memset(&msg, 0, sizeof msg);  msg.cmd = PROP_MSG_SETPROP;  strlcpy(msg.name, key, sizeof msg.name);  strlcpy(msg.value, value, sizeof msg.value);  // 设置属性值  err = send_prop_msg(&msg);  if(err < 0) {  return err;  }  return 0; }

扩展:android编译过程详解 / android 扫描过程详解 / android 启动过程详解

   在send_prop_msg函数中涉及到一个property_service_socket变量,定义如下:

static const char property_service_socket[] = "/dev/socket/" PROP_SERVICE_NAME;

    实际上,send_prop_msg通过这个设备文件与属性服务通讯的。读者可以在Android设备的终端进入/dev/socket目录,通常会看到一个property_service文件,该文件就是属性服务映射的设备文件。

    现在已经分析完了init如何确定与硬件相关的初始化文件名(init.grouper.rc),并且讨论了4个属性文件及其装载过程,以及属性服务实现的基本原理。在下一篇文章中将讨论更深入的内容,例如,init.rc文件中提供了很多action,那么什么是aciton呢,init有是如何解析init.rc文件呢?这些内容都将在下一篇文章中揭晓。

扩展:android编译过程详解 / android 扫描过程详解 / android 启动过程详解

三 : iPhone6/6 Plus的专属技巧汇总详解(附视频)

  2014年即将结束了,你是否已顺利抢购到心仪的iPhone6或iPhone6 Plus,当作这一年来自己努力工作的犒劳礼物呢?而你是否已经熟悉并玩透了它呢?作为苹果首款打破传统规格的大屏iPhone,自然也会拥有前代版本所没有的新功能,不过却隐藏着在当中,需要你有一双善于发现的眼睛哦。

  本文将持续汇总与你同已成为iPhone6或iPhone6 Plus的用户,经过实践得出一些技巧。相信能够帮助你能更快地熟悉自己的爱机,或发现它们的特别之处。

四个最基本的技巧  

  1. 双击(但不要按下)Home键开启单手模式

关于iPhone6/6 Plus的专属技巧汇总【持续更新】

  相比一些安卓机型,苹果拿出了更易用的单手模式解决方案。你不需要进入设置中调节,只需在需要时双击(但不按下)Home键,就能开启单手模式,你会发现主屏应用程序图标全部下移并缩减为三列,但增加了页面数量。这个模式也适用于任何应用,只是界面上方大片的空白稍微丑了些。

  2.显示缩放功能

关于iPhone6/6 Plus的专属技巧汇总【持续更新】

  如果你觉得单手模式图标仍不够大,那么可以进入“设置-显示和亮度”项目中开启显示缩放功能,图标和菜单项目会变得更大。

  3.iPhone6 Plus的横向显示模式

关于iPhone6/6 Plus的专属技巧汇总【持续更新】

  如果你使用的是iPhone6 Plus,那么它还支持主屏和应用的横向显示模式。部分应用如消息、电子邮件,会采用类似iPad那样的分屏显示效果,让你同时看到消息列表和详细内容,充分利用大屏幕空间。

  4.iPhone6 Plus的横向键盘

关于iPhone6/6 Plus的专属技巧汇总【持续更新】

  由于屏幕尺寸更大,iPhone6 Plus的横向键盘可以更直观地进行操作,不需要进入二级目录就能使用剪切、粘贴等功能,所以如果你需要进行大量文字操作,不妨使用横向模式。

  而来自YouTube频道TechSmartt更是总结并分享了一个iPhone6/6 Plus功能技巧50+的视频,Get!

还有这些简单技巧你也应该了解的:

  • 同时按住POWER键和HOME键可以截取当前屏幕内容;

  • 手指双击屏幕或三个手指同时点击屏幕,屏幕自动放大;

  • 长按HOME键8秒,可以推出任何卡死的软件;

  • 连按两下HOME键可以叫出任务栏,连按3下HOME键的功能可以自定义;

  • 输入短信时,输入“i”并晃动手机可以进行撤消操作。还有如果输入两个空格可以输入“。”(设置中可关闭此功能);

  • 在计算器中没有退格键,但你只需在数字屏幕上向左或是向右划一下就可以实现退格功能;

  • iPhone6的原装耳机除之前几代的功能之外,其音量+还能作为快门来拍照。

  • 非静音状态下iPhone6的相机快门声音不可关闭,但你可以按住扬声器静音效果非常好!

  iPhone6 Plus短信怎么转发步骤:

  1、首先请找到想要转发的短信,用手指按住这条短信不动

  2、然后在短信上方会弹出选项菜单,点击“更多”选项

  3、随后这条短信就被选中了,可以看到短信左边的小圆圈打上勾了

  4、如果想要转发多条短信,只需要点击短信左边的小圆圈即可。然后点击屏幕右下角的“转发”按钮

  5、接下来这个界面是不是很熟悉了,系统自动把我们之前选中的短信,作为新的发短信内容。这里以选择通讯录中的联系人为例,点击“+”按钮

   6、随后在通讯录中选择想要转发短信的联系人

  7、然后点击“发送”按钮。

熟悉并掌握其搭载的iOS8技巧很重要

  随机搭载的IOS8也算是苹果最大的一次升级了,虽然Bug的存在是难以避免的,不过不得承认苹果在iOS8系统仍是耗费了不少的心血,做了相当多的底层变动,譬如开了多个API(touchID、相机、分享)、增加多个kit(Photo、health等)、多种显示技术的更新(SpriteKit、SceneKit、Metal),更不用说全新编程预言swift了。这么多改变,要想程序兼容性能更为完美,还是有待一段不小的时间的。

送给iPhone6 Plus的专属彩蛋

  最后,再给大家分享两个有关iPhone6 Plus独有改变:

  • 更大的电池容量。国外拆解团队给出的电池容量为2915毫安,这比起iPhone5s的1560毫安相比,iPhone6 Plus的电池容量增加了将近一倍。

  • 无线更快。经过专业组织的实验,新款iPhone6 Plus的wifi更快,要比5s快至少一倍,峰值达到了两倍。这对于喜欢下载软件折腾的人来说还是不错的消息。对于那些喜欢在线看电影的朋友们来说,简直是极好极好。

四 : WinPE的启动过程详解

WinPE相信很多人都用过,但是极少有人知道它的启动过程,想知道PE是怎么启动的吗?下面为你一一道来。

1、U盘HDD(WinPE)的启动过程:

BIOS—>NTLDR—>SETUPLDR.BIN(PELDR或其它相关名字)—>NTDETECT.COM—>WINNT.SIF(WINNT.XPE)—>WINPE.ISO(WINPE.IMG)—>TXTSETUP.SIF—>WINPE系统桌面

2、U盘ZIP(WinPE)的启动过程:

BIOS—>IO.SYS—>COMMAND.COM—>AUTOEXEC.BAT—>SETUPLDR.BIN(PELDR或其它相关名字)—>NTDETECT.COM—>WINNT.SIF(WINNT.XPE)—>WINPE.ISO(WINPE.IMG)—>TXTSETUP.SIF—>WINPE系统桌面

3、光盘(WinPE)的启动过程:

BIOS—>PEBOOT.BIF(光盘引导信息)—>定位启动映像区...—>SETUPLDR.BIN(PELDR或其它相关名字)—>NTDETECT.COM—>WINNT.SIF(WINNT.XPE)—>WINPE.ISO(WINPE.IMG)—>TXTSETUP.SIF—>WINPE系统桌面

4、硬盘(WinPE)的启动过程:

BIOS—>MBR(主引导记录)—>PBR(分区引导)—>NTLDR—>BOOT.INI—>AVLDR.PE—>SETUPLDR.BIN(PELDR或其它相关名字)—>NTDETECT.COM—>WINNT.SIF(WINNT.XPE)—>WINPE.ISO(WINPE.IMG)—>TXTSETUP.SIF—>WINPE系统桌面

五 : 详解windows系统的超酷技巧! windows技巧图文教程

下面61阅读小编为大家详细的介绍在windows系统中的一些超酷技巧,是针对win系统通用的,win7,Win8.1,Win10都可以使用,一起来看看吧。

文件隐藏

谁的电脑里没点小秘密?东藏西藏到最后自己都找不到了有木有?今天教大家个隐藏文件的高招:将任意文件隐藏到图片中!怎么样?再也不用建什么“马列主义哲学”的文件夹啦! 看图:

当然,如果觉得手动输入命令麻烦的话,这里有图形化的快捷工具:

将文件隐藏到图片中的软件 更新(移除了对WinRAR的依赖)

记事本自动记录修改时间

你有用记事本记账或写日记的习惯吗?其实在记事本的文档开头输入".LOG"(无引号,字母为大写),之后记录内容并保存,这样以后打开就会看到之前每次修改的时间了。记录跟时间有关的内容会十分方便!

当然你也可以在每写完一条记录或想法后,按F5,同样可以插入时间!

批量重命名文件

Windows提供了非常方便的批量重命名文件的功能,在资源管理器中同时选择几个文件,接着按F2键,然后重命名这些文件中的一个,这样所有被选择的文件将会被重命名为新的文件名(在末尾处加上递增的数字)。

快捷复制文件路径、在新的进程打开文件夹

当我们想要说明一个具体的文件在哪里的时候,把“错综复杂”的路径告诉别人是非常麻烦的事情。其实只需要按住“Shift”键,右键点击文件或文件夹,选择“复制为路径”就可以粘贴给你的朋友啦

如果你右键的是文件夹的话还会有“在新的进程中打开”的选项,这样一个文件夹的崩溃就不会导致全部文件夹的崩溃(肯定有很多人遇到过资源管理器“未响应”的情况吧)。

特殊字符输入

¢、ĉ这些符号是怎么输入的知道嘛?今天来教大家一个简单实用的方法——字符映射表。Win+R输入“charmap”就可以调出它,找到自己需要的符号啦~选择复制就OK!

Windows窗口操作

如果你屏幕上有很多窗口都最大化了,虽然你只能看到当前的程序,但是被挡在后面最大化的窗口也会占用很多系统资源,这时候如果你按下Win+Home,就可以将除当前窗口外其他所有程序都最小化(拖住当前窗口晃动可以达到同样效果),节约系统资源快速有效,再次按下Win+Home(或再次晃动)可以恢复之前窗口。

当然你还可以试试Win+↑,Win+↓,Win+←,Win+→;win10还有Win+→↑,Win+←↑……依次类推

如果你有两个或者更多的显示器,试试 WIN+SHIFT+左或者右 可以把窗口从一个移动到另一个中去。

快速恢复已关闭网页

如果不小心把网页关了,你知道怎么找回来吗?记得,只要"Ctrl+Shift+T",就能立即开启刚刚关上的页面哦

快速打开一组网页

如果每天都要浏览一些固定的网页,可以网页放入收藏栏的文件夹中,然后右键文件夹,选择"打开全部"就可以一次性把你所要看的网页都打开。(360浏览器,其他浏览器类似)

定时关机,远程桌面关机

建一个文本文件然后输入shutdown -s -t 60 保存,把文本文件的txt格式改为bat,-t 后面的参数表示要延迟多少秒。运行脚本后60秒关机。

试试把参数设置为1秒,再文件名命名为“打开有惊喜.bat”……噗,太坏了!

快速预览文件内容

ALT+P隐藏或者显示资源管理器的预览窗口

可以预览的文件类型有很多(具体哪些可以自己试试,文件大的稍微需要等一下才显示),还可以在预览窗口类直接操作(翻页、复制什么 的),不用每次都打开查看,很是方便!

Robocopy-多线程加快Windows中文复制/传输功能

如果用户是个更高级使用者,用户肯定听说过Robocopy。自Windows7起就内置了此功能,用户可以通过命令行来执行多线程复制。用户可以选择任意数目的线程,就像"/MT[:n],它的数值范围在1到128之间。

在命令行输入ROBOCOPY/?有具体用法,不建议普通用户使用,普通用户还是老老实实图形界面比较好。

放大镜

WIN+加号来调出放大镜,按WIN+加号或者减号来进行放大或者缩小。用户可以缩放桌面上的任何地方,用户还可以配置放大镜。能选择反相颜色,跟随鼠标指针,跟随键盘焦点或者文本的输入点。

步骤记录器

写教程需要记录步骤?问问题截图不够详细?你需要一个步骤记录器!步骤记录器是一个强大的工具,而且是Windows内置的!按WIN+R输入PSR.exe运行它。它会记录用户所有的操作,并且储存为mht格式的文件,可以直接在浏览器中查看!

以上就是61阅读小编为大家整理的关于windows系统的一些超酷技巧,需要的用户快来试试吧,想了解更多精彩教程请继续关注61阅读网站!

本文标题:肛交的过程技巧详解-有车一族必备技能 详解换轮胎全过程
本文地址: http://www.61k.com/1095175.html

61阅读| 精彩专题| 最新文章| 热门文章| 苏ICP备13036349号-1