开源中文网

您的位置: 首页 > FreeBSD > 正文

freebsd系统编程[简体中文版]6

来源:  作者:

专用于FreeBSD的选项 

下面的选项是专用于FreeBSD的,知道的和使用的人都不是太多。但它们还是值得提一下,因为它们可以提供更多的灵活性。这些都是新的选项,poll并不保证能够检测这些条件,而且它们只能用于UFS文件系统。如果你的程序需要检测这些类型的事件,那最好使用kqueue接口,我们将在稍后介绍。 

  #define POLLEXTEND      0x0200


如果文件已经被执行,则设置POLLEXTEND事件。 

  #define POLLATTRIB      0x0400


如果有任一文件属性发生改变,则设置POLLATTIB事件。 

  #define POLLNLINK       0x0800


如果文件被重命名、删除或解除链接,则设置POLLNLINK事件。 

  #define POLLWRITE       0x1000


如果文件内容被修改,则设置POLLWRITE事件。 

下面的事件并不是pollfd events成员的有效标志,poll也将忽略它们。它们是在pollfd revents中返回的,用于表明发生了某个事件。 

  #define POLLERR         0x0008


POLLERR事件表明有错误发生。 

  #define POLLHUP         0x0010


POLLHUP表明在对应的STREAMS上发生了挂起事件。POLLHUP和POLLOUT是互斥事件,因为一个发生了挂起的STREAMS就不再是可写的了。 

  #define POLLNVAL        0x0020


POLLNVAL表明对poll的请求是无效的。 

poll的最后一个参数是超时值。可以通过这个参数告诉poll一个以微秒为单位的超时值。如果把超时值设置为-1,poll就会阻塞,直至所请求的事件发生为止。如果超时值设置为0,则poll将立即返回。 

如果对poll的调用成功,则返回一个正整数。这个正整数的值表示有多少个描述符发生了事件。如果超时,poll将返回0。如果有错误发生,poll则会返回-1。 

6.4 kqueue 

到目前为止,poll和select已经是相当不错的复用文件描述符的方法了。但为了使用这两个函数,你需要创建一个描述符的链表,然后把它们发送给内核,在返回的时候又要再次查看这个链表。这看上去有点效率低下。一个更好一些的模型是把描述符链表交给内核,然后就等待。一旦有某个或多个事件发生,内核就把一个只包含有发生了事件的描述符的链表通知给进程,由此避免了每次函数返回的时候都要去遍历整个链表。尽管对于只打开了几个描述符的进程而言这点改进算不得什么,但对于那些打开了几千个文件描述符的程序来说,这种性能改进就相当显著了。这就是kqueue诞生背后的主要目的。同时,设计者还希望进程能够检测更多类型的事件,比如文件修改、文件删除、信号交付或者子进程退出,并提供一个包含了其它任务的灵活的函数调用。处理信号、复用文件描述符、以及等待子进程等操作都可以封装到这个单一的kqueue接口中,因为它们都是在等待某个事件的发生。 

另一个设计考虑就是如何让一个进程毫无干扰地使用多个kqueue实例。如你所见,进程可以设置一个信号处理器,但是,当代码中的其它部分也想捕获那个指定信号的时候该怎么办?或者考虑更坏的情况,比如一个库函数对你的程序想要捕获的信号设置了信号处理器的时候?要想通过调试来找出你的程序为什么没有执行你所设置的信号处理器可能要花费几个小时的时间。不过一般说来,这些情况并不会经常发生。好的程序员应该避免在库函数中设置信号处理器。对于大型的、复杂的程序来说,这些情况就很难避免了,所以为了更完美一点,我们应当能够检测这些事件,而kqueue就可以。 

kqueue API由两个函数调用和一个辅助设置事件的宏组成。这些函数将在下面进行简要介绍。 

  int kqueue(void);


kqueue函数启动一个新的kqueue。如果调用成功,返回值将是一个用来和新创建的kqueue交互的描述符。每个kqueue都有一个与之关联的唯一的描述符。因此,一个程序可以同时打开多个kqueue。kqueue描述符的行为和常规文件描述符类似:它们也可以被复用。 

