Saturday, March 31, 2012

How to Get Your Change Into the Linux Kernel

http://git.kernel.org/?p=virt/kvm/kvm.git;a=blob;f=Documentation/SubmittingPatches;h=72651f788f4e3536149ef5e7ddfbed96a8f14d2f;hb=HEAD



 How to Get Your Change Into the Linux Kernel
  or
 Care And Operation Of Your Linus Torvalds



For a person or company who wishes to submit a change to the Linux
kernel, the process can sometimes be daunting if you're not familiar
with "the system."  This text is a collection of suggestions which
can greatly increase the chances of your change being accepted.

Read Documentation/SubmitChecklist for a list of items to check
before submitting code.  If you are submitting a driver, also read
Documentation/SubmittingDrivers.



--------------------------------------------
SECTION 1 - CREATING AND SENDING YOUR CHANGE
--------------------------------------------



1) "diff -up"
------------

Use "diff -up" or "diff -uprN" to create patches.

All changes to the Linux kernel occur in the form of patches, as
generated by diff(1).  When creating your patch, make sure to create it
in "unified diff" format, as supplied by the '-u' argument to diff(1).
Also, please use the '-p' argument which shows which C function each
change is in - that makes the resultant diff a lot easier to read.
Patches should be based in the root kernel source directory,
not in any lower subdirectory.

To create a patch for a single file, it is often sufficient to do:

 SRCTREE= linux-2.6
 MYFILE=  drivers/net/mydriver.c

 cd $SRCTREE
 cp $MYFILE $MYFILE.orig
 vi $MYFILE # make your change
 cd ..
 diff -up $SRCTREE/$MYFILE{.orig,} > /tmp/patch

To create a patch for multiple files, you should unpack a "vanilla",
or unmodified kernel source tree, and generate a diff against your
own source tree.  For example:

 MYSRC= /devel/linux-2.6

 tar xvfz linux-2.6.12.tar.gz
 mv linux-2.6.12 linux-2.6.12-vanilla
 diff -uprN -X linux-2.6.12-vanilla/Documentation/dontdiff \
  linux-2.6.12-vanilla $MYSRC > /tmp/patch

"dontdiff" is a list of files which are generated by the kernel during
the build process, and should be ignored in any diff(1)-generated
patch.  The "dontdiff" file is included in the kernel tree in
2.6.12 and later.  For earlier kernel versions, you can get it
from .

Make sure your patch does not include any extra files which do not
belong in a patch submission.  Make sure to review your patch -after-
generated it with diff(1), to ensure accuracy.

If your changes produce a lot of deltas, you may want to look into
splitting them into individual patches which modify things in
logical stages.  This will facilitate easier reviewing by other
kernel developers, very important if you want your patch accepted.
There are a number of scripts which can aid in this:

Quilt:
http://savannah.nongnu.org/projects/quilt

Andrew Morton's patch scripts:
http://userweb.kernel.org/~akpm/stuff/patch-scripts.tar.gz
Instead of these scripts, quilt is the recommended patch management
tool (see above).



2) Describe your changes.

Describe the technical detail of the change(s) your patch includes.

Be as specific as possible.  The WORST descriptions possible include
things like "update driver X", "bug fix for driver X", or "this patch
includes updates for subsystem X.  Please apply."

The maintainer will thank you if you write your patch description in a
form which can be easily pulled into Linux's source code management
system, git, as a "commit log".  See #15, below.

If your description starts to get long, that's a sign that you probably
need to split up your patch.  See #3, next.



3) Separate your changes.

Separate _logical changes_ into a single patch file.

For example, if your changes include both bug fixes and performance
enhancements for a single driver, separate those changes into two
or more patches.  If your changes include an API update, and a new
driver which uses that new API, separate those into two patches.

On the other hand, if you make a single change to numerous files,
group those changes into a single patch.  Thus a single logical change
is contained within a single patch.

If one patch depends on another patch in order for a change to be
complete, that is OK.  Simply note "this patch depends on patch X"
in your patch description.

If you cannot condense your patch set into a smaller set of patches,
then only post say 15 or so at a time and wait for review and integration.



4) Style check your changes.

Check your patch for basic style violations, details of which can be
found in Documentation/CodingStyle.  Failure to do so simply wastes
the reviewers time and will get your patch rejected, probably
without even being read.

At a minimum you should check your patches with the patch style
checker prior to submission (scripts/checkpatch.pl).  You should
be able to justify all violations that remain in your patch.



5) Select e-mail destination.

Look through the MAINTAINERS file and the source code, and determine
if your change applies to a specific subsystem of the kernel, with
an assigned maintainer.  If so, e-mail that person.

If no maintainer is listed, or the maintainer does not respond, send
your patch to the primary Linux kernel developer's mailing list,
linux-kernel@vger.kernel.org.  Most kernel developers monitor this
e-mail list, and can comment on your changes.


Do not send more than 15 patches at once to the vger mailing lists!!!


Linus Torvalds is the final arbiter of all changes accepted into the
Linux kernel.  His e-mail address is . 
He gets a lot of e-mail, so typically you should do your best to -avoid-
sending him e-mail. 

Patches which are bug fixes, are "obvious" changes, or similarly
require little discussion should be sent or CC'd to Linus.  Patches
which require discussion or do not have a clear advantage should
usually be sent first to linux-kernel.  Only after the patch is
discussed should the patch then be submitted to Linus.



6) Select your CC (e-mail carbon copy) list.

Unless you have a reason NOT to do so, CC linux-kernel@vger.kernel.org.

Other kernel developers besides Linus need to be aware of your change,
so that they may comment on it and offer code review and suggestions.
linux-kernel is the primary Linux kernel developer mailing list.
Other mailing lists are available for specific subsystems, such as
USB, framebuffer devices, the VFS, the SCSI subsystem, etc.  See the
MAINTAINERS file for a mailing list that relates specifically to
your change.

Majordomo lists of VGER.KERNEL.ORG at:
 

If changes affect userland-kernel interfaces, please send
the MAN-PAGES maintainer (as listed in the MAINTAINERS file)
a man-pages patch, or at least a notification of the change,
so that some information makes its way into the manual pages.

Even if the maintainer did not respond in step #5, make sure to ALWAYS
copy the maintainer when you change their code.

