Vold工作流程分析学习

article/2025/10/2 20:30:48

一 Vold工作机制分析

         vold进程:管理和控制Android平台外部存储设备,包括SD插拨、挂载、卸载、格式化等;

         vold进程接收来自内核的外部设备消息。

Vold框架图如下:

    

 

         Vold接收来自内核的事件,通过netlink机制。

         Netlink 是一种特殊的 socket;

         Netlink 是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的socket API 就可以使用 netlink 提供的强大功能;

         Netlink是一种异步通信机制,在内核与用户态应用之间传递的消息保存在socket缓存队列中;

内核通过Netlink发送uEvent格式消息给用户空间程序;外部设备发生变化,Kernel发送uevent消息。

二 Vold进程启动过程

service vold /system/bin/voldclass coresocket vold stream 0660 root mountioprio be 2 

vold进程执行过程:

         \system\vold\main.cpp

int main() 
{VolumeManager *vm;CommandListener *cl;NetlinkManager *nm;//创建vold设备文件夹mkdir("/dev/block/vold", 0755);//初始化Vold相关的类实例 singlevm = VolumeManager::Instance();nm = NetlinkManager::Instance();//CommandListener 创建vold socket监听上层消息cl = new CommandListener();vm->setBroadcaster((SocketListener *) cl);nm->setBroadcaster((SocketListener *) cl);//启动VolumeManager vm->start();//根据配置文件/etc/vold.fstab 初始化VolumeManager 
        process_config(vm);//启动NetlinkManager socket监听内核发送ueventnm->start();//向/sys/block/目录下所有设备uevent文件写入“add\n”,//触发内核sysfs发送uevent消息coldboot("/sys/block");//启动CommandListener监听vold socketcl->startListener();// Eventually we'll become the monitoring threadwhile(1) {sleep(1000);}exit(0);
} 

process_config解析vold.fstab文件:

static int process_config(VolumeManager *vm) {//打开vold.fstab的配置文件fp = fopen("/etc/vold.fstab", "r")//解析vold.fstab 配置存储设备的挂载点while(fgets(line, sizeof(line), fp)) {const char *delim = " \t";char *type, *label, *mount_point, *part, *mount_flags, *sysfs_path;type = strtok_r(line, delim, &save_ptr)label = strtok_r(NULL, delim, &save_ptr)mount_point = strtok_r(NULL, delim, &save_ptr)//判断分区 auto没有分区part = strtok_r(NULL, delim, &save_ptr)if (!strcmp(part, "auto")) {//创建DirectVolume对象 相关的挂载点设备的操作dv = new DirectVolume(vm, label, mount_point, -1);} else {dv = new DirectVolume(vm, label, mount_point, atoi(part));}//添加挂载点设备路径while ((sysfs_path = strtok_r(NULL, delim, &save_ptr))) {dv->addPath(sysfs_path)}//将DirectVolume 添加到VolumeManager管理vm->addVolume(dv);}fclose(fp);return 0;
}

vold.fstab文件:

  导出一个我的手机里面的vold.fstab文件 内容:

dev_mount sdcard /mnt/sdcard emmc@fat /devices/platform/goldfish_mmc.0 /devices/platform/mtk-sd.0/mmc_hostdev_mount external_sdcard /mnt/sdcard/external_sd auto /devices/platform/goldfish_mmc.1 /devices/platform/mtk-sd.1/mmc_host

 

  vold.fstab格式是:

         type         label        mount_point part         sysfs_path      sysfs_path

  sysfs_path可以有多个 part指定分区个数,如果是auto没有分区

三 Vold中各模块分析

         在vold进程main函数中创建了很多的类实例,并将其启动。

int main()
{……vm->start();nm->start();cl->startListener();
}

这些类对象之间是如何的,还需要现弄清楚每个类的职责和工作机制。

1 NetlinkManager模块

  NetlinkManager模块接收从Kernel发送的Uevent消息,解析转换成NetlinkEvent对象;再将此NetlinkEvent对象传递给VolumeManager处理。

此模块相关的类结构:

    

 

下面从start开始,看起如何对Kernel的Uevent消息进行监控的。

NetlinkManager start

