Android 启动分析 1

Android属性服务——初始化 属性服务是在Init的第二个初始化阶段去启动的,整体分了两个部分去做。第一部分主要是初始化属性空间,包括prop_area的内存区域初始化和系统默认属性的load工作;第二部分是属性服务的启动,创建了通信的socket,并注册了init和set两种事件的对应处理方法。另外在处理初始化的时候load了persist属性。 阅读详情

1.概述

Android虽然被称作一种操作系统,其实它仍然使用的Linux的kernel。所以本质上可以说,Android是一个适用于移动设备的Linux发行版。也就是说,之前的分析Linux内核的经验可以拿来用于分析Android。不过,值得注意的是,Android除去对Linux内核的一些改动外,它的大部分代码还是在Linux内核启动后的用户空间程序上。所以,分析Android代码时,不仅要对Linux内核代码熟悉,还要对熟悉Linux系统编程要用到的函数,比如fcntl、mmap、open、read、write等。

2. Android启动流程概述

像大多数的Linux发行版那样,在加载启动kernel后,会执行第一个用户空间程序/init。
简单流程就是在start_kernel函数的最后调用函数rest_init。rest_init函数如下

static noinline void __init_refok rest_init(void)
{
    ......
    /*这里是关键*/   
    kernel_thread(kernel_init, NULL, CLONE_FS | CLONE_SIGHAND);
    /*看上边*/
    numa_default_policy();
    pid = kernel_thread(kthreadd, NULL, CLONE_FS | CLONE_FILES);
    rcu_read_lock();
    kthreadd_task = find_task_by_pid_ns(pid, &init_pid_ns);
    rcu_read_unlock();
    complete(&kthreadd_done);
    .......
}

这里通过内核产生线程kernel_init--我们所说的0号进程。kernel_init函数如下:

static int __ref kernel_init(void *unused)
{
    kernel_init_freeable();

    ......
    /*关键*/
    if (ramdisk_execute_command) {
        if (!run_init_process(ramdisk_execute_command))
            return 0;
        pr_err("Failed to execute %s\n", ramdisk_execute_command);
    }
    /*这里ramdisk_execute_command是有值的,在kernel_init_freeable中设置*/
    ......

    panic("No init found.  Try passing init= option to kernel. "
          "See Linux Documentation/init.txt for guidance.");
}

我们再看kernel_init中调用的kernel_init_freeable。kernel_init_freeable函数如下:

static noinline void __init kernel_init_freeable(void)
{
    ......

    /* Open the /dev/console on the rootfs, this should never fail */
    if (sys_open((const char __user *) "/dev/console", O_RDWR, 0) < 0)
        pr_err("Warning: unable to open an initial console.\n");

    (void) sys_dup(0);
    (void) sys_dup(0);
    /*
     * check if there is an early userspace init.  If yes, let it do all
     * the work
     */
    /*这是关键*/
    if (!ramdisk_execute_command)
        ramdisk_execute_command = "/init";//这里设置成/init,android系统的第一个用户态程序

    if (sys_access((const char __user *) ramdisk_execute_command, 0) != 0) {
        ramdisk_execute_command = NULL;
        prepare_namespace();
    }
    /*向上看*/
    /*
     * Ok, we have completed the initial bootup, and
     * we're essentially up and running. Get rid of the
     * initmem segments and start the user-mode stuff..
     */

    /* rootfs is available now, try loading default modules */
    load_default_modules();
}

所以android在启动时会执行/init。我们整理一下android的启动流程:

  • start_kenel调用rest_init
  • rest_init调用kernel_init
  • kernel_init调用kernel_init_freeable
  • kernel_init_freeable中把ramdisk_execute_command设置为/init
  • 最后让在kernel_init中调用run_init_process(ramdisk_execute_command)

那么/init是什么程序呢?

/init就是Android自己的程序了,源代码位于/system/core/init/init.c中

仔细分析发现,该c文件中是有main函数的,说明该文件可以编译链接成用户态的可执行程序——即/init

3. /init分析

3.1 /init程序的第一部分

    umask(0);

        /* Get the basic filesystem setup we need put
         * together in the initramdisk on / and then we'll
         * let the rc file figure out the rest.
         */
    mkdir("/dev", 0755);
    mkdir("/proc", 0755);
    mkdir("/sys", 0755);
    /*dev下的文件系统是tmpfs,之后的null设备和klog设备都是在该文件夹下mknod做成的*/
    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);

        /* indicate that booting is in progress to background fw loaders, etc */
    close(open("/dev/.booting", O_WRONLY | O_CREAT, 0000));

