Linux kernel Wikipedia

http://en.wikipedia.org/wiki/Linux_kernel

Development model

The current development model of the Linux kernel is such that Linus Torvalds makes the releases of new versions, also called the "vanilla" or "mainline" kernels, meaning that they contain the main, generic branch of development. This branch is officially released as a new version approximately every three months, after Torvalds does an initial round of integrating major changes made by all other programmers, and several rounds of bug-fix pre-releases.

In the current scheme, the main branch of development is not a traditional "stable" branch, instead it incorporates all kinds of changes, both the latest features as well as security and bug fixes. For users who do not want to risk updating to new versions containing code that may not be well tested, a separate set of "stable" branches exist, one for each released version, which are meant for people who just want the security and bug fixes, but not a whole new version. These branches are maintained by the stable team (Greg Kroah-Hartman, Chris Wright, and others).

Most Linux users use a kernel supplied by their Linux distribution. Some distributions ship the "vanilla" and/or "stable" kernels. However, several Linux distribution vendors (such as Red Hat and Debian) maintain another set of Linux kernel branches which are integrated into their products. These are by and large updated at a slower pace compared to the "vanilla" branch, and they usually include all fixes from the relevant "stable" branch, but at the same time they can also add support for drivers or features which had not been released in the "vanilla" version the distribution vendor started basing their branch from.

The development model for Linux 2.6 was a significant change from the development model for Linux 2.5. Previously there was a stable branch (2.4) where only relatively minor and safe changes were merged, and an unstable branch (2.5), where bigger changes and cleanups were allowed. Both of these branches had been maintained by the same set of people, led by Torvalds. This meant that users would always have a well-tested 2.4 version with the latest security and bug fixes to use, though they would have to wait for the features which went into the 2.5 branch. The downside of this was that the "stable" kernel ended up so far behind that it no longer supported recent hardware and lacked needed features. In the late 2.5.x series kernel some maintainers elected to try and back port their changes to the stable series kernel which resulted in bugs being introduced into the 2.4.x series kernel. The 2.5 branch was then eventually declared stable and renamed to 2.6. But instead of opening an unstable 2.7 branch, the kernel developers decided to continue putting major changes into the 2.6 branch, which would then be released at a pace faster than 2.4.x but slower than 2.5.x. This had the desirable effect of making new features more quickly available and getting more testing of the new code, which was added in smaller batches and easier to test.

As a response to the lack of a stable kernel tree where people could coordinate the collection of bug fixes as such, in December 2005 Adrian Bunk announced that he would keep releasing 2.6.16.y kernels when the stable team moved on to 2.6.17.[92] He also included some driver updates, making the maintenance of the 2.6.16 series very similar to the old rules for maintenance of a stable series such as 2.4.[93] Since then, the "stable team" had been formed, and it would keep updating kernel versions with bug fixes. In October 2008 Adrian Bunk announced that he will maintain 2.6.27 for a few years as a replacement of 2.6.16.[94] The stable team picked up on the idea[95] and as of 2010 they continue to maintain that version and release bug fixes for it, in addition to others.

After the change of the development model with 2.6.x, developers continued to want what one might call an unstable kernel tree, one that changes as rapidly as new patches come in. Andrew Morton decided to repurpose his -mm tree from memory management to serve as the destination for all new and experimental code. In September 2007 Morton decided to stop maintaining this tree.[96] In February 2008, Stephen Rothwell created the linux-next tree to serve as a place where patches aimed to be merged during the next development cycle are gathered.[97][98] Several subsystem maintainers also adopted the suffix -next for trees containing code which is meant to be submitted for inclusion in the next release cycle.

mm tree

Among Linux kernel developers, the -mm tree refers to a version of the kernel source code maintained by Andrew Morton.

The -mm kernel tree used to fill the role of Linux kernel development builds, formerly identified by odd version numbers following "2.6." (see this section on Linux kernel version numbering). New and experimental code used to find its way into a 2.6.x-mm kernel build. Historically, the -mm tree focused on new developments for the memory management part of the kernel (mm).

Occasionally, the -mm tree was overloaded with new patches, so testing it became overly difficult. On Sep 17 2007, Andrew Morton sent a mail saying that "this just isn't working any more". The presence of the new linux-next git repository has offloaded much of the work that made mm maintenance so difficult, allowing Andrew Morton to continue to use quilt to manage his series of "mmotm" (mm of the moment) patches.