For small patches you may want to CC the Trivial Patch Monkey
trivial@kernel.org which collects "trivial" patches. Have a look
into the MAINTAINERS file for its current manager.
Trivial patches must qualify for one of the following rules:
 Spelling fixes in documentation
 Spelling fixes which could break grep(1)
 Warning fixes (cluttering with useless warnings is bad)
 Compilation fixes (only if they are actually correct)
 Runtime fixes (only if they actually fix things)
 Removing use of deprecated functions/macros (eg. check_region)
 Contact detail and documentation fixes
 Non-portable code replaced by portable code (even in arch-specific,
 since people copy, as long as it's trivial)
 Any fix by the author/maintainer of the file (ie. patch monkey
 in re-transmission mode)



7) No MIME, no links, no compression, no attachments.  Just plain text.

Linus and other kernel developers need to be able to read and comment
on the changes you are submitting.  It is important for a kernel
developer to be able to "quote" your changes, using standard e-mail
tools, so that they may comment on specific portions of your code.

For this reason, all patches should be submitting e-mail "inline".
WARNING:  Be wary of your editor's word-wrap corrupting your patch,
if you choose to cut-n-paste your patch.

Do not attach the patch as a MIME attachment, compressed or not.
Many popular e-mail applications will not always transmit a MIME
attachment as plain text, making it impossible to comment on your
code.  A MIME attachment also takes Linus a bit more time to process,
decreasing the likelihood of your MIME-attached change being accepted.

Exception:  If your mailer is mangling patches then someone may ask
you to re-send them using MIME.

See Documentation/email-clients.txt for hints about configuring
your e-mail client so that it sends your patches untouched.

8) E-mail size.

When sending patches to Linus, always follow step #7.

Large changes are not appropriate for mailing lists, and some
maintainers.  If your patch, uncompressed, exceeds 300 kB in size,
it is preferred that you store your patch on an Internet-accessible
server, and provide instead a URL (link) pointing to your patch.



9) Name your kernel version.

It is important to note, either in the subject line or in the patch
description, the kernel version to which this patch applies.

If the patch does not apply cleanly to the latest kernel version,
Linus will not apply it.



10) Don't get discouraged.  Re-submit.

After you have submitted your change, be patient and wait.  If Linus
likes your change and applies it, it will appear in the next version
of the kernel that he releases.

However, if your change doesn't appear in the next version of the
kernel, there could be any number of reasons.  It's YOUR job to
narrow down those reasons, correct what was wrong, and submit your
updated change.

It is quite common for Linus to "drop" your patch without comment.
That's the nature of the system.  If he drops your patch, it could be
due to
* Your patch did not apply cleanly to the latest kernel version.
* Your patch was not sufficiently discussed on linux-kernel.
* A style issue (see section 2).
* An e-mail formatting issue (re-read this section).
* A technical problem with your change.
* He gets tons of e-mail, and yours got lost in the shuffle.
* You are being annoying.

When in doubt, solicit comments on linux-kernel mailing list.



11) Include PATCH in the subject

Due to high e-mail traffic to Linus, and to linux-kernel, it is common
convention to prefix your subject line with [PATCH].  This lets Linus
and other kernel developers more easily distinguish patches from other
e-mail discussions.



12) Sign your work

To improve tracking of who did what, especially with patches that can
percolate to their final resting place in the kernel through several
layers of maintainers, we've introduced a "sign-off" procedure on
patches that are being emailed around.

The sign-off is a simple line at the end of the explanation for the
patch, which certifies that you wrote it or otherwise have the right to
pass it on as a open-source patch.  The rules are pretty simple: if you
can certify the below:

        Developer's Certificate of Origin 1.1

        By making a contribution to this project, I certify that:

        (a) The contribution was created in whole or in part by me and I
            have the right to submit it under the open source license
            indicated in the file; or

        (b) The contribution is based upon previous work that, to the best
            of my knowledge, is covered under an appropriate open source
            license and I have the right under that license to submit that
            work with modifications, whether created in whole or in part
            by me, under the same open source license (unless I am
            permitted to submit under a different license), as indicated
            in the file; or

        (c) The contribution was provided directly to me by some other
            person who certified (a), (b) or (c) and I have not modified
            it.

 (d) I understand and agree that this project and the contribution
     are public and that a record of the contribution (including all
     personal information I submit with it, including my sign-off) is
     maintained indefinitely and may be redistributed consistent with
     this project or the open source license(s) involved.

then you just add a line saying

 Signed-off-by: Random J Developer 

using your real name (sorry, no pseudonyms or anonymous contributions.)

Some people also put extra tags at the end.  They'll just be ignored for
now, but you can do this to mark internal company procedures or just
point out some special detail about the sign-off. 

If you are a subsystem or branch maintainer, sometimes you need to slightly
modify patches you receive in order to merge them, because the code is not
exactly the same in your tree and the submitters'. If you stick strictly to
rule (c), you should ask the submitter to rediff, but this is a totally
counter-productive waste of time and energy. Rule (b) allows you to adjust
the code, but then it is very impolite to change one submitter's code and
make him endorse your bugs. To solve this problem, it is recommended that
you add a line between the last Signed-off-by header and yours, indicating
the nature of your changes. While there is nothing mandatory about this, it
seems like prepending the description with your mail and/or name, all
enclosed in square brackets, is noticeable enough to make it obvious that
you are responsible for last-minute changes. Example :

 Signed-off-by: Random J Developer 
 [lucky@maintainer.example.org: struct foo moved from foo.c to foo.h]
 Signed-off-by: Lucky K Maintainer 

This practise is particularly helpful if you maintain a stable branch and
want at the same time to credit the author, track changes, merge the fix,
and protect the submitter from complaints. Note that under no circumstances
can you change the author's identity (the From header), as it is the one
which appears in the changelog.

Special note to back-porters: It seems to be a common and useful practise
to insert an indication of the origin of a patch at the top of the commit
message (just after the subject line) to facilitate tracking. For instance,
here's what we see in 2.6-stable :

    Date:   Tue May 13 19:10:30 2008 +0000

        SCSI: libiscsi regression in 2.6.25: fix nop timer handling

        commit 4cf1043593db6a337f10e006c23c69e5fc93e722 upstream

And here's what appears in 2.4 :

    Date:   Tue May 13 22:12:27 2008 +0200

        wireless, airo: waitbusy() won't delay

        [backport of 2.6 commit b7acbdfbd1f277c1eb23f344f899cfa4cd0bf36a]

Whatever the format, this information provides a valuable help to people
tracking your trees, and to people trying to trouble-shoot bugs in your
tree.


13) When to use Acked-by: and Cc:

The Signed-off-by: tag indicates that the signer was involved in the
development of the patch, or that he/she was in the patch's delivery path.

If a person was not directly involved in the preparation or handling of a
patch but wishes to signify and record their approval of it then they can
arrange to have an Acked-by: line added to the patch's changelog.

Acked-by: is often used by the maintainer of the affected code when that
maintainer neither contributed to nor forwarded the patch.

Acked-by: is not as formal as Signed-off-by:.  It is a record that the acker
has at least reviewed the patch and has indicated acceptance.  Hence patch
mergers will sometimes manually convert an acker's "yep, looks good to me"
into an Acked-by:.

Acked-by: does not necessarily indicate acknowledgement of the entire patch.
For example, if a patch affects multiple subsystems and has an Acked-by: from
one subsystem maintainer then this usually indicates acknowledgement of just
the part which affects that maintainer's code.  Judgement should be used here.
When in doubt people should refer to the original discussion in the mailing
list archives.

If a person has had the opportunity to comment on a patch, but has not
provided such comments, you may optionally add a "Cc:" tag to the patch.
This is the only tag which might be added without an explicit action by the
person it names.  This tag documents that potentially interested parties
have been included in the discussion


14) Using Reported-by:, Tested-by: and Reviewed-by:

If this patch fixes a problem reported by somebody else, consider adding a
Reported-by: tag to credit the reporter for their contribution.  Please
note that this tag should not be added without the reporter's permission,
especially if the problem was not reported in a public forum.  That said,
if we diligently credit our bug reporters, they will, hopefully, be
inspired to help us again in the future.

A Tested-by: tag indicates that the patch has been successfully tested (in
some environment) by the person named.  This tag informs maintainers that
some testing has been performed, provides a means to locate testers for
future patches, and ensures credit for the testers.

Reviewed-by:, instead, indicates that the patch has been reviewed and found
acceptable according to the Reviewer's Statement:

 Reviewer's statement of oversight

 By offering my Reviewed-by: tag, I state that:

   (a) I have carried out a technical review of this patch to
      evaluate its appropriateness and readiness for inclusion into
      the mainline kernel.

  (b) Any problems, concerns, or questions relating to the patch
      have been communicated back to the submitter.  I am satisfied
      with the submitter's response to my comments.

  (c) While there may be things that could be improved with this
      submission, I believe that it is, at this time, (1) a
      worthwhile modification to the kernel, and (2) free of known
      issues which would argue against its inclusion.

  (d) While I have reviewed the patch and believe it to be sound, I
      do not (unless explicitly stated elsewhere) make any
      warranties or guarantees that it will achieve its stated
      purpose or function properly in any given situation.

A Reviewed-by tag is a statement of opinion that the patch is an
appropriate modification of the kernel without any remaining serious
technical issues.  Any interested reviewer (who has done the work) can
offer a Reviewed-by tag for a patch.  This tag serves to give credit to
reviewers and to inform maintainers of the degree of review which has been
done on the patch.  Reviewed-by: tags, when supplied by reviewers known to
understand the subject area and to perform thorough reviews, will normally
increase the likelihood of your patch getting into the kernel.


15) The canonical patch format

The canonical patch subject line is:

    Subject: [PATCH 001/123] subsystem: summary phrase

The canonical patch message body contains the following:

  - A "from" line specifying the patch author.

  - An empty line.

  - The body of the explanation, which will be copied to the
    permanent changelog to describe this patch.

  - The "Signed-off-by:" lines, described above, which will
    also go in the changelog.

  - A marker line containing simply "---".

  - Any additional comments not suitable for the changelog.

  - The actual patch (diff output).

The Subject line format makes it very easy to sort the emails
alphabetically by subject line - pretty much any email reader will
support that - since because the sequence number is zero-padded,
the numerical and alphabetic sort is the same.

The "subsystem" in the email's Subject should identify which
area or subsystem of the kernel is being patched.

The "summary phrase" in the email's Subject should concisely
describe the patch which that email contains.  The "summary
phrase" should not be a filename.  Do not use the same "summary
phrase" for every patch in a whole patch series (where a "patch
series" is an ordered sequence of multiple, related patches).

Bear in mind that the "summary phrase" of your email becomes a
globally-unique identifier for that patch.  It propagates all the way
into the git changelog.  The "summary phrase" may later be used in
developer discussions which refer to the patch.  People will want to
google for the "summary phrase" to read discussion regarding that
patch.  It will also be the only thing that people may quickly see
when, two or three months later, they are going through perhaps
thousands of patches using tools such as "gitk" or "git log
--oneline".

For these reasons, the "summary" must be no more than 70-75
characters, and it must describe both what the patch changes, as well
as why the patch might be necessary.  It is challenging to be both
succinct and descriptive, but that is what a well-written summary
should do.

The "summary phrase" may be prefixed by tags enclosed in square
brackets: "Subject: [PATCH tag] ".  The tags are not
considered part of the summary phrase, but describe how the patch
should be treated.  Common tags might include a version descriptor if
the multiple versions of the patch have been sent out in response to
comments (i.e., "v1, v2, v3"), or "RFC" to indicate a request for
comments.  If there are four patches in a patch series the individual
patches may be numbered like this: 1/4, 2/4, 3/4, 4/4.  This assures
that developers understand the order in which the patches should be
applied and that they have reviewed or applied all of the patches in
the patch series.

A couple of example Subjects:

    Subject: [patch 2/5] ext2: improve scalability of bitmap searching
    Subject: [PATCHv2 001/207] x86: fix eflags tracking

The "from" line must be the very first line in the message body,
and has the form:

        From: Original Author 

The "from" line specifies who will be credited as the author of the
patch in the permanent changelog.  If the "from" line is missing,
then the "From:" line from the email header will be used to determine
the patch author in the changelog.

The explanation body will be committed to the permanent source
changelog, so should make sense to a competent reader who has long
since forgotten the immediate details of the discussion that might
have led to this patch.  Including symptoms of the failure which the
patch addresses (kernel log messages, oops messages, etc.) is
especially useful for people who might be searching the commit logs
looking for the applicable patch.  If a patch fixes a compile failure,
it may not be necessary to include _all_ of the compile failures; just
enough that it is likely that someone searching for the patch can find
it.  As in the "summary phrase", it is important to be both succinct as
well as descriptive.

The "---" marker line serves the essential purpose of marking for patch
handling tools where the changelog message ends.

One good use for the additional comments after the "---" marker is for
a diffstat, to show what files have changed, and the number of
inserted and deleted lines per file.  A diffstat is especially useful
on bigger patches.  Other comments relevant only to the moment or the
maintainer, not suitable for the permanent changelog, should also go
here.  A good example of such comments might be "patch changelogs"
which describe what has changed between the v1 and v2 version of the
patch.

If you are going to include a diffstat after the "---" marker, please
use diffstat options "-p 1 -w 70" so that filenames are listed from
the top of the kernel source tree and don't use too much horizontal
space (easily fit in 80 columns, maybe with some indentation).

See more details on the proper patch format in the following
references.


16) Sending "git pull" requests  (from Linus emails)

Please write the git repo address and branch name alone on the same line
so that I can't even by mistake pull from the wrong branch, and so
that a triple-click just selects the whole thing.

So the proper format is something along the lines of:

 "Please pull from

  git://jdelvare.pck.nerim.net/jdelvare-2.6 i2c-for-linus

  to get these changes:"

so that I don't have to hunt-and-peck for the address and inevitably
get it wrong (actually, I've only gotten it wrong a few times, and
checking against the diffstat tells me when I get it wrong, but I'm
just a lot more comfortable when I don't have to "look for" the right
thing to pull, and double-check that I have the right branch-name).


Please use "git diff -M --stat --summary" to generate the diffstat:
the -M enables rename detection, and the summary enables a summary of
new/deleted or renamed files.

With rename detection, the statistics are rather different [...]
because git will notice that a fair number of the changes are renames.

-----------------------------------
SECTION 2 - HINTS, TIPS, AND TRICKS
-----------------------------------

This section lists many of the common "rules" associated with code
submitted to the kernel.  There are always exceptions... but you must
have a really good reason for doing so.  You could probably call this
section Linus Computer Science 101.



1) Read Documentation/CodingStyle

Nuff said.  If your code deviates too much from this, it is likely
to be rejected without further review, and without comment.

One significant exception is when moving code from one file to
another -- in this case you should not modify the moved code at all in
the same patch which moves it.  This clearly delineates the act of
moving the code and your changes.  This greatly aids review of the
actual differences and allows tools to better track the history of
the code itself.

Check your patches with the patch style checker prior to submission
(scripts/checkpatch.pl).  The style checker should be viewed as
a guide not as the final word.  If your code looks better with
a violation then its probably best left alone.

The checker reports at three levels:
 - ERROR: things that are very likely to be wrong
 - WARNING: things requiring careful review
 - CHECK: things requiring thought

You should be able to justify all violations that remain in your
patch.



2) #ifdefs are ugly

Code cluttered with ifdefs is difficult to read and maintain.  Don't do
it.  Instead, put your ifdefs in a header, and conditionally define
'static inline' functions, or macros, which are used in the code.
Let the compiler optimize away the "no-op" case.

Simple example, of poor code:

 dev = alloc_etherdev (sizeof(struct funky_private));
 if (!dev)
  return -ENODEV;
 #ifdef CONFIG_NET_FUNKINESS
 init_funky_net(dev);
 #endif

Cleaned-up example:

(in header)
 #ifndef CONFIG_NET_FUNKINESS
 static inline void init_funky_net (struct net_device *d) {}
 #endif

(in the code itself)
 dev = alloc_etherdev (sizeof(struct funky_private));
 if (!dev)
  return -ENODEV;
 init_funky_net(dev);



3) 'static inline' is better than a macro

Static inline functions are greatly preferred over macros.
They provide type safety, have no length limitations, no formatting
limitations, and under gcc they are as cheap as macros.

Macros should only be used for cases where a static inline is clearly
suboptimal [there are a few, isolated cases of this in fast paths],
or where it is impossible to use a static inline function [such as
string-izing].

'static inline' is preferred over 'static __inline__', 'extern inline',
and 'extern __inline__'.



4) Don't over-design.

Don't try to anticipate nebulous future cases which may or may not
be useful:  "Make it as simple as you can, and no simpler."



----------------------
SECTION 3 - REFERENCES
----------------------

Andrew Morton, "The perfect patch" (tpp).
  

Jeff Garzik, "Linux kernel patch submission format".
  

Greg Kroah-Hartman, "How to piss off a kernel subsystem maintainer".
  
  
  
  

NO!!!! No more huge patch bombs to linux-kernel@vger.kernel.org people!
  

Kernel Documentation/CodingStyle:
  

Linus Torvalds's mail on the canonical patch format:
  

Andi Kleen, "On submitting kernel patches"
  Some strategies to get difficult or controversal changes in.
  http://halobates.de/on-submitting-patches.pdf

--

Monday, March 19, 2012

Wednesday, February 01, 2012

[轉錄]C/C++ 的多維動態陣列



http://ehome.hifly.to/showthread.php?threadid=357


多維動態陣列在 C 中 大概只能用 malloc,但這在一維時尚
不構成問題,但需要多維陣列時怎麼辦呢?這算是程式論壇
最常被問到的問題之一了。
我把它整理了相關的回覆,都只用二維做說明,更多維的陣列
類推即可。
就從 C 談起吧!

動態產生一個[m][n]陣列 Array 的方法 
code:
int i; int **Array; Array= (int **)malloc(m*sizeof(void *)); for (i=0; i Array[i]=(int *)malloc(n*sizeof(int *));

這樣你就有一個 int Array[m][n]; 可以用了
是C 喔!不是 C++
但這不夠好,若你要的是一個較大的mXn陣列,那麼太多的malloc
會使記憶體碎片化(memory fragment)!沒關係,窮則變,變則通!
問題既出在for loop不斷的 memory allocation,就從那兒下手: 
code:
int i; int **Array, *pData; Array= (int **)malloc(m*sizeof(int *)); pData= (int *)malloc(m*n*sizeof(int)); for (i=0; i Array[i]=pData;

注意到嗎?這次只用了二次的malloc。
當要release memory也只要free Array[0] 和 Array 就成了!
(注意先free Array[0] 再 free Array)
如果嫌兩個alloc/free還是太多,也可簡單併成一個: 
code:
int i; int **Array, *pData; Array= (int **)malloc(m*sizeof(int *)+m*n*sizeof(int)); for (i=0,pData= (int *)(Array+m); i Array[i]=pData;

要 free 時只要 free Array 就行了,帥吧?
如果用的是C++,那可用的方法就更多了!

《幼幼班》嘗試錯誤的階段
?> int Array[][] = new int [10][20];//這樣行ㄇ?
當然不行! int Array[][]不是一個指標,而且只能有
一維為不定大小。

《小班》終於會從1數到100了
?> 那...
?> int *Array[] = new int [10][20];//這樣行ㄇ?
有點想法了!但可惜的是 '*' 在 C++的語法是修飾前面的
識別字,所以 int *Array[]的意思是 "Array是一個 int 指標
的一維陣列!"

如果能使那個 '*' 以獨立指標型態去宣告Array,就會變成
"Array是一個指標,指向一維 int 的陣列",而我們知道指標
本身就可以當做一維的陣列,那麼是不是就成了"Array是一個二維
的int陣列"?
對了!這正是我們要的!問題是怎麼讓'*'成為獨立指標型態,
不會去修飾前面的int識別字?答案是使用括號:
int (*Array)[20] = new int [10][20];//這是正解!

但問題又來了, new 運算子可以使用 new int [m][n],但
int (*Array)[20] 的 [20]卻沒辦法以 [n] 來取代,所以就
沒辦法做到不定大小的宣告了。所以這只能算是小班的答案,
要做到不定大小的動態多維宣告,加上 STL 的運用,是不錯
的想法。

《中班》vector 模板的運用
vector *array=new vector[m];
for (int i=0; i
嘿嘿...不錯吧!?不定大小的二維陣列,而且每個維度還
可以隨時調整大小喔!
不過還是有缺點ㄟ!!那行 for loop 看起來有點礙眼,不能
拿掉嗎?拿掉的話,基本上Array 還是二維陣列,但第二維
並沒有預留空間放東西你可以用push.back等成員函式來增加
空間和存放data,但不能在還沒有空間時使用像
array[5][3]=3; 這種陣列的存取方式。沒更好的方法了嗎?
vector 不是有預留空間大小的建構子嗎?像一維的宣告:
vector *Array= new vector (n);
//這不是預留了 n 個元素的陣列了嗎?
只可惜,這是一維的宣告,若你嘗試做這樣的宣告:
vector *Array= new vector [m](n);
編譯器會給你無情的嘲諷:陣列不能呼叫帶參數的建構子!
這是很令人失望的!為十麼不行?不是邏輯的問題,或許下
一版本的C++會可以這樣宣告吧!但為今之計只能自力救濟
了。

《大班》使用模板在模版中
仔細觀察下面的宣告:
vector > Array(m, vector(n));

不用懷疑!這是相當於宣告一個動態二維不定大小的 int Array[m][n];
不相信的話你可以在m,n範圍內存取 Array[i][j] 看看。
若你能一眼看出這是在幹嘛,那麼你早已超出大班的程度了!
若你暫時不知所云,也沒關係,你可以安全的使用它帶給你的好處。
只是你也只好留在大班留班查看了 ^_^

解釋這個宣告,其實不難,但首先你要對模版的使用有相當的了解,當然
還要對 vector 模版的建構子不能陌生。這樣子自然很容易可以看得懂它。
了然於胸了嗎?恭喜你,大班可以畢業了 ^_@


註:大班的答案是Duncan在練功房提出的解答,注意到兩個
兩個角括號中間的空白了嗎?(> >)
               ^-------這裡要有空白
不加空白的話會被語法解析為右移運算子。 

Sunday, January 22, 2012

Twitter如何在數千台伺服器上快速部署程式碼?

轉自
http://www.inside.com.tw/2010/07/17/how-twitter-deploys-application-to-thousands-of-servers


答案是:用BT,也就是你我應該都很熟悉的BitTorrent。
對於網站經營者、創業者來說,延展性的問題是在網站流量成長過程中勢必會面對的問題,如何建立一個具有延展性的架構(scalable architecture)便是在規劃網站事業過程中不可或缺的專業知識。
如果服務本身的功能性合乎使用者需求,卻因為架構、程式效能、資料庫效能的問題導致服務成長出現瓶頸,如何評估、分析網站效能瓶頸?釐清問題後如何找出對應的解決方案,可以思考的相關議題可能包括:
  • 如何有效率地釐清問題?從使用者端的數據(讀取時間)或是從伺服器端的log檔案、硬體的負載率?
  • 網站效能瓶頸是出現在Client或Server端?是資料庫撐不住還是程式效能不好?是Request太多還是檔案太大?
  • Web Server、DB server如何擠出更多的資源?擠不出資源後如何擴展?擴展後會遇到什麼問題?
參考國外知名網站在架構上的作法是很好的一種方式,儘管服務的規模可能無法相比,但根據「正確的作法與經驗」踏出對的第一步,肯定是有助於突破網站營運的效能瓶頸。
Twitter身為全球最大的微網誌服務,運用數千台的伺服器提供服務給來自全球各地的使用者,然而每當網站內容、應用程式有更新時,如何盡可能地在越短的時間內將程式碼部署(deploy)到所有的伺服器便是相當重要的課題。
Twitter的開發部落格上的一篇文章:「Murder: Fast datacenter code deploys using BitTorrent」分享了Twitter如何持續改善應用程式的部署流程,在過去Twitter使用Capistrano部署應用程式,Capistrano是許多Ruby/Rails使用者(當然也有其他語言的開發人員會使用)用來部署程式碼的一個開源專案,開發人員在部署程式碼的過程都可以透過自動化的部署流程來簡化經常重複的動作,尤其在專案必須同時部署到多台應用程式伺服器時會特別方便。
Twitter在早期便依賴Capistrano來進行應用程式的部署,每當有新版本的程式碼需要釋出時,Capistrano會根據預設好的各種設定、流程到Twitter所有的伺服器上進行更新的動作,在過去伺服器還不多的情況下一切都很美好,但隨著Twitter伺服器數量的成長,到了幾百台伺服器時,事情已經不再像過去一樣美好,甚至到後來擁有數千台伺服器時,更新的作業會耗費40分鐘。
Twitter針對這個問題,認為問題的關鍵在於:使用集中式的系統,也就是所有的伺服器要輪流排隊到同一台版本控制系統上進行程式碼更新。Twitter最初的想法是將版本控制系統也做出分散式的架構,伺服器的程式碼更新就可以分散到不同的機器來壓縮部署時間,但事實上版本控制系統即使分散在多台伺服器上,也同樣會有這些伺服器要更新檔案的時間。因此Twitter發現或許是需要一個完全去中心化、最好像是BitTorrent,利用P2P的特色讓所有的節點都可以協助進行程式碼的更新。
以結果來看,在採用了BitTorrent的方式來更新程式碼後,部署的時間從40分鐘大幅減少到只要12秒鐘!實在是非常驚人的改善,數千台伺服器的程式碼居然只要短短12秒鐘就能完成。
Twitter也將此次部署流程改善的成果分享出來,專案名稱叫做Murder,如果對於技術細節有興趣的讀者,可以再進行深入的研究;筆者簡單摘錄幾個重點如下:
  • Murder是以BitTornado為基礎開發出來的(BitTornado是某一種BitTorrent client)
  • Murder的定位是「協助我們快速的將檔案部署到大批伺服器上」
  • 利用BitTorrent的部署方式可避免防火牆的問題、擁有非常快的傳輸速度
  • 實際的部署程式碼是搭配Capistrano進行,網頁上有很清楚的說明
以下是Twitter的架構工程師Larry Gadea談Murder的影片:
http://vimeo.com/11280885

Monday, November 21, 2011

[轉錄]如何客製化 Google Nexus S 的 Kernel

轉自http://www.ur-solution.com/?p=26


Google Nexus S 使用的是原生的 android 作業系統,不像各家廠商的手機,經過大量的客製化,所以這個手機可以說環境最單純、乾淨、簡潔的系統了,我們在進行 Android 驅動程式 Porting 時,都會先拿這隻手機來測試,所以我們會要重新編譯這個手機的 Linux Kernel,在開始之前,首先我們事先準備一些環境,先下載 Android NDK,如下:

wget http://dl.google.com/android/ndk/android-ndk-r6b-linux-x86.tar.bz2
tar -jxf android-ndk-r6b-linux-x86.tar.bz2
export ARCH=arm
export CROSS_COMPILE=$(pwd)/android-ndk-r6b/toolchains/arm-linux-androideabi-4.4.3/prebuilt/linux-x86/bin/arm-linux-androideabi-

接者我們要安裝 SDK:

wget http://dl.google.com/android/android-sdk_r12-linux_x86.tgz
tar -zxf android-sdk_r12-linux_x86.tgz
android-sdk-linux_x86/tools/android update sdk -u -t platform-tool
export PATH=$PATH:$(pwd)/android-sdk-linux_x86/platform-tools

現在我們可以開始進行編譯的工作,要編譯 Nexus S 的 kernel, 首先我們要取得 Samsung Nexus S 的 Kernel tree , 請執行:
git clone -b devrom-2.6.35 https://github.com/cgjones/samsung-android-kernel
cd samsung-android-kernel

接下來我們要設定 Kernel ,請執行:

make herring_defconfig

在開始編譯之前,我們先來看看手機上的 Kernel 版本資訊,請執行:

adb shell cat /proc/version
—–會得到以下的資訊
Linux version 2.6.35.7-ge382d80 (android-build@apa28.mtv.corp.google.com) (gcc v
ersion 4.4.3 (GCC) ) #1 PREEMPT Mon Feb 7 18:29:34 PST 2011

其中有一項很重要的資訊:2.6.35.7 ,這是我們 Kernel 的 版本。你要先確定使用的 kernel 版本是否正確,請執行:

shanchieh@ubuntu:~/nexus_s/samsung-android-kernel$ make kernelversion
2.6.35.7

很好,我們現在使用的也是 2.6.35.7。在版本後面有一個資訊:-ge382d80(2.3.4) (如果你的 OS 是 2.3.3 ,則會是:-g1d030a7 ),請將這個資訊寫入到 .scmversion 檔案中,請執行:

echo -ge382d80 > .scmversion
現在我們可以來開始編譯 kernel ,請執行:

make -j$(($(grep -c processor /proc/cpuinfo) * 3 / 2))

-j 這個參數後要接一個數字,例如 -j2 , -j4,等… 意思是說你同時要使用幾個核心來編譯。根據每一個人的環境不同,當然會不一樣,所以我們用一個參數來取得你目前硬體的核心數目。

編譯的過程中,我們先來看看 Nexus S 中的 boot 是在哪一個分割區中,請執行:

shanchieh@ubuntu:~$ adb shell cat /proc/mtd | awk -F’[:"]‘ ‘$3 == "boot" {print $1}’
mtd2
現在我們知道是放在 mtd2 的分割區中。在更換之前,我們先將手機上的 boot 保存下來,請執行:

shanchieh@ubuntu:~$ adb shell
# su
# dd if=/dev/mtd/mtd2 of=/sdcard/boot.img bs=4096
2048+0 records in
2048+0 records out
8388608 bytes transferred in 1.232 secs (6808935 bytes/sec)
# exit
# exit
這時在 /sdcard 之中。就會有一個 boot.img 的檔案。

請執行: adb pull /sdcard/boot.img 將這個檔案保存到我們的電腦中。

好。現在我們要來產生新的 boot.img 了,請執行:

git clone https://github.com/glandium/unbootimg.git
git clone git://git.linaro.org/android/platform/system/core.git
gcc -o unbootimg/unbootimg unbootimg/unbootimg.c core/libmincrypt/sha.c -Icore/include -Icore/mkbootimg
gcc -o mkbootimg core/mkbootimg/mkbootimg.c core/libmincrypt/sha.c -Icore/include
gcc -o fastboot core/fastboot/{protocol,engine,bootimg,fastboot,usb_linux,util_linux}.c core/libzipfile/{centraldir,zipfile}.c -Icore/mkbootimg -Icore/include –lz

這個時候就會產生 fastboot 跟 mkbootimg 兩個執行檔。

還記得剛剛我們備份下載的 boot.img 嗎?現在我們將這個檔案解開,請執行:

unbootimg/unbootimg boot.img

過程中會告訴你error ,沒關係,不用理會,還是可以順利解開,如下:
section sizes incorrect
kernel 1000 2b1b84
ramdisk 2b3000 22d55
second 2d6000 0
total 2d6000 800000
…but we can still continue

解開之後,會有三個檔案。

boot.img-mk, containing the mkbootimg options required to produce a working boot image,
boot.img-kernel, containing the kernel image,
boot.img-ramdisk.cpio.gz, containing the gzipped ramdisk, which we will reuse as-is.

好,現在我們要替換我們自己編譯的 kernek 了。請執行:

eval ./mkbootimg $(sed s,boot.img-kernel,samsung-android-kernel/arch/arm/boot/zImage, boot.img-mk)

現在我們使用 adb 來進行 fastboot

adb reboot bootloader

這個時候,你的手機應該進入到 fastbook 模式,如下圖:
IMG_0066

請執行: ./fastboot boot boot.img
或出現如下的訊息:

downloading ‘boot.img’…
OKAY [ 0.xxxs]
booting…
OKAY [ 0.xxxs]
finished. total time: 0.xxxs
這個時候手機會重新開機,開機就是使用我們剛剛編譯好的 kernel ,我們要如何驗證手機的 kernel 真的是我們自己編譯的呢?很簡單,在手機中執行:選單->設定->關於手機,就可以看到 kernel 的資訊,如下圖:

IMG_0067
但是手機下次你重新開機時, kernel 還是舊的,因為我們沒有燒入到手機的 ROM 中。如果要永久替換,請執行:
adb reboot bootloader
./fastboot flash boot boot.img
會出現如下的訊息:

sending ‘boot’ (2904 KB)…
OKAY [ 0.xxxs]
writing ‘boot’…
OKAY [ 0.xxxs]
finished. total time: 0.xxxs

再執行 ./fastboot reboot 進行重新開機 ,這樣就將手機中的 Kernel 換成我們自己編譯的了

飛鳥集 - 泰戈爾


世界對著它的愛人,把它浩翰的面具揭下了。
它變小了,小如一首歌,小如一回永恆的吻。
The world puts off its mask of vastness to its lover.
It becomes small as one song, as one kiss of the eternal.

有一次,我們夢見大家都是不相識的。
我們醒了,卻知道我們原是相親相愛的。
Once we dreamt that we were strangers.
We wake up to find that we were dear to each other.

神等待著人在智慧中重新獲得童年。
God waits for man to regain his childhood in wisdom.

創造的神秘,有如夜間的黑暗--是偉大的。
而知識的幻影卻不過如晨間之霧。
The mystery of creation is like the darkness of night--it is great.
Delusions of knowledge are like the fog of the morning.

光明如一個裸體的孩子,快快活活地在綠葉當中遊戲,它不知道人是會欺騙的。
The light that plays, like a naked child, among the green leaves happily
knows not that man can lie.

你微微地笑著,不同我說什麼話。而我覺得,為了這個,我已等待得很久了。
You smiled and talked to me of nothing
and I felt that for this I had been waiting long.

水裡的游魚是沉默的,陸地上的獸類是喧鬧的,空中的飛鳥是歌唱著的。
但是,人類卻兼有海裡的沉默,地上的喧鬧與空中的音樂。
The fish in the water is silent, the animal on the earth is noisy,
the bird in the air is singing.
But Man has in him the silence of the sea,
the noise of the earth and the music of the air.

那想做好人的,在門外敲著門;那愛人的看見門敞開著。
He who wants to do good knocks at the gate;
he who loves finds the gate open.

「可能」問「不可能」道:「你住在什麼地方呢?」
它回答道:「在那無能為力者的夢境裡。」
Asks the Possible to the Impossible, Where is your dwelling-place?
In the dreams of the impotent, comes the answer.

如果你把所有的錯誤都關在門外時,真理也要被關在門外面了。
If you shut your door to all errors truth will be shut out.

一個憂鬱的聲音,築巢於逝水似的年華中。
它在夜裡向我唱道:「我愛你。」
One sad voice has its nest among the ruins of the years.
It sings to me in the night, ---I loved you.

「誰如命運似的催著我向前走呢?」
「那是我自己,在身背後大跨步走著。」
Who drives me forward like fate ?
The Myself striding on my back.

我們的名字,便是夜裡海波上發出的光,痕跡也不留就消失了。
Our names are the light that glows on the sea waves at night
and then dies without leaving its signature.

鳥翼上繫上了黃金,這鳥便永不能再在天上翱翔了。
Set the bird's wings with gold and it will never again soar in the sky.

不要說:「這是早晨」,別用一個「昨天」的名詞把它打發掉。
你第一次看到它,把它當作還沒有名字的新生孩子吧。
Do not say, "It is morning," and dismiss it with a mane of yesterday.
See it for the first time as a new-born child that has no name.

雨點向茉莉花微語道:「把我永久地留在你的心裡吧。」
茉莉花嘆息了一聲,落在地上了。
The raindrop whispered to the jasmine, "Keep me in your heart for ever."
The jasmine sighed, "Alas," and dropped to the ground.

他們點了他們自己的燈,在他們的寺院內,吟唱他們自己的話語。
但是小鳥們卻在你的晨光中,唱著你的名字,--因為你的名字便是快樂。
They light their own lamps and sing their own words in their temples.
But the birds sing thy name in thine own morning light, --- for thy name is joy.

總有一天,我要在別的世界的晨光裡對你唱道:
「我以前在地球的光裡,在人的愛裡,已經見過你了。」
Some day I shall sing to thee in the sunrise of some other world,
I have seen thee before in the light of the earth, in the love of man.


「我相信你的愛。」讓這句話做我的最後的話。

Let this be my last word, that I trust thy love.

Get google nexus S kernel source code(git)


  • Get git source tree
    git clone http://android.googlesource.com/kernel/samsung.git
  • Create new branch, anndy
    git checkout --track -b anndy origin/android-samsung-2.6.35-gingerbread
  • Switch branch (if you want)
    git checkout  BRANCH_NAME




Sunday, November 20, 2011

To debug raw input event

frameworks/base/services/input/InputReader.cpp


// Log debug messages for each raw event received from the EventHub.#define DEBUG_RAW_EVENTS 1
// Log debug messages about touch screen filtering hacks.#define DEBUG_HACKS 1
// Log debug messages about virtual key processing.#define DEBUG_VIRTUAL_KEYS 1
// Log debug messages about pointers.#define DEBUG_POINTERS 1
// Log debug messages about pointer assignment calculations.#define DEBUG_POINTER_ASSIGNMENT 1
// Log debug messages about gesture detection.#define DEBUG_GESTURES 1

To Disable light sensor

disable light sensor
1.adb shell
2.su
3.cat /proc/bus/input/devices
    find out which one is light sensor
3.echo 0 > /sys/devices/virtual/input/input4/enable

Saturday, November 19, 2011

Android 4.0 on Nexus S(base on AOSP)

在15號 AOSP放出4.0的source code後
經過幾天的努力
終於...可以進入android 4.0的介面了!
進入的瞬間...超感動的!!!
不過很多HW都還不能動
最著名的Face unlock也不能用(camera 還沒porting...)
連觸控都還有點問題
還有的搞XD


有圖有真相
https://plus.google.com/u/0/photos/101888505277693924282/albums/5676631313143648721

Friday, November 18, 2011

夢想

將妳輕輕地捧起
再深埋我心田
種下我們相逢的伏筆
在一個陽光燦爛的午後

Thursday, November 17, 2011

Partition Alignment on linux

copy from : http://www.nuclex.org/blog/personal/80-aligning-an-ssd-on-linux




Partition Alignment

If the partitions of a hard drive aren't aligned to begin at multiples of 128 KiB, 256 KiB or 512 KiB (depending on the SSD used), aligning the file system is useless because everything is skewed by the start offset of the partition. Thus, the first thing you have to take care of is aligning the partitions you create.
A spindle with three discs with a red ring superimposed on each of the discs
A cylinder.
A spindle with three discs with a red pie slice superimposed on each of the discs
A sector.
Traditionally, hard drives were addressed by indicating the cylinderhead and sector at which data was to be read or written. These represented the radial position, the drive head (= platter and side) and the axial position of the data respectively. With LBA (logical block addressing), this is no longer the case. Instead, the entire hard drive is addressed as one continuous stream of data.
Linux' fdisk, however, still uses a virtual C-H-S system where you can define any number of heads and sectors yourself (the cylinders are calculated automatically from the drive's capacity), with partitions always starting and ending at intervals of heads x cylinders. Thus, you need to choose a number of heads and sectors of which the SSD's erase block size is a multiple.
I found two posts which detail this process: Aligning Filesystems to an SSD's Erase Block Size and Partition alignment for OCZ Vertex in Linux. The first one recommends 224 heads and 56 sectors, but I can't quite understand where those numbers come from, so I used the advice from the post on the OCZ forums with 32 heads and 32 sectors which means fdisk uses a cylinder size of 1024 bytes. And because fdisk partitions in units of 512 cylinders (= 512 x heads x sectors) fdisk's unit size now happens to be an SSD's maximum erase block size. Nice!
To make fdisk use 32 heads and 32 sectors, remove all partitions from a hard drive and then launch fdisk with the following command line when you create the first partition:
fdisk -S 32 -H 32 /dev/sda
The OCZ post also recommends starting at the second 512-cylinder unit because the first partition is otherwise shifted by one track. Don't ask me why :)
Here's how I partitioned my SSD in the end:
Screenshot of a linux console where fdisk reports 32 heads and 32 sectors
For a normal hard drive, I'd probably use 128 heads and 32 tracks now to achieve 4 KiB boundaries for my partitions.

RAID Chunk Size

If you plan on running a software RAID array, I've seen chunk sizes of 64 KiB and 128 KiB being recommended. This can be specified using the --chunk parameter for mdadm, eg.
mdadm --create /dev/md3 --level=1 --chunk=128 --raid-devices=2 /dev/sda3 /dev/sdb3
Probably the larger chunk size is more useful if you are storing large files on the RAID partition, but I haven't found any advice which included benchmarks or at least a solid explanation yet.

File System Alignment

Now that the partitions have been taken care of, the file systems need to use proper alignment as well. Generally all file systems use some kind of allocation blocks, usually with a size of 4 KiB. But increasing this size to 128 KiB (or even 512 KiB) would waste a lot of space since any file would use up memory in a multiple of that number.
Luckily, Linux file systems can be tweaked a lot. I'm using ext4, here the -E stride,stripe-width parameters control the alignment. The HowTos/Disk Optimization page in the CentOS wiki gives this advice:
The drive calculation works like this: You divide the chunk size by the block size for one spindle/drive only. This gives you your stride size. Then you take the stride size, and multiply it by the number of data-bearing disks in the RAID array. This gives you the stripe width to use when formatting the volume. This can be a little complex, so some examples are listed below.
For example if you have 4 drives in RAID5 and it is using 64K chunks and given a 4K file system block size. The stride size is calculated for the one disk by (chunk size / block size), (64K/4K) which gives 16K. While the stripe width for RAID5 is 1 disk less, so we have 3 data-bearing disks out of the 4 in this RAID5 group, which gives us (number of data-bearing drives * stride size), (3*16K) gives you a stripe width of 48K.
The Linux Kernel RAID wiki offers further insight:

Calculation

  • chunk size = 128kB (set by mdadm cmd, see chunk size advise above)
  • block size = 4kB (recommended for large files, and most of time)
  • stride = chunk / block = 128kB / 4k = 32kB
  • stripe-width = stride * ( (n disks in raid5) - 1 ) = 32kB * ( (3) - 1 ) = 32kB * 2 = 64kB
If the chunk-size is 128 kB, it means, that 128 kB of consecutive data will reside on one disk. If we want to build an ext2 filesystem with 4 kB block-size, we realize that there will be 32 filesystem blocks in one array chunk.
stripe-width=64 is calculated by multiplying the stride=32 value with the number of data disks in the array.
A raid5 with n disks has n-1 data disks, one being reserved for parity. (Note: the mke2fs man page incorrectly states n+1; this is a known bug in the man-page docs that is now fixed.) A raid10 (1+0) with n disks is actually a raid 0 of n/2 raid1 subarrays with 2 disks each.
So these are the stride and stripe-width parameters I'd use:
  • Intel SSDs with an erase block size of 128 (or 512 KiB -- Intel isn't quite straightforward with this, see the comments section for a discussion on the subject - if anyone from Intel is reading this, help us out! ;-)) that are not part of a software RAID:
    -E stride=32,stripe-width=32
  • OCZ Vertex SSDs with an erase block size of 512 KiB that are not part of a software RAID:
    -E stride=128,stripe-width=128
  • Normal hard drives that are not part of a software RAID
    trust the defaults
  • Any software RAID:
    -E stride=raid chunk size / file system block size,stripe-width=raid chunk size x number of data bearing disks
Thus, I set up the file systems on the Intel SSD like this:
mkfs.ext4 -b 1024 -E stride=128,stripe-width=128 -O ^has_journal /dev/sda1
mkfs.ext4 -b 4096 -E stride=32,stripe-width=32 /dev/sda3
mkfs.ext4 defaulted to 1024 byte allocation units on my boot partition, so I adjusted the stride up to 128 KiB according to the advice from the CentOS wiki. The alignment of my boot partition is probably not of any relevance because the system will read maybe 10 files from it and not modify anything, but I wanted to stay consistent :)