可以注意到的是,由于已经是用户态程序了。代码中可以放心大胆的使用umask、mkdir、mount这些函数,所以熟悉Linux系统编程是很必要的(虽然Android没用glibc,而是bionic,但是接口接口基本一致)。

这段代码之简单的挂载了一些必要的文件系统,剩下的要通过解析rc文件在挂载。

3.2 /init/程序的第二部分

    open_devnull_stdio();
    klog_init();
    property_init();

    get_hardware_name(hardware, &revision);

    process_kernel_cmdline();

3.2.1 函数open_devnull_stdio(在/system/core/init/util.c中)

void open_devnull_stdio(void)
{
    int fd;
    static const char *name = "/dev/__null__";
    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 3) == 0) {
        fd = open(name, O_RDWR);
        unlink(name);
        if (fd >= 0) {//重定向
            dup2(fd, 0);
            dup2(fd, 1);
            dup2(fd, 2);
            if (fd > 2) {
                close(fd);
            }
            return;
        }
    }

    exit(1);
}

该函数创建一个null设备,然后通过dup2系统调用把它重定向到stdio、stdout、stderr上。注意这时还在/init进程中。

3.2.2函数klong_init(在/system/core/libcutils/klog.c中)

void klog_init(void)
{
    static const char *name = "/dev/__kmsg__";
    if (mknod(name, S_IFCHR | 0600, (1 << 8) | 11) == 0) {
        klog_fd = open(name, O_WRONLY);//只能写,我发现在klog.c中只有klog_write函数
        fcntl(klog_fd, F_SETFD, FD_CLOEXEC);
        unlink(name);
    }
}

通过fcntl设置FD_CLOEXEC标志有什么用?(注意当前只有一个这样的file descriptor flag)

close on exec, not on-fork, 意为如果对描述符设置了FD_CLOEXEC,使用exec-family执行的程序里,此描述符被关闭,不能再使用它,但是在使用fork调用的子进程中,此描述符并不关闭,仍可使用。之后我们会发现/init中产生子进程都是通过fork后在exeve实现的,所以子进程(其实就是init启动的那些服务)中klog文件是关闭的。

3.2.3函数property_init

  • property_init是init_property_area的wrapper函数
  • init_property_area调用init_workspace函数并给libc中的__system_property_area__赋值

函数init_workspace

static int init_workspace(workspace *w, size_t size)
{
    void *data;
    int fd;

        /* dev is a tmpfs that we can use to carve a shared workspace
         * out of, so let's do that...
         */
    fd = open("/dev/__properties__", O_RDWR | O_CREAT, 0600);//没有用mknod
    if (fd < 0)
        return -1;

    if (ftruncate(fd, size) < 0)//调整文件大小
        goto out;

    data = mmap(NULL, size, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0);
    if(data == MAP_FAILED)
        goto out;

    close(fd);

    fd = open("/dev/__properties__", O_RDONLY);
    if (fd < 0)
        return -1;

    unlink("/dev/__properties__");

    w->data = data;
    w->size = size;
    w->fd = fd;
    return 0;

out:
    close(fd);
    return -1;
}

workspace定义

typedef struct {  
    void *data;
    size_t size;
    int fd;
} workspace;

pa_workspace就是类型为workspace的全局变量,init_workspace函数通过open和mmap函数创建文件并映射到内存空间,并为字段data、size、fd赋初值,而data字段就是刚刚mmap映射的空间地址。

函数init_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;

        /* plug into the lib property services */
    __system_property_area__ = pa;
    property_area_inited = 1;
    return 0;
}

struct prop_area定义

(/bionic/libc/include/sys/_system_properties.h)

struct prop_area {
    unsigned volatile count;
    unsigned volatile serial;
    unsigned magic;
    unsigned version;
    unsigned reserved[4];
    unsigned toc[1];
};

通过代码

pa = pa_workspace.data;
......
__system_property_area__ = pa;

libc库要用到的全局变量__system_property_area__的值就被赋成上面讲到的共享内存的地址了,这样方便之后的/init产生的各个子进程都能使用libc库的函数访问这个地址。

可以看出__system_property_area__地址开始的部分的内容就是struct prop_area所定义的字段。

自此我们知道__system_property_area__、pa_workspace.data指向的是同一地址,之所以区分的原因是前者是libc库的全局变量之后的进程都可以通过调用libc库函数使用,而后者是/init本身的全局变量可以直接使用。

struct prop_info定义

(/bionic/libc/include/sys/_system_properties.h)

struct prop_info {
    char name[PROP_NAME_MAX];
    unsigned volatile serial;
    char value[PROP_VALUE_MAX];
};

