xHCI驱动学习(1) 核心数据结构
2015-06-25 12:47
651 查看
虽然Linux内核拥有C语言构建的身体,但它骨子里散发的是面向对象的气质,这一个个的对象就是struct。面对一个内核模块的时候,首先要找出关键的struct和它们之间的关系,才能摸清代码的骨骼脉络。
大致浏览几眼xHCI相关的代码,很容易发现几个貌似重要的struct类型:
先上图,下面分析的过程结束之后各个结构体就是这种关系:
1. xhci-pci模块启动,执行
1.1
而xhci_hc_driver在xhci.c中定义,它包揽了所有的脏活累活:
(free-electrons是个好网站)
1.2
其中有两个成员需要重点关注,一个是
注意
当
2. PCI设备配对成功,执行
这个函数也定义在xhci-pci.c中,比较长(所以加了行号),先贴前半部分:
注意函数的两个参数,由于是被PCI总线驱动调用的,因此一个是
235行,调用
从上面可以看到,每个
2.1
回到正题,考察
为了节约显示器,中间一些与xHCI无关的细节和末尾的错误处理代码就先省略了。这里最关键的,189行中使用和
2.1.1
2440行,创建
2445行,由于
2454行,令
2468行,令
2479行,令
回到
2.1.2
4835行,创建
4838行,令
4839行,令
下面回到
2.2
现在USB 2.0 roothub设置完成,
243行,这次是
顺便,也设置了第二个
再次回到
243行,
253行,令新创建的
回到最初的示意图,现在所有的连接都已经完成!
大致浏览几眼xHCI相关的代码,很容易发现几个貌似重要的struct类型:
usb_hcd、
xhci_hcd和
hc_driver,还有几个全局变量
xhci_pci_driver、
xhci_hc_driver和
xhci_pci_hc_driver,再加上PCI总线相关的类型
pci_dev和
pci_driver。不要被这些眼花缭乱的名字吓到,今天要做的就是把这些结构之间的关系理顺。下面按照相关代码的执行顺序,看一下这些结构是如何被建立和初始化的。
先上图,下面分析的过程结束之后各个结构体就是这种关系:
1. xhci-pci模块启动,执行xhci_pci_init
函数
static int __init xhci_pci_init(void) { xhci_init_driver(&xhci_pci_hc_driver, xhci_pci_setup); #ifdef CONFIG_PM xhci_pci_hc_driver.pci_suspend = xhci_pci_suspend; xhci_pci_hc_driver.pci_resume = xhci_pci_resume; #endif return pci_register_driver(&xhci_pci_driver); }
1.1 xhci_pci_init
调用xhci_init_driver
,初始化xhci_pci_hc_driver
变量
xhci_init_driver函数是在xhci.c中定义的,主要作用就是把全局变量
xhci_hc_driver的值一股脑赋给另一个全局变量
xhci_pci_hc_driver。两者都是
struct hc_driver类型,
xhci_pci_hc_driver在xhci-pci.c中定义,是真正起作用的xHCI驱动,但它在定义的时候没有进行任何成员的初始化:
static struct hc_driver __read_mostly xhci_pci_hc_driver;
而xhci_hc_driver在xhci.c中定义,它包揽了所有的脏活累活:
static const struct hc_driver xhci_hc_driver = { .description = "xhci-hcd", .product_desc = "xHCI Host Controller", .hcd_priv_size = sizeof(struct xhci_hcd *), /* * generic hardware linkage */ .irq = xhci_irq, .flags = HCD_MEMORY | HCD_USB3 | HCD_SHARED, /* * basic lifecycle operations */ .reset = NULL, /* set in xhci_init_driver() */ .start = xhci_run, .stop = xhci_stop, .shutdown = xhci_shutdown, /* * managing i/o requests and associated device resources */ .urb_enqueue = xhci_urb_enqueue, .urb_dequeue = xhci_urb_dequeue, .alloc_dev = xhci_alloc_dev, .free_dev = xhci_free_dev, .alloc_streams = xhci_alloc_streams, .free_streams = xhci_free_streams, .add_endpoint = xhci_add_endpoint, .drop_endpoint = xhci_drop_endpoint, .endpoint_reset = xhci_endpoint_reset, .check_bandwidth = xhci_check_bandwidth, .reset_bandwidth = xhci_reset_bandwidth, .address_device = xhci_address_device, .enable_device = xhci_enable_device, .update_hub_device = xhci_update_hub_device, .reset_device = xhci_discover_or_reset_device, /* * scheduling support */ .get_frame_number = xhci_get_frame, /* * root hub support */ .hub_control = xhci_hub_control, .hub_status_data = xhci_hub_status_data, .bus_suspend = xhci_bus_suspend, .bus_resume = xhci_bus_resume, /* * call back when device connected and addressed */ .update_device = xhci_update_device, .set_usb2_hw_lpm = xhci_set_usb2_hardware_lpm, .enable_usb3_lpm_timeout = xhci_enable_usb3_lpm_timeout, .disable_usb3_lpm_timeout = xhci_disable_usb3_lpm_timeout, .find_raw_port_number = xhci_find_raw_port_number, }; void xhci_init_driver(struct hc_driver *drv, int (*setup_fn)(struct usb_hcd *)) { BUG_ON(!setup_fn); *drv = xhci_hc_driver; drv->reset = setup_fn; }
xhci_init_driver函数将
xhci_hc_driver的值赋给
xhci_pci_hc_driver后,前者也就退下了舞台。不信,你看:
(free-electrons是个好网站)
1.2 xhci_pci_init
调用pci_register_driver
,将xhci_pci_driver
注册为PCI设备驱动
xhci_pci_driver是xHCI控制器作为PCI设备对应的驱动,符合PCI设备驱动的标准类型
struct pci_driver,在xhci-pci.c中静态定义并初始化:
/* pci driver glue; this is a "new style" PCI driver module */ static struct pci_driver xhci_pci_driver = { .name = (char *) hcd_name, .id_table = pci_ids, .probe = xhci_pci_probe, .remove = xhci_pci_remove, /* suspend and resume implemented later */ .shutdown = usb_hcd_pci_shutdown, #ifdef CONFIG_PM .driver = { .pm = &usb_hcd_pci_pm_ops }, #endif };
其中有两个成员需要重点关注,一个是
id_table,一个是
probe。
id_table包含了驱动支持的PCI设备类型,PCI总线就是靠它判断驱动和设备能否配对。这里的
id_table成员设置为
pci_ids,它也是静态定义的全局变量:
/* PCI driver selection metadata; PCI hotplugging uses this */ static const struct pci_device_id pci_ids[] = { { /* handle any USB 3.0 xHCI controller */ PCI_DEVICE_CLASS(PCI_CLASS_SERIAL_USB_XHCI, ~0), .driver_data = (unsigned long) &xhci_pci_hc_driver, }, { /* end: all zeroes */ } }; MODULE_DEVICE_TABLE(pci, pci_ids);
注意
pci_ids中唯一一个元素的
driver_data成员指向刚才在
xhci_pci_init中完成初始化的
xhci_pci_hc_driver变量,这就将作为PCI设备驱动的
xhci_pci_driver和作为USB主机控制器设备驱动
xhci_pci_hc_driver联系了起来。本文最开始的图中右半部分画出了它们的关系。
当
pci_register_driver调用完成后,
xhci_pci_driver就被加入了PCI总线的驱动列表,当总线检测到与之匹配的设备,即xHCI控制器的时候,会调用驱动的probe成员函数,而
xhci_pci_driver.probe在初始化时被设置为
xhci_pci_probe函数,因此接下来就顺藤摸瓜考察它。
2. PCI设备配对成功,执行xhci_pci_probe
函数
这个函数也定义在xhci-pci.c中,比较长(所以加了行号),先贴前半部分:217 static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 218 { 219 int retval; 220 struct xhci_hcd *xhci; 221 struct hc_driver *driver; 222 struct usb_hcd *hcd; 223 224 driver = (struct hc_driver *)id->driver_data; 225 226 /* Prevent runtime suspending between USB-2 and USB-3 initialization */ 227 pm_runtime_get_noresume(&dev->dev); 228 229 /* Register the USB 2.0 roothub. 230 * FIXME: USB core must know to register the USB 2.0 roothub first. 231 * This is sort of silly, because we could just set the HCD driver flags 232 * to say USB 2.0, but I'm not sure what the implications would be in 233 * the other parts of the HCD code. 234 */ 235 retval = usb_hcd_pci_probe(dev, id); 236 237 if (retval) 238 goto put_runtime_pm; ...
注意函数的两个参数,由于是被PCI总线驱动调用的,因此一个是
pci_dev结构,一个是
pci_device_id结构。224行,程序从
pci_device_id结构中取出了
driver_data成员。刚才说到xHCI的
pci_device_id结构中的
driver_data成员指向
xhci_pci_hc_driver,因此接下来
driver指针都是指向它。
235行,调用
usb_hcd_pci_probe函数。这是USB 1.x和2.0的各种控制器驱动使用的probe函数,为什么此处要用它呢?从USB 3.1 Spec中Figure 3-1可以知道,USB 3.x的Host包含两个roothub,对应两条USB总线,一条连接USB 2.0设备,一条连接USB 3.x设备。在xHCI驱动里,需要为每个roothub建立一个
usb_hcd结构,而2.0 roothub需要首先创建,因此就直接调用
usb_hcd_pci_probe了。
usb_hcd结构类型的定义位于include/linux/usb/hcd.h:
79 struct usb_hcd { 80 81 /* 82 * housekeeping 83 */ 84 struct usb_bus self; /* hcd is-a bus */ 85 struct kref kref; /* reference counter */ ...
从上面可以看到,每个
usb_hcd中包含一个
usb_bus结构,代表对应的总线。再看下面的代码,每个
usb_bus中又包含
device结构指针指向对应的设备模型。由于这里的两条USB总线在系统中实际属于同一个物理设备(都对应xHCI控制器这个PCI设备里),因此可以想象同一个xHCI控制器对应的两个
usb_bus结构中的
device指针应当指向同一处。
usb_bus结构类型的定义位于include/linux/usb.h:
319 /* 320 * Allocated per bus (tree of devices) we have: 321 */ 322 struct usb_bus { 323 struct device *controller; /* host/master side hardware */ 324 int busnum; /* Bus number (in order of reg) */ 325 const char *bus_name; /* stable id (PCI slot_name etc) */ ...
2.1 xhci_pci_probe
调用usb_hcd_pci_probe
,创建usb_hcd
和xhci_hcd
回到正题,考察usb_hcd_pci_probe函数:
177 int usb_hcd_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 178 { 179 struct hc_driver *driver; 180 struct usb_hcd *hcd; 181 int retval; 182 int hcd_irq = 0; 183 184 if (usb_disabled()) 185 return -ENODEV; 186 187 if (!id) 188 return -EINVAL; 189 driver = (struct hc_driver *)id->driver_data; 190 if (!driver) 191 return -EINVAL; 192 193 if (pci_enable_device(dev) < 0) 194 return -ENODEV; 195 196 /* 197 * The xHCI driver has its own irq management 198 * make sure irq setup is not touched for xhci in generic hcd code 199 */ 200 if ((driver->flags & HCD_MASK) != HCD_USB3) { 201 if (!dev->irq) { 202 dev_err(&dev->dev, 203 "Found HC with no IRQ. Check BIOS/PCI %s setup!\n", 204 pci_name(dev)); 205 retval = -ENODEV; 206 goto disable_pci; 207 } 208 hcd_irq = dev->irq; 209 } 210 211 hcd = usb_create_hcd(driver, &dev->dev, pci_name(dev)); 212 if (!hcd) { 213 retval = -ENOMEM; 214 goto disable_pci; 215 } 216 217 hcd->amd_resume_bug = (usb_hcd_amd_remote_wakeup_quirk(dev) && 218 driver->flags & (HCD_USB11 | HCD_USB3)) ? 1 : 0; 219 220 if (driver->flags & HCD_MEMORY) { 221 /* EHCI, OHCI */ ... 237 } else { 238 /* UHCI */ ... 257 } 258 259 pci_set_master(dev); 260 261 /* Note: dev_set_drvdata must be called while holding the rwsem */ 262 if (dev->class == CL_EHCI) { 263 down_write(&companions_rwsem); 264 dev_set_drvdata(&dev->dev, hcd); 265 for_each_companion(dev, hcd, ehci_pre_add); 266 retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); 267 if (retval != 0) 268 dev_set_drvdata(&dev->dev, NULL); 269 for_each_companion(dev, hcd, ehci_post_add); 270 up_write(&companions_rwsem); 271 } else { 272 down_read(&companions_rwsem); 273 dev_set_drvdata(&dev->dev, hcd); 274 retval = usb_add_hcd(hcd, hcd_irq, IRQF_SHARED); 275 if (retval != 0) 276 dev_set_drvdata(&dev->dev, NULL); 277 else 278 for_each_companion(dev, hcd, non_ehci_add); 279 up_read(&companions_rwsem); 280 } 281 282 if (retval != 0) 283 goto unmap_registers; 284 device_wakeup_enable(hcd->self.controller); 285 286 if (pci_dev_run_wake(dev)) 287 pm_runtime_put_noidle(&dev->dev); 288 return retval; ...
为了节约显示器,中间一些与xHCI无关的细节和末尾的错误处理代码就先省略了。这里最关键的,189行中使用和
xhci_pci_probe函数中同样的方法令
driver指针指向了
xhci_pci_hc_driver。211行,调用
usb_create_hcd函数创建
usb_hcd结构。
2.1.1 usb_hcd_pci_probe
调用usb_create_hcd
,进而调用usb_create_shared_hcd
创建usb_hcd
结构
2501 struct usb_hcd *usb_create_hcd(const struct hc_driver *driver, 2502 struct device *dev, const char *bus_name) 2503 { 2504 return usb_create_shared_hcd(driver, dev, bus_name, NULL); 2505 }
2434 struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, 2435 struct device *dev, const char *bus_name, 2436 struct usb_hcd *primary_hcd) 2437 { 2438 struct usb_hcd *hcd; 2439 2440 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); 2441 if (!hcd) { 2442 dev_dbg (dev, "hcd alloc failed\n"); 2443 return NULL; 2444 } 2445 if (primary_hcd == NULL) { 2446 hcd->bandwidth_mutex = kmalloc(sizeof(*hcd->bandwidth_mutex), 2447 GFP_KERNEL); 2448 if (!hcd->bandwidth_mutex) { 2449 kfree(hcd); 2450 dev_dbg(dev, "hcd bandwidth mutex alloc failed\n"); 2451 return NULL; 2452 } 2453 mutex_init(hcd->bandwidth_mutex); 2454 dev_set_drvdata(dev, hcd); 2455 } else { 2456 mutex_lock(&usb_port_peer_mutex); 2457 hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; 2458 hcd->primary_hcd = primary_hcd; 2459 primary_hcd->primary_hcd = primary_hcd; 2460 hcd->shared_hcd = primary_hcd; 2461 primary_hcd->shared_hcd = hcd; 2462 mutex_unlock(&usb_port_peer_mutex); 2463 } 2464 2465 kref_init(&hcd->kref); 2466 2467 usb_bus_init(&hcd->self); 2468 hcd->self.controller = dev; 2469 hcd->self.bus_name = bus_name; 2470 hcd->self.uses_dma = (dev->dma_mask != NULL); 2471 2472 init_timer(&hcd->rh_timer); 2473 hcd->rh_timer.function = rh_timer_func; 2474 hcd->rh_timer.data = (unsigned long) hcd; 2475 #ifdef CONFIG_PM 2476 INIT_WORK(&hcd->wakeup_work, hcd_resume_work); 2477 #endif 2478 2479 hcd->driver = driver; 2480 hcd->speed = driver->flags & HCD_MASK; 2481 hcd->product_desc = (driver->product_desc) ? driver->product_desc : 2482 "USB Host Controller"; 2483 return hcd; 2484 }
2440行,创建
usb_hcd类型变量。
2445行,由于
usb_create_hcd调用
usb_create_shared_hcd时
primary_hcd参数为NULL,进入if语句的第一个分支。
2454行,令
pci_device中内嵌的
device结构的
driver_data成员指向刚创建的
usb_hcd结构。
2468行,令
usb_hcd中内嵌的
usb_bus结构的
controller指针指向
device结构。
2479行,令
usb_hcd中的
driver成员指向
xhci_pci_hc_driver。
回到
usb_hcd_pci_probe函数,264/273行又设置了一遍
driver_data,貌似跟上面重复了。
2.1.2 usb_hcd_pci_probe
调用usb_add_hcd
,最终调用xhci_gen_setup
,创建xhci_hcd
结构
2625 int usb_add_hcd(struct usb_hcd *hcd, 2626 unsigned int irqnum, unsigned long irqflags) 2627 { ... 2733 /* "reset" is misnamed; its role is now one-time init. the controller 2734 * should already have been reset (and boot firmware kicked off etc). 2735 */ 2736 if (hcd->driver->reset && (retval = hcd->driver->reset(hcd)) < 0) { 2737 dev_err(hcd->self.controller, "can't setup: %d\n", retval); 2738 goto err_hcd_driver_setup; 2739 } ...
usb_add_hcd函数很长,最关键的是它调用了
xhci_pci_hc_driver的
reset成员,即
xhci_pci_setup函数,后者又进一步调用
xhci_gen_setup。
187 static int xhci_pci_setup(struct usb_hcd *hcd) 188 { 189 struct xhci_hcd *xhci; 190 struct pci_dev *pdev = to_pci_dev(hcd->self.controller); 191 int retval; 192 193 retval = xhci_gen_setup(hcd, xhci_pci_quirks); 194 if (retval) 195 return retval; 196 197 xhci = hcd_to_xhci(hcd); 198 if (!usb_hcd_is_primary_hcd(hcd)) 199 return 0; 200 201 pci_read_config_byte(pdev, XHCI_SBRN_OFFSET, &xhci->sbrn); 202 xhci_dbg(xhci, "Got SBRN %u\n", (unsigned int) xhci->sbrn); 203 204 /* Find any debug ports */ 205 retval = xhci_pci_reinit(xhci, pdev); 206 if (!retval) 207 return retval; 208 209 kfree(xhci); 210 return retval; 211 }
4819 int xhci_gen_setup(struct usb_hcd *hcd, xhci_get_quirks_t get_quirks) 4820 { 4821 struct xhci_hcd *xhci; 4822 struct device *dev = hcd->self.controller; 4823 int retval; 4824 4825 /* Accept arbitrarily long scatter-gather lists */ 4826 hcd->self.sg_tablesize = ~0; 4827 4828 /* support to build packet from discontinuous buffers */ 4829 hcd->self.no_sg_constraint = 1; 4830 4831 /* XHCI controllers don't stop the ep queue on short packets :| */ 4832 hcd->self.no_stop_on_short = 1; 4833 4834 if (usb_hcd_is_primary_hcd(hcd)) { 4835 xhci = kzalloc(sizeof(struct xhci_hcd), GFP_KERNEL); 4836 if (!xhci) 4837 return -ENOMEM; 4838 *((struct xhci_hcd **) hcd->hcd_priv) = xhci; 4839 xhci->main_hcd = hcd; 4840 /* Mark the first roothub as being USB 2.0. 4841 * The xHCI driver will register the USB 3.0 roothub. 4842 */ 4843 hcd->speed = HCD_USB2; 4844 hcd->self.root_hub->speed = USB_SPEED_HIGH; 4845 /* 4846 * USB 2.0 roothub under xHCI has an integrated TT, 4847 * (rate matching hub) as opposed to having an OHCI/UHCI 4848 * companion controller. 4849 */ 4850 hcd->has_tt = 1; 4851 } else { 4852 /* xHCI private pointer was set in xhci_pci_probe for the second 4853 * registered roothub. 4854 */ 4855 return 0; 4856 } ...
xhci_gen_setup最大的作用就是创建了对xHCI至关重要的数据结构——
xhci_hcd类型,并完成了大量的初始化工作。今天只关注结构体之间的关系,因此省略了后面初始化的代码。
4835行,创建
xhci_hcd类型变量。
4838行,令
usb_hcd中的
hcd_priv成员指向新创建的
xhci_hcd。
4839行,令
xhci_hcd中的
main_hcd变量指向
usb_hcd。
下面回到
xhci_pci_probe函数。从此处返回的顺序是
xhci_gen_setup->
xhci_pci_setup->
usb_add_hcd->
usb_hcd_pci_probe->
xhci_pci_probe。
2.2 xhci_pci_probe
调用usb_create_shared_hcd
创建第二个usb_hcd
结构
现在USB 2.0 roothub设置完成,xhci_pci_probe从240行开始继续执行:
217 static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 218 { ... 235 retval = usb_hcd_pci_probe(dev, id); 236 237 if (retval) 238 goto put_runtime_pm; 239 240 /* USB 2.0 roothub is stored in the PCI device now. */ 241 hcd = dev_get_drvdata(&dev->dev); 242 xhci = hcd_to_xhci(hcd); 243 xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev, 244 pci_name(dev), hcd); 245 if (!xhci->shared_hcd) { 246 retval = -ENOMEM; 247 goto dealloc_usb2_hcd; 248 } ...
243行,这次是
xhci_pci_probe亲自调用
usb_create_shared_hcd,创建USB 3.x roothub对应的
usb_hcd。这次由于有了USB 2.0 roothub对应的
usb_hcd作为
primary_hcd,因此
usb_create_shared_hcd函数在其2445行落入第二个分支,一口气设置好了两个
usb_hcd变量的
primary_hcd和
shared_hcd成员:
2434 struct usb_hcd *usb_create_shared_hcd(const struct hc_driver *driver, 2435 struct device *dev, const char *bus_name, 2436 struct usb_hcd *primary_hcd) 2437 { 2438 struct usb_hcd *hcd; 2439 2440 hcd = kzalloc(sizeof(*hcd) + driver->hcd_priv_size, GFP_KERNEL); ... 2445 if (primary_hcd == NULL) { ... 2455 } else { 2456 mutex_lock(&usb_port_peer_mutex); 2457 hcd->bandwidth_mutex = primary_hcd->bandwidth_mutex; 2458 hcd->primary_hcd = primary_hcd; 2459 primary_hcd->primary_hcd = primary_hcd; 2460 hcd->shared_hcd = primary_hcd; 2461 primary_hcd->shared_hcd = hcd; 2462 mutex_unlock(&usb_port_peer_mutex); 2463 }
顺便,也设置了第二个
usb_hcd的
driver成员和
self.controller,跟前面一样。
再次回到
xhci_pci_probe:
217 static int xhci_pci_probe(struct pci_dev *dev, const struct pci_device_id *id) 218 { ... 240 /* USB 2.0 roothub is stored in the PCI device now. */ 241 hcd = dev_get_drvdata(&dev->dev); 242 xhci = hcd_to_xhci(hcd); 243 xhci->shared_hcd = usb_create_shared_hcd(driver, &dev->dev, 244 pci_name(dev), hcd); 245 if (!xhci->shared_hcd) { 246 retval = -ENOMEM; 247 goto dealloc_usb2_hcd; 248 } 249 250 /* Set the xHCI pointer before xhci_pci_setup() (aka hcd_driver.reset) 251 * is called by usb_add_hcd(). 252 */ 253 *((struct xhci_hcd **) xhci->shared_hcd->hcd_priv) = xhci; 254 255 retval = usb_add_hcd(xhci->shared_hcd, dev->irq, 256 IRQF_SHARED); 257 if (retval) 258 goto put_usb3_hcd; 259 /* Roothub already marked as USB 3.0 speed */ 260 261 if (!(xhci->quirks & XHCI_BROKEN_STREAMS) && 262 HCC_MAX_PSA(xhci->hcc_params) >= 4) 263 xhci->shared_hcd->can_do_streams = 1; 264 265 /* USB-2 and USB-3 roothubs initialized, allow runtime pm suspend */ 266 pm_runtime_put_noidle(&dev->dev); 267 268 return 0; ...
243行,
usb_create_shared_hcd返回的时候,直接令
xhci_hcd的
shared_hcd成员指向新创建的
usb_hcd。
253行,令新创建的
usb_hcd的
hcd_priv成员指向
xhci_hcd。
回到最初的示意图,现在所有的连接都已经完成!
相关文章推荐
- Python——基本数据结构
- 按层次遍历二元树
- Joomla添加分类和文章时的目录数据结构
- 数据结构和算法经典100题-第18题
- Redis 链表的数据结构
- 数据结构——Stack和Queue的互相实现
- Python数据结构:序列——字符串
- 《数据结构与算法分析c++描述》读书笔记二——二叉树
- POJ 1655 Balancing Act (树的重心)
- 用数组实现js中的堆栈 或者队列数据结构
- 【数据结构】队列
- 数据结构基础(6)--递归和函数调用--汉诺塔问题C语言实现
- 【数据结构】栈
- 分析数据结构IO_CACHE及相关处理函数
- 【数据结构】线性表之链式存储结构
- 【数据结构】线性表之顺序存储结构
- C# 数据结构 栈 Stack
- 数据结构 - 图的数组存储
- 数据结构-堆
- 数据结构和算法经典100题-第19题