Linux那些事儿之我是U盘(34)彼岸花的传说(二) – fudan

如果让观众短信投票的话,usb_stor_control_thread()这个函数中的代码无疑是整个模块中最为精华的代码.我们只需要它中间301行那个for(;;)就知道,这是一个死循环,即使别的代码都执行完了,即使别的函数都退出了,这个函数仍然像永不消逝的电波一般,经典常驻.显然,只有死循环才能代码永恒.才能代表忠诚.这是每一个守护者的职责.

usb_stor_control_thread(),其代码如下:

281 static int usb_stor_control_thread(void * __us) 282 { 283 struct us_data *us = (struct us_data *)__us; 284 struct Scsi_Host *host = us->host; 285 286 lock_kernel(); 287 288 /* 289 * This thread doesn’t need any user-level access, 290 * so get rid of all our resources. 291 */ 292 daemonize("usb-storage"); 293 294 current->flags |= PF_NOFREEZE; 295 296 unlock_kernel(); 297 298 /* signal that we’ve started the thread */ 299 complete(&(us->notify)); 300 301 for(;;) { 302 US_DEBUGP("*** thread sleeping./n"); 303 if(down_interruptible(&us->sema)) 304 break; 305 306 US_DEBUGP("*** thread awakened./n"); 307 308 /* lock the device pointers */ 309 down(&(us->dev_semaphore)); 310 311 /* if us->srb is NULL, we are being asked to exit */ 312 if (us->srb == NULL) { 313 US_DEBUGP("– exit command received/n"); 314 up(&(us->dev_semaphore)); 315 break; 316 } 317 318 /* lock access to the state */ 319 scsi_lock(host); 320 321 /* has the command timed out *already* ? */ 322 if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) { 323 us->srb->result = DID_ABORT << 16; 324 goto SkipForAbort; 325 } 326 327 /* don’t do anything if we are disconnecting */ 328 if (test_bit(US_FLIDX_DISCONNECTING, &us->flags)) { 329 US_DEBUGP("No command during disconnect/n"); 330 goto SkipForDisconnect; 331 } 332 333 scsi_unlock(host); 334 335 /* reject the command if the direction indicator 336 * is UNKNOWN 337 */ 338 if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) { 339 US_DEBUGP("UNKNOWN data direction/n"); 340 us->srb->result = DID_ERROR << 16; 341 } 342 343 /* reject if target != 0 or if LUN is higher than 344 * the maximum known LUN 345 */ 346 else if (us->srb->device->id && 347 !(us->flags & US_FL_SCM_MULT_TARG)) { 348 US_DEBUGP("Bad target number (%d:%d)/n", 349 us->srb->device->id, us->srb->device->lun); 350 us->srb->result = DID_BAD_TARGET << 16; 351 } 352 353 else if (us->srb->device->lun > us->max_lun) { 354 US_DEBUGP("Bad LUN (%d:%d)/n", 355 us->srb->device->id, us->srb->device->lun); 356 us->srb->result = DID_BAD_TARGET << 16; 357 } 358 359 /* Handle those devices which need us to fake 360 * their inquiry data */ 361 else if ((us->srb->cmnd[0] == INQUIRY) && 362 (us->flags & US_FL_FIX_INQUIRY)) { 363 unsigned char data_ptr[36] = { 364 0x00, 0x80, 0x02, 0x02, 365 0x1F, 0x00, 0x00, 0x00}; 366 367 US_DEBUGP("Faking INQUIRY command/n"); 368 fill_inquiry_response(us, data_ptr, 36); 369 us->srb->result = SAM_STAT_GOOD; 370 } 371 372 /* we’ve got a command, let’s do it! */ 373 else { 374 US_DEBUG(usb_stor_show_command(us->srb)); 375 us->proto_handler(us->srb, us); 376 } 377 378 /* lock access to the state */ 379 scsi_lock(host); 380 381 /* indicate that the command is done */ 382 if (us->srb->result != DID_ABORT << 16) { 383 US_DEBUGP("scsi cmd done, result=0x%x/n", 384 us->srb->result); 385 us->srb->scsi_done(us->srb); 386 } else { 387 SkipForAbort: 388 US_DEBUGP("scsi command aborted/n"); 389 } 390 391 /* If an abort request was received we need to signal that 392 * the abort has finished. The proper test for this is 393 * the TIMED_OUT flag, not srb->result == DID_ABORT, because 394 * a timeout/abort request might be received after all the 395 * USB processing was complete. */ 396 if (test_bit(US_FLIDX_TIMED_OUT, &us->flags)) 397 complete(&(us->notify)); 398 399 /* finished working on this command */ 400 SkipForDisconnect: 401 us->srb = NULL; 402 scsi_unlock(host); 403 404 /* unlock the device pointers */ 405 up(&(us->dev_semaphore)); 406 } /* for (;;) */ 407 408 /* notify the exit routine that we’re actually exiting now 409 * 410 * complete()/wait_for_completion() is similar to up()/down(), 411 * except that complete() is safe in the case where the structure 412 * is getting deleted in a parallel mode of execution (i.e. just 413 * after the down() — that’s necessary for the thread-shutdown 414 * case. 415 * 416 * complete_and_exit() goes even further than this — it is safe in 417 * the case that the thread of the caller is going away (not just 418 * the structure) — this is necessary for the module-remove case. 419 * This is important in preemption kernels, which transfer the flow 420 * of execution immediately upon a complete(). 421 */ 422 complete_and_exit(&(us->notify), 0); 423 }