Andrew Morton includes a subset of the mmotm patches in linux-next, which has a head called "linux-next/akpm". There is also a git tree that includes the patches that appear in releases of the mm tree. To get all of the mm patches at any given time, developers still need quilt(quilt是一个帮助我们管理补丁的程序) or ad hoc shell scripts to apply the full set of patches.

Andrew Keith Paul Morton (born 1959) is an Australian software engineer, best known as one of the lead developers of the Linux kernel. He is currently a co-maintainer of the Ext3 file system and the journaling layer for block devices (JBD).

Morton maintains a Linux kernel patchset known as the mm tree, which contains work-in-progress patches that might later be accepted into the official Linux tree maintained by Linus Torvalds. "mm" as a primary testing ground became unmanageably large and busy, and in 2008 the "linux-next" tree was created to fill much of this role.

https://www.kernel.org/category/releases.html

Active kernel releases

There are several main categories into which kernel releases may fall:
Prepatch
Prepatch or "RC" kernels are mainline kernel pre-releases that are mostly aimed at other kernel developers and Linux enthusiasts. They must be compiled from source and usually contain new features that must be tested before they can be put into a stable release. Prepatch kernels are maintained and released by Linus Torvalds.
Mainline
Mainline tree is maintained by Linus Torvalds. It's the tree where all new features are introduced and where all the exciting new development happens. New mainline kernels are released every 2-3 months.
Stable
After each mainline kernel is released, it is considered "stable." Any bug fixes for a stable kernel are backported from the mainline tree and applied by a designated stable kernel maintainer. There are usually only a few bugfix kernel releases until next mainline kernel becomes available -- unless it is designated a "longterm maintenance kernel." Stable kernel updates are released on as-needed basis, usually 2-3 a month.
Longterm
There are usually several "longterm maintenance" kernel releases provided for the purposes of backporting bugfixes for older kernel trees.(方便使用旧版本kernel的用户) Only important bugfixes are applied to such kernels and they don't usually see very frequent releases, especially for older trees.
Longterm release kernels
VersionMaintainerReleasedProjected EOL
3.10 Greg Kroah-Hartman 2013-06-30 Sep, 2015
3.4 Greg Kroah-Hartman 2012-05-20 Oct, 2014
3.2 Ben Hutchings 2012-01-04 2016
3.0 Greg Kroah-Hartman 2011-07-22 Oct, 2013
2.6.34 Paul Gortmaker 2010-05-16 Mid-2013
2.6.32 Willy Tarreau 2009-12-03 Mid-2014

Distribution kernels

Many Linux distributions provide their own "longterm maintenance" kernels that may or may not be based on those maintained by kernel developers. These kernel releases are not hosted at kernel.org and kernel developers can provide no support for them.

It is easy to tell if you are running a distribution kernel. Unless you downloaded, compiled and installed your own version of kernel from kernel.org, you are running a distribution kernel. To find out the version of your kernel, run uname -r:

# uname -r
3.7.5-201.fc18.x86_64

If you see anything at all after the dash, you are running a distribution kernel. Please use the support channels offered by your distribution vendor to obtain kernel support.

https://www.kernel.org/category/faq.html

What does "stable/EOL" and "longterm" mean?

As kernels move from the "mainline" into the "stable" category, two things can happen:

  1. They can reach "End of Life" after a few bugfix revisions, which means that kernel maintainers will release no more bugfixes for this kernel version, or
  2. They can be put into "longterm" maintenance, which means that maintainers will provide bugfixes for this kernel revision for a much longer period of time.

If the kernel version you are using is marked "EOL," you should consider upgrading to the next major version as there will be no more bugfixes provided for the kernel version you are using.

Please check the Releases page(above) for more info.

2011年7月22日,Linus亲自发布了Linux Kernel 3.0版本。虽然从内核的更新内容上跟普通的2.6.40没什么两样,不过版本号的修改,无疑在Linux内核20周年之际,被赋予了特殊的意义。

20年走来,Linux内核早已不是一开始Linus单打独斗开发的模式。各个Linux相关企业的开发者和很多单独的、因为爱好而参与的开发者都被有序的组织在一起。

下面,我们将以Linux Kernel 3.0为例,介绍一下现在的Linux内核是如何开发的。