int NetlinkManager::start() {//netlink使用的socket结构struct sockaddr_nl nladdr;//初始化socket数据结构memset(&nladdr, 0, sizeof(nladdr));nladdr.nl_family = AF_NETLINK;nladdr.nl_pid = getpid();nladdr.nl_groups = 0xffffffff;//创建socket PF_NETLINK类型mSock = socket(PF_NETLINK,SOCK_DGRAM,NETLINK_KOBJECT_UEVENT);//配置socket 大小setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz);setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on);//bindsocket地址bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr);//创建NetlinkHandler 传递socket标识,并启动mHandler = new NetlinkHandler(mSock);mHandler->start();return 0;
} 

NetlinkHandler start

int NetlinkHandler::start() {
    //父类startListener
    return this->startListener();
}

NetlinkListener start

int SocketListener::startListener() {
   //NetlinkHandler mListen为false if (mListen && listen(mSock, 4) < 0) {return -1;} else if (!mListen){//mListen为false 用于netlink消息监听//创建SocketClient作为SocketListener 的客户端 mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));}
//创建匿名管道 pipe(mCtrlPipe);//创建线程执行函数threadStart 参thispthread_create(&mThread, NULL, SocketListener::threadStart, this); }

线程监听Kernel netlink发送的UEvent消息:     

void *SocketListener::threadStart(void *obj) {//参数转换SocketListener *me = reinterpret_cast<SocketListener *>(obj);me->runListener();pthread_exit(NULL);return NULL;}

SocketListener 线程消息循环:

void SocketListener::runListener() {//SocketClient ListSocketClientCollection *pendingList = new SocketClientCollection();while(1) {fd_set read_fds;//mListen 为falseif (mListen) {max = mSock;FD_SET(mSock, &read_fds);}//加入一组文件描述符集合 选择fd最大的maxFD_SET(mCtrlPipe[0], &read_fds);pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {int fd = (*it)->getSocket();FD_SET(fd, &read_fds);if (fd > max)max = fd;}pthread_mutex_unlock(&mClientsLock);//监听文件描述符是否变化rc = select(max + 1, &read_fds, NULL, NULL, NULL);//匿名管道被写,退出线程if (FD_ISSET(mCtrlPipe[0], &read_fds))break;//mListen 为falseif (mListen && FD_ISSET(mSock, &read_fds)) {//mListen 为ture 表示正常监听socketstruct sockaddr addr;do {//接收客户端连接c = accept(mSock, &addr, &alen);} while (c < 0 && errno == EINTR);//此处创建一个客户端SocketClient加入mClients列表中,异步延迟处理pthread_mutex_lock(&mClientsLock);mClients->push_back(new SocketClient(c, true, mUseCmdNum));pthread_mutex_unlock(&mClientsLock);}/* Add all active clients to the pending list first */pendingList->clear();//将所有有消息的Client加入到pendingList中pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {int fd = (*it)->getSocket();if (FD_ISSET(fd, &read_fds)) {pendingList->push_back(*it);}}pthread_mutex_unlock(&mClientsLock);//处理所有消息while (!pendingList->empty()) {it = pendingList->begin();SocketClient* c = *it;pendingList->erase(it);//处理有数据发送的socket 虚函数if (!onDataAvailable(c) && mListen) {//mListen为false
            }}}
}

Netlink消息处理:

 

  在消息循环中调用onDataAvailable处理消息,onDataAvailable是个虚函数,NetlinkListener重写了此函数。

NetlinkListener onDataAvailable消息处理:

bool NetlinkListener::onDataAvailable(SocketClient *cli)
{//获取socket idint socket = cli->getSocket();//接收netlink uevent消息count = TEMP_FAILURE_RETRY(uevent_kernel_multicast_uid_recv(socket, mBuffer, sizeof(mBuffer), &uid));//解析uevent消息为NetlinkEvent消息NetlinkEvent *evt = new NetlinkEvent();evt->decode(mBuffer, count, mFormat);//处理NetlinkEvent onEvent虚函数
    onEvent(evt);
}

    将接收的Uevent数据转化成NetlinkEvent数据,调用onEvent处理,NetlinkListener子类NetlinkHandler重写了此函数。

