android sd 卡流程解析

 
 
 
本文是随笔所记,为了用时比较方便查到到对应的代码。文中可能会有些错误,如各位  IT
友发现,欢迎给我留言或发 email: [email protected]
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
android 的 sd 卡大概可以分为三层:
1:kernel 层的检测,并发送卡存在与否的消息
2:vold  service,这个是个可执行文件,在 init.rc 中以服务的形式启动。通过监听 kernel  sd
卡的插入与拔出的 uevent 及其它事件,并发送给 framwrok
3:framwork,接受 sd 卡状态变化的 netlink,并发相应命令给 vold
 
 
 
 
 
 
 
 
 
 
 
1:kernel 层的检测
 
在 sd 卡的对应驱动中 ret = sdhci_add_host(host);  
对应厂家 sd 卡的驱动中有
mmc_add_host(mmc); 
int mmc_add_host(struct mmc_host *host)
{
……
mmc_start_host(host); 
……
 
}
 
/**
  *    mmc_add_host - initialise host hardware
  *    @host: mmc host
  *
  *    Register the host with the driver model. The host must be 
  *    prepared to start servicing requests before this function
  *    completes.
  */
int mmc_add_host(struct mmc_host *host)
{
……
         mmc_start_host(host); 
……
 
 
}
void mmc_start_host(struct mmc_host *host)
{
         mmc_power_off(host);
         mmc_detect_change(host, 0);
}
 
/**
  *    mmc_detect_change - process change of state on a MMC socket
  *    @host: host which changed state.
  *    @delay: optional delay to wait before detection (jiffies)
  *
  *    MMC drivers should call this when they detect a card has been
  *    inserted or removed. The MMC layer will confirm that any
  *    present card is still functional, and initialize any newly
  *    inserted.
  */
void mmc_detect_change(struct mmc_host *host, unsigned long delay)
{
#ifdef CONFIG_MMC_DEBUG
         unsigned long flags; 
         spin_lock_irqsave(&host->lock, flags);
         WARN_ON(host->removed);
         spin_unlock_irqrestore(&host->lock, flags);
#endif
 
         mmc_schedule_delayed_work(&host->detect, delay);调用侦测 T 卡 workqueue
}
在 host.c 中有
INIT_DELAYED_WORK(&host->detect, mmc_rescan);
故 mmc_schedule_delayed_work(&host->detect, delay);将调用到 core.c 里的 mmc_rescan
 
void mmc_rescan(struct work_struct *work)
{
……
         /*
           * ...then normal SD...
           */
         err = mmc_send_app_op_cond(host, 0, &ocr);
         if (!err) {//如果是 sd 进入
                  if (mmc_attach_sd(host, ocr))//附上 SD 卡
                           mmc_power_off(host);
                  extend_wakelock = 1;
                  goto out;
         }
……
}
在 sd.c 中
/*
  * Starting point for SD card init.
  */
int mmc_attach_sd(struct mmc_host *host, u32 ocr)
{
……
         err = mmc_add_card(host->card);//
         if (err)
                  goto remove_card;
……
 
}
 
 
在 bus.c 中
* Register a new MMC card with the driver model. 
  */
int mmc_add_card(struct mmc_card *card)
{
……
         ret = device_add(&card->dev);//这边增加设备并发 uevent 通知用户空间
         if (ret)
                  return ret;
 
}
在/driver/base/core.c 中
/**
  * device_add - add device to device hierarchy.
  * @dev: device.
  *
  * This is part 2 of device_register(), though may be called
  * separately _iff_ device_initialize() has been called separately.
  *
  * This adds @dev to the kobject hierarchy via kobject_add(), adds it 
  * to the global and sibling lists for the device, then
  * adds it to the other relevant subsystems of the driver model.
  *
  * NOTE: _Never_ directly free @dev after calling this function, even 
  * if it returned an error! Always use put_device() to give up your
  * reference instead.
  */
int device_add(struct device *dev)
{
……
         kobject_uevent(&dev->kobj, KOBJ_ADD);
……
 
}
在/lib/kobject_uevent.c 中有
 
 
 
 
 
 
 
 
 
