兼容Windows文件名命名方式,避免在Windows下git clone失败

This commit is contained in:
Magic
2022-02-02 00:26:19 +08:00
parent 5fba5d21bf
commit 6a70b1847f
5 changed files with 957 additions and 3 deletions

View File

@@ -70,9 +70,9 @@
- [io_uring2- 从创建必要的文件描述符 fd 开始](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/io_uring%EF%BC%882%EF%BC%89-%20%E4%BB%8E%E5%88%9B%E5%BB%BA%E5%BF%85%E8%A6%81%E7%9A%84%E6%96%87%E4%BB%B6%E6%8F%8F%E8%BF%B0%E7%AC%A6%20fd%20%E5%BC%80%E5%A7%8B.md)
- [下一代异步 IO io_uring 技术解密](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/%E4%B8%8B%E4%B8%80%E4%BB%A3%E5%BC%82%E6%AD%A5%20IO%20io_uring%20%E6%8A%80%E6%9C%AF%E8%A7%A3%E5%AF%86.md)
- [小谈io_uring](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/%E5%B0%8F%E8%B0%88io_uring.md)
- [智汇华云 | 新时代IO利器-io_uring](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/%E6%99%BA%E6%B1%87%E5%8D%8E%E4%BA%91%20%7C%20%E6%96%B0%E6%97%B6%E4%BB%A3IO%E5%88%A9%E5%99%A8-io_uring.md)
- [智汇华云-新时代IO利器-io_uring](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/%E6%99%BA%E6%B1%87%E5%8D%8E%E4%BA%91-%E6%96%B0%E6%97%B6%E4%BB%A3IO%E5%88%A9%E5%99%A8-io_uring.md)
- [Linux 5.1 的 io_uring](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/Linux%205.1%20%E7%9A%84%20io_uring.md)
- [What is io_uring?](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/What%20is%20io_uring%3F)
- [What is io_uring](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/What%20is%20io_uring)
- [io_uring_setup](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/io_uring_setup.md)
- [io_uring_enter](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/io_uring_enter.md)
- [io_uring_register](https://github.com/0voice/kernel_new_features/blob/main/io_uring/%E6%96%87%E7%AB%A0/io_uring_register.md)
@@ -434,7 +434,7 @@
### 文章
- [LLVM 入门篇](https://github.com/0voice/kernel_new_features/blob/main/llvm/%E6%96%87%E7%AB%A0/LLVM%20%E5%85%A5%E9%97%A8%E7%AF%87.md)
- [LLVM :Clang入门](https://github.com/0voice/kernel_new_features/blob/main/llvm/%E6%96%87%E7%AB%A0/LLVM%20:Clang%E5%85%A5%E9%97%A8.md)
- [LLVM-Clang入门](https://github.com/0voice/kernel_new_features/blob/main/llvm/%E6%96%87%E7%AB%A0/LLVM-Clang%E5%85%A5%E9%97%A8.md)
- [LLVM编译器框架介绍](https://github.com/0voice/kernel_new_features/blob/main/llvm/%E6%96%87%E7%AB%A0/LLVM%E7%BC%96%E8%AF%91%E5%99%A8%E6%A1%86%E6%9E%B6%E4%BB%8B%E7%BB%8D.md)
- [Llvm编译的基本概念和流程](https://github.com/0voice/kernel_new_features/blob/main/llvm/%E6%96%87%E7%AB%A0/llvm%E7%BC%96%E8%AF%91%E7%9A%84%E5%9F%BA%E6%9C%AC%E6%A6%82%E5%BF%B5%E5%92%8C%E6%B5%81%E7%A8%8B.md)
- [后端技术的重用LLVM不仅仅让你高效](https://github.com/0voice/kernel_new_features/blob/main/llvm/%E6%96%87%E7%AB%A0/%E5%90%8E%E7%AB%AF%E6%8A%80%E6%9C%AF%E7%9A%84%E9%87%8D%E7%94%A8%EF%BC%9ALLVM%E4%B8%8D%E4%BB%85%E4%BB%85%E8%AE%A9%E4%BD%A0%E9%AB%98%E6%95%88.md)

View File

@@ -0,0 +1,51 @@
`io_uring` is a new asynchronous I/O API for Linux created by Jens Axboe from Facebook. It aims at providing an API without the limitations of the current [select(2)](http://man7.org/linux/man-pages/man2/select.2.html), [poll(2)](http://man7.org/linux/man-pages/man2/poll.2.html), [epoll(7)](http://man7.org/linux/man-pages/man7/epoll.7.html) or [aio(7)](http://man7.org/linux/man-pages/man7/aio.7.html) family of system calls, which we discussed in the previous section. Given that users of asynchronous programming models choose it in the first place for performance reasons, it makes sense to have an API that has very low performance overheads. We shall see how `io_uring` achieves this in subsequent sections.
## The io_uring interface
The very name io_uring comes from the fact that the interfaces uses ring buffers as the main interface for kernel-user space communication. While there are system calls involved, they are kept to a minimum and there is a polling mode you can use to reduce the need to make system calls as much as possible.
See also
- [Submission queue polling tutorial](https://unixism.net/loti/tutorial/sq_poll.html#sq-poll) with example program.
### The mental model
The mental model you need to construct in order to use `io_uring` to build programs that process I/O asynchronously is fairly simple.
- There are 2 ring buffers, one for submission of requests (submission queue or SQ) and the other that informs you about completion of those requests (completion queue or CQ).
- These ring buffers are shared between kernel and user space. You set these up with [`io_uring_setup()`](https://unixism.net/loti/ref-iouring/io_uring_setup.html#c.io_uring_setup) and then mapping them into user space with 2 [mmap(2)](http://man7.org/linux/man-pages/man2/mmap.2.html) calls.
- You tell io_uring what you need to get done (read or write a file, accept client connections, etc), which you describe as part of a submission queue entry (SQE) and add it to the tail of the submission ring buffer.
- You then tell the kernel via the [`io_uring_enter()`](https://unixism.net/loti/ref-iouring/io_uring_enter.html#c.io_uring_enter) system call that youve added an SQE to the submission queue ring buffer. You can add multiple SQEs before making the system call as well.
- Optionally, [`io_uring_enter()`](https://unixism.net/loti/ref-iouring/io_uring_enter.html#c.io_uring_enter) can also wait for a number of requests to be processed by the kernel before it returns so you know youre ready to read off the completion queue for results.
- The kernel processes requests submitted and adds completion queue events (CQEs) to the tail of the completion queue ring buffer.
- You read CQEs off the head of the completion queue ring buffer. There is one CQE corresponding to each SQE and it contains the status of that particular request.
- You continue adding SQEs and reaping CQEs as you need.
- There is a [polling mode available](https://unixism.net/loti/tutorial/sq_poll.html#sq-poll), in which the kernel polls for new entries in the submission queue. This avoids the system call overhead of calling [`io_uring_enter()`](https://unixism.net/loti/ref-iouring/io_uring_enter.html#c.io_uring_enter) every time you submit entries for processing.
See also
- [The Low-level io_uring Interface](https://unixism.net/loti/low_level.html#low-level)
## io_uring performance
Because of the shared ring buffers between the kernel and user space, io_uring can be a zero-copy system. Copying bytes around becomes necessary when system calls that transfer data between kernel and user space are involved. But since the bulk of the communication in `io_uring` is via buffers shared between the kernel and user space, this huge performance overhead is completely avoided. While system calls (and were used to making them a lot) may not seem like a significant overhead, in high performance applications, making a lot of them will begin to matter. Also, system calls are not as cheap as they used to be. Throw in workarounds the operating system has in place to deal with [Specter and Meltdown](https://meltdownattack.com/), we are talking non-trivial overheads. So, avoiding system calls as much as possible is a fantastic idea in high-performance applications indeed.
While using synchronous programming interfaces or even when using asynchronous programming interfaces under Linux, there is at least one system call involved in the submission of each request. In `io_uring`, you can add several requests, simply by adding multiple SQEs each describing the I/O operation you want and make a single call to io_uring_enter. For starers, thats a win right there. But it gets better.
You can have the kernel poll and pick up your SQEs for processing as you add them to the submission queue. This avoids the [`io_uring_enter()`](https://unixism.net/loti/ref-iouring/io_uring_enter.html#c.io_uring_enter) call you need to make to tell the kernel to pick up SQEs. For high-performance applications, this means even lesser system call overheads. See [the submission queue polling tutorial](https://unixism.net/loti/tutorial/sq_poll.html#sq-poll) for more details.
With some clever use of shared ring buffers, `io_uring` performance is really memory-bound, since in polling mode, we can do away with system calls altogether. It is important to remember that performance benchmarking is a relative process with some kind of a common point of reference. According to the [io_uring paper](https://kernel.dk/io_uring.pdf), on a reference machine, in polling mode, `io_uring` managed to clock 1.7M 4k IOPS, while [aio(7)](http://man7.org/linux/man-pages/man7/aio.7.html) manages 608k. Although much more than double, this isnt a fair comparison since [aio(7)](http://man7.org/linux/man-pages/man7/aio.7.html) doesnt feature a polled mode. But even when polled mode is disabled, `io_uring` hits 1.2M IOPS, close to double that of [aio(7)](http://man7.org/linux/man-pages/man7/aio.7.html).
To check the raw throughput of the `io_uring` interface, there is a no-op request type. With this, on the reference machine, `io_uring` achieves 20M messages per second. See [`io_uring_prep_nop()`](https://unixism.net/loti/ref-liburing/submission.html#c.io_uring_prep_nop) for more details.
## An example using the low-level API
Writing a small program that reads files and prints them on to the console, like how the Unix `cat` utility does might be a good starting point to get your hands wet with the `io_uring` API. Please see the next chapter for one such example.
## Just use liburing
While being acquainted with the low-level `io_uring` API is most certainly a good thing, in real, serious programs you probably want to use the higher-level interface provided by liburing. Programs like [QEMU](https://qemu.org/) already use it. If liburing never existed, youd have built some abstraction layer over the low-lever `io_uring` interface. liburing does that for you and it is a well thought-out interface as well. In short, you should probably put in some effort to understand how the low-level `io_uring` interface works, but by default you should really be using `liburing` in your programs.
While there is a reference section here for it, there are some examples based on `liburing` well see in the subsequent chapters.
> 原文链接https://unixism.net/loti/what_is_io_uring.html

View File

@@ -0,0 +1,342 @@
> io_uring是kernel 5.1中引入的一套新的syscall接口用于支持异步IO。随着客户在高性能计算中的求解问题规模的越来越大对计算能力和存储IO的需求不断增长并成为计算和存储技术发展最直接的动力。本文将对io_uring的原理和功能进行分析让大家了解io_uring的性能以及其应用场景、发展趋势。
io_uring是kernel 5.1中引入的一套新的syscall接口用于支持异步IO。随着客户在高性能计算中的求解问题规模的越来越大对计算能力和存储IO的需求不断增长并成为计算和存储技术发展最直接的动力。本文将对io_uring的原理和功能进行分析让大家了解io_uring的性能以及其应用场景、发展趋势。
**概述**
高性能计算是继理论科学和实验科学之后科学探索的第三范式,被广泛应用在高能物理研究、核武器设计、航天航空飞行器设计、国民经济的预测和决策等领域,对国民经济发展和国防建设具有重要的价值。它作为世界高技术领域的战略制高点,已经成为科技进步的重要标志之一,同时也是一个国家科技综合实力的集中体现。
作为信创云计算专家华云数据已经在政府金融、国防军工、教育医疗、能源电力、交通运输等十几个行业打造了行业标杆案例客户总量超过30万。在高性能计算方面也部下重局。在华云数据的研发和客户需求调研中发现随着客户在高性能计算中的求解问题规模的越来越大对计算能力和存储IO的需求不断增长并成为计算和存储技术发展最直接的动力。
io_uring是Linux Kernel在v5.1版本引入的一套新异步编程框架同时支持Buffer IO和Direct IO。io_uring 在设计之初就充分考虑框架自身的高性能和通用性不仅仅面向传统基于块设备的FS/Block IO领域对网络异步编程也提供支持以通用的系统调用提供比肩spdk/dpdk 的性能。
**IO演变过程**
**1、基于fd的阻塞式 I/Oread()/write()**
![image](https://user-images.githubusercontent.com/87457873/149942207-3af8a5c4-0c46-43be-8247-5176d414dabc.png)
作为大家最熟悉的读写方式Linux 内核提供了基于文件描述符的系统调用这些描述符指向的可能是存储文件storage file也可能是network sockets
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);
二者称为阻塞式系统调用blocking system calls因为程序调用这些函数时会进入sleep状态然后被调度出去让出处理器直到 I/O 操作完成:
- 如果数据在文件中并且文件内容已经缓存在page cache中调用会立即返回
- 如果数据在另一台机器上,就需要通过网络(例如 TCP获取会阻塞一段时间
- 如果数据在硬盘上,也会阻塞一段时间。
缺点:
随着存储设备越来越快,程序越来越复杂, 阻塞式blocking已经这种最简单的方式已经不适用了。
**2、非阻塞式 I/Oselect()/poll()/epoll()**
阻塞式之后出现了一些新的、非阻塞的系统调用例如select()、poll()以及更新的epoll()。应用程序在调用这些函数读写时不会阻塞而是立即返回返回的是一个已经ready的文件描述符列表。
![image](https://user-images.githubusercontent.com/87457873/149942253-f714b31b-f8c8-46b3-8c73-cf307fbb8d60.png)
缺点只支持network sockets和pipes——epoll()甚至连storage files都不支持。
**3、线程池方式**
对于storage I/O经典的解决思路是thread pool主线程将 I/O分发给worker线程后者代替主线程进行阻塞式读写主线程不会阻塞。
![image](https://user-images.githubusercontent.com/87457873/149942269-bec5c706-707e-4667-a5d7-239784765786.png)
这种方式的问题是线程上下文切换开销可能非常大,通过性能压测会看到。
**4、Direct** **I/O数据库软件绕过 page cache**
随后出现了更加灵活和强大的方式数据库软件database software有时并不想使用操作系统的page cache而是希望打开一个文件后直接从设备读写这个文件direct access to the device。这种方式称为直接访问direct access或直接 I/Odirect I/O
需要指定O_DIRECT flag
- 需要应用自己管理自己的缓存 —— 这正是数据库软件所希望的;
- 是zero-copy I/O因为应用的缓冲数据直接发送到设备或者直接从设备读取。
**5、异步IO**
随着存储设备越来越快主线程和worker线性之间的上下文切换开销占比越来越高。现在市场上的一些设备。换个方式描述更能让我们感受到这种开销上下文每切换一次我们就少一次dispatch I/O的机会。因此Linux 2.6 内核引入了异步I/Oasynchronous I/O接口。
![image](https://user-images.githubusercontent.com/87457873/149942297-b3711c0b-39ce-4512-9b8e-fc5221d64ab7.png)
Linux 原生AIO 处理流程:
- 当应用程序调用io_submit系统调用发起一个异步IO 操作后,会向内核的 IO 任务队列中添加一个IO 任务,并且返回成功。
- 内核会在后台处理 IO 任务队列中的IO 任务,然后把处理结果存储在 IO 任务中。
- 应用程序可以调用io_getevents系统调用来获取异步IO 的处理结果,如果 IO 操作还没完成那么返回失败信息否则会返回IO 处理结果。
从上面的流程可以看出Linux 的异步 IO 操作主要由两个步骤组成:
- 调用 io_submit 函数发起一个异步 IO 操作。
- 调用 io_getevents 函数获取异步 IO 的结果。
AIO的缺陷
- 仅支持direct IO。在采用AIO的时候只能使用O_DIRECT不能借助文件系统缓存来缓存当前的IO请求还存在size对齐直接操作磁盘所有写入内存块数量必须是文件系统块大小的倍数而且要与内存页大小对齐。等限制直接影响了aio在很多场景的使用。
- 仍然可能被阻塞。语义不完备。即使应用层主观上希望系统层采用异步IO但是客观上有时候还是可能会被阻塞。io_getevents(2)调用read_events读取AIO的完成eventsread_events中的wait_event_interruptible_hrtimeout等待aio_read_events如果条件不成立events未完成则调用__wait_event_hrtimeout进入睡眠当然支持用户态设置最大等待时间
- 拷贝开销大。每个IO提交需要拷贝64+8字节每个IO完成需要拷贝32字节总共104字节的拷贝。这个拷贝开销是否可以承受和单次IO大小有关如果需要发送的IO本身就很大相较之下这点消耗可以忽略而在大量小IO的场景下这样的拷贝影响比较大。
- API不友好。每一个IO至少需要两次系统调用才能完成submit和wait-for-completion),需要非常小心地使用完成事件以避免丢事件。
- 系统调用开销大。也正是因为上一条io_submit/io_getevents造成了较大的系统调用开销在存在spectre/meltdownCPU熔断幽灵漏洞CVE-2017-5754的机器上若如果要避免漏洞问题系统调用性能则会大幅下降。在存储场景下高频系统调用的性能影响较大。
软件工程中在现有接口基础上改进相比新开发一套接口往往有着更多的优势然而在过去的数年间针对上述限制的很多改进努力都未尽如人意。终于全新的异步IO引擎io_uring就在这样的环境下诞生了。
**io_uring原理**
io_uring 来自资深内核开发者 Jens Axboe 的想法,他在 Linux I/O stack 领域颇有研究。从最早的patch aio: support for IO polling 可以看出这项工作始于一个很简单的观察随着设备越来越快中断驱动interrupt-driven模式效率已经低于轮询模式polling for completions
- io_uring 的基本逻辑与 linux-aio 是类似的:提供两个接口,一个将 I/O 请求提交到内核,一个从内核接收完成事件。
- 但随着开发深入,它逐渐变成了一个完全不同的接口:设计者开始从源头思考 如何支持完全异步的操作。
**1、原理及核心数据结构**
每个 io_uring 实例都有两个环形队列ring在内核和应用程序之间共享
- 提交队列submission queue (SQ)
- 完成队列completion queue (CQ)
![image](https://user-images.githubusercontent.com/87457873/149942323-e05da76e-264d-486d-b824-0a29f7d992b3.png)
这两个队列:
- 都是单生产者、单消费者size 是 2 的幂次;
- 提供无锁接口lock-less access interface内部使用 内存屏障做同步coordinated with memory barriers
使用方式:
- 请求
- 应用创建 SQ entries (SQE),更新 SQ tail
- 内核消费 SQE更新 SQ head。
- 完成
- 内核为完成的一个或多个请求创建 CQ entries (CQE),更新 CQ tail
- 应用消费 CQE更新 CQ head。
- 完成事件completion events可能以任意顺序到达到总是与特定的 SQE 相关联的。
- 消费 CQE 过程无需切换到内核态。
**2、 三种工作模式**
io_uring 实例可工作在三种模式:
**(1)中断驱动模式interrupt driven**
默认模式。可通过 io_uring_enter() 提交 I/O 请求,然后直接检查 CQ 状态判断是否完成。
**(2)轮询模式polled**
Busy-waiting for an I/O completion而不是通过异步 IRQInterrupt Request接收通知。
这种模式需要文件系统如果有和块设备block device支持轮询功能。相比中断驱动方式这种方式延迟更低但可能会消耗更多 CPU 资源。
目前,只有指定了 O_DIRECT flag 打开的文件描述符,才能使用这种模式。当一个读 或写请求提交给轮询上下文polled context之后应用application必须调用 io_uring_enter() 来轮询 CQ 队列,判断请求是否已经完成。
对一个io_uring 实例来说,不支持混合使用轮询和非轮询模式。
**(3)内核轮询模式kernel polled**
这种模式中会创建一个内核线程kernel thread来执行SQ 的轮询工作。
使用这种模式的 io_uring 实例应用无需切到到内核态就能触发issueI/O 操作。通过SQ来提交SQE以及监控CQ的完成状态应用无需任何系统调用就能提交和收割 I/Osubmit and reap I/Os
如果内核线程的空闲时间超过了用户的配置值它会通知应用然后进入idle状态。这种情况下应用必须调用io_uring_enter()来唤醒内核线程。如果 I/O 一直很繁忙,内核线性是不会 sleep 的。
**3、重要特性**
- IORING_SETUP_SQPOLL创建一个内核线程进行sqe的处理IO 提交几乎完全消除用户态内核态上下文切换消除spectre/ meltdown 缓解场景下对系统调用的性能影响此特性需要额外消耗一个cpu核。
- ORING_SETUP_IOPOLL配合blk-mq多队列映射机制内核IO 协议栈开始真正完整支持IOpolling。
- IORING_REGISTER_FILES/ IORING_REGISTER_FILES_UPDATE/ IORING_UNREGISTER_FILES减少fget/ fput原子操作带来的开销
- IORING_REGISTER_BUFFERS/ IORING_UNREGISTER_BUFFERS通过提 前向内核 注册 buffer 减少 get_user_pages / unpin_user_pages 开销,应用适配存在一定难度。
- IORING_FEAT_FAST_POLL网络编程新利器向 epoll 等传统基于事件驱动的网络编程模型发起挑战,为用户态提供真正的异步编程 API 。
- ASYNC BUFFERED READS更好的支持异步 buffered reads ,但当前对于异步 buffered write 的支持还不够完。
**功能对比**
**1、Synchronous I/O、 Libaio和IO_uring对比**
![image](https://user-images.githubusercontent.com/87457873/149942361-1c19bf0f-818f-4cec-9556-d42a0f3a92c3.png)
**2、io_uring和spdk的对比**
![image](https://user-images.githubusercontent.com/87457873/149942381-3a9f6d36-9991-476a-941e-8750b1e3666a.png)
**性能表现**
![image](https://user-images.githubusercontent.com/87457873/149942400-b45feae5-dfb8-4b7f-94f0-ee9ce3604b3d.png)
非polling模式io_uring相比libaio提升不是很明显在polling模式下io_uring能与spdk接近甚至在queue depth较高时性能更好完爆libaio。
![image](https://user-images.githubusercontent.com/87457873/149942424-17bb8dea-5bef-4b06-9d31-5a8d2f559779.png)
在queue depth较低时有约7%的差距但在queue depth较高时基本接近。
![image](https://user-images.githubusercontent.com/87457873/149942440-4924df96-279e-4858-861f-be93aabc00f3.png)
以上数据来自来自Jens Axboe的测试https://lore.kernel.org/linux-block/20190116175003.17880-1-axboe@kernel.dk/
结论:
io_uring在非polling模式下相比libaio性能提升不是非常显著。
io_uring在polling模式下性能提升显著与spdk接近在队列深度较高时性能更好。
**io_uring在kvm-qemu下的性能表现**
**1、直接启用io_uring的场景**
下面测试结果在一台华云超融合一体机测试qemu 5.0 已经支持了Asynchronous I/O的io_uring只需要将虚拟机将驱动改成io_uring。
原理如下:
![image](https://user-images.githubusercontent.com/87457873/149942467-a7b1e4f6-e6e6-430a-b2f3-5ea67e18005c.png)
- guestOS的kernel和qemu通信通过 virtqueue
- io_uring通过SQ和CQ进行IO的管理
宿主机环境信息:
![image](https://user-images.githubusercontent.com/87457873/149942487-2a429103-f3b6-4f94-b273-0c50319b8399.png)
虚拟机环境信息
![image](https://user-images.githubusercontent.com/87457873/149942499-6d91b523-daf2-4abd-852c-e1cb5017a7e2.png)
测试参数
```
[global]
ioengine=libaio
time_based
direct=1
thread
group_reporting
randrepeat=0
norandommap
numjobs=4
ramp_time=10
runtime=600
filename=/dev/sdb
[randwrite-4k-io32]
bs=4k
iodepth=32
rw=randwrite
stonewall
[randread-4k-io32]
bs=4k
iodepth=32
rw=randread
stonewall
```
测试数据如下:
![image](https://user-images.githubusercontent.com/87457873/149942516-0ce842ca-85e8-4ee1-8b4f-6a0fe9adb8f8.png)
总结:
1、libaio和io_uring性能比较io_uring性能提高不明显没有跨越式的提升。
2、目前io_uring 的virtio block开发还在持续进行poll模式等高级功能没有release。很值得期待。具体参见https://wiki.qemu.org/Features/IOUring
**2、io passthrough场景**
原理:
![image](https://user-images.githubusercontent.com/87457873/149942524-a776e41b-6545-4107-b18a-fcec4b53f9de.png)
直接透传QEMU Block层
- io_uring的SQ和CQ都在GuestOS的kernel中
- qemu的virtio-blk修改了“fast path”
- 启用了polling模式
测试环境版本信息:
![image](https://user-images.githubusercontent.com/87457873/149942562-a724ac3f-f0b6-4484-b99e-921524186101.png)
创建虚拟机
aio=io_uring -device virtio-blk-pci,drive=hd1,bootindex=2,io-uring-pt=on
测试结果:
![image](https://user-images.githubusercontent.com/87457873/149942598-6875fb40-8bd6-4143-a71a-d8f04f99b43e.png)
测试数据来自:
https://static.sched.com/hosted_files/kvmforum2020/9c/KVMForum_2020_io_uring_passthrough_Stefano_Garzarella.pdf
**总结:**
1、virtio-blk的性能只有裸设备性能的一半左右。
2、io_uring passthrough的性能和裸设备持平。
3、缺点需要在guestOS的kernel中打patch。
**社区支持情况**
**1、内核支持情况**
![image](https://user-images.githubusercontent.com/87457873/149942648-62ae7e45-f25a-484c-96e2-aef574cdd63b.png)
**2、io_uring 社区开发现状**
当前 io_uring 社区开发主要聚焦在以下几个方面。
- **io_uring 框架自身的迭代优化**
前面提到io_uring 作为一种新型高性能异步编程框架,当前仍处于快速发展中。因此随着特性越来越丰富,以及各种稳定性问题的修复等等,代码也变得越来越臃肿。因此 io_uring 框架也需要同步”进化“,不断进行代码重构优化。
通常异步编程一般是将工作交由线程池来做,但对于 io_uring 来说,这只是最坏的 slow path例如异步读优化就是想尝试尽量在当前上下文中处理。另外在 sqpoll 模式下io_uring 接管用户提交的系统调用,一个系统调用的执行与特定的进程上下文相关,因此 io_uring 需要维护系统调用进程的内存上下文,文件系统上下文等大量信息。同时 io_uring 作为一种框架,框架本身的开销应该尽可能的小,才能与用户态高性能框架 SPDK 对标,因此需要持续优化。
- **特性增强**
- io_uring buffer registration 特性增强
io_uring 的 buffer registration 特性可以用来减少读写操作时频繁的 get_user_pages()/unpin_user_pages() 调用get_user_pages() 主要用来实现用户态虚拟地址到内核页的转换,会消耗一定的 cpu 资源。来自 Oracle 的同学这组patchset 让 buffer registration 特性支持更新和共享操作,更加方便用户态编程,目前已发到第三版。我们遇到一些业务也明确提出过需要 buffer registration 支持更新操作。
- fs/userfaultfd: support iouring and polling
- 来自 VMware 的同学这组 patchset 使得 userfaultfd 支持 io_uring且支持 polling 模式。在RDMA持久内存等高速场景系统调用用户态内核态上下文切换的开销在 userfualtfd 整体开销的占比会非常突出,支持 io_uring 后,可以显著减少用户态内核态上下文切换,带来明显的性能提升。
- add io_uring with IOPOLL support in scsi layer
- io_uring 出现后Linux 内核才真正完整地支持 iopolliopoll 相比于中断模式能带来明显的性能提升。此前只有nvme driver 对 iopoll 提供比较好的支持,现在 scsi 层也开始准备支持 iopoll。
- no-copy bvec
- 在用 io_uring 进行 IO 栈性能分析时,来自 Facebook 的 Pavel Begunkov 发现在 direct IO 场景下是不需要拷贝bvec 结构的,他提交的这组 patchset 可以进一步提高内核 direct IO 的性能。
- io_uring and Optane2
- block 社区已经开始重视提高 IO 栈性能,一种思路是利用高性能设备,借助 io_uring 压测 IO 栈,从而发现软件性能瓶颈。比如 Intel提供最新的 Gen2 Optane SSD 给 block / io_uring 的维护者 Jens Axboe 来做内核 IO 栈性能分析。
- fs: Support for LOOKUP_NONBLOCK / RESOLVE_NONBLOCK
- Jens Axboe 优化 vfs 的文件查询逻辑,从而可以使 io_uring 的 IORING_OP_OPENAT 操作效率更高。
- **IO 栈协同优化**
借助 io_uirng 这一高性能异步编程框架,能比较容易地发现其他内核子系统的软件性能瓶颈,从而对其进行优化,以提高内核 IO 栈的整体性能。我们之前也基于这个思路发现了 block 层的几处优化,同时提出了 device mapper polling 打通的 RFC。
**总结**
1、华云数据产品技术中心始终贴近用户解决用户的实际问题同时时刻关注业界的先进技术。得益于io_uring的优异的表现华云数据在此方面投入大量资源。同时我们在RDMA、SPDK、vdpa、DPU、io_uring、eBPF等热门技术也有相关的研究及产品化的丰富经验同时对社区不断的共享。
2、io_uring 是完全为性能而生的新一代 native async IO 模型,比 libaio 高级不少。通过全新的设计共享内存IO 过程不需要系统调用,由内核完成 IO的提交 以及Polled IO机制实现了高IOPS。相比kernel bypass这种原生内核的方式显得友好一些。一定程度上io_uring代表了Linux kernel block层的未来至少从中我们可以看出一些block层进化的方向而且我们看到io_uring也在快速发展相信未来io_uring会更加的高效、易用并拥有更加丰富的功能和更强的扩展能力。这让我们充满期待NVMe的存储时代需要一个属于自己的高速IO引擎。
3、在kvm方面qemu支持io_uring在操作和运维方便比spdk的vhost简单很多。虽然支持的还不是很完善但社区仍然在积极推进。希望这个早日release。
4、国内阿里在io_uring走的比较靠前阿里有个专门的内核组在做相关的开发。OpenAnolis已经将io_uring纳入并且在io_uring方面做了很多探索。通过社区的一些文章可以看到阿里在数据库、web、echo_sever等相关的领域都已经应用了io_uring。
> 原文链接https://www.huayun.com/news/1521.html

View File

@@ -0,0 +1,145 @@
#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <fcntl.h>
#include <liburing.h>
#include <string.h>
#define BUF_SIZE 512
#define FILE_NAME1 "/tmp/io_uring_sq_test.txt"
#define STR1 "What is this life if, full of care,\n"
#define STR2 "We have no time to stand and stare."
void print_sq_poll_kernel_thread_status() {
if (system("ps --ppid 2 | grep io_uring-sq" ) == 0)
printf("Kernel thread io_uring-sq found running...\n");
else
printf("Kernel thread io_uring-sq is not running.\n");
}
int start_sq_polling_ops(struct io_uring *ring) {
int fds[2];
char buff1[BUF_SIZE];
char buff2[BUF_SIZE];
char buff3[BUF_SIZE];
char buff4[BUF_SIZE];
struct io_uring_sqe *sqe;
struct io_uring_cqe *cqe;
int str1_sz = strlen(STR1);
int str2_sz = strlen(STR2);
fds[0] = open(FILE_NAME1, O_RDWR | O_TRUNC | O_CREAT, 0644);
if (fds[0] < 0 ) {
perror("open");
return 1;
}
memset(buff1, 0, BUF_SIZE);
memset(buff2, 0, BUF_SIZE);
memset(buff3, 0, BUF_SIZE);
memset(buff4, 0, BUF_SIZE);
strncpy(buff1, STR1, str1_sz);
strncpy(buff2, STR2, str2_sz);
int ret = io_uring_register_files(ring, fds, 1);
if(ret) {
fprintf(stderr, "Error registering buffers: %s", strerror(-ret));
return 1;
}
sqe = io_uring_get_sqe(ring);
if (!sqe) {
fprintf(stderr, "Could not get SQE.\n");
return 1;
}
io_uring_prep_write(sqe, 0, buff1, str1_sz, 0);
sqe->flags |= IOSQE_FIXED_FILE;
sqe = io_uring_get_sqe(ring);
if (!sqe) {
fprintf(stderr, "Could not get SQE.\n");
return 1;
}
io_uring_prep_write(sqe, 0, buff2, str2_sz, str1_sz);
sqe->flags |= IOSQE_FIXED_FILE;
io_uring_submit(ring);
for(int i = 0; i < 2; i ++) {
int ret = io_uring_wait_cqe(ring, &cqe);
if (ret < 0) {
fprintf(stderr, "Error waiting for completion: %s\n",
strerror(-ret));
return 1;
}
/* Now that we have the CQE, let's process the data */
if (cqe->res < 0) {
fprintf(stderr, "Error in async operation: %s\n", strerror(-cqe->res));
}
printf("Result of the operation: %d\n", cqe->res);
io_uring_cqe_seen(ring, cqe);
}
print_sq_poll_kernel_thread_status();
sqe = io_uring_get_sqe(ring);
if (!sqe) {
fprintf(stderr, "Could not get SQE.\n");
return 1;
}
io_uring_prep_read(sqe, 0, buff3, str1_sz, 0);
sqe->flags |= IOSQE_FIXED_FILE;
sqe = io_uring_get_sqe(ring);
if (!sqe) {
fprintf(stderr, "Could not get SQE.\n");
return 1;
}
io_uring_prep_read(sqe, 0, buff4, str2_sz, str1_sz);
sqe->flags |= IOSQE_FIXED_FILE;
io_uring_submit(ring);
for(int i = 0; i < 2; i ++) {
int ret = io_uring_wait_cqe(ring, &cqe);
if (ret < 0) {
fprintf(stderr, "Error waiting for completion: %s\n",
strerror(-ret));
return 1;
}
/* Now that we have the CQE, let's process the data */
if (cqe->res < 0) {
fprintf(stderr, "Error in async operation: %s\n", strerror(-cqe->res));
}
printf("Result of the operation: %d\n", cqe->res);
io_uring_cqe_seen(ring, cqe);
}
printf("Contents read from file:\n");
printf("%s%s", buff3, buff4);
}
int main() {
struct io_uring ring;
struct io_uring_params params;
if (geteuid()) {
fprintf(stderr, "You need root privileges to run this program.\n");
return 1;
}
print_sq_poll_kernel_thread_status();
memset(&params, 0, sizeof(params));
params.flags |= IORING_SETUP_SQPOLL;
params.sq_thread_idle = 2000;
int ret = io_uring_queue_init_params(8, &ring, &params);
if (ret) {
fprintf(stderr, "Unable to setup io_uring: %s\n", strerror(-ret));
return 1;
}
start_sq_polling_ops(&ring);
io_uring_queue_exit(&ring);
return 0;
}

View File

@@ -0,0 +1,416 @@
# LLVM是什么
LLVM项目是可重用(reusable)、模块化(modular)的编译器以及工具链技术(toolchain technologies)的集合。有人将其理解为“底层虚拟机(Low Level Virtual Machine)”的简称,但是[官方](https://llvm.org/)原话为:
> “The name “LLVM” itself is not an acronym; it is the full name of the project.”
意思是LLVM不是首字母缩写而是这整个项目的全名。
LLVM项目的发展起源于2000年伊利诺伊大学厄巴纳-香槟分校维克拉姆·艾夫Vikram Adve与克里斯·拉特纳Chris Lattner的研究他们想要为所有静态及动态语言创造出动态的编译技术。2005年苹果计算机雇用了克里斯·拉特纳及他的团队为苹果计算机开发应用程序系统LLVM为现今Mac OS X及iOS开发工具的一部分。
# 1.LLVM&&clang安装
官网安装教程在[这里](http://clang.llvm.org/get_started.html)。这里简单介绍一下。
## 1.1Linux环境
### 1.1.1 安装前注意事项
由于本人使用的是虚拟机,所以在创建虚拟机的时候需要分配较大的内存和磁盘空间。这里踩了很多坑。
### 1.1.2 下载有关库
```
$ sudo apt-get install cmake
$ sudo apt-get install git
$ sudo apt-get install gcc
$ sudo apt-get install g++
```
### 1.1.3 下载项目源码
可以选择直接git整个工程也可以去[官网](http://releases.llvm.org/download.html)下载源码然后自行安装。这里就按官网的git方法。
```
$ git clone https://github.com/llvm/llvm-project.git
```
下载好后会在当前目录下看到llvm-project文件夹。
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/1.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/1.jpg)
### 1.1.4 构建项目
```
$ cd llvm-project
```
创建构建目录
```
$ mkdir build
$ cd build
```
利用cmake构建
```
$ cmake -G <generator> [options] ../llvm
```
常用的generator有
- Ninja ——生成Ninja 文件
- Unix Makefiles ——生成兼容make的makefile文件
- Visual Studio ——生成Visual Studio项目与解决方案
- Xcode ——用于生成Xcode项目
个人使用Unix Makefiles
常用的options有
- DCMAKE_INSTALL_PREFIX=directory ——为目录指定要在其中安装LLVM工具和库的完整路径名默认/usr/local
- DCMAKE_BUILD_TYPE=type ——type选项有DebugReleaseRelWithDebInfo和MinSizeRel。默认值为Debug。
- DLLVM_ENABLE_ASSERTIONS=On ——启用断言检查进行编译。
- DLLVM_ENABLE_PROJECTS=”…” ——要另外构建的LLVM子项目的列表;分隔。例如要构建LLVMClanglibcxx和libcxxabi使用:`DLLVM_INSTALL_PROJECTS="clang;libcxx;libcxxabi"`
- DLLVM_TARGETS_TO_BUILD=”…” ——构建针对的平台的部分项目,以’;’分隔。默认面向所有平台编译(all)指定只编译自己需要的CPU架构可以节省时间。
官方文档在[](http://llvm.org/docs/CMake.html#options-and-variables)。
由于全部构建真的很耗费资源和时间我使用的构建clang命令可供参考
```
$ cmake -DCMAKE_BUILD_TYPE=Release -DLLVM_TARGETS_TO_BUILD="X86" -DLLVM_ENABLE_PROJECTS=clang -DLLVM_USE_LINKER=gold -G "Unix Makefiles" ../llvm
```
当然你如果愿意(~~而且设备跑得动~~)也可以:
```
$ cmake -G "Unix Makefiles" ../llvm
```
### 1.1.5 编译
```
$ make [-j <core>]
$ sudo make install
```
直接make也可以但LLVM也支持并行编译其中core取决于核心数。如
```
$ make -j 4
```
这两步一般会很久……
### 1.1.6 测试
在编译结束后尝试在命令行中使用clang
```
$ clang -v
```
本人得到结果如下:
```
clang version 10.0.0
Target: x86_64-unknown-linux-gnu
Thread model: posix
InstalledDir: /usr/local/bin
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
Found candidate GCC installation: /usr/lib/gcc/x86_64-linux-gnu/8
Selected GCC installation: /usr/lib/gcc/x86_64-linux-gnu/7.4.0
Candidate multilib: .;@m64
Selected multilib: .;@m64
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/2.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/2.jpg)
编写一段C语言代码试试看(C++也可以):
```
//helloworld.c
#include <stdio.h>
int main() {
printf("hello world\n");
return 0;
}
```
用clang编译
```
$ clang helloworld.c -o hello.out
$ ./hello.out
```
如果是C++代码则:
```
//helloworld.cpp
#include <iostream>
using namespace std;
int main() {
cout << "hello world" << endl;
return 0;
}
```
用clang编译注意命令是clang++本人刚开始只写clang提示编译错误…
```
$ clang++ helloworld.cpp -o hello.out
$ ./hello.out
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/3.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/3.jpg)
大功告成!
## 1.2.Windows环境
llvm+clang在windows下有两种类型一种利用mingw编译使用gcc的库和头文件另一种利用vc编译使用vc的库和头文件。推荐使用vc编译的版本因为其程序更原生且不依赖于mingw而只依赖于vc的dll库。
具体步骤[官方的教程](http://clang.llvm.org/get_started.html)已经说得很明白啦,下面简单整理介绍一下。
### 1.2.1 安装工具
- **Git.** 下载源代码。下载地址https://git-scm.com/download
- **Cmake.** 用于生成Visual Studio的解决方案和项目文件。下载地址https://cmake.org/download/
- **Visual Studio(推荐2017或之后版本).** 生成项目的容器。
- **Python.** 用于运行clang测试套件。下载地址https://www.python.org/download/
- **GnuWin32 tools.** clang和LLVM的测试套件需要GNU工具。下载地址http://getgnuwin32.sourceforge.net/
### 1.2.2 clone项目源代码
你也可以选择去github上直接download。
```
$ git clone https://github.com/llvm/llvm-progect.git
```
### 1.2.3 Cmake编译
在项目总目录下新建build目录避免污染项目源代码
```
$ mkdir build
$ cd build
```
运行Cmake。
```
$ cmake -DLLVM_ENABLE_PROJECTS=clang -G "Visual Studio 15 2017" -A x64 -Thost=x64 ..\llvm
```
- 如果使用Visual Studio 2019则改为 `-G "Visual Studio 16 2019"`
- 如果需要适配x86则改为 `-A Win32`
### 1.2.4 构建build
在Visual Studio中打开LLVM.sin右击ALL_BUILD项目选择生成等待数小时。
### 1.2.5 设置环境变量
将llvm/debug/bin 添加到自己的环境变量中。
其余具体测试步骤请看[Hacking on clang - Testing using Visual Studio on Windows](http://clang.llvm.org/hacking.html#testingWindows)。
有待补充…
# 2.LLVM简介
用户文档:[llvm.org/docs/LangRef.html](https://llvm.org/docs/LangRef.html)
LLVM是基于静态单一分配的表示形式可提供类型安全性、底层操作、灵活性并且适配几乎所有高级语言具有通用的代码表示。现在LLVM已经成为多个编译器和代码生成相关子项目的母项目。
> The LLVM code representation is designed to be used in three different forms: as an in-memory compiler IR, as an on-disk bitcode representation (suitable for fast loading by a Just-In-Time compiler), and as a human readable assembly language representation.
其中LLVM提供了完整编译系统的中间层并将中间语言Intermediate Repressentation, IR从编译器取出并进行最优化最优化后的IR接着被转换及链接到目标平台的汇编语言。
我们知道,传统的编译器主要结构为:
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/4.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/4.jpg)
[传统编译器结构,图片摘自网络](https://clheveningflow.github.io/2019/09/28/LLVM1/4.jpg)
Frontend前端词法分析、语法分析、语义分析、生成中间代码
Optimizer优化器进行中间代码优化
Backend后端生成机器码
LLVM主要结构为
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/5.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/5.jpg)
[LLVM结构图片摘自网络](https://clheveningflow.github.io/2019/09/28/LLVM1/5.jpg)
也就是说对于LLVM来说不同的前后端使用统一的中间代码LLVM IR。如果需要支持一种新的编程语言/硬件设备,那么只需要实现一个新的前端/后端就可以了而优化截断是一个通用的阶段针对统一的LLVM IR都不需要对于优化阶段修改。对比GCC其前端和后端基本耦合在一起所以GCC支持一门新的语言或者目标平台会变得很困难。
以下内容摘自维基百科:
> LLVM也可以在编译时期、链接时期甚至是运行时期产生可重新定位的代码Relocatable Code
> LLVM支持与语言无关的指令集架构及类型系统。每个在静态单赋值形式SSA的指令集代表着每个变量被称为具有类型的寄存器仅被赋值一次这简化了变量间相依性的分析。LLVM允许代码被静态的编译包含在传统的GCC系统底下或是类似JAVA等后期编译才将IF编译成机器代码所使用的即时编译JIT技术。它的类型系统包含基本类型整数或是浮点数及五个复合类型指针、数组、向量、结构及函数在LLVM具体语言的类型建制可以以结合基本类型来表示举例来说C++所使用的class可以被表示为结构、函数及函数指针的数组所组成。
> LLVM JIT编译器可以最优化在运行时期时程序所不需要的静态分支这在一些部分求值Partial Evaluation的案例中相当有效即当程序有许多选项而在特定环境下其中多数可被判断为是不需要。这个特色被使用在Mac OS X Leopardv10.5底下OpenGL的管线当硬件不支持某个功能时依然可以被成功地运作。OpenGL堆栈下的绘图程序被编译为IR接着在机器上运行时被编译当系统拥有高端GPU时这段程序会进行极少的修改并将传递指令给GPU当系统拥有低级的GPU时LLVM将会编译更多的程序使这段GPU无法运行的指令在本地端的中央处理器运行。LLVM增进了使用Intel GMA芯片等低端机器的性能。一个类似的系统发展于Gallium3D LLVMpipe它已被合并到GNOME使其可运行在没有GPU的环境。
> 根据2011年的一项测试GCC在运行时期的性能平均比LLVM高10%。而2013年测试显示LLVM可以编译出接近GCC相同性能的运行码。
# 3.Clang简介
Clang是LLVM针对C语言及其家族语言的前端(a C language family frontend for LLVM)。它的主要目标是提供一个GNU编译器套装GCC的替代品支持GNU编译器大多数便已设置以及非官方语言拓展。项目包括Clang前端和Clang静态分析器。
> The Clang project provides a language front-end and tooling infrastructure for languages in the C language family (C, C++, Objective C/C++, OpenCL, CUDA, and RenderScript) for the LLVM project. Both a GCC-compatible compiler driver (clang) and an MSVC-compatible compiler driver (clang-cl.exe) are provided. You can get and build the source today.
Clang项目为LLVM项目中的C语言家族提供了一个语言前端和工具基础设施。其提供了兼容GCC和MSVC的编译器驱动程序clang和clang-cl.exe
官方手册http://clang.llvm.org/docs/UsersManual.html#basicusage
针对于GCCClang的优点有
- 占用内存小
- 设计清晰简单,容易理解
- 编译速度快
- 设计偏向模块化,易于集成
- 诊断信息可读性强
## Clang(Clang++)使用
我们先随便写一段以下代码:
```
//test.cpp
#include <iostream>
#include <algorithm>
using namespace std;
int a[10] = {4,2,7,5,6,1,8,9,3,0};
int main() {
for(int i = 0; i < 10; ++i)
cout << a[i] << (i == 9?"\n":" ");
sort(a,a+10);
for(int i = 0; i < 10; ++i)
cout << a[i] << (i == 9?"\n":" ");
return 0;
}
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/6.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/6.jpg)
### 3.1.生成预处理文件:
```
$ clang++ -E test.cpp -o test.i
```
生成文件test.i部分如下
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/7.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/7.jpg)
### 3.2.生成汇编程序:
```
$ clang++ -S test.i
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/8.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/8.jpg)
### 3.3.生成目标文件:
```
$ clang++ -c test.s
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/9.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/9.jpg)
### 3.4.生成可执行文件:
```
$ clang++ -o test.out test.o
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/10.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/10.jpg)
emm…~~和GCC大致还是一样的嘛~~ 非常的好用。
### 3.5.查看Clang编译的过程
```
$ clang -ccc-print-phases A.c
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/11.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/11.jpg)
- 0.获取输入A.c文件C语言
- 1.预处理器处理define、include等
- 2.编译生成中间代码IR
- 3.后端:生成汇编代码
- 4.汇编:生成目标代码
- 5.链接器:链接其他动态库
### 3.6.词法分析
```
$ clang -fmodules -E -Xclang -dump-tokens A.c
```
如图,写一个小函数对其进行词法分析。
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/12.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/12.jpg)
### 3.7.语法分析
```
$ clang -fmodules -fsyntax-only -Xclang -ast-dump A.c
```
生成语法树如下:
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/13.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/13.jpg)
有颜色区分还是比较美观的。
### 3.8.语义分析
生成LLVM IR。LLVM IR有3种表示形式本质是等价的
- (1).text:便于阅读的文本格式,类似于汇编语言,拓展名.ll
- (2).memory:内存格式
- (3).bitcode:二进制格式,拓展名.bc
生成text格式
```
$ clang -S -emit-llvm A.c
```
[![LLVM](https://clheveningflow.github.io/2019/09/28/LLVM1/14.jpg)](https://clheveningflow.github.io/2019/09/28/LLVM1/14.jpg)
# 4.学习体会
安装LLVM绝对是一件痛苦的事情我在Linux上安装LLVM+Clang起码花费有40小时Windows上还没有成功55555读了很久文档才搞清楚cmake的各种选项的用处然后就是漫长的等待…
对编译原理的学习才刚刚起步读了一下Clang的用户文档很多选项都~~搞不明白,~~没有机会使用但是经过使用后感觉LLVM真的是一个很强大的模块化编译器工具集合而Clang的各种编译选项确实可以帮助理解编译的各个流程光听编译原理课程是看不见一个实际的编译器是如何生成词法分析、语法分析的结果或者中间代码的。
LLVM不仅仅是编译器那么简单我们可以利用其做各种NB的操作比如开发新的编译器、新的编程语言、开发编译器插件、进行代码规范检查。它也绝对不是IOS领域特有的因为它是一个高度模块化、可重用的组件可适用于多门编程语言和多个硬件设备平台。可以说大部分从事计算机工作的人都该懂点LLVM而绝不仅仅只是开发者。
友情链接:
LLVM官网[http://llvm.org](http://llvm.org/)
Clang官网[http://clang.llvm.org](http://clang.llvm.org/)
LLVM用户文档[llvm.org/docs/LangRef.html](https://llvm.org/docs/LangRef.html)
Clang用户文档http://clang.llvm.org/docs/UsersManual.html