NetlinkHandler NetlinkEvent数据处理:

void NetlinkHandler::onEvent(NetlinkEvent *evt) {//获取VolumeManager实例VolumeManager *vm = VolumeManager::Instance();//设备类型const char *subsys = evt->getSubsystem();//将消息传递给VolumeManager处理if (!strcmp(subsys, "block")) {vm->handleBlockEvent(evt);}}

    NetlinkManager通过NetlinkHandler将接收到Kernel内核发送的Uenvet消息,

  转化成了NetlinkEvent结构数据传递给VolumeManager处理。

2 VolumeManager模块

  此模块管理所有挂载的设备节点以及相关操作执行;下面是VolumeManager模块类结构图:

 

    

 

    DirectVolume:一个实体存储设备在代码中的抽象。

    SocketListenner:创建线程,监听socket。

  这里VolumeManager构造的SocketListenner与NetlinkManager构造的SocketListenner有所不同的:

    NetlinkManager构造的SocketListenner:Kernel与Vold通信;

    VolumeManager构造的SocketListenner:Native Vold与Framework MountService 通信;

  VolumeManager构造的SocketListenner,由vold进程main函数中创建的CommandListener:

 int main() {……CommandListener *cl;cl = new CommandListener();vm->setBroadcaster((SocketListener *) cl);//启动CommandListener监听cl->startListener();}          

VolumeManager工作流程:

//从main函数中的start开始:
int VolumeManager::start() {return 0;
}

  NetlinkManager接收到Kernel通过netlink发送的Uevent消息,转化成了NetlinkEvent消息,再传递给了VolumeManager处理。

NetlinkManager与VolumeManager交互流程图:

    

 

VolumeManager处理消息 handleBlockEvent

  从NetlinkManager到VolumeManager代码过程

  函数执行从onEvent到handleBlockEvent

    void NetlinkHandler::onEvent(NetlinkEvent *evt) {……//将消息传递给VolumeManager处理if (!strcmp(subsys, "block")) {vm->handleBlockEvent(evt);}}void VolumeManager::handleBlockEvent(NetlinkEvent *evt) {//有状态变化设备路径const char *devpath = evt->findParam("DEVPATH");
//遍历VolumeManager中所管理Volume对象(各存储设备代码抽象)for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {if (!(*it)->handleBlockEvent(evt)) {hit = true;break;}} }

将消息交给各个Volume对象处理:DirectVolume

  从VolumeManager到所管理的Volume对象

    这里的Volume为其派生类DirectVolume。

int DirectVolume::handleBlockEvent(NetlinkEvent *evt) 
{
//有状态变化设备路径const char *dp = evt->findParam("DEVPATH");PathCollection::iterator it;for (it = mPaths->begin(); it != mPaths->end(); ++it) {//匹配 设备路径 if (!strncmp(dp, *it, strlen(*it))) {int action = evt->getAction();const char *devtype = evt->findParam("DEVTYPE");//动作判断if (action == NetlinkEvent::NlActionAdd) {int major = atoi(evt->findParam("MAJOR"));int minor = atoi(evt->findParam("MINOR"));char nodepath[255];//设备节点路径名称snprintf(nodepath,sizeof(nodepath), "/dev/block/vold/%d:%d",major, minor);
//创建设备节点 createDeviceNode(nodepath, major, minor);if (!strcmp(devtype, "disk")) {//添加disk handleDiskAdded(dp, evt);} else {//添加分区 handlePartitionAdded(dp, evt);}} else if (action == NetlinkEvent::NlActionRemove) {} else if (action == NetlinkEvent::NlActionChange) {} else {SLOGW("Ignoring non add/remove/change event");}return 0;}} }

     为什么要让VolumeManager中的每一个Volume对象都去处理SD状态变换消息,

  每一个Volume可能对应多个Path;即一个挂载点对应多个物理设备。

抽象存储设备DirectVolume 动作状态变化处理:

void DirectVolume::handleDiskAdded(const char *devpath, NetlinkEvent *evt) {//主次设备号mDiskMajor = atoi(evt->findParam("MAJOR"));mDiskMinor = atoi(evt->findParam("MINOR"));//设备分区情况const char *tmp = evt->findParam("NPARTS");mDiskNumParts = atoi(tmp);if (mDiskNumParts == 0) {//没有分区,Volume状态为Idle
        setState(Volume::State_Idle);} else {//有分区未加载,设置Volume状态Pending
        setState(Volume::State_Pending);}//格式化通知msg:"Volume sdcard /mnt/sdcard disk inserted (179:0)"char msg[255];snprintf(msg, sizeof(msg), "Volume %s %s disk inserted (%d:%d)",getLabel(), getMountpoint(), mDiskMajor, mDiskMinor);//调用VolumeManager中的Broadcaster——>CommandListener 发送此msgmVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeDiskInserted,msg, false);
}

消息通知Framework层存储设备状态变化:

  类继承关系:

    

 

  发送消息通知Framework层是在SocketListener中完成;

void SocketListener::sendBroadcast(int code, const char *msg, bool addErrno) 
{pthread_mutex_lock(
&mClientsLock);//遍历所有的消息接收时创建的Client SocketClient// SocketClient将消息通过socket(“vold”)通信for (i = mClients->begin(); i != mClients->end(); ++i) {(*i)->sendMsg(code, msg, addErrno, false);}pthread_mutex_unlock(&mClientsLock); }

  这里工作的SocketListener是VolumeManager的,SocketListener的派生类CommandListener,

用来与Framework交互的,监听Socket消息。通过VolumeManager中调用sendBroadcast,与CommandListener模块进行交互。

由此需要清楚CommandListener模块工作流程。

 

3 CommandListener模块

  CommandListener监听Socket,使Vold与Framework层进行进程通信;

其相关类继承结构图如下:

    

 

CommandListener工作流程:

int main() 
{VolumeManager *vm;CommandListener *cl;NetlinkManager *nm;//CommandListener 创建vold socket监听上层消息cl = new CommandListener();//作为VolumeManager与NetlinkManager的Broadcastervm->setBroadcaster((SocketListener *) cl);nm->setBroadcaster((SocketListener *) cl);//启动CommandListener监听cl->startListener();……
}

CommandListener实例的创建:构造函数

         CommandListener构造函数:

  CommandListener::CommandListener() :FrameworkListener("vold", true) {//注册Framework发送的相关命令 Command模式
registerCmd(new DumpCmd());registerCmd(new VolumeCmd());registerCmd(new AsecCmd());registerCmd(new ObbCmd());registerCmd(new StorageCmd());registerCmd(new XwarpCmd());registerCmd(new CryptfsCmd());}        

 

 

 

FrameworkListener构造函数:

FrameworkListener::FrameworkListener(const char *socketName, bool withSeq) :SocketListener(socketName, true, withSeq) {mCommands = new FrameworkCommandCollection();mWithSeq = withSeq;}

注册Command

void FrameworkListener::registerCmd(FrameworkCommand *cmd) {mCommands->push_back(cmd);
}

SocketListener构造函数:

SocketListener::SocketListener(const char *socketName, bool listen, bool useCmdNum) {//mListen = true 正常的socket监听mListen = listen;//socket 名称“vold”mSocketName = socketName;mSock = -1;mUseCmdNum = useCmdNum;//初始化锁pthread_mutex_init(&mClientsLock, NULL);//构造Listener Client ListmClients = new SocketClientCollection();}

CommandListener启动 startListener:

int SocketListener::startListener() {//mSocketName = “Vold”mSock = android_get_control_socket(mSocketName);//NetlinkHandler mListen为true 监听socketif (mListen && < 0) {return -1;} else if (!mListen){mClients->push_back(new SocketClient(mSock, false, mUseCmdNum));}//创建匿名管道
    pipe(mCtrlPipe);//创建线程执行函数threadStart 参数thispthread_create(&mThread, NULL, SocketListener::threadStart, this);
}void *SocketListener::threadStart(void *obj) {SocketListener *me = reinterpret_cast<SocketListener *>(obj);me->runListener();
}void SocketListener::runListener() {//SocketClient ListSocketClientCollection *pendingList = new SocketClientCollection();while(1) {fd_set read_fds;//mListen 为trueif (mListen) {max = mSock;FD_SET(mSock, &read_fds);}//加入一组文件描述符集合 选择fd最大的max select有关FD_SET(mCtrlPipe[0], &read_fds);pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {int fd = (*it)->getSocket();FD_SET(fd, &read_fds);if (fd > max)max = fd;}pthread_mutex_unlock(&mClientsLock);//监听文件描述符是否变化rc = select(max + 1, &read_fds, NULL, NULL, NULL);//匿名管道被写,退出线程if (FD_ISSET(mCtrlPipe[0], &read_fds))break;//mListen 为trueif (mListen && FD_ISSET(mSock, &read_fds)) {//mListen 为ture 表示正常监听socketstruct sockaddr addr;do {c = accept(mSock, &addr, &alen);} while (c < 0 && errno == EINTR);//创建一个客户端SocketClient,加入mClients列表中 到异步延迟处理pthread_mutex_lock(&mClientsLock);mClients->push_back(new SocketClient(c, true, mUseCmdNum));pthread_mutex_unlock(&mClientsLock);}/* Add all active clients to the pending list first */pendingList->clear();//将所有有消息的Client加入到pendingList中pthread_mutex_lock(&mClientsLock);for (it = mClients->begin(); it != mClients->end(); ++it) {int fd = (*it)->getSocket();if (FD_ISSET(fd, &read_fds)) {pendingList->push_back(*it);}}pthread_mutex_unlock(&mClientsLock);/* Process the pending list, since it is owned by the thread,*/while (!pendingList->empty()) {it = pendingList->begin();SocketClient* c = *it;//处理有数据发送的socket if (!onDataAvailable(c) && mListen) {//mListen为true
               ……}}}
} 

  CommandListener启动的线程监听Socket消息,接收到的消息处理onDataAvailable。

CommandListener父类FrameworkCommand重写了此函数。

CommandListener监听Socket消息处理:

bool FrameworkListener::onDataAvailable(SocketClient *c) {char buffer[255];//读取socket消息len = TEMP_FAILURE_RETRY(read(c->getSocket(), buffer, sizeof(buffer)));for (i = 0; i < len; i++) {if (buffer[i] == '\0') {//根据消息内容 派发命令dispatchCommand(c, buffer + offset);offset = i + 1;}}return true;
}void FrameworkListener::dispatchCommand(SocketClient *cli, char *data) {char *argv[FrameworkListener::CMD_ARGS_MAX];//解析消息内容 命令 参数
    ……//执行对应的消息for (i = mCommands->begin(); i != mCommands->end(); ++i) {FrameworkCommand *c = *i;//匹配命令if (!strcmp(argv[0], c->getCommand())) {//执行命令c->runCommand(cli, argc, argv);goto out;}}
out:return;
}

Command执行处理:以VolumeCommand为例 

CommandListener::VolumeCmd::VolumeCmd() :VoldCommand("volume") {
}int CommandListener::VolumeCmd::runCommand(SocketClient *cli,int argc, char **argv) {//获取VolumeManager实例VolumeManager *vm = VolumeManager::Instance();//Action判断 传递给VolumeManager处理if (!strcmp(argv[1], "list")) {return vm->listVolumes(cli);} else if (!strcmp(argv[1], "debug")) {vm->setDebug(!strcmp(argv[2], "on") ? true : false);} else if (!strcmp(argv[1], "mount")) {rc = vm->mountVolume(argv[2]);} else if (!strcmp(argv[1], "unmount")) {rc = vm->unmountVolume(argv[2], force, revert);} else if (!strcmp(argv[1], "format")) {rc = vm->formatVolume(argv[2]);} else if (!strcmp(argv[1], "share")) {rc = vm->shareVolume(argv[2], argv[3]);} else if (!strcmp(argv[1], "unshare")) {rc = vm->unshareVolume(argv[2], argv[3]);} else if (!strcmp(argv[1], "shared")) {……return 0;
}

    CommandListener使用Command模式。

  CommandListener接收到来自Framework层得消息,派发命令处理,再传递给VolumeManager处理。

VolumeManager中Action处理:

int VolumeManager::unmountVolume(const char *label) {//查找VolumeVolume *v = lookupVolume(label);//Volume执行动作return v-> unmountVol ();
}
//VolumeAction处理:
int Volume::unmountVol(bool force, bool revert) {doUnmount(Volume::SEC_STG_SECIMGDIR, force);……
}int Volume::doUnmount(const char *path, bool force) {……//systemcall
    umount(path);
}

整个Vold处理过程框架图如下:

    

 


http://chatgpt.dhexx.cn/article/KaLCZVOg.shtml

相关文章

Android vold介绍

目录 1. 前言2. vold概述3. vold初始化|- -vm->start()|- -process_config|- -hardware::configureRpcThreadpool|- -vold::VoldNativeService::start|- -nm->start() 4. StorageManagerService|- -SM与vold建立关联|- - -startService(serviceClass) |- -StorageManager…

<Android开发> Android vold - 第一篇 vold前言简介

本系列主要介绍 Android vold&#xff0c;分为以下篇章 &#xff1c;Android开发&#xff1e; Android vold - 第一篇 vold前言简介 &#xff1c;Android开发&#xff1e; Android vold - 第二篇 vold 的main()函数简介 &#xff1c;Android开发&#xff1e; Android vold - 第…

Vold原理介绍

一、 Vold简介 Android中Vold是volume Daemon&#xff0c;即Volume守护进程&#xff0c;用来管理Android中存储类的热拔插事件。这里的热插拔涉及的场景如&#xff1a; 1. 手机usb以MTP或者传输照片方式插拔PC端后磁盘数据的挂卸载&#xff1b; 2. 设备开关机过程中存储设备各分…

C语言Switch....case用法

概述 C语言switch语句通常用于多个条件判断&#xff0c;根据不同情况执行不同的代码块。它的使用形式如下&#xff1a; switch&#xff08;表达式&#xff09; { case 常量表达式1&#xff1a; 语句序列1 break; case 常量表达式2&#xff1a; 语句序列2 break; ...... default…

MySQL 入门:Case 语句很好用

引言 MySQL CASE 是一个 MySQL 语句查询关键字&#xff0c;它定义了处理循环概念以执行条件集并使用 IF ELSE 返回匹配案例的方式。 MySQL 中的 CASE 是一种控制语句&#xff0c;它验证条件案例集&#xff0c;并在第一个案例满足 else 值时显示值并退出循环。 如果没有找到 T…

Shell 编程之 case 语句

一、case 语句 1、case 语句概述 (1)case 语句的作用 使用 case 语句改写 if 多分支可以使脚本结构更加清晰、层次分明。针对变量的不同取 值&#xff0c;执行不同的命令序列。 2、case 语句的结构: case 变量值 in 模式 1) 命令序列 1 ;; 模式 2) 命令序列 2 ;; *…

【shell】case实现简单的系统工具箱

case实现简单的系统工具箱 case实现简单的系统工具箱 #!/usr/bin/bash #system manage #by racon 2020-04-19menu() {cat <<-EOF########################################## h. help ## f. disk partition ## d. filesystem mount ## m. memory ## u. system lo…

SQL CASE语句的使用

SQL CASE语句的使用 CASE是一个控制流语句&#xff0c;其作用与IF-THEN-ELSE语句非常相似&#xff0c;可根据数据选择值。 CASE语句遍历条件并在满足第一个条件时返回值。 因此&#xff0c;一旦条件成立&#xff0c;它将短路&#xff0c;从而忽略后面的子句并返回结果。 正如我…

数据库 case 用法

【转载】:数据库中case when 的用法 CASE WHEN 及 SELECT CASE WHEN的用法Case具有两种格式。简单Case函数和Case搜索函数。 1.简单Case函数 CASE sex WHEN 1 THEN 男 WHEN 2 THEN 女 ELSE 其他 END 2.Case搜索函数 CASE WHEN sex 1 THEN 男 WHEN sex 2 THEN 女 ELSE 其他 …

SQL之CASE WHEN用法详解

简单CASE WHEN函数&#xff1a; CASE SCORE WHEN A THEN 优 ELSE 不及格 END CASE SCORE WHEN B THEN 良 ELSE 不及格 END CASE SCORE WHEN C THEN 中 ELSE 不及格 END 等同于&#xff0c;使用CASE WHEN条件表达式函数实现&#xff1a; CASE WHEN SCORE A THEN 优WHEN SCORE…

switch case语法

文章目录 switch case组合不要拿青龙偃月刀去削苹果case的作用是什么&#xff1f;break的作用是什么&#xff1f;case后面的值有什么要求吗&#xff1f;case语句的排列顺序问题default语句相关问题使用case语句的一些注意事项 switch case组合 基本语法结构 switch(整型常量/…

SQL中case的使用方法

Case具有两种格式。简单Case函数和Case搜索函数。 1.简单Case函数 CASE sex WHEN 1 THEN 男WHEN 2 THEN 女 ELSE 其他 END2.Case搜索函数 CASE WHEN sex 1 THEN 男WHEN sex 2 THEN 女ELSE 其他 END3.简单case函数 VS case搜索函数 这两种方式&#xff0c;可以实现相同的功…

[转载]常用CASE工具介绍

[转载]常用CASE工具介绍转载自&#xff1a;http://www.cnblogs.com/powerlc/archive/2006/01/12/315959.html 一&#xff0c;概述 今天, 代码变得日益简单, 在Model的指导下, 思想, 设计, 分析都变得异常重要。企业业务建模工具, 产品非常多, 特别是在MDA日益流行的今天. Work…

CASE语句的使用方法

CASE语句有两种&#xff1a; 一种是case [column] when&#xff0c;指定了判断条件所在的列。 另一种是case when [column]&#xff0c;因为条件在子句中所以能对任意列进行判断。 本例建立一个员工表&#xff0c;有员工id&#xff08;id&#xff09;&#xff0c;员工姓名&am…

常用CASE工具介绍 ZZ

常用CASE工具介绍 一&#xff0c;概述  今天, 代码变得日益简单, 在Model的指导下, 思想, 设计, 分析都变得异常重要。企业业务建模工具, 产品非常多, 特别是在MDA日益流行的今天. WorkFlow是典型的业务及流程建模。 二&#xff0c;软件开发CASE工具简介   (一)图稿绘制&…

CASE 工具有哪些

CASE 工具 CASE工具设置的软件应用程序。这使用为自动的SDLC活动。 CASE工具所使用的软件项目经理,分析师和工程师开发的软件系统. 有许多CASE工具做软件开发生命周期的各个阶段,如工具,设计工具,项目管理工具,数据库管理工具,文档工具分析. 为了得到所需的结果,CASE工具…

网络:简述路由算法之动态路由算法

网络&#xff1a;简述路由算法之动态路由算法 在计算机网络中&#xff0c;路由器的一个很重要责任就是要在端对端的节点中找出一条最佳路径出来&#xff0c;通过自己与相邻节点之间的信息&#xff0c;来计算出从自己位置到目的节点之间的最佳线路&#xff0c;这种算法我们可以理…

路由选择算法——链路状态算法

好久没写东西了&#xff0c;好生疏的感觉。。 链路状态算法 这是一种全局式的路由选择算法&#xff0c;也就是说&#xff0c;一个路由器知道到其他路由器的所有链路的状态信息&#xff08;例如某条链路上堵不堵&#xff09;&#xff0c;并且假设这种信息是被量化好了的&#…

示例演示“距离矢量路由算法”工作原理

以下内容摘自刚刚上市&#xff0c;已被纳入全国高校教材系统&#xff0c;并在全国热销、好评如潮的《深入理解计算机网络》新书。 7.5.3 距离矢量路由算法 现代计算机网络通常使用动态路由算法&#xff0c;因为这类算法能够适应网络的拓扑和流量变化&#xff0c;其中最流行的…

距离矢量路由算法

现代计算机网络通常使用动态路由算法&#xff0c;因为这类算法能够适应网络的拓扑和流量变化&#xff0c;其中最流行的两种动态路由算法是“距离矢量路由算法”和“链路状态路由算法”。 距离矢量路由算法&#xff08;Distance Vector Routing&#xff0c;DV&#xff09;是ARPA…