/**
  * kobject_uevent - notify userspace by ending an uevent 
  *
  * @action: action that is happening 
  * @kobj: struct kobject that the action is happening to
  *
  * Returns 0 if kobject_uevent() is completed with success or the 
  * corresponding error when it fails.
  */
int kobject_uevent(struct kobject *kobj, enum kobject_action action) 
{
         return kobject_uevent_env(kobj, action, NULL);
}
 
/**
  * kobject_uevent_env - send an uevent with environmental data
  *
  * @action: action that is happening 
  * @kobj: struct kobject that the action is happening to
  * @envp_ext: pointer to environmental data
  *
  * Returns 0 if kobject_uevent() is completed with success or the 
  * corresponding error when it fails.
  */
int kobject_uevent_env(struct kobject *kobj, enum kobject_action action,
                                char *envp_ext[])
{
……
         /* default keys */
         retval = add_uevent_var(env, "ACTION=%s", action_string);//填充给用户空间的一些信息
         if (retval)
                  goto exit;
         retval = add_uevent_var(env, "DEVPATH=%s", devpath);
         if (retval)
                  goto exit;
         retval = add_uevent_var(env, "SUBSYSTEM=%s", subsystem);
         if (retval)
                  goto exit;
……
                           NETLINK_CB(skb).dst_group = 1; 
                           retval = netlink_broadcast(uevent_sock, skb, 0, 1,
                                                            GFP_KERNEL);//发送 uevent_socket,也就 netlink
}
 
而 uevent_sock 在
static int __init kobject_uevent_init(void)
{
         uevent_sock = netlink_kernel_create(&init_net, NETLINK_KOBJECT_UEVENT,
                                                     1, NULL, NULL, THIS_MODULE);
         if (!uevent_sock) {
                  printk(KERN_ERR
                                "kobject_uevent: unable to create netlink socket!\n");
                  return -ENODEV;
         }
         netlink_set_nonroot(NETLINK_KOBJECT_UEVENT, NL_NONROOT_RECV);
         return 0;
}
从中可以更上层联系起来。
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
2:vold service
 