主干负责人:Linus Torvalds

当下,Linux内核版本众多,每个主版本都有一个专人负责维护,叫做Maintainer。比如Kernel 2.6.32的负责人是Greg Kroah-Hartman(目前在Novell任职),Kernel 2.6.35的负责人是Andi Kleen(曾在Novell任职,目前在Intel开源技术中心)。而按照惯例,每一个最新的主分支,都是由Linus本人进行维护和发布。Linux 3.0也不例外。

Maintainer的主要工作是将其他开发者提供的代码和补丁集成到一起,并在发布前测试修改各种bug。

其他分支负责人

其实Linux内核的开发模式就是一个树状模式,使用过Git的开发者们应该会比较了解。一般来说,开发者们如果要对Linux内核做一些修改,那 么他首先会在Git上将Linus的主干代码复制一份,形成自己的分支。在Git系统上,每个开发者在建立的分支上进行的每一个变更都有一个单独的ID, 当开发者认为自己进行的变更没有问题的时候,可以申请让主要分支的负责人将自己的变更“拉”入其负责的分支当中。

说到这里就要说一下Linux内核目前的几个分支。最新的分支mainline里面包含最新的特性,但是由于缺乏测试,并不建议在生产环境中使用; 标注stable的分支则是每一个之前发布的版本都有,进入stable之后的版本只会进行安全补丁和bug更新,不会再添加任何新特性。另外还有 snapshot、linux-next等分支,里面包含了很多比较新的变更。大部分Linux发行版包含的内核都是来自stable分支,而有些发行版 则会在这些分支上进行一些定制(比如红帽和Debian),当然也会有自己的负责人。

可以说,Linux内核开发的分工模式就像一棵树一样,每一个开发者都有一条自己的分支,而且每一条分支都和主干有直接的联系。

所以,Linux内核的开发者社区其实是一个扁平的结构,除了几条主干的负责人之外,其他上千开发者并没有什么明确的分工,大家主要以“为 Linux内核贡献了多少代码”作为贡献多少的判定——也就是说,你有多少代码被接受并入了mainline或stable分支当中,你就是更加重量级的 开发者。无论你的代码是关于CPU、显卡、网卡、虚拟化还是别的什么,全部一视同仁(当然,在主干维护者那里会有自己的优先级)。

Version numbering

The Linux kernel has had three different numbering schemes.

The first scheme was used in the run-up to "1.0". The first version of the kernel was 0.01. This was followed by 0.02, 0.03, 0.10, 0.11, 0.12 (the first GPL version), 0.95, 0.96, 0.97, 0.98, 0.99 and then 1.0.[158] From 0.95 on there were many patch releases between versions.

After the 1.0 release and prior to version 2.6, the number was composed as "A.B.C", where the number A denoted the kernel version, the number B denoted the major revision of the kernel, and the number C indicated the minor revision of the kernel. The version was changed only when major changes in the code and the concept of the kernel occurred, twice in the history of the kernel: in 1994 (version 1.0) and in 1996 (version 2.0). Version 3.0 was released in 2011, but it was not a major change in kernel concept. The major revision was assigned according to the traditional even-odd system version numbering system. The minor revision had been changed whenever security patches, bug fixes, new features or drivers were implemented in the kernel.

In 2004, after version 2.6.0 was released, the kernel developers held several discussions regarding the release and version scheme[159][160] and ultimately Linus Torvalds and others decided that a much shorter "time-based" release cycle would be beneficial. For about seven years, the first two numbers remained "2.6", and the third number was incremented with each new release, which rolled out after two to three months. A fourth number was sometimes added to account for bug and security fixes (only) to the kernel version. The even-odd system of alternation between stable and unstable was gone.

On 29 May 2011, Linus Torvalds announced[161] that the kernel version would be bumped to 3.0 for the release following 2.6.39, to commemorate the 20th anniversary of Linux. It continued the time-based release practice introduced with 2.6.0, but using the second number—i.e. 3.1 would follow 3.0 after a few months. An additional number (now the third number) would be added on when necessary to designate security and bug fixes, as for example with 3.0.18.