该结构就是property system中的key/alue键值对,也是全局变量pa_info_array的类型。

通过代码

pa_info_array = (void*) (((char*) pa_workspace.data) + PA_INFO_START);

使得pa_info_array指向那块共享内存地址后面PA_INFO_START处。

整个property system的数据结构如图:

 这里只是对property system的数据结构的初始化,真正的服务还要在之后启动。

3.2.4 函数get_hardware_name

/system/core/init/util.c

void get_hardware_name(char *hardware, unsigned int *revision)
{
    ......
    fd = open("/proc/cpuinfo", O_RDONLY);
    if (fd < 0) return;

    n = read(fd, data, 1023);
    close(fd);
    if (n < 0) return;

    data[n] = 0;
    hw = strstr(data, "\nHardware");
    rev = strstr(data, "\nRevision");

    if (hw) {
        x = strstr(hw, ": ");
        ......
    }

    if (rev) {
        x = strstr(rev, ": ");
        ......
    }
}

函数很简单就是通过读取/proc/cpuinfo中的信息获得hardware和revision信息。函数的参数指向的是全局变量。

3.2.5 函数process_kernel_cmdline

/system/core/init/init.c

static void process_kernel_cmdline(void)
{
    ......
    import_kernel_cmdline(0, import_kernel_nv);
    if (qemu[0])//qemu是`/init`的全局变量
        import_kernel_cmdline(1, import_kernel_nv);

    export_kernel_boot_props();
}

该函数分两部分,第一部分读取kernel cmdline,第二部分输出kernel cmdline.

函数import_kernel_cmdline(在/system/core/init/util.c中)

void import_kernel_cmdline(int in_qemu,
                           void (*import_kernel_nv)(char *name, int in_qemu))
{
    char cmdline[1024];
    char *ptr;
    int fd;

    fd = open("/proc/cmdline", O_RDONLY);
    if (fd >= 0) {
        int n = read(fd, cmdline, 1023);
        .....
        close(fd);
    } else {
        cmdline[0] = 0;
    }

    ptr = cmdline;
    while (ptr && *ptr) {
        char *x = strchr(ptr, ' ');
        if (x != 0) *x++ = 0;
        import_kernel_nv(ptr, in_qemu);
        ptr = x;
    }
}

函数很简单,就是读取/proc/cmdline中内容,以空格为分隔符分解字符串,把分得的字符串传给import_kernel_nv函数。(值得注意的是import_kernel_nv有两个定义,此处通过参数传进来的是/system/core/init/init.c中的static原型,在同目录下的ueventd.c中也有)

函数import_kernel_nv

static void import_kernel_nv(char *name, int in_qemu)
{
    char *value = strchr(name, '=');

    if (value == 0) return;
    *value++ = 0;
    if (*name == 0) return;

    if (!in_qemu)
    {
        /* on a real device, white-list the kernel options */
        if (!strcmp(name,"qemu")) {
            strlcpy(qemu, value, sizeof(qemu));
        } else if (!strcmp(name,"androidboot.console")) {
            strlcpy(console, value, sizeof(console));
        } else if (!strcmp(name,"androidboot.mode")) {
            strlcpy(bootmode, value, sizeof(bootmode));
        } else if (!strcmp(name,"androidboot.serialno")) {
            strlcpy(serialno, value, sizeof(serialno));
        } else if (!strcmp(name,"androidboot.baseband")) {
            strlcpy(baseband, value, sizeof(baseband));
        } else if (!strcmp(name,"androidboot.carrier")) {
            strlcpy(carrier, value, sizeof(carrier));
        } else if (!strcmp(name,"androidboot.bootloader")) {
            strlcpy(bootloader, value, sizeof(bootloader));
        } else if (!strcmp(name,"androidboot.hardware")) {
            strlcpy(hardware, value, sizeof(hardware));
        } else if (!strcmp(name,"androidboot.modelno")) {
            strlcpy(modelno, value, sizeof(modelno));
        }
    } else {
        /* in the emulator, export any kernel option with the
         * ro.kernel. prefix */
        char  buff[32];
        int   len = snprintf( buff, sizeof(buff), "ro.kernel.%s", name );
        if (len < (int)sizeof(buff)) {
            property_set( buff, value );
        }
    }
}

该函数分两种情况处理传进来的字符串。

  1. 不再模拟器中时,解析字符串,赋值给qemu,console等/init的全局变量。
  2. 在模拟器中时,则给字符串添加ro.kernel.的前缀,然后添加到property system中。