process_config()
{
……
            if (!(fp = fopen("/etc/vold.fstab", "r"))) {
                return -1;
        }
 
        while(fgets(line, sizeof(line), fp)) {    
 
 
if (line[0] == '#' || line[0] == '\0')
                        continue;
 
                if (!(type = strsep(&next, " \t"))) {
                        SLOGE("Error parsing type");
                        goto out_syntax;
                }
                if (!(label = strsep(&next, " \t"))) {
                        SLOGE("Error parsing label");
                        goto out_syntax;
                }
                if (!(mount_point = strsep(&next, " \t"))) {
                        SLOGE("Error parsing mount point");
                        goto out_syntax;
                }
          获取/etc/vold.fstab 文件里的信息
#######################
## Regular device mount 
##
## Format: dev_mount <label> <mount_point> <part> <sysfs_path1...>  
## label                - Label for the volume
## mount_point    - Where the volume will be mounted
## part                  - Partition # (1 based), or 'auto' for first usable partition. 
## <sysfs_path> - List of sysfs paths to source devices
######################
 
# Mounts the first usable partition of the specified device
#dev_mount sdcard /mnt/sdcard auto /block/mmcblk0 
dev_mount sdcard /mnt/sdcard auto /devices/platform/sprd-sdhci.0/mmc_host/mmc0
 
          if (!strcmp(part, "auto")) {
                                dv = new DirectVolume(vm, label, mount_point, -1);
                        } else {
                                dv = new DirectVolume(vm, label, mount_point, atoi(part));
                        }
 
其中 new DirectVolume
DirectVolume::DirectVolume()
{
mPaths = new PathCollection(); 
……
setState(Volume::State_NoMedia);直接跑到
void Volume::setState(int state)  
{
……
  mVm->getBroadcaster()->sendBroadcast(ResponseCode::VolumeStateChange,
                                                                                  msg, false);
 
}直接跑到
SocketListener::sendBroadcast()
{
        SocketClientCollection::iterator i;
 
        for (i = mClients->begin(); i != mClients->end(); ++i)  
{
                if ((*i)->sendMsg(code, msg, addErrno))  
{
                        SLOGW("Error sending broadcast (%s)", strerror(errno)); 
                }
          }
 
}
在 socketclient.h 中有
typedef android::List<SocketClient *> SocketClientCollection; 
故(*i)->sendMsg(code, msg, addErrno)将运行到 SocketClient::sendMsg()
 
 
}
 
}
 
 
 
 
int NetlinkManager::start() {
        struct sockaddr_nl nladdr; 
        int sz = 64 * 1024; 
        int on = 1;
 
        memset(&nladdr, 0, sizeof(nladdr)); 
        nladdr.nl_family = AF_NETLINK;
        nladdr.nl_pid = getpid(); 
        nladdr.nl_groups = 0xffffffff;
 
        if ((mSock = socket(PF_NETLINK,
                                                SOCK_DGRAM,NETLINK_KOBJECT_UEVENT)) < 0) {
                SLOGE("Unable to create uevent socket: %s", strerror(errno)); 
                return -1;
        }
 
        if (setsockopt(mSock, SOL_SOCKET, SO_RCVBUFFORCE, &sz, sizeof(sz)) < 0) {
                SLOGE("Unable to set uevent socket SO_RECBUFFORCE option: %s", strerror(errno));
                return -1;
                }
                
                if (setsockopt(mSock, SOL_SOCKET, SO_PASSCRED, &on, sizeof(on)) < 0) {
                        SLOGE("Unable to set uevent socket SO_PASSCRED option: %s", strerror(errno)); 
                return -1;
        }
 
        if (bind(mSock, (struct sockaddr *) &nladdr, sizeof(nladdr)) < 0) {
                SLOGE("Unable to bind uevent socket: %s", strerror(errno));
                return -1;
        }
 
        mHandler = new NetlinkHandler(mSock); 
        if (mHandler->start()) {
                SLOGE("Unable to start NetlinkHandler: %s", strerror(errno)); 
                return -1;
        }
        return 0;
}
建立与内核的 socket 通讯
netlink 的使用及其优点见附录:
 
 
 
 
mHandler = new NetlinkHandler(mSock); 
 
NetlinkHandler::NetlinkHandler(int listenerSocket) : 
                                NetlinkListener(listenerSocket) {
}
 
NetlinkListener::NetlinkListener(int socket) :
                                                        SocketListener(socket, false) {
}
 
SocketListener::SocketListener(const char *socketName, bool listen) {
        mListen = listen;
        mSocketName = socketName;
        mSock = -1;
        pthread_mutex_init(&mClientsLock, NULL); 
        mClients = new SocketClientCollection();
}
 
建立 socket 相对应的处理函数
      mHandler->start()
int NetlinkHandler::start() {
        return this->startListener(); 
}
 
int SocketListener::startListener()  
{
……
pthread_create(&mThread, NULL, SocketListener::threadStart, this)
 
}
 
void *SocketListener::threadStart(void *obj) {
        SocketListener *me = reinterpret_cast<SocketListener *>(obj);
 
        me->runListener();
        pthread_exit(NULL);
        return NULL;
}
 
 
 
 
 
 
void SocketListener::runListener()  
{
while(1)  
{
……
if (!onDataAvailable(*it)) {
……
 
}
 
 
}
又由于
NetlinkListener::NetlinkListener(int socket) :
                                                        SocketListener(socket, false) {
}
故将调用到
bool NetlinkListener::onDataAvailable(SocketClient *cli)
{
……
        if ((count = recvmsg(socket, &hdr, 0)) < 0) {
                SLOGE("recvmsg failed (%s)", strerror(errno));
                return false;
}
如果 kernel 层没有消息,这边将阻塞,如果一有消息,这边将立即开始接收。
……
NetlinkEvent *evt = new NetlinkEvent(); 
……
onEvent(evt);
 
}
又 NetlinkHandler::NetlinkHandler(int listenerSocket) :
                                NetlinkListener(listenerSocket) {
}
故将跳到
void NetlinkHandler::onEvent(NetlinkEvent *evt) {
        VolumeManager *vm = VolumeManager::Instance();
        const char *subsys = evt->getSubsystem(); 
 
        if (!subsys) {
                SLOGW("No subsystem found in netlink event");
                return;
        }
 
        if (!strcmp(subsys, "block")) {
                vm->handleBlockEvent(evt); 
        } else if (!strcmp(subsys, "switch")) {
                vm->handleSwitchEvent(evt); 
        } else if (!strcmp(subsys, "battery")) {
        } else if (!strcmp(subsys, "power_supply")) {
        }
}
 
void VolumeManager::handleBlockEvent(NetlinkEvent *evt)  
{
      ……
        for (it = mVolumes->begin(); it != mVolumes->end(); ++it) {
                if (!(*it)->handleBlockEvent(evt)) {
}
……
}
又 DirectVolume::DirectVolume(VolumeManager *vm, const char *label,
                                                      const char *mount_point, int partIdx) :
                            Volume(vm, label, mount_point)  
      dv = new DirectVolume(vm, label, mount_point, -1);
vm->addVolume(dv); 
 
故    if (!(*it)->handleBlockEvent(evt)) {
将调用到
int DirectVolume::handleBlockEvent(NetlinkEvent *evt)
{
 
}
 
 
 
vm->handleSwitchEvent(evt)()  将跳到
void VolumeManager::handleSwitchEvent()这个函数主要处理充电器,usb 的插入拔出
{
……
if (!strcmp(name, "usb_mass_storage")) { usb 的插入拔出
 
                if (!strcmp(state, "online"))    {
                        notifyUmsConnected(true); 
                } else {
                        notifyUmsConnected(false); 
                }
        //add by liguxiang 09-15-11 for whether usb available begin
        }else if(!strcmp(name,"charger_cable")){充电器
                if(!strcmp(state,"1")){
                  notifyUsbAvailable(true); 
                }else{
                            notifyUsbAvailable(false); 
                }
        //add by liguxiang 09-15-11 for whether usb available end
        }else {
                SLOGW("Ignoring unknown switch '%s'", name);//忽略其他事件,比如耳机的插入拔
出,电池的电量等,他们有专门的 socket 监听
        }
 
}
{
 
 
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
3:framework mountservice
    在 systemserver.java 中  
                        ServiceManager.addService("mount", new MountService(context));
 
public MountService(Context context)
{
……
                mConnector = new NativeDaemonConnector(this, "vold", 10, "VoldConnector");
                mReady = false;
                Thread thread = new Thread(mConnector, NativeDaemonConnector.class.getName());
                thread.start();
 
 
 
}
 
thread.start();将运行
        public void run() {
 
                while (true) {
                        try {
                                listenToSocket();
                        } catch (Exception e) {
                                Slog.e(TAG, "Error in NativeDaemonConnector", e);
                                SystemClock.sleep(5000); 
                        }
                }
        }
 
private void listenToSocket() throws IOException {
 
 
 
}
 
 
 
 
 
 
 
 
 
 
 
 
private int doMountVolume(String path)
{
……
mConnector.doCommand(String.format("volume mount %s", path)); 
……
 
}开始执行 mount sd 卡,
  public synchronized ArrayList<String> doCommand(String cmd) 
      throws NativeDaemonConnectorException
{
……
          sendCommand(cmd);发送挂载对应目录设备的命令
……
 
}    
这个函数在 NativeDaemonConnector.java 中
        private void sendCommand(String command) {
                sendCommand(command, null); 
}
private void sendCommand(String command, String argument)  
{
……
          StringBuilder builder = new StringBuilder(command); 
          f (argument != null) {
            builder.append(argument);
          }
            builder.append('\0');
 
            try {
            mOutputStream.write(builder.toString().getBytes());
……
 
}
又在系统初始化的时候
                        socket = new LocalSocket(); 
                        LocalSocketAddress address = new LocalSocketAddress(mSocket,
                                        LocalSocketAddress.Namespace.RESERVED);
 
                        socket.connect(address); 
                        mCallbacks.onDaemonConnected();
 
                        InputStream inputStream = socket.getInputStream();
                        mOutputStream = socket.getOutputStream();
其中 mOutputStream = socket.getOutputStream();
        /**
          * Retrieves the output stream for this instance.
          *
          * @return output stream
          * @throws IOException if socket has been closed or cannot be created.
          */
        public OutputStream getOutputStream() throws IOException {
                implCreateIfNeeded(); 
                return impl.getOutputStream();
        }
 
        /**
          * It's difficult to discern from the spec when impl.create() should be 
          * called, but it seems like a reasonable rule is "as soon as possible, 
          * but not in a context where IOException cannot be thrown"
          *
          * @throws IOException from SocketImpl.create()
          */
        private void implCreateIfNeeded() throws IOException {
                if (!implCreated) {
                        synchronized (this) {
                                if (!implCreated) {
                                        implCreated = true;
                                        impl.create(true);
                                }
                        }
                
      /**
          * Creates a socket in the underlying OS.
          *
          * @param stream true if this should be a stream socket, false for 
          * datagram.
          * @throws IOException
          */
        public void create (boolean stream) throws IOException {
                // no error if socket already created
                // need this for LocalServerSocket.accept()
                if (fd == null) {
        fd = create_native(stream);将调用 JNI android_net_LocalSocketImpl.cpp 中的 socket_create
                }
}
在 android_net_LocalSocketImpl.cpp 中有
{"create_native", "(Z)Ljava/io/FileDescriptor;", (void*)socket_create},
static jobject
socket_create (JNIEnv *env, jobject object, jboolean stream)
{
        int ret;
 
        ret = socket(PF_LOCAL, stream ? SOCK_STREAM : SOCK_DGRAM, 0);
 
        if (ret < 0) {
                jniThrowIOException(env, errno); 
                return NULL;
        }
 
        return jniCreateFileDescriptor(env,ret);
}
接下来继续 impl.getOutputStream();
 
        /**
          * Retrieves the output stream for this instance.
          *
          * @return output stream
          * @throws IOException if socket has been closed or cannot be created.
          */
        protected OutputStream getOutputStream() throws IOException 
        {  
                if (fd == null) {
                        throw new IOException("socket not created");
                }
 
                synchronized (this) {
                        if (fos == null) {
                                fos = new SocketOutputStream(); 
                        }
 
                        return fos;
                }
        }
故 mOutputStream.write(builder.toString().getBytes());
将跳到 LocalSocketImpl.java 这个文件的
      public void write (byte[] b) throws IOException {
                        write(b, 0, b.length);
                }
                public void write (byte[] b, int off, int len) throws IOException {
                        synchronized (writeMonitor) {
                                FileDescriptor myFd = fd; 
                                if (myFd == null) throw new IOException("socket closed"); 
 
                                if (off < 0 || len < 0 || (off + len) > b.length ) {
                                        throw new ArrayIndexOutOfBoundsException();
                                }
                                writeba_native(b, off, len, myFd); 
                        }
                }
将调用 JNI 里的函数
{"writeba_native", "([BIILjava/io/FileDescriptor;)V", (void*) socket_writeba}, 
 
static void socket_writeba (JNIEnv *env, jobject object,  
                jbyteArray buffer, jint off, jint len, jobject fileDescriptor) 
{
……
err = socket_write_all(env, object, fd,  
                        byteBuffer + off, len); 
……
 
 
}
 
/**
  * Writes all the data in the specified buffer to the specified socket.
  *
  * Returns 0 on success or -1 if an exception was thrown. 
  */
static int socket_write_all(JNIEnv *env, jobject object, int fd,
                void *buf, size_t len)
{
……
                memset(&iv, 0, sizeof(iv)); 
 
                iv.iov_base = buffer; 
                iv.iov_len = len;
 
                msg.msg_iov = &iv; 
                msg.msg_iovlen = 1; 
                
                do {
                        ret = sendmsg(fd, &msg, MSG_NOSIGNAL);
                } while (ret < 0 && errno == EINTR);
……
}这边将上面要发送的内容通过 socket 发送出去。
 
而在 SocketListener.cpp 中有个线程一直在跑,用于监听上层发送的 socket 内容
void SocketListener::runListener()  
{
……
    if ((rc = select(max + 1, &read_fds, NULL, NULL, NULL)) < 0) {在没有数据的时候,将会阻塞在
这。
                        SLOGE("select failed (%s)", strerror(errno)); 
                        sleep(1);
                        continue;
                } else if (!rc)
                        continue;
 
 
……
……
                                if (FD_ISSET(fd, &read_fds)) {
                                        pthread_mutex_unlock(&mClientsLock);
                                        if (!onDataAvailable(*it)) {
                                                close(fd);
                                                pthread_mutex_lock(&mClientsLock); 
                                                delete *it;
                                                it = mClients->erase(it);
                                                pthread_mutex_unlock(&mClientsLock);
                                        }再次检验是否有新的数据,有将运行 onDataAvailable
……
 
}

FrameworkListener::FrameworkListener(const char *socketName) : 
                                                        SocketListener(socketName, true) {
        mCommands = new FrameworkCommandCollection();
}
故 onDataAvailable 将运行 FrameworkListener 里的 onDataAvailable 函数
bool FrameworkListener::onDataAvailable(SocketClient *c)   
{
len  =  TEMP_FAILURE_RETRY(read(c->getSocket(),  buffer,  sizeof(buffer)));  TEMP_FAILURE_RETRY
的用处是:一直获取 socket 直到成功为止
……
        for (i = 0; i < len; i++) {
                if (buffer[i] == '\0') {
                        /* IMPORTANT: dispatchCommand() expects a zero-terminated string */
                        dispatchCommand(c, buffer + offset); 
                        offset = i + 1;
                }
        }
 
}
 
void FrameworkListener::dispatchCommand(SocketClient *cli, char *data)  
{
……
    for (i = mCommands->begin(); i != mCommands->end(); ++i) {
                FrameworkCommand *c = *i;
 
                if (!strcmp(argv[0], c->getCommand())) {
                        if (c->runCommand(cli, argc, argv)) {
                                SLOGW("Handler '%s' error (%s)", c->getCommand(), strerror(errno));
                        }
                        goto out;
                }
}
……
 
}
 

CommandListener::CommandListener() : 
                                  FrameworkListener("vold") {
        registerCmd(new DumpCmd()); 
        registerCmd(new VolumeCmd()); 
        registerCmd(new AsecCmd()); 
        registerCmd(new ShareCmd()); 
        registerCmd(new StorageCmd()); 
        registerCmd(new XwarpCmd()); 
}
故 c->runCommand(cli, argc, argv)将调用
int CommandListener::VolumeCmd::runCommand(SocketClient *cli,
                                                                                                            int argc, char **argv)  
{
……
        } else if (!strcmp(argv[1], "mount")) {
                if (argc != 3) {
                        cli->sendMsg(ResponseCode::CommandSyntaxError,     "Usage:     volume     mount
<path>", false);
                        return 0;
                }
                rc = vm->mountVolume(argv[2]);
……
 
}
rc = vm->mountVolume(argv[2]);将跳到
int VolumeManager::mountVolume(const char *label) {
        Volume *v = lookupVolume(label);
 
        if (!v) {
                errno = ENOENT;
                return -1;
        }
        return v->mountVol();
}将跑到
int Volume::mountVol()  
{
……
setState(Volume::State_Checking);设置 sd 卡的状态,供上层使用,更新图标这类的
if (Fat::check(devicePath))  检测 sd 的的一些格式信息
{
……
 
}
……
 
}
 
int Fat::check(const char *fsPath)  
{
if (access(FSCK_MSDOS_PATH, X_OK)) {
                SLOGW("Skipping fs checks\n");
                return 0;
}
检查 static char FSCK_MSDOS_PATH[] = "/system/bin/fsck_msdos";这个文件是否存在,这
个是个可执行的 bin 文件,用于 sd 卡的文件格式检查。
rc = logwrap(4, args, 1);
 
}
int logwrap(int argc, const char* argv[], int background)  
{
……
pid = fork(); //创建两个线程,子进程返回 0,父进程返回子进程的 ID 号
        } else if (pid == 0) {
 
 
在子进程中
    child(argc, argv);
}
 
}
这个函数中还做了输出的重定向
void child(int argc, const char**argv) {//这个函数在 logwrapper.c 中
 
        // create null terminated argv_child array
        char* argv_child[argc + 1];
        memcpy(argv_child, argv, argc * sizeof(char *));
        argv_child[argc] = NULL;
 
        // XXX: PROTECT FROM VIKING KILLER
        if (execv(argv_child[0], argv_child)) {//运行 fsck_msdos 这个文件
                LOG(LOG_ERROR, "logwrapper",
                        "executing %s failed: %s", argv_child[0], strerror(errno));
                exit(-1);
        }
 
}
fsck_msdos 有 external/fsck_msdos 这个目录生成
 
 
int
main(int argc, char **argv)
{
……
         while (--argc >= 0) {
//              setcdevname(*argv, preen);
                  erg = checkfilesys(*argv++);
                  if (erg > ret)
                           ret = erg;
         }
 
}
 
checkfilesys(const char *fname)//这个函数在 check.c 中
{
这边将完成 sd 挂载前的 4 个工作。
 
}
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
附录一:
Netlink  是一种特殊的  socket,它是  Linux  所特有的,类似于  BSD  中的 AF_ROUTE  但又远
比它的功能强大,目前在  Linux  内核  中使用 netlink  进行应用与内核通信的应用很 多,包 括:
路由  daemon(NETLINK_ROUTE),1-wire  子系统(NETLINK_W1),用户态  socket  协议
(NETLINK_USERSOCK),防火墙(NETLINK_FIREWALL),socket  监视
(NETLINK_INET_DIAG),netfilter  日志(NETLINK_NFLOG),ipsec  安全策略
(NETLINK_XFRM),SELinux  事件通知(NETLINK_SELINUX),iSCSI  子系统
(NETLINK_ISCSI),进程审计(NETLINK_AUDIT),转发信息表查询
(NETLINK_FIB_LOOKUP),netlink connector(NETLINK_CONNECTOR),netfilter  子系统
(NETLINK_NETFILTER),IPv6  防火墙(NETLINK_IP6_FW),DECnet  路由信息
(NETLINK_DNRTMSG),内核事件向用户态通知(NETLINK_KOBJECT_UEVENT),通用
netlink(NETLINK_GENERIC)。
Netlink  是一种在内核与用户应用间进行双向数据传输的非常好的方式,用户态应用使用标准的
socket API  就可以使用  netlink  提供的强大功能,内核态需要使用专门的内核  API  来使用
netlink。
Netlink  相对于系统调用,ioctl  以及  /proc  文件系统而言具有以下优点:
1,为了使用  netlink,用户仅需要在  include/linux/netlink.h  中增加一个新类型的  netlink  协议
定义即可,  如  #define NETLINK_MYTEST 17  然后,内核和用户态应用就可以立即通过
socket API  使用该  netlink  协议类型进行数据交换。但系统调用需要增加新的系统调用,ioctl  则
需要增加设备或文件,  那需要不少代码,proc  文件系统则需要在  /proc  下添加新的文件或目
录,那将使本来就混乱的  /proc  更加混乱。
2. netlink 是一种异步通信机制,在内核与用户态应用之间传递的消息保存在 socket 缓存队列中,
发送消息只是把消息保存在接收者的 socket 的接收队列,而不需要等待接收者收到消息,但系
统调用与  ioctl  则是同步通信机制,如果传递的数据太长,将影响调度粒度。
3.使用  netlink  的内核部分可以采用模块的方式实现,使用  netlink  的应用部分和内核部分没
有编译时依赖,但系统调用就有依赖,而且新的系统调用的实现必须静态地连接到内核中,它无
法在模块中实现,使用新系统调用的应用在编译时需要依赖内核。
4.netlink  支持多播,内核模块或应用可以把消息多播给一个 netlink 组,属于该 neilink  组的任
何内核模块或应用都能接收到该消息,内核事件向用户态的通知机制就使用了这一特性,任何对
内核事件感兴趣的应用都能收到该子系统发送的内核事件,在后面的文章中将介绍这一机制的使
用。
5.内核可以使用  netlink  首先发起会话,但系统调用和  ioctl  只能由用户应用发起调用。
6.netlink  使用标准的  socket API,因此很容易使用,但系统调用和  ioctl 则需要专门的培训才
能使用。
 

你可能感兴趣的:(android sd 卡流程解析)