The first use of the fourth number occurred when a grave error, which required immediate fixing, was encountered in 2.6.8's NFS code. However, there were not enough other changes to legitimize the release of a new minor revision (which would have been 2.6.9). So, 2.6.8.1 was released, with the only change being the fix of that error. With 2.6.11, this was adopted as the new official versioning policy. Later it became customary to continuously back-port major bug-fixes and security patches to released kernels and indicate that by updating the fourth number.

Regular development pre-releases are titled release candidates, which is indicated by appending the suffix 'rc' to the kernel version, followed by an ordinal number.

Also, sometimes the version will have a suffix such as 'tip', indicating another development branch, usually (but not always) the initials of a person who made it. For example, 'ck' stands for Con Kolivas, 'ac' stands for Alan Cox, etc. Sometimes, the letters are related to the primary development area of the branch the kernel is built from, for example, 'wl' indicates a wireless networking test build. Also, distributors may have their own suffixes with different numbering systems and for back-ports to their "enterprise" (i.e. stable but older) distribution versions.

版本命名

Linux内核有三个不同的命名方案。

早期版本:
第一个版本的内核是0.01。其次是0.02,0.03,0.10,0.11,0.12(第一GPL版本),0.95,0.96,0.97,0.98,0.99及1.0。[48]

从0.95版有许多的补丁发布于主要版本版本之间。

旧计划(1.0和2.6版之间),版本的格式为A.B.C,其中A,B,C代表:
A大幅度转变的内核。这是很少发生变化,只有当发生重大变化的代码和内核发生才会发生。在历史上曾改变两次的内核:1994年的1.0及1996年的2.0。
B是指一些重大修改的内核。
内核使用了传统的奇数次要版本号码的软件号码系统(用偶数的次要版本号码来表示稳定版本)。
C是指轻微修订的内核。这个数字当有安全补丁,bug修复,新的功能或驱动程序,内核便会有变化。

自2.6.0(2003年12月)发布后,人们认识到,更短的发布周期将是有益的。自那时起,版本的格式为A.B.C.D,其中A,B,C,D代表:
A和B是无关紧要的
C是内核的版本
D是安全补丁

自3.0(2011年7月)发布后,版本的格式为3.A.B,其中A,B代表:
A是内核的版本
B是安全补丁

关于Linux内核的开发模式

研究过Linux内核的人都知道,Linux内核开发一般是分两个支流(branch)的,一个是第二个数字为偶数的版本号如2.4.x,是稳定性比较好的版本;另一个是第二个数字为奇数的版本如2.5.x,里面有很多新功能,但是不稳定。

但是在去年的kernel summit大会中,大家决定了不需要为2.6内核引入一个2.7的支流。因为Linus Torvalds现在和Andrew
Morton现在合作的也很愉快,他们可以先把新的东西放到Andrew的-mm内核中,然后进行测试,没有问题的话再并入2.6内核中。对于2.6内核,有人总结说:

"Andrew’s vision, as expressed at the summit, is that the mainline
kernel will be the fastest and most feature-rich kernel around, but
not, necessarily, the most stable. Final stabilization is to be done by
distributors (as happens now, really), but the distributors are
expected to merge their patches quickly."

也就是Linux内核的稳定性交给了RedHat之类的开发商。对于开发商,这自然是好事,他们买的本来就是服务,现在就有更多的理由让你掏钱买它的发行 版了。对于我们这种个人用户,应该也不算坏事,至少我们总是会有fedora、gentoo之类的Linux可用,虽然性能、稳定性等发面肯定比RHEL 之类的要差一些;对于喜欢研究内核的,自然是好事了,因为总是可以最快的得到最新的功能特性。

之所以在十个月之后再提到这件事,主要是因 为最近LKML的一些列邮件基本上给新的开发模式定下了结论:更好的设计,更多的测试,更快速的更新,以及不会引入一些在将奇数版本的功能合并入偶数版本 时常出现的莫名其妙的错误(因为合并的内容很多,往往这类错误很难找到错误源)。总之:

"Things get merged one change at a time, and stabilised one change at a
time. This is a big change from the even/odd numbered kernel series,
where sometimes a bug crops up without anybody knowing exactly what
change introduced it. The current development model seems to go much
smoother than anything I’ve seen before."

 ARM Linux Developer - rmk's GIT tree

http://www.arm.linux.org.uk/developer/git-arm.php

About GIT Trees