最后一点,这些描述符是不能被fork创建的子进程继承的。如果子进程是通过rfork调用创建的,那就需要设置RFFDG标志,以免这些描述符被子进程共享。如果kqueue函数失败,将返回-1,同时相应的设置errno。 

  int kevent(int kq, const struct kevent *changelist, int nchanges, struct kevent *eventlist, int nevents, const struct timespec *timeout);


kevent函数用于和kqueue的交互。第一个参数是kqueue返回的描述符。changelist参数是一个大小为nchanges的kevent结构体数组。changelist参数用于注册或修改事件,并且将在从kqueue读出事件之前得到处理。 

eventlist参数是一个大小为nevents的kevent结构体数组。kevent通过把事件放在eventlist参数中来向调用进程返回事件。如果需要的话,eventlist和changelist参数可以指向同一个数组。最后一个参数是kevent所期待的超时时间。如果超时参数被指定为NULL,kevent将阻塞,直至有事件发生为止。如果超时参数不为NULL,则kevent将阻塞到超时为止。如果超时参数指定的是一个内容为0的结构体,kevent将立即返回所有当前尚未处理的事件。 

kevent的返回值指定了放在eventlist数组中的事件的数目。如果事件数目超过了eventlist的大小,可以通过后续的kevent调用来获得它们。在处理事件的过程中发生的错误也会在还有空间的前提下被放到eventlist参数中。带有错误的事件会设置EV_ERROR位,系统错误也会被放到data成员中。对于其它的所有错误都将返回-1,并相应地设置errno。 

kevent结构体用于和kqueue的通信。FreeBSD上的头文件位于/usr/include/sys/event.h。在这个文件中有对kevent结构体的声明,以及其它的一些选项和标志。和select和poll比起来,kqueue还相当的年轻,所以它一直都在发展和添加新的特性。请查看你的系统头文件以确定任何新的或者特定于系统的选项。 

原始的kevent结构体的声明如下: 

  struct kevent {

        uintptr_t       ident;

        short           filter;

        u_short         flags;

        u_int           fflags;

        intptr_t        data;

        void            *udata;

  };


现在,让我们来看看各个成员: 

ident 

ident成员用于存储kqueue的唯一标识。换句话说,如果你想给一个事件添加一个文件描述符的话,ident成员就应当被设置成目标描述符的值。 

filter 

filter成员用于指定你希望内核用于ident成员的过滤器。 

flags 

flags成员将告诉内核应当对该事件完成哪些操作和处理哪些必要的标志。在返回的时候,flags成员可用于保存错误条件。 

fflags 

fflags成员用于指定你想让内核使用的特定于过滤器的标志。在返回的时候,fflags成员可用于保存特定于过滤器的返回值。 

data 

data成员用于保存任何特定于过滤器的数据。 

udata 

udata成员并不由kqueue使用,kqueue会把它的值不加修改地透传。这个成员可被进程用来发送信息甚至是一个函数给它自己,用于一些依赖于事件检测的场合。 

kqueue 过滤器 

下面列出的是kqueue使用的过滤器。某些过滤器会有专用于它的标志。这些标志是在kevent结构体的fflags成员中设置的。 

  #define EVFILT_READ     (-1)


EVFILT_READ过滤器用于检测什么时候数据可读。kevent的ident成员应当被设成一个有效的描述符。尽管这个过滤器的行为和select或这poll很像,但它返回的事件将是特定于所使用的描述符的类型的。 

如果描述符引用的打开文件是一个vnode,该事件就表明读取偏移量尚未到达文件末尾。data成员保存的是当前距文件末尾的偏移量,这可以是负值。如果描述符引用的是一个pipe或者fifo,那么过滤器将在有实际数据可读时返回。data成员保存的是可供读取的字节数目。EV_EOF bit用于表示是哪个写入者关闭了连接。(关于使用socket时EVFILT_READ的行为细节请参见kqueue的手册页。) 

  #define EVFILT_WRITE    (-2)


EVFILT_WRITE过滤器用于检测是否可以对描述符执行写操作。如果描述符引用的是一个pipe、fifo或者socket,则data成员将存有写缓冲区中可用的字节数目。EV_EOF bit表示读取方已经关闭了连接。这个标志对于打开的文件描述符无效。 

  #define EVFILT_AIO      (-3)


