In addition to Weibo, there is also WeChat
Please pay attention
WeChat public account
Shulou
2025-04-09 Update From: SLTechnology News&Howtos shulou NAV: SLTechnology News&Howtos > Servers >
Share
Shulou(Shulou.com)06/03 Report--
At the congressional hearing on May 15, 1951, U.S. Army five-star Admiral MacArthur suggested that the Korean War be extended to China. Bradley then said: "if we extend the war to × × China, then we will be involved in a wrong war with the wrong opponent at the wrong time and in the wrong place."
Writing code applies to the same principle, which is to put the right code in the right place, not the other way around. The same code can appear in multiple possible locations, where it should be, is the result of software architecture design, to put it bluntly, everything is for high kernel and low coupling.
1. Fall into a desperate situation
Let's imagine a simple network card called ABC, which needs to be connected to a memory bus of CPU (assuming CPU is X) and requires an address, data, and control bus (and interrupt pin pins, etc.).
Then in the network card driver of ABC, we need to define the base address, interrupt number and other information of ABC. Suppose that on the circuit board of CPU X, the address of ABC is 0x100000 and the interruption number is 10. Suppose we define macros as follows:
# define ABC_BASE 0x100000 # define ABC_INTERRUPT 10
And write the code to send the message and initialize the request interrupt:
# define ABC_BASE 0x100000 # define ABC_IRQ 10 int abc_send (...) {writel (ABC_BASE + REG_X, 1); writel (ABC_BASE + REG_Y, 0x3);...} int abc_init (...) {request_irq (ABC_IRQ,...);}
The problem with this code is that once the board is changed, ABC_BASE and ABC_IRQ are no longer the same, and the code needs to be changed accordingly.
Some programmers say I can do this:
# ifdef BOARD_A # define ABC_BASE 0x100000 # define ABC_IRQ 10 # elif defined (BOARD_B) # define ABC_BASE 0x110000 # define ABC_IRQ 20 # elif defined (BOARD_C) # define ABC_BASE 0x120000 # define ABC_IRQ 10... # endif
It's OK to do this, but if you have 10, 000 different boards, you have to ifdef 10, 000 times. Writing the code in this way gives you an obvious feeling of building a wall. (you feel that writing code is like building a wall. When you put it in like a brick, simply repeat the machine, at this time, it is very dangerous, maybe there is already a bad "smell" in the code. Considering the adaptation of Linux to various products around the world and the characteristics of various hardware adaptations, it is really unclear how many boards use ABC.
So, is it true that the problem can be solved by going to ifdef 10, 000 times? I really can't. Suppose there is a circuit board with two ABC network cards, it will be completely dumbfounded. Is it defined in this way?
# ifdef BOARD_A # define ABC1_BASE 0x100000 # define ABC1_IRQ 10 # define ABC2_BASE 0x101000 # define ABC2_IRQ 11 # elif defined (BOARD_B) # define ABC1_BASE 0x110000 # define ABC1_IRQ 20... # endif
If so, how should abc_send () and abc_init () be changed? Is it like this:
Int abc1_send (...) {writel (ABC1_BASE + REG_X, 1); writel (ABC1_BASE + REG_Y, 0x3);...} int abc1_init (...) {request_irq (ABC1_IRQ,...);} int abc2_send (...) {writel (ABC2_BASE + REG_X, 1) Writel (ABC2_BASE + REG_Y, 0x3);...} int abc2_init (...) {request_irq (ABC2_IRQ,...);}...
Or like this?
Int abc_send (int id,...) {if (id = = 0) {writel (ABC1_BASE + REG_X, 1); writel (ABC1_BASE + REG_Y, 0x3);} else if (id = = 1) {writel (ABC2_BASE + REG_X, 1); writel (ABC2_BASE + REG_Y, 0x3);}.}
No matter how you change it, the code is so terrible that you can't even read it yourself. The reason why we are in this predicament is that we made the mistake of failing to "put the right code in the right place", which introduced a great deal of coupling.
two。 Lost reflection
The fatal mistake we made was to couple the board-level interconnection information into the driver code, so that the driver could not cross the platform.
Let's think about it. The real responsibility of the ABC driver is to complete the sending and receiving process of the ABC network card. Does this process really have anything to do with what CPU (TI, Samsung, Broad, Allwinner, etc.) it connects to? Does it have anything to do with which board you connect to?
The answer is that it really doesn't matter! The ABC network card will not make any difference because you are the ARM of TI, you are Godson, or you are Blackfin. No matter what boards are overwhelming outside you, ABC himself is standing still.
Since it doesn't matter, why should these board-level interconnection information be put in the driver code? Basically, we can assume that ABC will not change by anyone, so its code should be naturally cross-platform. Therefore, we think that codes like "# defineABC_BASE 0x100000, # defineABC_ IRQ 10" appear in the driver and belong to "fighting the wrong war with the wrong enemy in the wrong place". It is not put in the right place, but we write the code, must "let heaven return to heaven, let dust return to dust". I'm afraid our real expectations look like this:
Software engineering emphasizes high cohesion and low coupling. If the elements in a module are more closely related, the cohesion will be higher, and the less closely connected the modules will be, the lower the coupling will be. So high cohesion and low coupling emphasize that those on the inside should hold tight and those on the outside should get out of here. As far as the driver is concerned, the board-level interconnection information obviously belongs to the one that should go away. Each software module had better be an otaku, do not fall in love, do not watch movies, do not eat big meals, do not play enough, the only connection with the outside world is "hungry", such software is obviously high cohesion and low coupling.
Once in a German foreign company, I asked engineers "what is the relationship between high cohesion and low coupling". One engineer replied very actively that "high cohesion and low coupling are a pair of contradictions." I think his mind is very confused. If a relationship must be used to describe the relationship between high cohesion and low coupling, I think they are in line with Marxism-Leninism. "High cohesion and low coupling, interdependent, indispensable, complement each other, and promote together", which actually reflects two different aspects of the same thing. In short, it is right to recite the political textbook.
You write a serial port code, which is serial port-related things from beginning to end, the ground is tight, it will not run around the world to SPI inside to couple. SPI and serial port low coupling, it is bound to require UART internal code to put all the serial port things together, do not run around, there is no SPI hukou, residence permit is not issued to you, go back to my hometown.
3. Liu an Hua Ming
Now the board-level interconnection information has been separated from the driver, allowing them to appear in different software modules. However, in the end, they still have a certain connection, because the driver finally has to take out the board-level information such as base address, interrupt number and so on. How to get it is a big problem.
One way is to use ABC drivers all over the world to ask each board, "May I ask your base address, what is the interrupt number?" "what's your mother's last name?" This is still a serious coupling. Because, the driver still needs to know whether there is ABC on the board, which board has it, and how it works. It's still directly coupled to the board.
Whether there is another way, we maintain a common database-like thing, what the network card on the board, what the base address interrupt number is, are all maintained in one place. Then, the driver asks a unified place, through a unified API to get it?
Based on this idea, linux divides the device driver into three entities: the bus, the device and the driver. The bus is the unified link in the figure above, and the device is the board-level interconnection information in the figure above. The duties of these three entities are as follows:
Entity
Function
Code
Equipment
Describe the base address, interrupt number, clock, DMA, reset, etc.
Arch/arm
Arch/blackfin
Arch/xxx
Wait for catalogue
Drive
Complete the functions of peripherals, such as network card sending and receiving packets, sound card recording and playback, SD card reading and writing.
Drivers/net
Sound
Drivers/mmc
Wait for catalogue
Bus
Complete the association between device and driver
Drivers/base/platform.c
Drivers/pci/pci-driver.c
...
We fill all the board interconnection information into the device side, and then let the device side register with the bus to inform the bus of its own existence, which is naturally associated with these devices, and further indirectly related to the board-level connection information of the device. For example, the arch/blackfin/mach-bf533/boards/ip0x.c board has two DM9000 network cards. It is registered as follows:
Static struct resource dm9000_resource1 [] = {{.start = 0x20100000, .end = 0x20100000 + 1, .flags = IORESOURCE_MEM}, {.start = 0x20100000 + 2, .end = 0x20100000 + 3, .flags = IORESOURCE_MEM}, {.start = IRQ_PF15, .end = IRQ_PF15 .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE}} Static struct resource dm9000_resource2 [] = {{.start = 0x20200000, .end = 0x20200000 + 1, .flags = IORESOURCE_MEM}... };... Static struct platform_device dm9000_device1 = {.name = "dm9000", .id = 0, .num _ resources = ARRAY_SIZE (dm9000_resource1), .resource = dm9000_resource1,};... Static struct platform_device dm9000_device2 = {.name = "dm9000", .id = 1, .num _ resources = ARRAY_SIZE (dm9000_resource2), .resource = dm9000_resource2,}; static struct platform_device * ip0x_devices [] _ initdata = {& dm9000_device1, & dm9000_device2,... }; static int _ init ip0x_init (void) {platform_add_devices (ip0x_devices, ARRAY_SIZE (ip0x_devices)); … }
In this way, on the unified link of the platform bus, we naturally know that there are two DM9000 network cards on the board. Once the DM9000 driver is also registered, since the platform bus has been associated with the device, the driver can naturally learn the following memory base address, interrupt and other information based on the existing DM9000 device information:
Static struct resource dm9000_resource1 [] = {{.start = 0x20100000, .end = 0x20100000 + 1, .flags = IORESOURCE_MEM}, {.start = 0x20100000 + 2, .end = 0x20100000 + 3, .flags = IORESOURCE_MEM}, {.start = IRQ_PF15, .end = IRQ_PF15 .flags = IORESOURCE_IRQ | IORESOURCE_IRQ_HIGHEDGE}}
The purpose of the bus is to match these drivers with these devices one by one. As shown in the figure below, there are two ABC,1 DEF,1 HIJ devices and one ABC, DEF and HIJ driver on a circuit board, so the bus matches two ABC devices and one ABC driver, DEF devices and drivers one-to-one, and HIJ devices and drivers one-to-one.
For the driver itself, you can use the simplest API to take out the interconnection information entered by the device side and take a look at the dm9000_probe () code of drivers/net/ethernet/davicom/dm9000.c:
Static int dm9000_probe (struct platform_device * pdev) {… Db- > addr_res = platform_get_resource (pdev, IORESOURCE_MEM, 0); db- > data_res = platform_get_resource (pdev, IORESOURCE_MEM, 1); db- > irq_res = platform_get_resource (pdev, IORESOURCE_IRQ, 0); … }
In this way, board-level interconnection information will no longer break into the driver, and the driver does not seem to be directly coupled with the device, because it calls the bus-level standard API:platform_get_resource (). There is a match () function in the bus to perform the responsibility of which device is served by which driver. For example, for a platform bus hanging on memory, its matching is similar (the simplest matching method is that the device is the same as the driver's name field):
Static int platform_match (struct device * dev, struct device_driver * drv) {struct platform_device * pdev = to_platform_device (dev); struct platform_driver * pdrv = to_platform_driver (drv); / * When driver_override is set, only bind to the matching driver * / if (pdev- > driver_override) return! strcmp (pdev- > driver_override, drv- > name) / * Attempt an OF style match first * / if (of_driver_match_device (dev, drv)) return 1; / * Then try ACPI style match * / if (acpi_driver_match_device (dev, drv)) return 1 / * Then try to match against the id table * / if (pdrv- > id_table) return platform_match_id (pdrv- > id_table, pdev)! = NULL; / * fall-back to driver name match * / return (strcmp (pdev- > name, drv- > name) = 0);}
VxBus is the new device driver architecture of Fenghe Company. It was added to VxWorks in VxWorks 6.2 and later, and has basically been VxBusized until VxWorks 6.9. However, this VxBus can be said to be very similar to the bus, device and driver model of Linux. But, excuse me, why are you called VxBus? is it very Vx?
So, at this time we see the code will be like this, no matter which board of the ABC device, all use an unchanged drivers/net/ethernet/abc.c driver, and arch/arm/mach-yyy/board-a.c such code, there are many copies.
4. Go to a higher level
We still see a lot of code like arch/arm/mach-yyy/board-a.c sprinting for detailed code that describes board-level information, even though it itself is decoupled from the driver. The existence of this code is simply a contamination of the Linux kernel and a merciless contempt for Linus Torvalds, because it is too technical!
We have reason to use a non-C scripting language to describe the device-side information. This script file is the legendary Device Tree (device tree).
The device tree is an dts file that describes all the devices on each board and their connection information in the simplest syntax. For example, the DM9000 under arch/arm/boot/dts/ imx1-apf9328.dts is such a script, where the base address and interrupt number become an attribute of the DM9000 device node:
Eth: eth@4,c00000 {compatible = "davicom,dm9000"; reg =
< 4 0x00c00000 0x2 4 0x00c00002 0x2 >; interrupt-parent =; interrupts =; … }
After that, the C code is removed, and files like arch/arm/mach-xxx/board-a.c go into the old pile of history forever, and the code becomes this kind of architecture. Change the board, just change the Device Tree. "Let heaven return to heaven, let dust return to dust", let the driver return to the drive C code, let the device return to the device tree script.
We are pleased and saddened to see that the new version of VxWorks 7 also uses Device Tree. We are glad that it has finally come; to our grief, it is late again. The wheels of Linux roll forward, crushing everything mercilessly. The thousand-year-old track of mankind, vicissitudes of life, stars shift, repeatedly carry on the history of attribution to history, the future is still attributed to the process of history. This is not only the sorrow of reality, but also the heroism of history.
Sun Tzu's Art of War said: "the water controls the flow because of the land, and the soldiers win because of the enemy." therefore, the army has no constant potential, and the water has no regular shape; he who can win because of the enemy's change is called the god. " It's all about following the trend and putting the right code in the right place.
In order to further explore this topic, CSDN College and bloggers organized a live broadcast on "exploring the bus, device and driver Model of Linux" on 8PM~9PM on July 5, 2017. 314 people participated in the online live broadcast, and the event has ended. Readers who want to watch the recorded video can go to:
Http://edu.csdn.net/huiyiCourse/detail/426?ref=0
Welcome to subscribe "Shulou Technology Information " to get latest news, interesting things and hot topics in the IT industry, and controls the hottest and latest Internet news, technology news and IT industry trends.
Views: 0
*The comments in the above article only represent the author's personal views and do not represent the views and positions of this website. If you have more insights, please feel free to contribute and share.
Continue with the installation of the previous hadoop.First, install zookooper1. Decompress zookoope
"Every 5-10 years, there's a rare product, a really special, very unusual product that's the most un
© 2024 shulou.com SLNews company. All rights reserved.