LINUX DEVICE DRIVERS 3rd edition phần 9 ppt

64 460 0
LINUX DEVICE DRIVERS 3rd edition phần 9 ppt

Đang tải... (xem toàn văn)

Tài liệu hạn chế xem trước, để xem đầy đủ mời bạn chọn Tải xuống

Thông tin tài liệu

This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. 494 | Chapter 16: Block Drivers Quick Reference #include <linux/fs.h> int register_blkdev(unsigned int major, const char *name); int unregister_blkdev(unsigned int major, const char *name); register_blkdev registers a block driver with the kernel and, optionally, obtains a major number. A driver can be unregistered with unregister_blkdev. struct block_device_operations Structure that holds most of the methods for block drivers. #include <linux/genhd.h> struct gendisk; Structure that describes a single block device within the kernel. struct gendisk *alloc_disk(int minors); void add_disk(struct gendisk *gd); Functions that allocate gendisk structures and return them to the system. void set_capacity(struct gendisk *gd, sector_t sectors); Stores the capacity of the device (in 512-byte sectors) within the gendisk structure. void add_disk(struct gendisk *gd); Adds a disk to the kernel. As soon as this function is called, your disk’s methods can be invoked by the kernel. int check_disk_change(struct block_device *bdev); A kernel function that checks for a media change in the given disk drive and takes the required cleanup action when such a change is detected. #include <linux/blkdev.h> request_queue_t blk_init_queue(request_fn_proc *request, spinlock_t *lock); void blk_cleanup_queue(request_queue_t *); Functions that handle the creation and deletion of block request queues. struct request *elv_next_request(request_queue_t *queue); void end_request(struct request *req, int success); elv_next_request obtains the next request from a request queue; end_request may be used in very simple drivers to mark the completion of (or part of) a request. void blkdev_dequeue_request(struct request *req); void elv_requeue_request(request_queue_t *queue, struct request *req); Functions that remove a request from a queue and put it back on if necessary. void blk_stop_queue(request_queue_t *queue); void blk_start_queue(request_queue_t *queue); If you need to prevent further calls to your request method, a call to blk_stop_queue does the trick. A call to blk_start_queue is necessary to cause your request method to be invoked again. ,ch16.28124 Page 494 Friday, January 21, 2005 9:09 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. Quick Reference | 495 void blk_queue_bounce_limit(request_queue_t *queue, u64 dma_addr); void blk_queue_max_sectors(request_queue_t *queue, unsigned short max); void blk_queue_max_phys_segments(request_queue_t *queue, unsigned short max); void blk_queue_max_hw_segments(request_queue_t *queue, unsigned short max); void blk_queue_max_segment_size(request_queue_t *queue, unsigned int max); blk_queue_segment_boundary(request_queue_t *queue, unsigned long mask); void blk_queue_dma_alignment(request_queue_t *queue, int mask); void blk_queue_hardsect_size(request_queue_t *queue, unsigned short max); Functions that set various queue parameters that control how requests are cre- ated for a particular device; the parameters are described in the section “Queue control functions.” #include <linux/bio.h> struct bio; Low-level structure representing a portion of a block I/O request. bio_sectors(struct bio *bio); bio_data_dir(struct bio *bio); Two macros that yield the size and direction of a transfer described by a bio structure. bio_for_each_segment(bvec, bio, segno); A pseudocontrol structure used to loop through the segments that make up a bio structure. char *__bio_kmap_atomic(struct bio *bio, int i, enum km_type type); void __bio_kunmap_atomic(char *buffer, enum km_type type); __bio_kmap_atomic may be used to create a kernel virtual address for a given segment within a bio structure. The mapping must be undone with __bio_ kunmap_atomic. struct page *bio_page(struct bio *bio); int bio_offset(struct bio *bio); int bio_cur_sectors(struct bio *bio); char *bio_data(struct bio *bio); char *bio_kmap_irq(struct bio *bio, unsigned long *flags); void bio_kunmap_irq(char *buffer, unsigned long *flags); A set of accessor macros that provide access to the “current” segment within a bio structure. void blk_queue_ordered(request_queue_t *queue, int flag); int blk_barrier_rq(struct request *req); Call blk_queue_ordered if your driver implements barrier requests—as it should. The macro blk_barrier_rq returns a nonzero value if the current request is a bar- rier request. ,ch16.28124 Page 495 Friday, January 21, 2005 9:09 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. 496 | Chapter 16: Block Drivers int blk_noretry_request(struct request *req); This macro returns a nonzero value if the given request should not be retried on errors. int end_that_request_first(struct request *req, int success, int count); void end_that_request_last(struct request *req); Use end_that_request_first to indicate completion of a portion of a block I/O request. When that function returns 0, the request is complete and should be passed to end_that_request_last. rq_for_each_bio(bio, request) Another macro-implemented control structure; it steps through each bio that makes up a request. int blk_rq_map_sg(request_queue_t *queue, struct request *req, struct scatterlist *list); Fills the given scatterlist with the information needed to map the buffers in the given request for a DMA transfer. typedef int (make_request_fn) (request_queue_t *q, struct bio *bio); The prototype for the make_request function. void bio_endio(struct bio *bio, unsigned int bytes, int error); Signal completion for a given bio. This function should be used only if your driver obtained the bio directly from the block layer via the make_request function. request_queue_t *blk_alloc_queue(int flags); void blk_queue_make_request(request_queue_t *queue, make_request_fn *func); Use blk_alloc_queue to allocate a request queue that is used with a custom make_request function. That function should be set with blk_queue_make_ request. typedef int (prep_rq_fn) (request_queue_t *queue, struct request *req); void blk_queue_prep_rq(request_queue_t *queue, prep_rq_fn *func); The prototype and setup functions for a command preparation function, which can be used to prepare the necessary hardware command before the request is passed to your request function. int blk_queue_init_tags(request_queue_t *queue, int depth, struct blk_queue_tag *tags); int blk_queue_resize_tags(request_queue_t *queue, int new_depth); int blk_queue_start_tag(request_queue_t *queue, struct request *req); void blk_queue_end_tag(request_queue_t *queue, struct request *req); struct request *blk_queue_find_tag(request_queue_t *qeue, int tag); void blk_queue_invalidate_tags(request_queue_t *queue); Support functions for drivers using tagged command queueing. ,ch16.28124 Page 496 Friday, January 21, 2005 9:09 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. 497 Chapter 17 CHAPTER 17 Network Drivers Having discussed char and block drivers, we are now ready to move on to the world of networking. Network interfaces are the third standard class of Linux devices, and this chapter describes how they interact with the rest of the kernel. The role of a network interface within the system is similar to that of a mounted block device. A block device registers its disks and methods with the kernel, and then “transmits” and “receives” blocks on request, by means of its request function. Similarly, a network interface must register itself within specific kernel data struc- tures in order to be invoked when packets are exchanged with the outside world. There are a few important differences between mounted disks and packet-delivery interfaces. To begin with, a disk exists as a special file in the /dev directory, whereas a network interface has no such entry point. The normal file operations (read, write, and so on) do not make sense when applied to network interfaces, so it is not possi- ble to apply the Unix “everything is a file” approach to them. Thus, network inter- faces exist in their own namespace and export a different set of operations. Although you may object that applications use the read and write system calls when using sockets, those calls act on a software object that is distinct from the interface. Several hundred sockets can be multiplexed on the same physical interface. But the most important difference between the two is that block drivers operate only in response to requests from the kernel, whereas network drivers receive packets asynchronously from the outside. Thus, while a block driver is asked to send a buffer toward the kernel, the network device asks to push incoming packets toward the ker- nel. The kernel interface for network drivers is designed for this different mode of operation. Network drivers also have to be prepared to support a number of administrative tasks, such as setting addresses, modifying transmission parameters, and maintain- ing traffic and error statistics. The API for network drivers reflects this need and, therefore, looks somewhat different from the interfaces we have seen so far. ,ch17.13860 Page 497 Friday, January 21, 2005 11:10 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. 498 | Chapter 17: Network Drivers The network subsystem of the Linux kernel is designed to be completely protocol- independent. This applies to both networking protocols (Internet protocol [IP] versus IPX or other protocols) and hardware protocols (Ethernet versus token ring, etc.). Interaction between a network driver and the kernel properly deals with one network packet at a time; this allows protocol issues to be hidden neatly from the driver and the physical transmission to be hidden from the protocol. This chapter describes how the network interfaces fit in with the rest of the Linux kernel and provides examples in the form of a memory-based modularized network interface, which is called (you guessed it) snull. To simplify the discussion, the inter- face uses the Ethernet hardware protocol and transmits IP packets. The knowledge you acquire from examining snull can be readily applied to protocols other than IP, and writing a non-Ethernet driver is different only in tiny details related to the actual network protocol. This chapter doesn’t talk about IP numbering schemes, network protocols, or other general networking concepts. Such topics are not (usually) of concern to the driver writer, and it’s impossible to offer a satisfactory overview of networking technology in less than a few hundred pages. The interested reader is urged to refer to other books describing networking issues. One note on terminology is called for before getting into network devices. The net- working world uses the term octet to refer to a group of eight bits, which is generally the smallest unit understood by networking devices and protocols. The term byte is almost never encountered in this context. In keeping with standard usage, we will use octet when talking about networking devices. The term “header” also merits a quick mention. A header is a set of bytes (err, octets) prepended to a packet as it is passed through the various layers of the networking subsystem. When an application sends a block of data through a TCP socket, the networking subsystem breaks that data up into packets and puts a TCP header, describing where each packet fits within the stream, at the beginning. The lower lev- els then put an IP header, used to route the packet to its destination, in front of the TCP header. If the packet moves over an Ethernet-like medium, an Ethernet header, interpreted by the hardware, goes in front of the rest. Network drivers need not con- cern themselves with higher-level headers (usually), but they often must be involved in the creation of the hardware-level header. How snull Is Designed This section discusses the design concepts that led to the snull network interface. Although this information might appear to be of marginal use, failing to understand it might lead to problems when you play with the sample code. The first, and most important, design decision was that the sample interfaces should remain independent of real hardware, just like most of the sample code used in this ,ch17.13860 Page 498 Friday, January 21, 2005 11:10 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. How snull Is Designed | 499 book. This constraint led to something that resembles the loopback interface. snull is not a loopback interface; however, it simulates conversations with real remote hosts in order to better demonstrate the task of writing a network driver. The Linux loop- back driver is actually quite simple; it can be found in drivers/net/loopback.c. Another feature of snull is that it supports only IP traffic. This is a consequence of the internal workings of the interface—snull has to look inside and interpret the packets to properly emulate a pair of hardware interfaces. Real interfaces don’t depend on the protocol being transmitted, and this limitation of snull doesn’t affect the frag- ments of code shown in this chapter. Assigning IP Numbers The snull module creates two interfaces. These interfaces are different from a simple loopback, in that whatever you transmit through one of the interfaces loops back to the other one, not to itself. It looks like you have two external links, but actually your computer is replying to itself. Unfortunately, this effect can’t be accomplished through IP number assignments alone, because the kernel wouldn’t send out a packet through interface A that was directed to its own interface B. Instead, it would use the loopback channel without passing through snull. To be able to establish a communication through the snull interfaces, the source and destination addresses need to be modified during data transmission. In other words, packets sent through one of the interfaces should be received by the other, but the receiver of the outgoing packet shouldn’t be recog- nized as the local host. The same applies to the source address of received packets. To achieve this kind of “hidden loopback,” the snull interface toggles the least signif- icant bit of the third octet of both the source and destination addresses; that is, it changes both the network number and the host number of class C IP numbers. The net effect is that packets sent to network A (connected to sn0, the first interface) appear on the sn1 interface as packets belonging to network B. To avoid dealing with too many numbers, let’s assign symbolic names to the IP num- bers involved: • snullnet0 is the network that is connected to the sn0 interface. Similarly, snullnet1 is the network connected to sn1. The addresses of these networks should differ only in the least significant bit of the third octet. These networks must have 24-bit netmasks. • local0 is the IP address assigned to the sn0 interface; it belongs to snullnet0. The address associated with sn1 is local1. local0 and local1 must differ in the least significant bit of their third octet and in the fourth octet. • remote0 is a host in snullnet0, and its fourth octet is the same as that of local1. Any packet sent to remote0 reaches local1 after its network address has been ,ch17.13860 Page 499 Friday, January 21, 2005 11:10 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. 500 | Chapter 17: Network Drivers modified by the interface code. The host remote1 belongs to snullnet1, and its fourth octet is the same as that of local0. The operation of the snull interfaces is depicted in Figure 17-1, in which the host- name associated with each interface is printed near the interface name. Here are possible values for the network numbers. Once you put these lines in /etc/ networks, you can call your networks by name. The values were chosen from the range of numbers reserved for private use. snullnet0 192.168.0.0 snullnet1 192.168.1.0 The following are possible host numbers to put into /etc/hosts: 192.168.0.1 local0 192.168.0.2 remote0 192.168.1.2 local1 192.168.1.1 remote1 The important feature of these numbers is that the host portion of local0 is the same as that of remote1, and the host portion of local1 is the same as that of remote0. You can use completely different numbers as long as this relationship applies. Be careful, however, if your computer is already connected to a network. The num- bers you choose might be real Internet or intranet numbers, and assigning them to your interfaces prevents communication with the real hosts. For example, although Figure 17-1. How a host sees its interfaces localnet lo localhost eth0 morgana sn0 local0 sn1 local1 snullnet0 remote0 snullnet1 remote1 ,ch17.13860 Page 500 Friday, January 21, 2005 11:10 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. How snull Is Designed | 501 the numbers just shown are not routable Internet numbers, they could already be used by your private network. Whatever numbers you choose, you can correctly set up the interfaces for operation by issuing the following commands: ifconfig sn0 local0 ifconfig sn1 local1 You may need to add the netmask 255.255.255.0 parameter if the address range cho- sen is not a class C range. At this point, the “remote” end of the interface can be reached. The following screen- dump shows how a host reaches remote0 and remote1 through the snull interface: morgana% ping -c 2 remote0 64 bytes from 192.168.0.99: icmp_seq=0 ttl=64 time=1.6 ms 64 bytes from 192.168.0.99: icmp_seq=1 ttl=64 time=0.9 ms 2 packets transmitted, 2 packets received, 0% packet loss morgana% ping -c 2 remote1 64 bytes from 192.168.1.88: icmp_seq=0 ttl=64 time=1.8 ms 64 bytes from 192.168.1.88: icmp_seq=1 ttl=64 time=0.9 ms 2 packets transmitted, 2 packets received, 0% packet loss Note that you won’t be able to reach any other “host” belonging to the two net- works, because the packets are discarded by your computer after the address has been modified and the packet has been received. For example, a packet aimed at 192.168.0.32 will leave through sn0 and reappear at sn1 with a destination address of 192.168.1.32, which is not a local address for the host computer. The Physical Transport of Packets As far as data transport is concerned, the snull interfaces belong to the Ethernet class. snull emulates Ethernet because the vast majority of existing networks—at least the segments that a workstation connects to—are based on Ethernet technology, be it 10base-T, 100base-T, or Gigabit. Additionally, the kernel offers some generalized support for Ethernet devices, and there’s no reason not to use it. The advantage of being an Ethernet device is so strong that even the plip interface (the interface that uses the printer ports) declares itself as an Ethernet device. The last advantage of using the Ethernet setup for snull is that you can run tcpdump on the interface to see the packets go by. Watching the interfaces with tcpdump can be a useful way to see how the two interfaces work. As was mentioned previously, snull works only with IP packets. This limitation is a result of the fact that snull snoops in the packets and even modifies them, in order for the code to work. The code modifies the source, destination, and checksum in the IP header of each packet without checking whether it actually conveys IP information. ,ch17.13860 Page 501 Friday, January 21, 2005 11:10 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. 502 | Chapter 17: Network Drivers This quick-and-dirty data modification destroys non-IP packets. If you want to deliver other protocols through snull, you must modify the module’s source code. Connecting to the Kernel We start looking at the structure of network drivers by dissecting the snull source. Keeping the source code for several drivers handy might help you follow the discus- sion and to see how real-world Linux network drivers operate. As a place to start, we suggest loopback.c, plip.c, and e100.c, in order of increasing complexity. All these files live in drivers/net, within the kernel source tree. Device Registration When a driver module is loaded into a running kernel, it requests resources and offers facilities; there’s nothing new in that. And there’s also nothing new in the way resources are requested. The driver should probe for its device and its hardware loca- tion (I/O ports and IRQ line)—but not register them—as described in “Installing an Interrupt Handler” in Chapter 10. The way a network driver is registered by its mod- ule initialization function is different from char and block drivers. Since there is no equivalent of major and minor numbers for network interfaces, a network driver does not request such a number. Instead, the driver inserts a data structure for each newly detected interface into a global list of network devices. Each interface is described by a struct net_device item, which is defined in <linux/ netdevice.h>. The snull driver keeps pointers to two of these structures (for sn0 and sn1) in a simple array: struct net_device *snull_devs[2]; The net_device structure, like many other kernel structures, contains a kobject and is, therefore, reference-counted and exported via sysfs. As with other such struc- tures, it must be allocated dynamically. The kernel function provided to perform this allocation is alloc_netdev, which has the following prototype: struct net_device *alloc_netdev(int sizeof_priv, const char *name, void (*setup)(struct net_device *)); Here, sizeof_priv is the size of the driver’s “private data” area; with network devices, that area is allocated along with the net_device structure. In fact, the two are allo- cated together in one large chunk of memory, but driver authors should pretend that they don’t know that. name is the name of this interface, as is seen by user space; this name can have a printf-style %d in it. The kernel replaces the %d with the next available interface number. Finally, setup is a pointer to an initialization function that is called to set up the rest of the net_device structure. We get to the initialization function ,ch17.13860 Page 502 Friday, January 21, 2005 11:10 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. Connecting to the Kernel | 503 shortly, but, for now, suffice it to say that snull allocates its two device structures in this way: snull_devs[0] = alloc_netdev(sizeof(struct snull_priv), "sn%d", snull_init); snull_devs[1] = alloc_netdev(sizeof(struct snull_priv), "sn%d", snull_init); if (snull_devs[0] = = NULL || snull_devs[1] = = NULL) goto out; As always, we must check the return value to ensure that the allocation succeeded. The networking subsystem provides a number of helper functions wrapped around alloc_netdev for various types of interfaces. The most common is alloc_etherdev, which is defined in <linux/etherdevice.h>: struct net_device *alloc_etherdev(int sizeof_priv); This function allocates a network device using eth%d for the name argument. It pro- vides its own initialization function (ether_setup) that sets several net_device fields with appropriate values for Ethernet devices. Thus, there is no driver-supplied initial- ization function for alloc_etherdev; the driver should simply do its required initializa- tion directly after a successful allocation. Writers of drivers for other types of devices may want to take advantage of one of the other helper functions, such as alloc_fcdev (defined in <linux/fcdevice.h>) for fiber-channel devices, alloc_fddidev (<linux/ fddidevice.h>) for FDDI devices, or alloc_trdev (<linux/trdevice.h>) for token ring devices. snull could use alloc_etherdev without trouble; we chose to use alloc_netdev instead, as a way of demonstrating the lower-level interface and to give us control over the name assigned to the interface. Once the net_device structure has been initialized, completing the process is just a matter of passing the structure to register_netdev. In snull, the call looks as follows: for (i = 0; i < 2; i++) if ((result = register_netdev(snull_devs[i]))) printk("snull: error %i registering device \"%s\"\n", result, snull_devs[i]->name); The usual cautions apply here: as soon as you call register_netdev, your driver may be called to operate on the device. Thus, you should not register the device until every- thing has been completely initialized. Initializing Each Device We have looked at the allocation and registration of net_device structures, but we passed over the intermediate step of completely initializing that structure. Note that struct net_device is always put together at runtime; it cannot be set up at compile time in the same manner as a file_operations or block_device_operations structure. This initialization must be complete before calling register_netdev. The net_device ,ch17.13860 Page 503 Friday, January 21, 2005 11:10 AM [...]... on multiport devices This field is used, for example, with devices that support both coaxial (IF_PORT_10BASE2) and twisted-pair (IF_PORT_100BASET) Ethernet connections The full set of known port types is defined in unsigned char dma; The DMA channel allocated by the device The field makes sense only with some peripheral buses, such as ISA It is not used outside of the device driver... are device specific and must be explicitly assigned at initialization time Some non-Ethernet interfaces can use helper functions similar to ether_setup drivers/ net/net_init.c exports a number of such functions, including the following: void ltalk_setup(struct net _device *dev); Sets up the fields for a LocalTalk device void fc_setup(struct net _device *dev); Initializes fields for fiber-channel devices... *pkt = NULL; /* * As usual, check the "device" pointer to be sure it is * really interrupting * Then assign "struct device *dev" */ struct net _device *dev = (struct net _device *)dev_id; /* and check with hw if it's really ours */ /* paranoid */ if (!dev) return; /* Lock the device */ priv = netdev_priv(dev); spin_lock(&priv->lock); /* retrieve statusword: real netdevices use I/O instructions */ statusword... simple devices They are a holdover from the earlier days of Linux networking; most modern drivers do make use of them (with the possible exception of if_port) We list them here for completeness unsigned unsigned unsigned unsigned long long long long rmem_end; rmem_start; mem_end; mem_start; Device memory information These fields hold the beginning and ending addresses of the shared memory used by the device. .. implement this method 512 | Chapter 17: Network Drivers This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc All rights reserved ,ch17.13860 Page 513 Friday, January 21, 2005 11:10 AM The remaining device operations are optional: int weight; int (*poll)(struct net _device *dev; int *quota); Method provided by NAPI-compliant drivers to operate the interface in a polled... we get into driver operations struct net _device *next; Pointer to the next device in the global linked list This field shouldn’t be touched by the driver int (*init)(struct net _device *dev); An initialization function If this pointer is set, the function is called by register_netdev to complete the initialization of the net _device structure Most modern network drivers do not use this function any longer;... fiber-channel devices void fddi_setup(struct net _device *dev); Configures an interface for a Fiber Distributed Data Interface (FDDI) network The net _device Structure in Detail | This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc All rights reserved 507 ,ch17.13860 Page 508 Friday, January 21, 2005 11:10 AM void hippi_setup(struct net _device *dev); Prepares fields for a High-Performance... your device (which they really shouldn’t), set the NETIF_F_VLAN_CHALLENGED flag NETIF_F_TSO Set this flag if your device can perform TCP segmentation offloading TSO is an advanced feature that we cannot cover here The Device Methods As happens with the char and block drivers, each network device declares the functions that act on it Operations that can be performed on network interfaces are listed in... system resource it needs (I/O ports, IRQ, The net _device Structure in Detail | This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc All rights reserved 511 ,ch17.13860 Page 512 Friday, January 21, 2005 11:10 AM DMA, etc.), turn on the hardware, and perform any other setup your device requires int (*stop)(struct net _device *dev); Stops the interface The interface... cleanup (done in snull_teardown_pool) cannot happen until the device has been unregistered It must, however, happen before we return the net _device structure to the system; once we have called free_netdev, we cannot make any further references to the device or our private area Connecting to the Kernel | This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc All rights . interface: morgana% ping -c 2 remote0 64 bytes from 192 .168.0 .99 : icmp_seq=0 ttl=64 time=1.6 ms 64 bytes from 192 .168.0 .99 : icmp_seq=1 ttl=64 time=0 .9 ms 2 packets transmitted, 2 packets received,. private use. snullnet0 192 .168.0.0 snullnet1 192 .168.1.0 The following are possible host numbers to put into /etc/hosts: 192 .168.0.1 local0 192 .168.0.2 remote0 192 .168.1.2 local1 192 .168.1.1 remote1 The. request. ,ch16.28124 Page 495 Friday, January 21, 2005 9: 09 AM This is the Title of the Book, eMatter Edition Copyright © 2005 O’Reilly & Associates, Inc. All rights reserved. 496 | Chapter 16: Block Drivers int

Ngày đăng: 09/08/2014, 04:21

Tài liệu cùng người dùng

Tài liệu liên quan