EVFILT_AIO用于异步I/O操作,用于检测和aio_error系统调用相似的条件。 

  #define EVFILT_VNODE    (-4)


EVFILT_VNODE过滤器用于检测对文件系统上一个文件的某种改动。把ident成员设置成一个有效的打开文件描述符,用fflags成员指定所关心的事件。返回时,fflags成员将含有所发生事件的比特掩码。这些事件如下: 

  #define NOTE_DELETE     0x0001


NOTE_DELETE fflag表示进程想知道该文件何时被删。 

  #define NOTE_WRITE      0x0002


NOTE_WRITE fflag表示进程想知道该文件内容何时被改变。 

  #define NOTE_EXTEND     0x0004


NOTE_EXTEND fflag表示进程想知道该文件何时被扩展。 

  #define NOTE_ATTRIB     0x0008


NOTE_ATTRIB fflag表示进程想知道该文件属性何时被改变。 

  #define NOTE_LINK       0x0010


NOTE_LINK fflag表示进程想知道该文件的链接计数何时被改变。当文件通过link函数调用进行硬链接的时候,它的链接计数就会改变。(详情请参见man(2) link。) 

  #define NOTE_RENAME     0x0020


NOTE_RENAME fflag表示进程想知道该文件是否被重新命名了。 

  #define NOTE_REVOKE     0x0040


NOTE_REVOKE fflag表示对文件的访问被revoke了。详情请见man(2) revoke。 

  #define EVFILT_PROC     (-5)  /* attached to struct proc */


EVLILT_PROC过滤器被进程用来检测发生在另外一个进程里的事件。所关心进程的PID存储在ident成员中,fflags成员则被设成所关心的事件。返回时,事件将被放在fflags成员中。这些事件由下列事件按比特OR的方式设置: 

  #define NOTE_EXIT       0x80000000


NOTE_EXIT fflag用于检测该进程何时退出。 

  #define NOTE_FORK       0x40000000


NOTE_FORK fflag用于检测该进程何时调用fork。 

  #define NOTE_EXEC       0x20000000


NOTE_EXEC fflag用于检测该进程何时调用exec函数。 

  #define NOTE_TRACK      0x00000001


NOTE_TRACK fflag让kqueue去跟踪一个跨越fork调用的进程。子进程返回时将设置fflags中的NOTE_CHILD标志,父进程的PID将放在data成员中。 

  #define NOTE_TRACKERR   0x00000002


当在跟踪子进程的过程中有错误发生时,就会设置NOTE_TRACKERR fflag。这是一个仅用于返回的fflag。 

  #define NOTE_CHILD      0x00000004


NOTE_CHILD fflag在子进程内设置。这是一个仅用于返回的fflag。 

  #define EVFILT_SIGNAL      (-6)


EVFILT_SIGNAL过滤器用于检测是否有信号发送给该进程。每当有信号发送时这个过滤器就会检测到,并把计数值放在data成员中。这包括设置了SIG_IGN标志的信号。事件将在执行完常规的信号处理过程之后放到kqueue上。注意,这个过滤器将在内部设置EV_CLEAR标志。 

  #define EVFILT_TIMER    (-7)


EVFILT_TIMER过滤器会给kqueue创建一个定时器,用于记录消逝的事件。如果需要一个一次性的定时器,可以设置EV_ONESHOT标志。这个定时器是在ident成员中指定的,data成员用来指定以毫秒为单位的超时时间。返回值放在data成员中。注意,这个过滤器将在内部设置EV_CLEAR标志。 

kqueue操作 

kqueue操作由所需的操作和标志以比特OR的方式进行设置。 

  #define EV_ADD          0x0001


EV_ADD操作向kqueue添加事件。由于kqueue中不允许出现重复,所以如果你想添加一个已经存在的事件的话,现有事件将被新的添加操作覆盖。注意,在添加事件的时候,它们已经被默认激活了,除非你设置了EV_DISABLE标志。 

  #define EV_DELETE       0x0002


EV_DELETE操作从kqueue中删除事件。 

  #define EV_ENABLE       0x0004


EV_ENABLE用于激活kqueue中的事件。注意,新添加的事件默认就是激活的。 

  #define EV_DISABLE      0x0008


