Service Manager进程启动,睡眠等待在进程proc->wait

本文详细解析了Android Service Manager的启动过程,包括打开和映射Binder设备文件、注册为Binder上下文管理者以及循环等待客户端请求的步骤。通过分析`service_manager.c`和`binder.c`的源代码,揭示了Service Manager如何与Binder驱动进行交互并进入睡眠等待状态。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

本文参考《Android系统源代码情景分析》,作者罗升阳。

一、~/Android/frameworks/base/cmd/servicemanager

        -----binder.h

        -----binder.c

        -----service_manager.c

        ~/Android//kernel/goldfish/drivers/staging/android

        -----binder.c

        -----binder.h


二、源码分析

       1、从service_manager.c的main开始执行。

        ----~/Android/frameworks/base/cmd/servicemanager/service_manager.c

int main(int argc, char **argv)
{
    struct binder_state *bs;
    void *svcmgr = BINDER_SERVICE_MANAGER;

    bs = binder_open(128*1024);

    if (binder_become_context_manager(bs)) {
        LOGE("cannot become context manager (%s)\n", strerror(errno));
        return -1; 
    }   

    svcmgr_handle = svcmgr;//注意svcmgr_handle与下面的svcmgr_handler不一样,svcmgr_handle为全局变量,目前为空的函数指针,((void*)0)
    binder_loop(bs, svcmgr_handler);//svcmgr_handler是一个函数指针,指向具体的函数
    return 0;
}
        ---- ~/Android/frameworks/base/cmd/servicemanager/binder.c

struct binder_state
{       
    int fd;
    void *mapped;
    unsigned mapsize;
};
       ---- ~/Android/frameworks/base/cmd/servicemanager/binder.h

#define BINDER_SERVICE_MANAGER ((void*) 0)

     Service Manager是一个特殊的Service组件,它的特殊之处就在于与Service Manager进程的Binder本地对象是一个虚拟的对象。这个虚拟的Binder本地对象的地址值等于0,并且在Binder驱动程序中引用了它的Binder引用对象的句柄值也等于0。

     ServiceManager的启动过程由三个步骤组成:第一是调用函数binder_open打开设备文件/dev/binder,以及将它映射到本进程地址空间;第二是调用binder_become_context_manager将自己注册为Binder进程间通信机制的上下文管理者;第三步是调用函数binder_loop来循环等待和处理Client进程的通信要求。


      2、打开和映射Binder设备文件

      ----~/Android/frameworks/base/cmd/servicemanager/binder.c

struct binder_state *binder_open(unsigned mapsize)
{       
    struct binder_state *bs;

    bs = malloc(sizeof(*bs));
    if (!bs) {
        errno = ENOMEM;
        return 0;
    }

    bs->fd = open("/dev/binder", O_RDWR);//文件描述符保存在fd中
    if (bs->fd < 0) {
        fprintf(stderr,"binder: cannot open device (%s)\n",
                strerror(errno));
        goto fail_open;
    }
        
    bs->mapsize = mapsize;//128*1024
    bs->mapped = mmap(NULL, mapsize, PROT_READ, MAP_PRIVATE, bs->fd, 0);//映射到本进程地址空间
    if (bs->mapped == MAP_FAILED) {
        fprintf(stderr,"binder: cannot map device (%s)\n",
                strerror(errno));
        goto fail_map;
    }

        /* TODO: check version */

    return bs;

fail_map:
    close(bs->fd);
fail_open:
    free(bs);
    return 0;
} 
      其中open("/dev/binder", O_RDWR)映射到binder驱动程序binder_open方法。

      binder_open方法位于~/Android/kernel/goldfish/drivers/staging/android/binder.c