284行,定义了一个Scsi_Host的指针host,令她指向us->host,也就是刚刚用scsi_host_alloc()申请的那个Scsi_Host结构体变量.

292行,daemonize("usb-storage"),其实,这句话才是真正创建精灵进程的,daemonize()函数来自内核的核心位置,kernel/exit.c中,她完成了这么一件事情,把一个普通的进程转换成为了精灵进程,不过此处咱们可以不去深究精灵进程的原理,甚至咱们可以认为这句话没有做任何事情,只是从此之后咱们ps命令一看能够看到有一个叫做usb-storage的进程.比如下面所看到的:

localhost:~ # ps -elF S UID PID PPID C PRI NI ADDR SZ WCHAN TTY TIME CMD4 S 0 1 0 0 76 0 – 195 – ? 00:00:02 init1 S 0 2 1 0 -40 – – 0 migrat ? 00:00:00 migration/01 S 0 3 1 0 94 19 – 0 ksofti ? 00:00:00 ksoftirqd/01 S 0 18 1 0 70 -5 – 0 worker ? 00:00:00 events/01 S 0 26 1 0 71 -5 – 0 worker ? 00:00:00 khelper1 S 0 27 1 0 70 -5 – 0 worker ? 00:00:00 kthread1 S 0 45 27 0 72 -5 – 0 worker ? 00:00:00 kacpid1 S 0 229 27 0 80 0 – 0 pdflus ? 00:00:00 pdflush1 S 0 230 27 0 75 0 – 0 pdflus ? 00:00:00 pdflush1 S 0 231 1 0 76 0 – 0 kswapd ? 00:00:00 kswapd01 S 0 961 27 0 70 -5 – 0 scsi_e ? 00:00:00 scsi_eh_01 S 0 1033 27 0 70 -5 – 0 scsi_e ? 00:00:00 scsi_eh_11 S 0 1045 27 0 71 -5 – 0 scsi_e ? 00:00:00 scsi_eh_21 S 0 1047 27 0 70 -5 – 0 worker ? 00:00:00 scsi_wq_25 S 0 1262 1 0 72 -4 – 1774 – ? 00:00:02 udevd1 S 0 1939 27 0 70 -5 – 0 hub_th ? 00:00:00 khubd1 S 0 7804 27 0 70 -5 – 0 scsi_e ? 00:00:00 scsi_eh_31 S 0 7805 27 0 70 -5 – 0 – ? 00:00:00 usb-storage4 S 0 13905 13902 0 75 0 – 2430 wait pts/1 00:00:00 bash0 R 0 19098 13905 0 77 0 – 821 – pts/1 00:00:00 ps

显然,您在终端按ctrl-c是不可能中止这个usb-storage进程的.这是精灵进程诸多特性中的一个,她运行于后台.试一下kill -9加进程号,看你能杀死她不?(系统崩溃了我可不负责哦…)

294行,这里为目前的进程设置一个flag,PF_NOFREEZE,在整个内核代码中,这个flag也只出现过几次.这个flag是与电源管理相关的,2.6的内核为了实现与Windows相似的一个功能,Hibernate,也就是"冬眠",(别说您不知道,Windows关机选项里面有"关机","重启","注销","Stand by",以及"Hibernate").在内核编译菜单里面,Power managerment options中,有一个选项Software Suspend,也就是内核编译选项中的CONFIG_SOFTWARE_SUSPEND,选择了她使得机器可以被suspended.显然咱们不用care她.但是这里之所以要设置这个flag,是因为suspend要求把内存里的冬冬写到磁盘上,而一个进程设置了这个flag就表明它在suspend的时候不会被冻住,用行话来讲就是,they’re not refrigerated during a suspend.freeze就是冷冻,冻住的意思,过去分词frozen是形容词,冻结的,冰冻的,其实就是让进程睡眠.所以总的来说,这里的做法就是说,即使系统suspend了,这个进程,或者准确地说,这个内核线程,也不应该进入睡眠.

要执行这两个操作需要执行lock_kernel()/unlock_kernel()这一对函数.然后299行,执行complete唤醒前面那节的父进程.而子进程并不退出,她继续行走,她无怨无悔的行走,只不过余秋雨先生是为追寻人类文明足迹而进行的域外旅程,而此处子进程(执)行的她对内核的守护.她像天使一般,守护着心爱的人.

于是咱们也继续跟着她行走,299行, complete(&(us->notify)),这正是和刚才在父进程里看到的那句使进程进入睡眠的wait_for_completion(&(us->notify))相对应,这里自然是唤醒父进程,我们先继续看一下子进程,稍候马上去看父进程.

301行,一个for语句死循环,尽管外面的世界很精彩,但是咱们去看看for里面的世界也不妨.

303行,down_interruptible()函数,事实上302行的注释已经告诉咱们,thread将进入睡眠了…,也许她累了.down_interruptible的参数是&us->sema,不陌生吧,我们之前讲信号量讲互斥锁的时候就已经提过了us->sema.所以这里很简单,就是想获得这把锁,但是别忘了,我们当初就介绍过,这把锁一开始就被初始化为0了,也就是说它属于那种指腹为婚的情形,一到这个世界来就告诉别人自己已经是名花有主了.因此,这里只能进入睡眠,等待一个up()函数去释放锁.谁会调用up()函数呢?暂时先不管它,我们先关注一下父进程,毕竟我们自己进入了睡眠,而之前我们把父进程唤醒了.

用最少的悔恨面对过去

Linux那些事儿之我是U盘(34)彼岸花的传说(二) – fudan

相关文章:

你感兴趣的文章:

标签云: