注:[2014-3-13] 向android提交patch的方式已经更新,本文所叙述的方式不再完全匹配,请参考 http://source./source/submit-patches.html 向开源社区贡献代码的方式很多,Android采用repo的方式。 由于在提交patch的时候,必须使用repo upload命令,所以,首先要安装repo在自己的项目中。安装repo在自己项目的步骤分两步: 1-1. $ curl http://android.git./repo > ~/bin/repo $ chmod a+x ~/bin/repo 1-2.然后把~/bin加入到PATH环境变量中,如果不加的话,用repo就需要全路径,比较麻烦,不管怎样,下面这一步是可选的: $ export PATH=~/bin:$PATH 2.安装repo到自己的项目中,只有安装到自己项目中后,才可以使用repo,即使是repo help也不例外。 $ repo init -u git://android.git./platform/manifest.git 题外话:使用repo比使用git更麻烦,在教育网中,repo platform几乎是不能完成的任务,当然,可以使用git proxy,不过这种方法并不好使,一个简单且方便的方法是搞一个VPS 。 如果自己要提交的project没有在manifest管辖的范围内,那就需要自己去修改.repo/manifest.xml文件了。 比如,我要修改的Project项目名称是kernel/common,路径是kernel/common,而这个Project并没有在manifest.xml,所以在.repo/mainfest.xml文件中添加这么一行: <project path=”kernel/common” name=”kernel/common/” /> 然后运行repo sync去下载整个platform,整个platform的容量基本上2GB左右,如果没有必要下载那么多的项目,修改.repo/manifest.xml,只保留自己的项目就可以了。 比如,我只需要其中的platform/build项目,那么在.repo/manifest.xml中只保留<project path=”build”>的条目就可以了,更改.repo/manifest.xml修改为:
<?xml version=”1.0″ encoding=”UTF-8″?>
<manifest> <remote name=”korg” fetch=”git://android.git./“ review=”review.source.” /> <default revision=”master” remote=”korg” /> <project path=”build” name=”platform/build”> <copyfile src=”core/root.mk” dest=”Makefile” /> </project> </manifest>
在repo sync的时候,可能会弹出如下的错误: error: revision master in kernel/common not found 这是因为,在manifest.xml中,默认的revision为master。因此,需要在相关的项目中设置revision。 例如:我需要kernel/common项目中的android-2.6.27分支,那么,应该在manifest.xml中写为: <project path=”kernel/common” name=”kernel/common” revision=”android-2.6.27″/> repo sync完成后,进入相关的项目,建立一个分支,修改后,并提交.
例如, $cd kernel/common #进入项目目录中 $repo start goldfish . #建立一个新的goldfish分支 $git checkout goldfish #切换到goldfish分支上 完成修改任务 $git add . $git commit
Android社区要求提交的commit:
- Start with a one-line summary (60 characters max), followed by a blank line.
- The description should focus on what issue it solves, and how it solves it. The second part is somewhat optional when implementing new features, though desirable.
- Include a brief note of any assumptions or background information that may be important when another contributor works on this feature next year.
然后就可以repo upload了,不过在upload的时候,需要在https://review.source./中注册并提交自己的ssh public keys. 提交完成ssh public keys后,就OK啦,成功后,会自动返回一个review。然后等待人工review,这一切,都可以在https://review.source. 中看到。 友情提示:欢迎转载,转载请说明出处。如果有任何问题,欢迎批评,交流,指正。
<全文完>
在kernel子目录下存放的就是Android的Linux Kernel了, 通过和标准的Linux 2.6.25 Kernel的对比,我们可以发现,其主要增加了以下的内容: 1. 基于ARM架构增加Gold-Fish平台,相应增加的目录如下: kernel/arch/arm/mach-goldfish kernel/include/asm-arm/arch-goldfish Gold-Fish平台采用的是ARM926T CPU作为BaseBand处理器, 该CPU主频至少为200M HZ. 采用MSM7201A CPU(ARM 11)作为主CPU, 其主频为528M HZ. 2. 增加了yaffs2 FLASH文件系统,相应增加的目录为: kernel/fs/yaffs2 实际上,Android包经过编译后生成的system.img和ramdisk.img文件就是yaffs2格式的包. 3. 增加了Android的相关Driver,相应目录为: kernel/drivers/android 主要分为: Android IPC系统: Binder Android 日志系统: Logger Android 电源管理: Power Android 闹钟管理: Alarm Android 内存控制台: Ram_console Android 时钟控制的gpio: Timed_gpio 4. 增加了switch处理, 相应的目录为: kernel/drivers/switch/ 5. 增加了一种新的共享内存处理方式, 相应增加的文件为: kernel/mm/ashmem.c 6. 其他为Linux-2.6.25内核所做的补丁等等,例如BlueTooth, 在此不做详细分析 另外GoldFish平台相关的驱动文件如下: 1. 字符输出设备: kernel/drivers/char/goldfish_tty.c 2. 图象显示设备: (Frame Buffer) kernel/drivers/video/goldfishfb.c 3. 键盘输入设备: kernel/drivers/input/keyboard/goldfish_events.c 4. RTC设备: (Real Time Clock) kernel/drivers/rtc/rtc-goldfish.c . USB Device设备: kernel/drivers/usb/gadget/android_adb.c 6. SD卡设备: kernel/drivers/mmc/host/goldfish.c 7. FLASH设备: kernel/drivers/mtd/devices/goldfish_nand.c kernel/drivers/mtd/devices/goldfish_nand_reg.h 8. LED设备: kernel/drivers/leds/ledtrig-sleep.c 9. 电源设备: kernel/drivers/power/goldfish_battery.c 10. 音频设备: kernel/arch/arm/mach-goldfish/audio.c 11. 电源管理: kernel/arch/arm/mach-goldfish/pm.c 12. 时钟管理: kernel/arch/arm/mach-goldfish/timer.c 以上为Android内核的大致分析,希望能给有兴趣的人员以简单帮助
Goldfish Audio <-> arch/arm/mach-goldfish/audio.c Goldfish pdev-bus <-> arch/arm/mach-goldfish/board-goldfish.c arch/arm/mach-goldfish/pdev_bus.c Goldfish power management <-> arch/arm/mach-goldfish/pm.c Goldfish switch device(我觉得这是一个test device) <-> arch/arm/mach-goldfish/switch.c Goldfish timer <-> arch/arm/mach-goldfish/timer.c Goldfish tty <-> drivers/char/goldfish_tty.c Goldfish mmc(multi-media card) <-> drivers/mmc/host/goldfish.c Goldfish keyboard <->drivers/input/keyboard/goldfish_events.c Goldfish Nand <-> drivers/mtd/devices/goldfish_nand.c goldfish battery <-> drivers/power/goldfish_battery.c Goldfish rtc <-> drivers/rtc/rtc-goldfish.c Goldfish framebuffer <-> drivers/video/goldfishfb.c And others: drivers/leds/ledtrig-sleep.c drivers/misc/lowmemorykiller.c drivers/misc/qemutrace/* drivers/switch/*
Normally we can find all the device hardware info in the android-emulator/qemu/hw directory including goldfish_audio, goldfish_mmc, goldfish_nand, goldfish_tty, goldfish_events_device, goldfish_battery, goldfish_fb, goldfish_timer,goldfish_switch.
Android / Anonymous Shared Memory Subsystem, ashmem <-> mm/ashmem.c
drivers/android/(alarm.c binder.c logger.c power.c ram_console.c timed_gpio.c)
From android-emulator boot log goldfish_add_device: goldfish_interrupt_controller, base ff000000 1000, irq 0 0 goldfish_add_device: goldfish_device_bus, base ff001000 1000, irq 1 1 goldfish_add_device: goldfish_timer, base ff003000 1000, irq 3 1 goldfish_add_device: goldfish_rtc, base ff010000 1000, irq 10 1 goldfish_add_device: goldfish_tty, base ff002000 1000, irq 4 1 goldfish_add_device: goldfish_tty, base ff011000 1000, irq 11 1 goldfish_add_device: smc91x, base ff012000 1000, irq 12 1 goldfish_add_device: goldfish_fb, base ff013000 1000, irq 13 1 goldfish_add_device: goldfish_memlog, base ff006000 1000, irq 0 0 goldfish_add_device: goldfish-battery, base ff014000 1000, irq 14 1 goldfish_add_device: goldfish_events, base ff015000 1000, irq 15 1 goldfish_add_device: goldfish_nand, base ff016000 1000, irq 0 0 goldfish_add_device: goldfish-switch, base ff017000 1000, irq 16 1 goldfish_add_device: goldfish-switch, base ff018000 1000, irq 17 1
In the end the Google Android use YAFFS2 file system which is a NAND file system, more info is here (http://discuz-android./2008/01/yaffsyaffs2-and-jffs2-comparison.html)
drivers/char/goldfish_tty.c drivers/char/Kconfig config GOLDFISH_TTY tristate “Goldfish TTY Driver” default n help TTY driver for Goldfish Virtual Platform.
drivers/mmc/host/goldfish.c drivers/mmc/host/Kconfig config MMC_GOLDFISH tristate “goldfish qemu Multimedia Card Interface support” depends on ARCH_ANDROIDSIM help This selects the Goldfish Multimedia card Interface emulation.
If unsure, say N.
drivers/input/keyboard/goldfish_events.c drivers/input/keyboard/Kconfig config KEYBOARD_GOLDFISH_EVENTS tristate “Generic Input Event device for Goldfish” help no help drivers/mtd/devices/goldfish_nand.c drivers/mtd/devices/goldfish_nand_reg.h drivers/mtd/devices/Kconfig config MTD_GOLDFISH_NAND tristate “Goldfish NAND device” help none
drivers/power/goldfish_battery.c drivers/power/Kconfig config BATTERY_GOLDFISH tristate “Goldfish battery driver” help Say Y to enable support for the battery and AC power in the Goldfish emulator. drivers/rtc/rtc-goldfish.c drivers/rtc/Kconfig config RTC_DRV_GOLDFISH tristate “GOLDFISH” depends on RTC_CLASS help RTC driver for Goldfish Virtual Platform drivers/video/goldfishfb.c drivers/video/Kconfig config FB_GOLDFISH tristate “Goldfish Framebuffer depends on FB select FB_CFB_FILLRECT select FB_CFB_COPYAREA select FB_CFB_IMAGEBLIT —help— Framebuffer driver for Goldfish Virtual Platform mm/ashmem.c mm/Makefile obj-$(CONFIG_ASHMEM) += ashmem.o
drivers/android/(alarm.c binder.c logger.c power.c ram_console.c timed_gpio.c) drivers/Kconfig source “drivers/android/Kconfig” drivers/Makefile obj-y += android/
|
Linux进程调度中队列的使用
作者:西邮 王聪
Linux内核中大量使用了队列,这里仅列举它在进程调度中的几处应用。Linux内核中的队列是以双链表的形式连接起来的,include/linux/list.h中定义了队列并提供了一些接口,详细的介绍可以参考[1]中的附录。 Linux中的进程有如下几个主要状态:
进程状态
|
说明
|
TASK_RUNNING
|
进程正在运行或将要被运行。
|
TASK_INTERRUPTIBLE
|
进程正在睡眠,等待某个条件的完成。
|
TASK_UNINTERRUPTIBLE
|
深度睡眠,不会被信号打扰。
|
TASK_STOPPED
|
进程运行被停止。
|
TASK_TRACED
|
进程被调试程序停止,被另一个进程跟踪。
|
两个额外的状态是EXIT_ZOMBIE和EXIT_DEAD,表示进程处于僵死状态还是真正死亡。处于僵死状态的进程会等待其父进程的收养(否则就会被init进程收养),而真正死亡的进程会被直接删除。 状态为TASK_RUNNING的进程都会被放入运行队列(runqueue)中,这是通过task_struct(定义在include/linux/sched.h)中的run_list成员来链接的。不过,为了让内核每次都能选取优先级最合适的进程,Linux为每个优先级构建了一个queue。这是通过struct prio_array来实现的,struct prio_array的定义在kernel/sched.c,大致如下: struct prio_array { int nr_active; unsigned long bitmap[BITMAP_SIZE]; struct list_head queue[MAX_PRIO]; }; queue成员就是队列数组。每个CPU有各自的runqueue,每一个runqueue又有包含两个prio_array,一个是活动队列,一个是时间片耗尽的队列。当运行队列空时,内核便会交换两个队列的指针,原来的耗尽队列就成了新的活动队列!这和prio_array中的bitmap是决定调度算法为O(1)的关键。 状态为TASK_STOPPED,EXIT_ZOMBIE或EXIT_DEAD的进程不会被放入专门的队列中,它们直接通过pid或者通过父进程的孩子队列来访问。 TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE状态的进程会被放入等待队列。不同的是,每个等待事件都会有一个等待队列,该队列中的进程等待同一事件的完成。(“事件”一个动态的过程,不好通过具体的结构来定义一个“事件”。这里等待一个事件就是查看某个条件是否为真,比如某个标志变量是否为真。)wait_queue_head_t的定义在include/linux/wait.h中,如下: typedef struct _ _wait_queue_head { spinlock_t lock; struct list_head task_list; }wait_queue_head_t; wait_queue_t的定义如下: typedef struct _ _wait_queue { unsigned int flags; struct task_struct * task; wait_queue_func_t func; struct list_head task_list; }wait_queue_t; 进入等待状态的接口有两类: prepare_to_wait*()/finish_wait() wait_event*() 其实wait_event*()内部也是调用prepare_to_wait*(),把它放入一个循环中。而且wait_event*()在事件完成时会自动调用finish_wait()。决定使用哪个要看情况而定。(sleep_on*()是遗弃的接口,现在已经不再使用,虽然还支持。)等待队列中的进程有两种,一种是exclusive的进程,另一种是nonexclusive的进程。所谓exclusive是指唤醒的进程等待的资源是互斥的,每次只唤醒一个(唤醒多个也可以,不过最后还是只有一个会被唤醒,其余的又被重新添加到等待队列中,这样效率会大打折扣)。一般,等待函数会把进程设为nonexclusive和uninterruptible,带“interruptible”的会专门指定状态为interruptible;而带“timeout”的会在超时后退出,因为它会调用schedule_timeout();带“exclusive”的则会把进程设为exclusive。 唤醒的接口虽然只有wake_up*(),但它内部也分成好几种。带“interruptible”的唤醒函数只会唤醒状态是TASK_INTERRUPTIBLE的进程,而不带的则会唤醒TASK_INTERRUPTIBLE和TASK_UNINTERRUPTIBLE的进程;所有唤醒函数都会把等待同一事件的nonexclusive进程全部唤醒,或者把其中一个exclusive的进程唤醒;而带“nr”的则会唤醒指定个数的exclusive的进程,带“all”的会把全部exclusive进程唤醒。带“sync”会忽略优先级的检查,高优先级的进程可能会被延迟。最后,持有自旋锁时只能调用wait_up_locked()。 进程管理是Linux内核中最关键的部分,它的性能几乎直接决定着内核的好坏,其中使用的一些设计和算法非常复杂。这里仅仅是介绍了队列在其中的使用情况,更深入的探索还有待继续去探索。 参考资料:
Linux Kernel Development, Second Edition, Robert Love, Sam Publishing.
Understanding the Linux Kernel, 3rd Edition, Daniel P. Bovet, Marco Cesati, O'Reilly.
The Linux Kernel Primer: A Top-Down Approach for x86 and PowerPC Architectures, Claudia Salzberg Rodriguez, Gordon Fischer, Steven Smolski, Prentice Hall PTR.
内核抢占 ————————————————— 与其他大部分Unix变体和其他大部分的操作系统不同, Linux完整地支持内核抢占。 在不支持内核抢占的内核中,内核代码可以一直执行,到它完成为止。也就是说,调度程序没有办法在一个内核级的任务正在执行的时候重新调度 — 内核中的各任务是协作方式调度的,不具备抢占性。 在2.6版的内核中,内核引人了抢占能力;现在,只要重新调度是安全的,那么内核就可以在任何时间抢占正在执行的任务。 那么,什么时候重新调度才是安全的呢?只要没有持有锁,内核就可以进行抢占。锁是非抢占区域的标志。由于内核是支持SMP的,所以,如果没有持有锁,那么正在执行的代码就是可重新导入的,也就是可以抢占的。 为了支持内核抢占所作的第一处变动就是每个进程的thread_info引入了preempt_count(thread_info.preempt_count)计数器。该计数器初始值为0,每当使用锁的时候数值加1,释放锁的时候数值减1。当数值为0的时候,内核就可执行抢占。从中断返回内核空间的时候,内核会检查need_resched和preempt_count的值。如果need_resched被设置,并且preempt_count为0的话,这说明有一个更为重要的任务需要执行并且可以安全地抢占,此时,调度程序就会调度(抢占当前进程)。如果preempt_count不为0,说明当前任务持有锁,所以抢占是不安全的。这时,就会像通常那样直接从中断返回当前执行进程。如果当前进程所持有的所有的锁都被释放了。那么preempt_count就会重新为0。此时,释放锁的代码会检查need_resched是否被设置。如果是的话,就会调用调度程序。有些内核代码需要允许或禁止内核抢占。 如果内核中的进程被阻塞了,或它显式地调用了schedule(),内核抢占也会显式地发生。这种形式的内核代码从来都是受支持的,因为根本无需额外的逻辑来保证内核可以安全地发生被抢占。如果代码显式的调用了schedule(),那么它应该清楚自己是可以安全地被抢占的。 内核抢占发生在: (1) 当"从中断处理程序"正在执行,且返回内核空间之前 (2) 内核代码再一次具有可抢占性的时候 (3) 如果内核中的任务显式的调用schedule() (4) 如果内核中的任务阻塞(这同样也会导致调用schedule()) 对应的英文: Kernel Preemption The Linux kernel, unlike most other Unix variants and many other operating systems, is a fully preemptive kernel. In non-preemptive kernels, kernel code runs until completion. That is, the scheduler is not capable of rescheduling a task while it is in the kernelkernel code is scheduled cooperatively, not preemptively. Kernel code runs until it finishes (returns to user-space) or explicitly blocks. In the 2.6 kernel, however, the Linux kernel became preemptive: It is now possible to preempt a task at any point, so long as the kernel is in a state in which it is safe to reschedule. So when is it safe to reschedule? The kernel is capable of preempting a task running in the kernel so long as it does not hold a lock. That is, locks are used as markers of regions of non-preemptibility. Because the kernel is SMP-safe, if a lock is not held, the current code is reentrant and capable of being preempted. The first change in supporting kernel preemption was the addition of a preemption counter, preempt_count, to each process's thread_info. This counter begins at zero and increments once for each lock that is acquired and decrements once for each lock that is released. When the counter is zero, the kernel is preemptible. Upon return from interrupt, if returning to kernel-space, the kernel checks the values of need_resched and preempt_count. If need_resched is set and preempt_count is zero, then a more important task is runnable and it is safe to preempt. Thus, the scheduler is invoked. If preempt_count is nonzero, a lock is held and it is unsafe to reschedule. In that case, the interrupt returns as usual to the currently executing task. When all the locks that the current task is holding are released, preempt_count returns to zero. At that time, the unlock code checks whether need_resched is set. If so, the scheduler is invoked. Enabling and disabling kernel preemption is sometimes required in kernel code and is discussed in Chapter 9. Kernel preemption can also occur explicitly, when a task in the kernel blocks or explicitly calls schedule(). This form of kernel preemption has always been supported because no additional logic is required to ensure that the kernel is in a state that is safe to preempt. It is assumed that the code that explicitly calls schedule() knows it is safe to reschedule. Kernel preemption can occur When an interrupt handler exits, before returning to kernel-space When kernel code becomes preemptible again If a task in the kernel explicitly calls schedule() If a task in the kernel blocks (which results in a call to schedule())
几乎所有的Linux项目都是使用make来进行项目管理的.make是依据Makefile来进行工作的.因此书写和阅读Makefile就是Linux程序员必备的一项基本功. 对于像我这样的newbie,写一个稍微复杂的Makefile必定会错误百出,原因是Makefile的命令语法与bash有相似之处,但是还有许多细节上的差别,比如
- 赋值bash要求"="两边是不能有空格的,但是Makefile就允许.
- 在bash中用if条件判断,就像VB一样,但是如果要在Makefile中使用的话,需要写到一行中,如果写不下,需要用""来告诉make,下面的一行和上面的这一行属于一个逻辑行.
- Makefile还对TAB,空格有严格的要求,这个仿佛就像是Python.如果你不TAB就写一个命令,make不认为是一个命令,而如果写个ifdef而用了TAB的话,make会把ifdef当作一个命令来执行,而不是一个条件判断.
- exit在Makefile中貌似起不到Bash中的效果,你要在Makefile中控制其执行情况必须用error才可以.
读Makefile就是执果索因(这是别人总结的),根据结果顺瓜摸藤,就比较容易了.而对于Makefile的语法如果单单能够要求读懂的话,也是比较简单。 一般情况,在一个项目中,用Makefile就完全能够进行项目管理了。但一些项目会由像Rules.mk,Post.mk之类的东西让我们感到望而生畏,仿佛Make是一项深不可测的东西。其实 Rules.mk和Post.mk同 1.mk 2.mk没有什么区别。在一个论坛里面有下面一句话: IIRC, traditionally, when people set up Makefiles and used files named rules.mk and make.mk, they put all the platform-independent stuff in make.mk, and all the platform-specific stuff in rules.mk. And then their Makefiles just included those, and each Makefile contained the things specific to each thing that was being built (like source filenames and such). 关于Makefile的学习,有一本介绍Makefile的电子书,写得非常好,它是由陈皓撰写,内容与文笔一样的优秀的–《跟我一起写Makefile》。需要下载的朋友,可以google之。BTW,能够表达能力极强的程序员着实太少,倘若都能够像陈皓这样,我们国家的计算机事业应该比印度高不少了吧。他的主页: http://。 <!—全文完—> 附录: 关于项目上的东西,以后时间久了,看它就可以了。 xenarm中的Makefile Makefile生成的目标文件有4个: xen-bin xen xen.gz xen-syms xen-bin arch/arm/Makefile 17 $(TARGET): $(TARGET)-bin $(TARGET)-syms 18 @cp $(TARGET)-syms $@ fuck,xen-syms和xen原来是一个东西!
… 29 $(TARGET)-bin: $(TARGET)-syms 30 $(OBJCOPY) $(OBJCOPYFLAGS) $< $@ xen-syms 20 $(TARGET)-syms: arch-$(TARGET_MACHINE)/start.o $(ALL_OBJS) xen.lds 21 $(OBJCOPY) -I binary -O elf32-littlearm -B arm 24.bmp image.o 22 $(OBJCOPY) -I binary -O elf32-littlearm -B arm 8×16 en.o 23 #$(OBJCOPY) -I binary -O elf32-littlearm -B arm shut.wav sound.o 24 #$(OBJCOPY) -I binary -O elf32-littlearm -B arm mini mini.o 25 $(LD) $(LDFLAGS) -N -T xen.lds arch-$(TARGET_MACHINE)/start.o $(ALL_OBJS ) en.o image.o -o $@ xen ./Rules.mk 30 TARGET := $(BASEDIR)/xen xen.gz ./Makefile 18 $(TARGET).gz: $(TARGET) 19 gzip -f -9 < $< > $@.new 20 mv $@.new $@ – Li Haifeng Laboratory of Service Computing Technology and System Home page:http://
|