函数export_kernel_boot_props(在/system/core/init/init.c中)

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);
    }

    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 */
    pval = property_get("ro.boot.hardware");
    if (pval)
        strlcpy(hardware, pval, sizeof(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");
}

该函数就是把读取的kernel cmdline赋值给property system.

3.3 /init/程序的第三部分

    /*is_charger和bootmode的意义是一样的,把bootmode转换成is_charger的init变量方便之后的判断*/
    is_charger = !strcmp(bootmode, "charger");

    INFO("property init\n");
    if (!is_charger)
        property_load_boot_defaults();//装载/default.prop

    INFO("reading config file\n");
    init_parse_config_file("/init.rc");//解析/init.rc文件

    action_for_each_trigger("early-init", action_add_queue_tail);

    queue_builtin_action(wait_for_coldboot_done_action, "wait_for_coldboot_done");
    queue_builtin_action(keychord_init_action, "keychord_init");
    queue_builtin_action(console_init_action, "console_init");

    /* execute all the boot actions to get us started */
    action_for_each_trigger("init", action_add_queue_tail);

    /* skip mounting filesystems in charger mode */
    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");

这里charger mode就是android的充电模式,就是没开机,屏幕显示一个不断充电的图标的模式。 给函数做了以下事情(我们假设是非充电模式):

  1. 装载默认的prop文件,在文件一般是/default.prop

  2. 解析/init.rc文件,在/init中注册Action 和 Service,如何解析/init.rc,之后做详细分析。

  3. 接下来的一些系列函数把从1)init.rc中注册的Action以及2)一些Builtin Action加入到Action Queue中,因为只有Action Queue中的Action才会被执行。执行顺序是先进先出的队列模式。

其实本部分是Android启动的重点内容,我会另开一篇文章详细分析的。

3.4 /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;
        }
        /*只要还有action要执行,timeout就是0,也就是不等待*/
        if (!action_queue_empty() || cur_action)
            timeout = 0;

        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();
            }
        }
    }

/init的最后一部分就是在for循环中不断从Action Queue中取得Action来执行Action中的命令。

   execute_one_command();
   restart_processes();  

在execute_one_command函数中执行Action中的Command(关于Action的结构,我会在详细介绍/init第三部分程序的文章中分析的)。然后在restart_processes中运行需要重启的服务。

循环的最后通过poll处理property_set_fd、signal_fd、keychord_fd文件发生的POLLIN事件。这些文件的建立也是通过之前的Action中执行的命令建立的。

Activity 的启动流程分析 (基于 Android 8.1 ) 0. 前言 Activity 组件的启动流程在 Android 8.0 版本以后得到了很大程度上的变更,很多类或方法已经废弃不用,本人参考最新源代码与指导书籍重新分析 Activity 启动流程,其中大都是自己的观点,如有错误,烦请不吝赐教。 1. 启动调用 作为android四大组件之一,同时也是唯一可被用户感知的部分, Activity 的作用和地位毋庸置疑,而实际开发中,我们要启动... 阅读详情

相关推荐

Android 7.1 Activity启动流程分析其一