EV_DISABLE禁止kqueue返回某个事件的信息。注意,kqueue并不会删除过滤器。 

kqueue操作标志 

kqueue的操作标志定义如下。它们和上面列出的操作结合使用。它们是通过和所需操作进行比特OR来设置的。 

  #define EV_ONESHOT      0x0010


EV_ONESHOT标志用于通知kqueue只返回第一个。 

  #define EV_CLEAR        0x0020


EV_CLEAR标志用于通知kqueue,一旦进程从kqueue中获取到了该事件就将该事件的状态复位。 

kqueue返回值 

仅用于返回的值是放在kevent结构体的flags成员中的。这些值的定义如下: 

  #define EV_EOF          0x8000


EV_EOF用于表示文件结束的情况。 

  #define EV_ERROR        0x4000


EV_ERROR用于表示有错误发生了。系统错误将被放到data成员中。 

6.5 结论 

本章研究了BSD中的描述符复用。作为一个程序员,你可以选择三个接口:select、poll和kqueue。对于小数量的描述符来说,这三者的性能差不多,但是当描述符数量很大时,kqueue则是最好的选择。除此之外,kqueue还可以检测比I/O事件更为丰富的条件。它可以检测信号、文件修改以及子进程相关的事件。在下一章中,我们将针对FreeBSD 5.x中的新特性,研究其它的获取子进程信息和当前进程统计信息的方法。 



                 第七章 进程资源和系统限制 
                  译者:[email=finalbsd@hotmail.com]FinalBSD[/email] 
7.1 进程资源和系统限制 
  为了支持多用户同时登录以及多个应用连接,BSD UNIX系统给系统管理员提供了控制系统资源的许多方法。这种资源限制包括CPU时间、内存使用量以及磁盘使用量。资源控制允许你调整系统到最佳的使用率。UNIX的早期版本中,一些在编译时设置的系统限制如果需要修改,则需要重新编译整个系统。然而,如果并非所有的运行中的系统资源都需要重新编译整个系统,那么现代的BSD系统可以调整大多数这些资源的限制。 

  本章阐述和进程相关的限制,包括系统端和用户使用的。我们将会看到如何发现这些限制以及怎么修改之,还将阐述进程是如何查询它的资源使用率。 


7.2  确定系统限制 
getrlimit,setrlimit 

getrlimit允许一个进程查询所受的的系统限制.这些系统限制通过一对硬/软限制对来指定。当一个软限制被超过时,进程还可以继续,当然这取决于限制的类型,同时一个信号会发送给进程。另一方面,进程不可以超过它的硬限制。软限制值可以被进程设置在位于0和最大硬限制间的任意值。硬限制值不能被任何进程降低,仅仅超级用户可以增加之。 

#include <sys/types.h>

   #include <sys/time.h>

   #include <sys/resource.h>



    int  getrlimit(int resource, struct rlimit *rlp);

    int  setrlimit(int resource, const struct rlimit *rlp);


getrlimit和setrlimit都使用下面的数据结构: 
struct rlimit {

  rlim_t rlim_cur;

  rlim_t rlim_max;

 };

我们来看每个成员变量。rlim_cur为指定的资源指定当前的系统软限制。rlim_max将为指定的资源指定当前的系统硬限制。 

getrlimit和setrlimit函数的第一个参数是资源参数。这个参数用来指定进程获取信息的那个资源。可能的资源值列于下面。你也可以在/usr/include/sys/resource.h中找到它们: 
#define  RLIMIT_CPU  0     /* cpu time in milliseconds */

RLIMIT_CPU资源限制指定一个进程可以取得CPU执行任务的毫秒数。一般地,一个进程仅仅有一个软限制而没有硬限制。如果超出软限制,进程会收到一个SIGXCPU信号。 
  #define  RLIMIT_FSIZE   1     /* maximum file size */

RLIMIT_FSIZE限制指定一个进程可以创建的最大文件大小,以字节为单位。比如,如果RLIMIT_FSIZE设置为0,那么进程将根本不能创建文件。如果进程超出此限制,就会发出SIGFSZ信号。 
#define  RLIMIT_DATA 2     /* data size */

