现在的位置: 首页 > 综合 > 正文

cygwin关键技术:设备模拟

2013年09月12日 ⁄ 综合 ⁄ 共 4205字 ⁄ 字号 评论关闭

快乐虾

http://blog.csdn.net/lights_joy/

lights@hb165.com

 

本文适用于

Cygwin checkout-2008-09-28

vs2008

 

欢迎转载,但请保留作者信息

 

Linux下有众多的设备,但是这些设备都有统一的接口,可以很方便地用open/close/read/write函数进行操作,而在windows下,很多不同的设备都由不同的API进行操作,为此cygwin引入了fhandler,其文档这样解释:

fhandlers are the core mechanism by which cygwin provides a file descripter (fd)

interface to things such as a random number generated, winsock sockets, raw disk

devices, the clipboard, the console and so on. Under unix access to all such

devices is via a combination of IOCTL's and open/close/read/write calls. Some

special functions do exist - such as bind () and listen () for sockets, but

these consistently operate on fd's. Under Win32 there are disparate interfaces

that have little in common with each other. See for example Direct Sound and

the Clipboard.

下面我们就来学习一下它的实现过程。

1.1    类关系图

使用vs2008提供的类关系图分析,可以发现很多相关的类:

class diagram

这些类都从一个叫fhandler_base的父类继承而来,很容易猜想fhandler_base将为设备定义一个统一的接口,其具体实现则由子类完成。当需要对某个设备进行操作时再创建相应的对象。

查看fhandler_base的定义,可以发现一些很熟悉的虚函数:

class fhandler_base

{

………………

  virtual int open (int, mode_t = 0);

  virtual int close ();

  virtual int ioctl (unsigned int cmd, void *);

  virtual int fcntl (int cmd, void *);

  virtual ssize_t readv (const struct iovec *, int iovcnt, ssize_t tot = -1);

  virtual ssize_t writev (const struct iovec *, int iovcnt, ssize_t tot = -1);

  virtual ssize_t __stdcall pread (void *, size_t, _off64_t) __attribute__ ((regparm (3)));

  virtual ssize_t __stdcall pwrite (void *, size_t, _off64_t) __attribute__ ((regparm (3)));

  virtual _off64_t lseek (_off64_t offset, int whence);

……………….

}

 

1.2    设备创建

Cygwin模拟了大部分Linux pc下的设备,基本上每一类设备都可以找到一个实现对应功能的类,这些类都从fhandler_base继承而来,但是cygwin并不鼓励用new创建这些类的实例,其原因如下:

Note: In most case, it is safe to assume that using new/delete (or

malloc/free) in an fhandler is dangerous and to be avoided.  The reason

for this is that memory allocated to fhandlers is copied between execed

processes in the cygwin heap.  Memory allocated in new/delete is only

copied to forked processes.  So use cmalloc/cfree.

 

Obviously it is possible to use new/delete in some situations but if you're

seeing strange core dumps with usages like cat < /my/newfhandler then the

above may well be the culprit.

实际上,cygwin希望用户使用open函数来打开设备,这个函数将最终调用一个叫build_fh_pc的函数进行实例的创建:

#define fh_unset ((fhandler_base *) 1)

fhandler_base *

build_fh_pc (path_conv& pc)

{

  fhandler_base *fh = fh_unset;

 

  switch (pc.dev.major)

{

…………

    default:

      switch (pc.dev)

     {

     case FH_CONSOLE:

     case FH_CONIN:

     case FH_CONOUT:

       fh = cnew (fhandler_console) ();

       break;

…………….

      }

    }

 

  if (fh == fh_unset)

    fh = cnew (fhandler_nodevice) ();

 

  if (fh)

    fh->set_name (pc);

  else

    set_errno (EMFILE);

 

  debug_printf ("fh %p", fh);

  return fh;

}

注意上述函数在创建实例时使用了一个叫cnew的宏:

#define cnew(name) new ((void *) ccalloc (HEAP_FHANDLER, 1, sizeof (name))) name

这样,创建出来的实例将使用cygwin内部使用的heap空间。

1.3    设备对象管理

对于上面创建出来的实例,cygwin又是如何管理的呢?因为cygwin下的设备可以使用open函数打开,我们就从open函数看起:

int open (const char *unix_path, int flags, ...)

{

     int res = -1;

     va_list ap;

     mode_t mode = 0;

     sig_dispatch_pending ();

 

     syscall_printf ("open (%s, %p)", unix_path, flags);

     myfault efault;

     //if (efault.faulted (EFAULT))

     //   /* errno already set */;

     //else if (!*unix_path)

     if (!*unix_path)

         set_errno (ENOENT);

     else

     {

         /* check for optional mode argument */

         va_start (ap, flags);

         mode = va_arg (ap, mode_t);

         va_end (ap);

 

         fhandler_base *fh;

         cygheap_fdnew fd;

 

         if (fd >= 0)

         {

              if (!(fh = build_fh_name (unix_path, NULL,

                   (flags & (O_NOFOLLOW | O_EXCL))

                   ?  PC_SYM_NOFOLLOW : PC_SYM_FOLLOW,

                   stat_suffixes)))

                   res = -1;     // errno already set

              else if ((flags & O_NOFOLLOW) && fh->issymlink ())

              {

……………

              }

              else if (!fh->open (flags, (mode & 07777) & ~cygheap->umask))

              {

                   delete fh;

                   res = -1;

              }

              else

              {

                   cygheap->fdtab[fd] = fh;

                   if ((res = fd) <= 2)

                       set_std_handle (res);

              }

         }

     }

 

     syscall_printf ("%d = cyg_open (%s, %p)", res, unix_path, flags);

     return res;

}

在创建对象的实例后,关键操作在

                   cygheap->fdtab[fd] = fh;

在这里init_cygheap里的一个成员

  dtable fdtab;

其类型为dtablecygwin用它来对这些创建出来的实例进行管理。注意这里的fdtab是放在cygheap上的,这样在传递给fork出来的子进程时将可以很容易知道父进程打开的设备,并能根据这些设备的种类采取不同的措施,如句柄的继承,复制特定的数据等等。

 

 

 

2       参考资料

cygwin关键技术:cygheap(2009-9-2)

cygwin关键技术:tls(2009-8-24)

vs2008下使用cygwin23):stdinstdoutstderr(2008-10-21)

vs2008下使用cygwin22):使用tls(2008-10-20)

 

 

 

 

 

 

 

抱歉!评论已关闭.