final int startActivityMayWait(......){ ...... //从PackageManagerService获取package信息,包括将要启动的activity信息 ResolveInfo rInfo = mSupervisor.resolveIntent(intent, resolvedType, use...

流星悄悄~划过的博客 973

Android 9 (P)之init进程启动源码分析指南之二

      Android P之init进程启动源码分析指南之二 前言   在上一篇章Android P之init进程启动源码分析指南之一中我们讲解了init启动的第一阶段工作,在第一阶段中init主要做了如下几方面的工作: ueventd/watchdogd跳转判断以及其它初始化 创建并挂载相关的文件系统 初始化内核Log系统 文件系统挂载 SELinux Init初始化 第一阶段收尾和第二阶段准备工作 在本篇章中我们将要讲解init的第二阶段相关内容。 注意:本文演示的代码是Android

IT先森 4120

【SemiDrive源码分析】【X9芯片启动流程】29 - AP1 Android Bootloader启动流程分析(加载并跳转kernel)

【SemiDrive源码分析】【X9芯片启动流程】29 - AP1 Android Bootloader启动流程分析(加载并跳转kernel)

|~~~热爱生活、努力学习的小伙汁~~~| 837

在linux kernel或android中解析cmdline参数

Kernel command line: earlycon androidboot.selinux=permissive uart_dma keep_dbgclk_on clk_ignore_unused initrd=0xd0000000,38711808 rw crash_page=0x8f040000 initrd=/recoveryrc boot_reason=0x2000 ota_sta...

baron-周贺贺-代码改变世界ctw 6397

全志平台Android init启动分析(1)init启动

1. 前言 总结下android init启动流程; 为平时debug梳理思路 2.Init代码结构 Init 源代码目录:system/core/init/ , 源文件如下: ├── Android.mk ├── bootchart.c ├── bootchart.h ├── builtins.c ├── devices.c ├── devices.h ├── drv_display.h ├── fswatcherd.c ├── fswatcherd.h ├── grab-bootchart.s

bingdund的博客 2041

AD7768与STM32F439的硬件设计避坑指南:如何优化高精度ADC系统的电源与PCB布局

本文详细解析了AD7768与STM32F439在高精度ADC系统硬件设计中的关键问题与优化方案。从电源系统设计、PCB布局到基准电压选型,提供了实用的避坑指南和实测数据,帮助工程师提升系统性能至110dB动态范围。特别强调了SPI信号布线和电源滤波对ADC精度的重要影响。

sugar的博客 734

Android init进程启动过程分析

田海立@优快云2013-3-16 本文分析Android中init进程的执行过程,只是分析init进程启动的流水,具体细节在今后的各个专题中再分别详细分析。本文虽是后面各个专题的基础,读者初看可能理解不深,可以在阅读后面各个专题的时候,结合本文的整体流程会有更清晰的理解。 Init进程从 /system/core/init/init.c里的main()函数开始 1.      mkdir &&

田海立@优快云 1万+

离线反激转换器反馈环路设计:从理论到实践的Type II补偿器详解

在开关电源设计中,反馈环路稳定性是决定电源性能与可靠性的核心。其本质是通过补偿网络对功率级的小信号特性进行校正,以构建一个具有足够相位裕度和增益裕度的闭环系统。反激转换器作为一种广泛应用的隔离拓扑,其功率级在连续导通模式(CCM)和非连续导通模式(DCM)下呈现不同的零极点特性,这直接决定了补偿器的选型。Type II补偿器因其传递函数中固有的积分环节、低频零点与高频极点,恰好能与反激功率级的1P2Z(一个极点、两个零点)特性形成互补,有效塑造理想的回路增益曲线,实现快速稳定的动态响应。本文聚焦于采用TL4

weixin_30162121的博客 501

安卓之启动

本源码介绍是基于安卓4.2.2_r1和安卓安卓4.1的,这里我交叉讲解是因为我的安卓4.2.2_r1代码下载的不全,所有交叉引用讲解。 本文结构约定:          1大标题用标题1格式,次表次按标题2,标题3以此类推。          2每章节开篇会有简单的关于该章的介绍,每章后面会对该章的讲解进行一个总结。          3在开篇介绍后面会列出,该章涉及到的重要的源文件。

mergerly的专栏 682

【校园网代理连接问题】已解决,“该设备或资源(Web 代理)未设置为接受端口“7890”上的连接“

我遇到这个问题很多次。每次都是vpn的问题,不止通俗意义上的梯子(clash之类的),还包括校园网vpn。点击“ 拨号和虚拟专用网络设置”中的“设置”, 关闭代理服务器。在打开vpn后或者关闭vpn后无法浏览网页,网页诊断显示“关闭“手动设置代理”中的“使用代理服务器”,关闭对应的本地代理即可。

聆雨。 1万+

双斜率积分ADC:高精度测量中的经典选择与TC530/TC534实战指南

模数转换器(ADC)是连接模拟世界与数字系统的关键桥梁,其核心任务是将连续的模拟信号转换为离散的数字量。在众多ADC类型中,双斜率积分ADC以其独特的原理脱颖而出,它通过积分与反积分的线性过程,用时间换取精度,实现了对噪声的卓越抑制和极高的线性度。这种技术价值使其在对抗工频干扰、追求长期稳定性的场景中无可替代,尤其适用于精密测量仪表和工业传感器信号采集。本文聚焦于双斜率积分ADC的经典代表TC530与TC534,深入剖析其工作原理、关键参数如积分电阻、积分电容的选择,并提供从硬件电路设计到软件驱动的完整实战

weixin_30847063的博客 358

【安卓系统笔记】基于android7.1分析android启动过程

开机按Power,正常启动系统,加载boot.img,boot.img包含内核,基本文件系统,用于正常启动手机。 1.引导启动bootloader 开机按组合键可进入相应的模式,fastboot/recovery模式 按开机则正常启动流程。 Bootloader做的事情主要有:初始化CPU时钟,内存,串口等;设置linux启动参数;加载Linux各种各样的内核镜像到SDRAM中。 2.linux内...

Paul的博客 817

【无标题】EXCEL实现刷题

运用Excel开发的刷题工具,具有普遍实用性,可将WORD的试题自动导入并实现刷题。

weixin_44081794的博客 2970

android 7.1.2程序崩溃,android7.1.2启动流程分析之init程序

android7.1.2启动流程分析之init程序板子平台ROC_RK3328_CC安卓源码树Android7.1.2–>kernel/init/main.c 文件asmlinkage void __init start_kernel(void){/* Do the rest non-__init’ed, we’re now alive */rest_init();//在start_kern...

weixin_32445333的博客 430

CS5212芯片实战:手把手教你设计DP转VGA转换电路(附完整原理图)

本文详细介绍了CS5212芯片在DP转VGA转换电路设计中的实战应用,包括芯片架构解析、核心电路设计要点、PCB布局与电磁兼容设计、调试技巧及进阶应用开发。通过完整的原理图和实用设计指南,帮助工程师快速掌握高可靠转换器的设计方法,特别适合需要国产化替代方案的场景。

weixin_30576827的博客 390

Power domain 划分该如何下手

假如PD1比PD3更always on的话,我们只需要插入普通buffer即可,因为PD3 on,PD1也肯定是ON,PD1 off,PD3肯定时off,PD1能cover PD3的电源情况,因此使用普通buffer也可,但是处于省电角度,我们还是使用AO buffer;在我们做多power domain时,在切分power domain时,需要谨慎,稍有不慎,就会出现congestion以及timing问题,那问题来了,我们在切分power domain时有什么考究么,今天小编就和大家一起讨论讨论。

芯冰乐 205

java -jar 启动优化_Android 8.1 启动时间优化--耗时分析

之前分析各个部分耗时,都是通过分析log,在SecureCRT中设置时间戳,打印出如下log[ 22.266201] c1 [saudio] saudio_wait_monitor_cmd error -5[ 22.266205] c1 sblock_receive:sblock-5-13 not ready![ 22.266209] c1 [saudio] Error: sblock...

weixin_42524708的博客 508

Transformer实战(39)——多模态生成式Transformer

能够理解多种类型输入的模型被称为多模态模型。多模态学习是人工智能 (Artificial Intelligence, AI) 领域的重要研究方向之一,长期受到众多研究人员的关注。本节将介绍使用多模态模型的生成式 AI,包括文本到图像 (text-to-image) 和文本到音乐 (text-to-music) 的生成模型。将介绍 Stable Diffusion 及其工作原理,同时也将介绍 MusicGen 和 AudioGen 模型。

盼小辉丶的博客 2399

Android6.0 7.1 重启机器, 最近启动任务源码分析

frameworks/base/services/core/java/com/android/server/am/ActivityManagerService.java: public void systemReady(final Runnable goingCallback) { synchronized(this) { if (mSystemRe...

qq_27256793的博客 974

深度学习工具包 DeepLearningToolbox

深度学习工具包Deprecation notice.-----This toolbox is outdated and no longer maintained.There are much better tools available for deep learning than this toolbox, e.g. [Theano](http://deeplearning.net/software/theano/), [torch](http://torch.ch/) or [tensorflow](http://www.tensorflow.org/)I would suggest you use one of the tools mentioned above rather than use this toolbox.Best, Rasmus.DeepLearnToolbox================A Matlab toolbox for Deep Learning.Deep Learning is a new subfield of machine learning that focuses on learning deep hierarchical models of data.It is inspired by the human brain's apparent deep (layered, hierarchical) architecture.A good overview of the theory of Deep Learning theory is[Learning Deep Architectures for AI](http://www.iro.umontreal.ca/~bengioy/papers/ftml_book.pdf)For a more informal introduction, see the following videos by Geoffrey Hinton and Andrew Ng.* [The Next Generation of Neural Networks](http://www.youtube.com/watch?v=AyzOUbkUf3M) (Hinton, 2007)* [Recent Developments in Deep Learning](http://www.youtube.com/watch?v=VdIURAu1-aU) (Hinton, 2010)* [Unsupervised Feature Learning and Deep Learning](http://www.youtube.com/watch?v=ZmNOAtZIgIk) (Ng, 2011)If you use this toolbox in your research please cite [Prediction as a candidate for learning deep hierarchical models of data](http://www2.imm.dtu.dk/pubdb/views/publication_details.php?id=6284)```@MASTERSTHESIS\{IMM2012-06284, author = "R. B. Palm", title = "Prediction as a candidate for learning deep hierarchical models of data", year = "2012",}```Contact: rasmusbergpalm at gmail dot comDirectories included in the toolbox-----------------------------------`NN/` - A library for Feedforward Backpropagation Neural Networks`CNN/` - A library for Convolutional Neural Networks`DBN/` - A library for Deep Belief Networks`SAE/` - A library for Stacked Auto-Encoders`CAE/` - A library for Convolutional Auto-Encoders`util/` - Utility functions used by the libraries`data/` - Data used by the examples`tests/` - unit tests to verify toolbox is workingFor references on each library check REFS.mdSetup-----1. Download.2. addpath(genpath('DeepLearnToolbox'));Example: Deep Belief Network---------------------```matlabfunction test_example_DBNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit RBM and visualize its weightsrand('state',0)dbn.sizes = [100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);figure; visualize(dbn.rbm{1}.W'); % Visualize the RBM weights%% ex2 train a 100-100 hidden unit DBN and use its weights to initialize a NNrand('state',0)%train dbndbn.sizes = [100 100];opts.numepochs = 1;opts.batchsize = 100;opts.momentum = 0;opts.alpha = 1;dbn = dbnsetup(dbn, train_x, opts);dbn = dbntrain(dbn, train_x, opts);%unfold dbn to nnnn = dbnunfoldtonn(dbn, 10);nn.activation_function = 'sigm';%train nnopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.10, 'Too big error');```Example: Stacked Auto-Encoders---------------------```matlabfunction test_example_SAEload mnist_uint8;train_x = double(train_x)/255;test_x = double(test_x)/255;train_y = double(train_y);test_y = double(test_y);%% ex1 train a 100 hidden unit SDAE and use it to initialize a FFNN% Setup and train a stacked denoising autoencoder (SDAE)rand('state',0)sae = saesetup([784 100]);sae.ae{1}.activation_function = 'sigm';sae.ae{1}.learningRate = 1;sae.ae{1}.inputZeroMaskedFraction = 0.5;opts.numepochs = 1;opts.batchsize = 100;sae = saetrain(sae, train_x, opts);visualize(sae.ae{1}.W{1}(:,2:end)')% Use the SDAE to initialize a FFNNnn = nnsetup([784 100 10]);nn.activation_function = 'sigm';nn.learningRate = 1;nn.W{1} = sae.ae{1}.W{1};% Train the FFNNopts.numepochs = 1;opts.batchsize = 100;nn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.16, 'Too big error');```Example: Convolutional Neural Nets---------------------```matlabfunction test_example_CNNload mnist_uint8;train_x = double(reshape(train_x',28,28,60000))/255;test_x = double(reshape(test_x',28,28,10000))/255;train_y = double(train_y');test_y = double(test_y');%% ex1 Train a 6c-2s-12c-2s Convolutional neural network %will run 1 epoch in about 200 second and get around 11% error. %With 100 epochs you'll get around 1.2% errorrand('state',0)cnn.layers = { struct('type', 'i') %input layer struct('type', 'c', 'outputmaps', 6, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %sub sampling layer struct('type', 'c', 'outputmaps', 12, 'kernelsize', 5) %convolution layer struct('type', 's', 'scale', 2) %subsampling layer};cnn = cnnsetup(cnn, train_x, train_y);opts.alpha = 1;opts.batchsize = 50;opts.numepochs = 1;cnn = cnntrain(cnn, train_x, train_y, opts);[er, bad] = cnntest(cnn, test_x, test_y);%plot mean squared errorfigure; plot(cnn.rL);assert(er<0.12, 'Too big error');```Example: Neural Networks---------------------```matlabfunction test_example_NNload mnist_uint8;train_x = double(train_x) / 255;test_x = double(test_x) / 255;train_y = double(train_y);test_y = double(test_y);% normalize[train_x, mu, sigma] = zscore(train_x);test_x = normalize(test_x, mu, sigma);%% ex1 vanilla neural netrand('state',0)nn = nnsetup([784 100 10]);opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samples[nn, L] = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.08, 'Too big error');%% ex2 neural net with L2 weight decayrand('state',0)nn = nnsetup([784 100 10]);nn.weightPenaltyL2 = 1e-4; % L2 weight decayopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex3 neural net with dropoutrand('state',0)nn = nnsetup([784 100 10]);nn.dropoutFraction = 0.5; % Dropout fraction opts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex4 neural net with sigmoid activation functionrand('state',0)nn = nnsetup([784 100 10]);nn.activation_function = 'sigm'; % Sigmoid activation functionnn.learningRate = 1; % Sigm require a lower learning rateopts.numepochs = 1; % Number of full sweeps through dataopts.batchsize = 100; % Take a mean gradient step over this many samplesnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex5 plotting functionalityrand('state',0)nn = nnsetup([784 20 10]);opts.numepochs = 5; % Number of full sweeps through datann.output = 'softmax'; % use softmax outputopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, train_x, train_y, opts);[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');%% ex6 neural net with sigmoid activation and plotting of validation and training error% split training data into training and validation datavx = train_x(1:10000,:);tx = train_x(10001:end,:);vy = train_y(1:10000,:);ty = train_y(10001:end,:);rand('state',0)nn = nnsetup([784 20 10]); nn.output = 'softmax'; % use softmax outputopts.numepochs = 5; % Number of full sweeps through dataopts.batchsize = 1000; % Take a mean gradient step over this many samplesopts.plot = 1; % enable plottingnn = nntrain(nn, tx, ty, opts, vx, vy); % nntrain takes validation set as last two arguments (optionally)[er, bad] = nntest(nn, test_x, test_y);assert(er < 0.1, 'Too big error');```[![Bitdeli Badge](https://d2weczhvl823v0.cloudfront.net/rasmusbergpalm/deeplearntoolbox/trend.png)](https://bitdeli.com/free "Bitdeli Badge")

RU DOS版、EFI版32bit/64bit BIOS常用工具

DOS版、EFI版的RU工具,可用来查看IO、MEM、PCI、SMBIOS等资源。亲测在EFI SHELL下可用。

cm-14.1 Android系统启动过程分析(4)-init进程的启动、rc脚本解析、zygote启动、属性服务

前阶段在项目中涉及到了Android系统定制任务,Android系统定制前提要知道Android系统是如何启动的。

Android系统工程师--小馬佩德罗 1万+

数学建模模型总结

这是本人在两年内准备参加数学建模竞赛搜集到的模型资料,已经经过了本人的筛选和整理,希望能够对准备参加数学建模竞赛的学弟学妹们提供一些帮助!

Activity启动流程分析(基于android 5.1)

Activity启动流程分析(基于android 5.1) 2016-09-08 14:54 19人阅读 评论(0) 收藏 举报 分类: android AMS(16) http://blog.youkuaiyun.com/turkeycock/article/details/50538971 最近由于工作需要,需要深入了解AM

CCJHDOPC的专栏: 538

大中型超市收银系统(可直接运行)

光步前台收银系统系统功能:商品销售、商品退货、挂单、收银员权限、会员销售、单品取消、整单取消、商品查询、脱机销售、脱机数据上传,基本资料下载,开钱箱、小票打印、收款结算,支持多币种付款方式(现金、储值卡、礼券、微信等),支持多种促销。运行环境:.net 4.0登陆信息用户名:1001 密码:123456内置商品信息,商品编码可输入00001000 - 00010000,测试效果相关功能键F5 结算 F9挂单 F11商品查询 F8退货 Insert单品取消 Del整单取消 F12开钱箱F2 会员 Ctrl+Z退出系统 Home 锁屏 相关源码待公开

android8.1开机动画启动分析

init.cpp AOSP/system/core/init/init.cpp SurfaceFlinger是由init进程启动的 int main(int argc, char** argv) { ... if (bootmode == "charger") { am.QueueEventTrigger("charger"); } else { am.QueueEventTrigger("late-init"); } ...

we1less的博客 1060

Android DHCP 启动分析【1】

2019独角兽企业重金招聘Python工程师标准>>> ...

weixin_34185364的博客 446

Android init 启动过程分析1

  未完 资料:Simple Guide for Porting Android Kernelhttp://feixf1974.javaeye.com/blog/187773分析android的启动过程,从内核之上,我们首先应该从文件系统的init开始,因为 init 是内核进入文件系统后第一个运行的程序,通常我们可以在linux的命令行中指定内核第一个调用谁,如果没指定那么内

ruixj的专栏 2462

【Android 14源码分析】Activity启动流程-1

执行 startActivity 方法的 Activity ,也就是发起请求的Activity ,当前是 Launcher 的 Activity。:被启动的 Activity 当前就是“电话”应用在清单文件配置的MainActivityAMS: 不仅仅是指 AMS 这一个类,而是指在这个过程中 system_service 进程参与处理的相关类。

隔壁小王的博客 3539

Android 12 init(1) 启动流程分析

init是 Android 启动的第一个用户空间进程,它的地位非常重要,它fork产生系统的一些关键进程(如zygote,surfaceflinger进程),而zygote进一步fork产生system_server和其他应用进程,通过这套逻辑构建了Android的进程层次结构体系。init进程的功能包含但不限于以下:通过ps命令看看init进程信息 在启动内核的start_kernel函数流程中,会调用run_init_process函数执行init程序,来启动init进程在Android中执行的init

pecuyu 3429
上一篇: Linux Container 研究报告
下一篇: Android init.rc分析
Omni-Space
博客等级 码龄11年 2158粉丝 23原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值