RLIMIT_DATA 限制指定一个进程数据段可占据的最大字节值。一个进程的数据段就是放置动态内存的一个区域(C/C++中用malloc()分配的内存)。如果超出限制,分配新内存的操作将会遭到失败。 
#define  RLIMIT_STACK   3     /* stack size */

RLIMIT_STACK限制指定进程栈可占据的最大字节数。一旦超出硬限制,进程会收到SIGSEV信号。 
#define  RLIMIT_CORE 4     /* core file size */

RLIMIT_CORE限制指定了进程可以创建的最大core文件的大小。如果此限制设为0,将不能创建。另外,当达到此限制时,所有正在写core文件的进程都将被中断。 

#define  RLIMIT_RSS  5     /* resident set size */

RMIMIT_RSS限制了进程的常驻集大小(resident set size)可占据的最大字节数.这个进程的常驻集和进程所使用的物理内存数有关。 
  #define  RLIMIT_MEMLOCK 6     /* locked-in-memory address space */

RLIMIT_MEMLOCK限制指定了进程可以使用系统调用到mlock进行锁定的最大字节数。  
  #define  RLIMIT_NPROC   7     /* number of processes */

RLIMIT_NPROC 限制指定了一个指定用户可以开启的最多并发进程数。这里的用户是通过进程来确定的有效用户ID.  
  
#define  RLIMIT_NOFILE  8     /* number of open files */

RLIMIT_NOFILE 限制指定了进程可以打开的最多文件数。  
  #define  RLIMIT_SBSIZE  9     /* maximum size of all socket buffers */

RLIMIT_SBSIZE限制指定用户在任何时刻可使用的mbufs数。可以查看socket man页来获得mbufs的定义。  
 
 #define  RLIMIT_VMEM 10    /* virtual process size (inclusive of mmap) */

RLIMIT_VMEM限制说明一个进程的映射地址空间可以占据的字节数。如果超出限制,分配动态内存和到mmap的调用会失败。  
  
#define  RLIM_INFINITY

RLIM_INFINITY宏用来去除对一个资源的限制。换句话说,将一个资源的硬限制设置为RLIM_INFINITY将会导致此种资源的使用没有任何系统限制。 将软限制设置为RLIM_INFINITY将会阻止进程收到任何软限制警告。如果你的进程不想为那些会导致进程在超过软限制时发送信号的资源设置一个信号处理器,这个参数将变得非常有用。  
如果使用了getrlimit参数,那么第二个参数需要设置为一个到rlimit结构的有效指针。然后getrlimit会将适当的限制值放入此结构。另外,在改变限制时,setrlimit会使用在第二个参数中设置值。将值设置为0将会阻止使用此资源。将值设置为RLIM_INFINITY会除去对该资源的所有限制。这些函数都在执行成功后都返回0,反之为-1.有任何错误产生,这些函数会相应的设置errno。  

getpagesize函数 
#include <unistd.h>    

int  getpagesize(void);


  在介绍getrusage函数前,我们需要讨论一下getpagesize函数。一些进程状态 是根据使用的分页(pages)来显示的。分页的内存仅仅是一段内存,通常为4096字节左右。但是分页大小却千差万别,并且它不会固定编入(hard-coded)你的系统。取而代之的是,要确定本地系统的分页大小(pagesize)需要使用getpagesezi函数。getpagesize的返回值就是每个分页使用的字节数。  

7.3 确定进程资源使用量 

getrusage函数   
   现在我们知道如何查看系统限制,我们还需要知道如何确定当前进程资源的使用量。getrusage函数就是用于此目的。此函数很容易被理解。一个进程可以确定它的内存使用量、CPU执行时间、甚至可获得其子进程的相关信息。因此,getrusage函数的一个用途就是帮助系统避免逃逸进程(runaway)的出现。逃逸进程指的是那些不受系统控制的进程,它们或者使用了过多的CPU(比如循环调用)、或者是超过了内存使用量的限制(导致内存泄漏)。 

  #include <sys/types.h>

  #include <sys/time.h>

  #include <sys/resource.h>

  

  #define   RUSAGE_SELF     0

  #define   RUSAGE_CHILDREN     -1

  

    int   getrusage(int who, struct rusage *rusage);


      getrusage函数有两个参数。第一个参数可以设置为RUSAGE_SELF或者RUSAGE_CHILDREN。如果设置成RUSAGE_SELF,那么将会以当前进程的相关信息来填充rusage(数据)结构。反之,如果设置成RUSAGE_CHILDREN,那么rusage结构中的数据都将是当前进程的子进程的信息。 

    rusage(数据)结构定义在/usr/include/sys/resource.h中。它含有以下成员变量: 
