首页 > 系统 > Linux >

《Linux那些事儿之我是USB》我是U盘(23)彼岸花的传说(二)

2011-10-26

如果让大家投票的话,usb_stor_control_thread()这个函数中的代码无疑是整个模块中最为精华的代码。我们只需要它中间306行的for(;;)就知道,这是一个死循环,即使别的代码都执行完了,即使别的函数都退出了,这个...

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

usb_stor_control_thread(),其代码如下:

299 static int usb_stor_control_thread(void *__us)

300 {

301 structus_data *us = (struct us_data *)__us;

302 structScsi_Host *host = us_to_host(us);

303

304 current->flags|= PF_NOFREEZE;

305

306 for(;;) {

307 US_DEBUGP("*** thread sleeping.\n");

308 if(down_interruptible(&us->sema))

309 break;

310

311 US_DEBUGP("*** thread awakened.\n");

312

313 /* lock the device pointers */

314 mutex_lock(&(us->dev_mutex));

315

316 /* if the device has disconnected, we are free to exit */

317 if (test_bit(US_FLIDX_DISCONNECTING,&us->flags)) {

318 US_DEBUGP("--exiting\n");

319 mutex_unlock(&us->dev_mutex);

320 break;

321 }

322

323 /* lock access to the state */

324 scsi_lock(host);

325

326 /* has the command timed out *already* ? */

327 if (test_bit(US_FLIDX_TIMED_OUT,&us->flags)) {

328 us->srb->result = DID_ABORT << 16;

329 goto SkipForAbort;

330 }

331

332 scsi_unlock(host);

333

334 /* reject the command if the direction indicator

335 *is UNKNOWN

336 */

337 if (us->srb->sc_data_direction == DMA_BIDIRECTIONAL) {

338 US_DEBUGP("UNKNOWN datadirection\n");

339 us->srb->result = DID_ERROR<< 16;

340 }

341

342 /* reject if target != 0 or if LUN is higher than

343 * the maximum known LUN

344 */

345 else if (us->srb->device->id&&

346 !(us->flags &US_FL_SCM_MULT_TARG)) {

347 US_DEBUGP("Bad target number(%d:%d)\n",

348 us->srb->device->id,us->srb->device->lun);

349 us->srb->result =DID_BAD_TARGET << 16;

350 }

351

352 else if (us->srb->device->lun >us->max_lun) {

353 US_DEBUGP("Bad LUN(%d:%d)\n",

354 us->srb->device->id,us->srb->device->lun);

355 us->srb->result =DID_BAD_TARGET << 16;

356 }

357

358 /* Handle those devices which need us to fake

359 * their inquiry data */

360 else if ((us->srb->cmnd[0] == INQUIRY)&&

361 (us->flags & US_FL_FIX_INQUIRY)) {

362 unsigned char data_ptr[36] = {

363 0x00, 0x80, 0x02, 0x02,

364 0x1F, 0x00, 0x00, 0x00};

365

366 US_DEBUGP("Faking INQUIRYcommand\n");

367 fill_inquiry_response(us, data_ptr, 36);

368 us->srb->result = SAM_STAT_GOOD;

369 }

370

371 /* we&#39;ve got a command, let&#39;s do it! */

372 else {

373 US_DEBUG(usb_stor_show_command(us->srb));

374 us->proto_handler(us->srb, us);

375 }

376

377 /* lock access to the state */

378 scsi_lock(host);

379

380 /* did the command already complete because of a disconnect? */

381 if (!us->srb)

382 ; /* nothing to do */

383

384 /* indicate that the command is done */

385 else if (us->srb->result != DID_ABORT << 16) {

386 US_DEBUGP("scsi cmd done,result=0x%x\n",

387 us->srb->result);

388 us->srb->scsi_done(us->srb);

389 } else {

390 SkipForAbort:

391 US_DEBUGP("scsi commandaborted\n");

392 }

393

394 /* If an abort request was received we need to signal that

395 * the abort has finished. The proper test for this is

396 *the TIMED_OUT flag, not srb->result == DID_ABORT, because

397 *the timeout might have occurred after the command had

398 *already completed with a different result code. */

399 if (test_bit(US_FLIDX_TIMED_OUT,&us->flags)) {

400 complete(&(us->notify));

401

402 /* Allow USB transfers to resume */

403 clear_bit(US_FLIDX_ABORTING,&us->flags);

404 clear_bit(US_FLIDX_TIMED_OUT,&us->flags);

405 }

406

407 /* finished working on this command */

408 us->srb = NULL;

409 scsi_unlock(host);

410

411 /* unlock the device pointers */

412 mutex_unlock(&us->dev_mutex);

413 } /* for (;;) */

414

415 scsi_host_put(host);

416

417 /* notify the exit routine thatwe&#39;re actually exiting now

418 *

419 * complete()/wait_for_completion() issimilar to up()/down(),

420 * except that complete() is safe in the case where thestructure

421 *is getting deleted in a parallel mode of execution (i.e. just

422 * after the down() -- that&#39;s necessary for thethread-shutdown

423 *case.

424 *

425 * complete_and_exit() goes even further than this -- it is safein

426 * the case that the thread of the caller is going away (notjust

427 * the structure) -- this is necessary for the module-removecase.

428 * This is important in preemption kernels, which transfer theflow

429 * of execution immediately upon a complete().

430 */

431 complete_and_exit(&threads_gone,0);

432 }

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

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

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

308行,down_interruptible()函数,事实上307行的调式信息已经告诉我们,thread将进入睡眠了。down_interruptible的参数是&us->sema,这是一把锁,而这里就是想获得这把锁,但是别忘了,这把锁一开始就被初始化为0了,参考drivers/usb/storage/usb.c中第973行,storage_probe()函数:

973 init_MUTEX_LOCKED(&(us->sema));

也就是说它属于那种“指腹为婚”的情形,一到这个世界来就告诉别人自己已经是名花有主了。因此,这里只能进入睡眠,等待一个up()函数去释放锁。谁会调用up()函数呢?暂时先不管它,我们先关注一下父进程,父进程在执行完wake_up_process(th)之后, usb_stor_acquire_resources()将结束,带着0返回了storage_probe()中去

相关文章
最新文章
热点推荐