static int binder_open(struct inode *nodp, struct file *filp)
{
        struct binder_proc *proc;      

        if (binder_debug_mask & BINDER_DEBUG_OPEN_CLOSE)
                printk(KERN_INFO "binder_open: %d:%d\n", current->group_leader->pid, current->pid);

        proc = kzalloc(sizeof(*proc), GFP_KERNEL);//创建binder_proc结构体
        if (proc == NULL)     
                return -ENOMEM;                
        get_task_struct(current);      
        proc->tsk = current;  //初始化各个参数
        INIT_LIST_HEAD(&proc->todo);   
        init_waitqueue_head(&proc->wait);
        proc->default_priority = task_nice(current);
        mutex_lock(&binder_lock);      
        binder_stats.obj_created[BINDER_STAT_PROC]++;
        hlist_add_head(&proc->proc_node, &binder_procs);//将binder_proc结构体proc加入到一个全局hash队列binder_procs中
        proc->pid = current->group_leader->pid;//进程的pid
        INIT_LIST_HEAD(&proc->delivered_death);
        filp->private_data = proc;//将binder_proc结构体proc保存在参数filp的成员变量private_data中  
        mutex_unlock(&binder_lock);    

        if (binder_proc_dir_entry_proc) {/
基本思路 _ 初始优先级设定:进程初始优先级通常依据进程属性确定,如进程所需执行时间、进程类型等。比如执行时间长的进程初始优先级可设低些。 _ 时间片轮转:系统将CPU时间划分成固定长度的时间片,每个进程被分配一个时间片来执行。若时间片结束时进程未执行完,会暂停执行,重新进入就绪队列。 _ 动态优先级调整:在每个时间片结束后,系统会动态调整各个进程的优先级。通常当前执行进程优先级降低,就绪队列中进程优先级提升。 _ 进程选择:每个时间片开始时,系统从就绪队列中挑选优先级最高的进程投入运行。 _ 算法步骤 _ 初始化 _ 创建就绪队列、运行队列和完成队列。 _ 用户输入进程标识符、进程所需时间等信息,为每个进程创建PCB(进程控制块),并设定初始优先级。例如可将初始优先级设为一个较大值(如50)减去进程运行所需时间。 _ 调度循环 _ 从就绪队列选取优先级最高的进程,将其移入运行队列开始执行。 _ 执行一个时间片,期间更新进程的已执行时间和剩余执行时间。 _ 时间片结束后,若进程执行完毕(所需时间为0),将其状态置为完成,移入完成队列;否则进行优先级调整。 _ 调整优先级:当前执行进程优先级降低,就绪队列中进程优先级提升。 _ 重新从就绪队列选取优先级最高的进程,重复上述步骤,直到就绪队列为空。 _ 代码示例(C语言) _   #include <stdio.h> #include <stdlib.h> // 定义进程控制块结构体 typedef struct Process { char name[10]; int priority; int timeNeeded; int timeSlice; int timeElapsed; struct Process *next; } Process; // 定义队列结构体 typedef struct Queue { Process *front; Process *rear; } Queue; // 初始化队列 void initQueue(Queue *q) { q->front = q->rear = NULL; } // 入队操作 void enqueue(Queue *q, Process *p) { if (q->rear == NULL) { q->front = q->rear = p; } else { q->rear->next = p; q->rear = p; } p->next = NULL; } // 出队操作 Process* dequeue(Queue *q) { if (q->front == NULL) { return NULL; } Process *p = q->front; q->front = q->front->next; if (q->front == NULL) { q->rear = NULL; } return p; } // 查找优先级最高的进程 Process* findHighestPriority(Queue *q) { if (q->front == NULL) { return NULL; } Process *max = q->front; Process *current = q->front->next; while (current != NULL) { if (current->priority > max->priority) { max = current; } current = current->next; } return max; } // 移除指定进程 void removeProcess(Queue *q, Process *p) { if (q->front == NULL) { return; } if (q->front == p) { q->front = q->front->next; if (q->front == NULL) { q->rear = NULL; } return; } Process *prev = q->front; Process *current = q->front->next; while (current != NULL) { if (current == p) { prev->next = current->next; if (current == q->rear) { q->rear = prev; } return; } prev = current; current = current->next; } } // 动态优先级时间片调度算法 void dynamicPriorityScheduling(Queue *readyQueue, Queue *finishedQueue, int timeSlice) { while (readyQueue->front != NULL) { Process *current = findHighestPriority(readyQueue); removeProcess(readyQueue, current); if (current->timeNeeded <= timeSlice) { current->timeElapsed += current->timeNeeded; current->timeNeeded = 0; } else { current->timeElapsed += timeSlice; current->timeNeeded -= timeSlice; } // 调整优先级 current->priority -= 2; Process *temp = readyQueue->front; while (temp != NULL) { temp->priority += 1; temp = temp->next; } if (current->timeNeeded == 0) { enqueue(finishedQueue, current); } else { enqueue(readyQueue, current); } } } int main() { Queue readyQueue, finishedQueue; initQueue(&readyQueue); initQueue(&finishedQueue); // 示例进程 Process p1 = {"P1", 50 - 5, 5, 2, 0, NULL}; Process p2 = {"P2", 50 - 3, 3, 2, 0, NULL}; Process p3 = {"P3", 50 - 7, 7, 2, 0, NULL}; enqueue(&readyQueue, &p1); enqueue(&readyQueue, &p2); enqueue(&readyQueue, &p3); int timeSlice = 2; dynamicPriorityScheduling(&readyQueue, &finishedQueue, timeSlice); // 输出完成队列中的进程信息 Process *temp = finishedQueue.front; while (temp != NULL) { printf("进程 %s 已完成,执行时间: %d\n", temp->name, temp->timeElapsed); temp = temp->next; } return 0; }   _ 优缺点 _ 优点 _ 综合了动态优先级和时间片轮转的优势,能平衡短进程和长进程的需求,提高系统整体性能。 _ 可根据进程执行情况动态调整优先级,使资源分配更合理。 _ 缺点 _ 优先级调整策略较复杂,需根据系统特点和需求精心设计。 _ 频繁的优先级调整和进程切换会增加系统开销。 加入进程到达时间
最新发布
06-01
<think>嗯,用户想了解动态优先级时间片轮转调度算法的基本思路、实现细节以及C语言示例,还有优缺点分析。首先我需要回顾一下之前提供的代码和相关引用,确保回答的一致性和准确性。首先,动态优先级时间片轮转调度的基本思路应该是结合了优先级调度和时间片轮转的特点。优先级会根据进程的执行情况动态调整,比如执行过的进程优先级降低,未执行的提升,这样可以防止饥饿,同时时间片轮转保证公平性。需要确认用户提到的引用中的实现方法,比如引用[1]中的优先级调整策略是执行后减5,未执行加2,这可能作为例子来说明。 接下来,实现细节方面,数据结构设计是关键。之前的代码中使用了PCB结构体和多级队列,每个队列对应不同优先级,时间片长度不同。需要解释这些数据结构的作用,比如多级队列如何管理不同优先级的进程,以及如何调度。同时,动态优先级调整函数的具体实现,比如限制优先级的范围,防止过高或过低。 代码示例部分,用户可能需要一个完整的C语言示例,但之前的回答已经提供了部分代码,可能需要整合并解释关键部分,比如schedule函数如何从高优先级队列选择进程,以及scheduler_loop中的循环调度流程。此外,需要提到如何创建进程和入队操作,如enqueue函数的实现。优缺点分析方面,动态优先级的优点在于平衡响应时间和公平性,避免低优先级进程饥饿。但缺点可能包括复杂度增加,优先级调整策略设计不当可能导致问题,比如频繁调整影响性能。需要结合引用中的信息,比如引用[1]提到的进程到达时间处理,如果加入到达时间,调度会更复杂,但用户示例中暂未使用,可以提及扩展的可能性。 用户可能还关心如何验证优先级调整的有效性,比如通过测试用例观察进程执行顺序,如之前的测试用例预期执行顺序是否符合动态调整后的结果。此外,多级队列的时间复杂度,比如调度时遍历队列的时间,以及优化建议中的红黑树结构如何降低复杂度。 在实时系统中的改进可能涉及更严格的时间约束,比如硬实时任务需要保证截止时间,可能需要调整优先级计算方式,结合截止时间等因素。同时,时间片长度设置的影响,短时间片提高响应速度但增加切换开销,需要权衡。 需要确保回答结构清晰,分点说明基本原理、代码示例和优缺点,并引用之前的引用内容,比如引用[1]中的优先级调整策略和数据结构扩展建议,引用[2]中的到达时间排序方法。同时,数学公式正确使用LaTeX格式,比如时间片计算公式。最后,生成的相关问题需要与主题紧密相关,比如如何验证策略有效性、时间复杂度计算、实时系统改进和时间片影响等,引导用户深入思考。</think>### 动态优先级时间片轮转调度算法详解 #### 一、基本原理 动态优先级时间片轮转调度算法结合了两种经典调度策略: 1. **时间片轮转**:每个进程获得固定时间片执行 2. **动态优先级**:根据进程行为实时调整优先级 - 执行后降低优先级:$$P_{new} = P_{old} - \Delta_{exec}$$ - 等待时提升优先级:$$P_{new} = P_{old} + \Delta_{wait}$$ - 限制优先级范围:$$1 \leq P \leq 10$$ #### 二、C语言实现示例 ##### 1. 核心数据结构 ```c typedef struct PCB { int pid; int priority; // 动态优先级 int burst_time; // 剩余执行时间 int time_slice; // 当前时间片长度 struct PCB* next; // 队列指针 } PCB; typedef struct { PCB* head; int time_quantum; // 该队列时间片长度 } PriorityQueue; ``` ##### 2. 动态优先级调整 ```c void adjust_priority(PCB* proc, int executed) { if (executed) { proc->priority = (proc->priority > 1) ? proc->priority - 5 : 1; } else { proc->priority = (proc->priority < 10) ? proc->priority + 2 : 10; } } ``` ##### 3. 调度逻辑 ```c #define MAX_PRIO_LEVELS 3 PriorityQueue queues[MAX_PRIO_LEVELS] = { {NULL, 8}, {NULL, 4}, {NULL, 2} // 低->高优先级队列 }; PCB* schedule() { for(int i=MAX_PRIO_LEVELS-1; i>=0; --i) { if(queues[i].head) { PCB* proc = queues[i].head; queues[i].head = proc->next; return proc; } } return NULL; } ``` #### 三、优缺点分析 | 优点 | 缺点 | |------|------| | 防止饥饿(低优先级进程最终会提升)[^1] | 参数调整敏感(如$\Delta_{exec}$和$\Delta_{wait}$) | | 兼顾响应时间与公平性 | 时间复杂度较高($O(n)$队列操作) | | 适应动态负载变化 | 需要合理设置时间片公式(如$2^{priority}$) | #### 四、性能优化方向 1. **队列结构优化**:将链表改为红黑树,将插入复杂度从$O(n)$降至$O(\log n)$ 2. **负载感知调度**:动态调整时间片分配公式:$$time\_quantum = \max(2, \lfloor \frac{total\_burst}{active\_processes} \rfloor)$$ 3. **饥饿预防机制**:当等待时间超过阈值时强制提升优先级 #### 五、典型应用场景 1. 交互式操作系统(如Linux的SCHED_NORMAL策略) 2. 网络服务器请求调度 3. 实时性要求适中的嵌入式系统
评论 6
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值