struct   rusage {

      struct timeval ru_utime;   /* user time used */

      struct timeval ru_stime;   /* system time used */

      long  ru_maxrss;           /* max resident set size */

      long  ru_ixrss;            /* integral shared memory size */

      long  ru_idrss;            /* integral unshared data */

      long  ru_isrss;            /* integral unshared stack */

      long  ru_minflt;           /* page reclaims */

      long  ru_majflt;           /* page faults */

      long  ru_nswap;            /* swaps */

      long  ru_inblock;          /* block input operations */

      long  ru_oublock;          /* block output operations */

      long  ru_msgsnd;           /* messages sent */

      long  ru_msgrcv;           /* messages received */

      long  ru_nsignals;         /* signals received */

      long  ru_nvcsw;            /* voluntary context switches */

      long  ru_nivcsw;           /* involuntary " */

  };
       我们来详细分析每一个成员变量。 
ru_utime,ru_stime  
ru_utime和ru_stime成员变量包含了在用户模式和系统模式中执行时间的总和。它们都使用timeval结构(请查看前一章来了解此结构。) 

ru_maxrss  
ru_maxrss保存了常驻集的内存使用数。其值根据内存分页的使用来确定。  

ru_ixrss  
ru_ixrss值指文本段(text segment)使用的内存数乘以执行滴答数。  

ru_idrss  
ru_idrss 值指进程所使用的私有内存数(KB)乘以执行滴答数来。  

ru_isrss  
ru_isrss 指栈使用的内存数(KB为单位)乘以执行滴答数。 

ru_minflt  
ru_minflt值指不需要I/O的页缺失数。页缺失发生在内核需要得到一个内存页以供进程访问时。  

ru_majflt  
ru_majflt值指需要I/O的页缺失数。页缺失发生在内核需要得到一个内存页以供进程访问时。  

ru_nswap  
有时,一个进程会被调出内存,以提供空间给其他进程使用。ru_nswap指的就是一个进程将要调出内存的次数。 

ru_inblock  
ru_inblock 指文件系统需要为一个读请求执行输入操作的次数。  

ru_oublock  
ru_oublock指文件系统需要为一个写入请求执行输出操作的次数。  

ru_msgsnd  
ru_msgsnd指发送的IPC信息总数 

ru_msgrcv  
ru_msgrcv指收到的IPC信息总数。 

ru_nsignals  
ru_nsignals指进程收到的信号总数。 
  
ru_nvcsw  
一个进程主动上下文且混总数。主动上下文切换发生在一个进程放弃它的CPU分时时。通常发生在一个进程等待某可用资源时。 

ru_nivcsw  
ru_nivcsw包含了因高优先级进程运行导致的上下文切换总数。 

7.4 小结 
本章主要阐述了程序如何得到系统限制。这些限制不应该固定写入你的代码。取而代之的是,你的程序应该使用这些接口。这是因为这些限制是与平台相关的,甚至不同系统间都会有差别。比如,系统管理员可以调整允许打开的最大文件数,因此如果你需要增加或者减小这个值,就不能将值4096固定写入程序中。另一个例子是分页大小。一些64位系统使用8096作为默认的分页大小,但是大多数的32位系统将此默认值设置位4096。再次强调,这是个可调整的参数,不应该使用一个固定的值。 
我们还学习了一个程序如何得到其资源的当前使用量,或者查看其子进程的资源使用状况。使用这些接口可以帮助我们检测甚至避免出现失控的进程。它们用在程序试图超越其软/硬限制时调试错误也非常不错。  



                   第八章 FreeBSD 5.x 
                                                 译者:[email=finalbsd@hotmail.com]FinalBSD[/email] 

8.1  FreeBSD 5.x 
  2003年1月发布的FreeBSD-5.x 分支,是FreeBSD项目的一个重要的里程碑。在近3年的开发中,FreeBSD不管是在内核还是基本系统上都有了许多的变化。大部分的这些变化会影响到系统管理员,FreeBSD 编程者则不会,因此也不会影响到此书中讨论的任何一部分。一些例外将在下面进行阐述。 