GIT is an distributed source control system, created by Linus Torvalds, and is widely used by kernel developers and others to manage source trees. For the kernel, anyone with an up-to-date copy of Linus' GIT tree has the complete kernel history back to v2.6.12-rc2 stored locally.

A commit in the GIT sense is a container for a set of changes to a set of files, which gives an overall explanation of the change and various attributions. It will normally encompass one logical change. In the classical emailed patch sense, the commit text is equivalent to the textual description at the beginning of the email, and the set of changes are equivalent to the appended patch.

GIT trees can be merged together via a 'git pull', which can fetch the history and changes from another git tree into your local tree.

GIT allows the creation of multiple branches of development, sometimes called 'topic branches' which can then be merged together.

A full description of GIT is available from other sources; please refer to the git documentation for a more complete description.

Stephen Rothwell's linux-next Tree

Stephen Rothwell runs a project called linux-next, which is a testing ground for GIT trees. Stephen merges lots of GIT trees together from different parts of the Linux community, and then tries to build the result for several kernel configurations. Full details can be found on the linux-next project site.

As part of this activity, Stephen generates reports on merge conflicts between trees, copying them to the conflicting tree owners and people mentioned in the responsible conflicting commits, as well as reporting some build failures.

RMK's GIT Tree - General Operation

Russell uses git as a means of storing and sorting patches, and queueing up work for submission to mainline. These patches are organised as a set of `topic' branches, which are created as necessary.

It is important to note that there are two types of branches - stable and unstable - but there is no way to know by looking at a GIT tree whether a any particular branch is stable or unstable. This depends entirely on the behaviour of the tree owner, and how the tree owner operates that tree.

  • Stable branches guarantee that any commit which has been published becomes immutable. This branch can be relied upon as a basis for further work, and can be pulled into other git trees without fear that the commits may become invalidated.
  • Unstable branches do not give that guarantee, and the commits found on such branches may be deleted, updated, re-based, or even the entire branch thrown away. Commit messages may be updated to include the 'acked-by', 'reviewed-by' and 'tested-by' attributions which are a normal part of the way the Linux community reviews and handles patches. For this reason, unstable branches must never be used as a basis for further work.

It is possible through agreement that an unstable branch can be converted to being a stable branch - but this requires discussion with the tree owner.

Examples of topic branches are: `aaci' for ARMs AC'97 audio codec interface, `mmci' for ARMs multimedia card interface, `ep93xx' for Cirrus EP93xx related patches and `p2v' for the dynamic physical to virtual mapping patches. These branches generally start off as 'unstable' as comments are received and attributions added, and become more 'stable' as they settle down.

There are three main visible branches:

  • master (unstable)
    This branch generally contains Linus' head of tree, and occasionally may have the fixes branch merged at its head. This is an unstable branch.
  • fixes
    This branch contains minor fixes for -rc kernels, and is regularly requested to be merged into mainline kernels. This may also include trees from 3rd parties.
  • devel-stable (stable)
    When Russell pulls git trees from 3rd parties, he pulls them into the devel-stable branch, which as the name suggests, this is a stable branch. Russell guarantees that any commit in the 'devel-stable' branch will never change.

    Some topic branches which have become stable are also merged into 'devel-stable' as necessary.

  • for-next (unstable) [1]
    The for-next branch is a testing branch, created by regularly merging a set of branches together, including the fixes and devel-stable branches. It is primarily published for inclusion into Stephen Rothwell's linux-next tree, but may be used by others for a basis of their own testing. As this branch is regularly destroyed and re-merged, sometimes several times a day, it is 'unstable'.