8.2  启动布局(Boot Layout) 
  第一个变化是启动文件的组织方式。FreeBSD 5.x系统已将所有的模块和内核文件都移到/boot目录下。和老版本的FreeBSD一样,一些和系统启动相关的配置文件也位于此目录下。这个看起来很小的变化实际上提供了更多的便利,因为现在,在不同的设备间分离/和/boot(包含所有的内核和内核模块)十分容易。 

8.3  Devfs 
  FreeBSD 5.x中,我们最喜欢的特性是devfs.之前的版本,/dev被塞满了超过1000个文件。设备节点和大多数支持的设备都有一项作为文件保存在该目录下。如你所想,这个目录变得非常的大并且包含了许多不必要的文件。比如,一个有IDE设备的系统会在此目录下包含SCSI设备文件,哪怕是该系统没有任何SCSI设备。devfs中这一切弊病都之前将不复存在. 
现在/dev下仅仅包含那些真实存在的设备项。实际上,FreeBSD 5.x并不和之前版本一样将/dev当作到文件系统的一个挂载点,并称之为devfs。 

  devfs文件系统和proc文件系统相近。二者都是挂载点,该挂载点包含在硬盘上不存在的文件。这些文件由内核创建并且仅以文件方式出现。事实上,它们是在系统启动后被建立的。Devfs提供了更多的便利性,因为你能支持多个devfs挂载点。比如,如果你想chroot或者jail一个进程,你不需要手动创建/dev目录,取而代之的是,你可以简单的为你的进程创建一个新的挂载点并挂载devfs. 

  devfs的另一个优点是可以告诉你系统真实存在哪些设备。你要做的仅仅是cd到/dev目录然后列出那些文件。这就很方便用户得到系统上所有的设备列表,更重要的是,检测到哪些设备。 

8.4  a.out 
  FreeBSD 5.x系列已经在基本系统中去除了对a.out二进制格式的支持。但是你仍然可以加入a.out二进制支持。这是因为a.out是种相当老的格式,并且现在都会优先选择新的ELF格式。ELF格式更灵活而且目前被广泛使用。 

8.5  gcc-3.2工具链 
  FreeBSD 5.x 现在使用gcc-3.2 工具链作为基本系统。这是个很重要的改变:gcc-3.x更接近ISO,并且它的C++ ABI更稳定。然而,这或许会给一些人带来麻烦。他们编写的一些程序在使用gcc-3.x进行编译前也许需要进行更新。如果你使用flex或者yacc,请确定你使用的是最新的版本,或者为你当前的版本打好补丁,因为已经确认知道他们会导致问题出现。 

8.6  SMPng 
  FreeBSD 5.x 已经改进为支持SMP的系统了,这一改进都来自于我们常说的SMPng(下一代SMP).尽管之前版本也支持SMP,但是性能有待提高。 

8.7 内核调度实体(KSE) 
  另一个新的特性是内核调度实体(KSE).KSE是个内核支持的线程系统,和Scheduler Activations在概念概念上很接近。特别的,在内核端,KSE在于对FreeBSD的调度的修改。并且在用户端使用的是POSIX线程实现方式,这种方式会利用内核提供的额外工具。然而,你不需要配置任何特殊的内核参数,就可以编译得到一个具有KSE相关修改的内核。 

  为了在应用程序中使用KSE,你可以使用libpthreads来链入之。libpthreads默认并没编译进系统,所以你首先需要在系统上安装好libpthreads。然后,在它的makefile中,将-pthread选项改为-lpthread并重新链入(relink). 

8.8 小结 
  FreeBSD在几年的发展中已经变得成熟,并且现在是一个可用的非常稳定的操作系统了。有了SMP的增强支持和内核线程,FreeBSD会一如既往的提供强稳定性和高性能。已经加入了对一些新平台的支持,比如Sparc64和ia64.这些新的平台会帮助BSD发行版多年来继续提供高质量的开源选择。 

Tags:freebsd 系统 编程
关于开源中文网 - 联系我们 - 广告服务 - 网站地图 - 版权声明