These branches are available in several forms:

  • Overall combined patches
    Overall combined patches are generated where possible from the GIT branch, and contain a header with each commit text. These are available from the FTP site with a suffix of ".diff". The files are named arm.diff for the master branch, or arm:<branch-name>.diff for individual branches.
  • Mailbox archive
    Mailbox archive format contains one commit per message, and contains a thread structure which reflects the branches in the GIT tree. For simple branches, it's possible to re-apply these to a git tree using git am. For branches containing merges, it's possible to directly recreate the GIT tree structure from these mailbox archives. The mailbox archives are available from the FTP site with a suffix of ".mbox". The files are named arm.mbox for the master branch, or arm:<branch-name>.mbox for individual branches.
  • GIT tree
    Russell's git tree is available via the GIT URL: http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur/linux-arm.git and the URL should be followed by the branch name. Eg,

    git fetch http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur/linux-arm.git fixes

    This URL no longer uses the smart GIT protocol over HTTP as it loads the server too much. Russell recommends that you ensure that the tree into which you're fetching is first up to date with Linus' GIT tree before you fetch from his tree. This will avoid lengthy fetches caused by git downloading (currently) 450MB of data when Linus' tree is 'repacked'. This size will only get bigger over time.

    The other issue to watch out for is that an aborted fetch will leave a partially transferred pack file or index file in .git/objects/pack, which will cause problems when you next try to fetch. If git complains about missing objects, this is most likely what has happened.

    You could set up your repository like this:

    git remote add linus git://git.kernel.org/pub/scm/linux/kernel/git/torvalds/linux-2.6
    git remote add rmk http://ftp.arm.linux.org.uk/pub/linux/arm/kernel/git-cur/linux-arm.git

    and then, to periodically bring your local copy of those remote branches up to date:

    git fetch linus
    git fetch rmk

    It is then easy to see what's in Russell's branches which has not reached Linus' tree, simply by looking at the branch of interest such as rmk/fixes, rmk/devel-stable, merging those together in a test branch with your own local branches, etc.

  • Gitweb
    Gitweb allows on-line viewing of a GIT tree. Russell's GIT tree can be viewed via gitweb here.

RMK's GIT Tree - Timing

Immediately after a final kernel release (such as v2.6.38), a merge window of up to two weeks opens to allow new development into the main kernel. The end of this merge window is marked by the -rc1 release, and then there are a series of -rc releases until the next final kernel release. The cycle then starts over.

The number of -rc releases is indeterminate, but the last -rc release is usually somewhere between -rc6 and -rc9. It is a good idea to keep an eye on Linus' -rc announcements to get a feel of how the cycle will pan out.

As the merge window is relatively short, merge issues and tree ordering needs to be resolved prior to the merge window opening. This requires all git-based changes for a merge window to be in their respective git trees prior to the merge window opening - and preferably in linux-next so that the merge conflicts can be flagged. Russell would prefer to allow about a week for things to settle before the merge window opens.

New development which has not been included in linux-next from other trees and new patches will not be merged into Russell's git tree until after the merge window has closed. However, regression fixes and obvious bug fixes are welcome at any time and will be merged irrespective of the merge window timing. Bug fixes are welcome up until the last couple of -rc releases, depending on complexity.

RMK's GIT tree - Pull Requests

When a 3rd party requests a tree to be pulled into Russell's tree, they send Russell a 'pull request'. This pull request is an email message containing certain basic information:

  • The git-compatible URL to fetch the tree from. This should be on a single line.
  • The base commit which both trees should have.
  • The expected diffstat for the resulting merge. This is so that Russell can check that what was intended to be pulled was what was pulled.
  • A summary of the included changes.

git request-pull is an ideal way to generate suitably formatted messages, which ensures that all the above information is included. Custom pull messages are also acceptable provided the basic information is included. Please refer to GIT documentation for further information.

It is also good practice to include an overall explanation for the pull, which will be passed on to Linus when Russell sends his pull request.

Once a pull request has been sent, the requested branch should not be altered until it has been pulled.

Patches corresponding to the commits should have been sent to the relevant mailing lists for review, and attributions added prior to the pull request being sent.

RMK's GIT Tree - Patches

Emailed patches tend to be a problem for Russell for two reasons. They tend to get buried beneath lots of other ARM related email, resulting in them being missed. Secondly, Russell reads email on a different machine to the one which the GIT tree resides upon, and this requires additional steps to copy patches between machines. Thirdly, email clients tend to corrupt patch files, making them impossible to apply.

The overall result of this means that Russell has a very poor record for applying patches sent by email to his addresses. To work around this, Russell setup a patch system many years ago which allows contributors to submit their patches to a system which won't accidentally forget them. (Russell has said that sometimes the patch system is sometimes too good at remembering patches.)

Full details can be found in the patch system's extensive documentation. Essentially, the process is similar to that used to send patches to mailing lists, with the exception that the patch system requires a kernel version tag.

Notes

1- This used to be called 'devel', but Russell has recently decided to rename this branch to make its purpose clear.    

原文地址:https://www.cnblogs.com/baiyw/p/3301970.html