杂七杂八
标签(空格分隔): 杂七杂八
##1.top的改善
1.在内核线程中使用msleep的时候,内核top统计的时候会认为这种不可中断的线程仍然被占用,但实际上使用msleep是不会长时间占用cpu的。
使用可中断的msleep_interrupt,可以避免这个问题,可以使top时候的系统loading下降
##2.后台运行
ctrl + z 进入后台
fg退出后台
##3.启动时间的优化
http://www.cnblogs.com/pengdonglin137/p/3797276.html
##4.关于UBOOT,LINUX内核编译,根文件系统的15个弱智问题
http://blog.csdn.net/jikaishihuaidan/article/details/26278683
(1)内核默认运行地址和加载地址在哪里设置?
(2)从FLASH什么地址找内核和根文件系统,UBOOT在哪里设置?
(3)启动时UBOOT将根文件系统和内核拷贝到RAM中何处在哪里设置?
(4)UBOOT 如何知道内核与根文件系统的格式和大小?
(5)内核启动参数在编译UBOOT时候哪里指定?
(6)内核启动后如何知道在SDRAM中何处找到根文件系统来挂载?如何知道根文件系统大小和格式?
(7)内核如何知道UBOOT欲传递给内核的参数放在SDRAM中何处?
(8)内核编译时候的运行地址在哪里指定?内核是位置无关码吗?
(9)友善之臂MINI2440的说明书写道:
由于本系统采用了可读写文件系统 yaffs(在嵌入式系统中,专门管理 Flash 存储器的
一种文件系统),因此可以很方便的动态保存数据,掉电后不会丢失。开机后在串口终端运行
以下命令: #cp / shanghaitan.mp3 /home/plg 此时将在/home/fa目录下复制一个同样的文件,然后关机,重新开启系统,可以查看
到/home/plg 目录下的文件依然存在。
----- 根文件系统不是已经搬运到在RAM中了?COPY文件是在RAM中啊怎么会写到FLASH里呢?
(10)内核启动参数在配置内核时候可以选。BOOTLOADER也可以传参数。这两种参数是互补的关系还是谁优先的关系(如果有冲突的话)?
(11) 友善的板子SUPERVIVI/UBOOT从NOR启动然后可以烧写文件到NAND,但是不能从NAND启动然后烧写文件到NOR ----烧文件到NOR需要HJTAG或者
JLINK,BOOTLOADER,如UBOOT有支持烧写NOR的吗?
(12) 配置内核(或VIVI)运行MAKE MENUCONFIG 时是否顶层如果已经有专门配好的设置.config,make就会自动调入.config的配置,但是如果没有.config按照哪个默认值显示呢?
(13)LINUX内核和根文件系统映像在NAND中的起始地址和大小的信息,是不是要UBOOT在程序中写死,这样的话,对某一个特定版本的UBOOT,LINUX内
核和根文件系统就不能在NAND中任意位置烧写了。还是,内核和根文件系统在NAND中的位置和大小都是可变的,同一个版本的UBOOT可以智能地检测
出这些信息从而正常启动linux?
(14) 修改UBOOT代码让UBOOT支持“JFFS2/CRAMFS/YAFFS” -------- 这句话是什么意思? uboot的作用不就是把FLASH里的根文件系统搬运到SDRAM里吗?管它是什么格式能COPY到SDRAM不久行了吗?
(15)据说lINUX内核可以在FLASH本地也可以加载到内存中运行,UBOOT是哪种方式?编译UBOOT可以选择用哪种方式吗?
- 看《嵌入式LINUX应用开发完全手册》302页
- UBOOT里有环境变量,UBOOT启动后,会根据bootcmd这个环境变量来执行命令。就是在这个环境变量里写明:从哪里读出内核;
文件系统在哪里,是由内核来确定的,建议你先按顺序好好看书。 - 启动时UBOOT不拷贝根文件系统,把内核拷贝到哪里,请看bootcmd环境变量
- UBOOT不管文件系统,内核就是一个可以直接运行的映象,没什么特殊格式
- 自己看UBOOT那章
- 内核启动后,不是在SDRAM中找文件系统,而是在FLASH上找,你看内核移植、根文件系统那2章
- 看UBOOT那章
- 看内核那章,内核开始一小部分的代码是位置无关的
- 跟RAM没有关系
- 传递的参数优先
- 有,本论坛的UBOOT就可以直接烧写NOR
- 试试就知道
- UBOOT只管内核在哪,这个地址可以通过命令设置,不是写死;
文件系统由内核来找,这个地址一般是写死在内核中 - 支持烧写这些文件系统类型的映象文件
- 内核可以在NOR上直接运行,这跟UBOOT没关系,UBOOT只是负责初始化一下硬件,然后可能的话把内核复制到SDRAM中,然后启动内核;
如果是NOR上运行内核,那么UBOOT初始化完硬件后,直接启动内核
顶
##5.关于地址总线的使用
Q:
疑问,一般所有的外设都是直接挂在cpu的地址总线上的,在规划的时候会去给各个IP分配,但是flash可以做的很大,还有DDR也会做的很大。
如果我制式一个32位地址总线的cpu,那地址的寻址空间就只有4G。如果我要挂一个2G的flash,和1G的DDR,他们直接就会吃掉3G的物理地址空间,
那其他ip还怎么够用呢?,如果我要挂一个8G的flash呢,那岂不是地址总线的空间都不够用了?
A:唉,还是上学的时候没有好好学,现在用上了又要补了。。。
先看一下32位cpu的地址总线空间一般是怎么样规划的吧。
0G | IO reg | DDR | flash | reserved |4GB
|_____________________|_________________|__________________|___________________|
/ 各种外设控制器的 \
/ 寄存器的地址空间 \
如上图可以看到,32位地址总线会有4GB的地址空间,其中分配IO的空间是一部分,一般根据需要在10来M左右,里面是分配给外设控制寄存器的空间,
比如DRAM控制器,flash控制器,watchdog etc。
回到上面的问题,flash不是直接挂在地址总线上的,分配给flash的地址空间是用于和flash的片选线挂载的,通过片选的方式可以挂载超过本身地址空间大小的flash,
然后通过flash控制器去控制就可以了,这样就解决了上述地址空间不够的问题。
但是ddr就不是用这种方式了,ddr是直接挂在地址总线上的,为了访问速度的考虑没有做片选的动作,所以就是多大的DDR就会占用多大的地址总线空间。
ddr的控制就在IO空间中的ddr控制寄存器来控制。
所以这就是为什么32位的地址总线却不能挂4G的DDR,或者说挂载了,但是实际是用不到这么的DDR,存在浪费。因为地址总线除了要挂DDR,还要挂载其他的ip
##6.cpu占比高的案例
1.由于线程中使用了不能中断的sleep导致,换做sleep_interrupt即可。同时线程的sleep时间要大于内核节拍的时间
2.内核中kworker占比很高
详细见sleep中的相关案例记录
##7.reboot和手动掉电的区别
reboot是执行命令去重启,实现方式很多,看门狗,pmu粗暴断电方式等等
但是reboot可以做很多处理,正因为它是去执行命令的,是一种主动的操作,执行是可控制的,所以在reboot命令触发时可以按照我们的设定去做一系列动作。
reboot和手动断电的比较常见的区别:
reboot在执行后会去保存参数,类似于有安排的演习
手动断电是未知的,直接板端断电,类似于紧急事故
-------------------------------------------------------分割线--------------------------------------------
reboot和poweroff的区别
这两个命令在kernrl的reboot.c中定义
char poweroff_cmd[POWEROFF_CMD_PATH_LEN] = "/sbin/poweroff";
static const char reboot_cmd[] = "/sbin/reboot";
reboot最终是调用看门狗的接口去调用的
poweroff的做法很多,最简单是调用的函数中直接去置一个IO口,这个IO口用于控制电源的上下电
reboot全流程
影响reboot失败的case有哪些?
http://blog.csdn.net/davion_zhang/article/details/52233043
最终会执行到machine结构中的.restart 成员
例如:
define_machine(p2020_rdb_pc) {
.name = "P2020RDB-PC",
.probe = p2020_rdb_pc_probe,
.setup_arch = mpc85xx_rdb_setup_arch,
.init_IRQ = mpc85xx_rdb_pic_init,
#ifdef CONFIG_PCI
.pcibios_fixup_bus = fsl_pcibios_fixup_bus,
#endif
.get_irq = mpic_get_irq,
.restart = fsl_rstcr_restart,
.calibrate_decr = generic_calibrate_decr,
.progress = udbg_progress,
};
void fsl_rstcr_restart(char *cmd)
{
local_irq_disable();
if (rstcr)
/* set reset control register */
out_be32(rstcr, 0x2); /* HRESET_REQ */
while (1) ;
}
##8.bit,byte,word
(字节)byte = 8bit(位),
word,与系统硬件(总线、cpu命令字位数等)有关,如数据总线为16位,则1word为2byte。32位 1word为4byte。
1G=1024MB,1M=1024KB,1K=1024byte(字节),1yte=8bit(位)
##9.linux驱动的执行顺序
动态加载则不用阐述:主要是编译为模块的驱动文件,可以在脚本中使用insmod命令去加载驱动模块(.ko文件),节点会由udev自动的创建。
静态加载:静态加载即将驱动编译进内核中,但此时这个驱动的编译顺序是怎么控制的呢?
核心进程(/init/main.c)kernel_init do_basic_setup()->kernel_init_freeable()-> do_initcalls()该函数中会将在__initcall_start和__initcall_end之间定 义的各个模块依次加载。那么在__initcall_start 和 __initcall_end之间都有些什么呢?
找到/arch/powerpc/kernel/vmlinux.lds文件,找到.initcall.init段:
.initcall.init : {
__initcall_start = .;
*(.initcall0.init) *(.initcall0s.init) *(.initcall1.init) *(.initcall1s.init) *(.initcall2.init) *(.initcall2s.init) *(.initcall3.init) *(.initcall3s.init) *(.initcall4.init) *(.initcall4s.init) *(.initcall5.init) *(.initcall5s.init) *(.initcallrootfs.init) *(.initcall6.init) *(.initcall6s.init) *(.initcall7.init) *(.initcall7s.init)
__initcall_end = .;
}
可以看出在这两个宏之间依次排列了14个等级的宏,由于这其中的宏是按先后顺序链接的,所以也就表示,这14个宏有优先级:0>1>1s>2>2s………>7>7s。
那么这些宏有什么具体的意义呢,这就要看/include/linux/init.h文件:
#define pure_initcall(fn) __define_initcall("0",fn,0)
#define core_initcall(fn) __define_initcall("1",fn,1)
#define core_initcall_sync(fn) __define_initcall("1s",fn,1s)
#define postcore_initcall(fn) __define_initcall("2",fn,2)
#define postcore_initcall_sync(fn) __define_initcall("2s",fn,2s)
#define arch_initcall(fn) __define_initcall("3",fn,3)
#define arch_initcall_sync(fn) __define_initcall("3s",fn,3s)
#define subsys_initcall(fn) __define_initcall("4",fn,4)
#define subsys_initcall_sync(fn) __define_initcall("4s",fn,4s)
#define fs_initcall(fn) __define_initcall("5",fn,5)
#define fs_initcall_sync(fn) __define_initcall("5s",fn,5s)
#define rootfs_initcall(fn) __define_initcall("rootfs",fn,rootfs)
#define device_initcall(fn) __define_initcall("6",fn,6)
#define device_initcall_sync(fn) __define_initcall("6s",fn,6s)
#define late_initcall(fn) __define_initcall("7",fn,7)
#define late_initcall_sync(fn) __define_initcall("7s",fn,7s)
这里就定义了具体的宏,我们平时用的module_init在静态编译时就相当于device_initcall。举个例子,在2.6.24的内核 中:gianfar_device使用的是arch_initcall,而gianfar_driver使用的是module_init,因为 arch_initcall的优先级大于module_init,所以gianfar设备驱动的device先于driver在总线上添加。
但如果我有a,b,c三个驱动都是使用module_init这个宏来进行加载的,此时他们三个的优先级是一致的,这样他们的执行顺序又是怎么样的呢?
一个子系统下有两个module_init(fn),则这两个初始化函数的执行顺序只跟链接顺序有关。
如: 下面是drivers/serial/Makefile部分内容
#
# $Id: Makefile,v 1.8 2002/07/21 21:32:30 rmk Exp $
#
serial-8250-y :=
serial-8250-$(CONFIG_SERIAL_8250_ACPI) += 8250_acpi.o
serial-8250-$(CONFIG_PNP) += 8250_pnp.o
serial-8250-$(CONFIG_GSC) += 8250_gsc.o
serial-8250-$(CONFIG_PCI) += 8250_pci.o
serial-8250-$(CONFIG_HP300) += 8250_hp300.o
obj-$(CONFIG_SERIAL_CORE) += serial_core.o
obj-$(CONFIG_SERIAL_21285) += 21285.o
obj-$(CONFIG_SERIAL_VIRGO) += serial_virgo.o
obj-$(CONFIG_SERIAL_8250) += 8250.o $(serial-8250-y)
obj-$(CONFIG_SERIAL_8250_CS) += serial_cs.o
obj-$(CONFIG_SERIAL_8250_ACORN) += 8250_acorn.o
obj-$(CONFIG_SERIAL_8250_ORION) += 8250_orion.o
obj-$(CONFIG_SERIAL_8250_CONSOLE) += 8250_early.o
obj-$(CONFIG_SERIAL_AMBA_PL010) += amba-pl010.o
obj-$(CONFIG_SERIAL_AMBA_PL011) += amba-pl011.o
obj-$(CONFIG_SERIAL_CLPS711X) += clps711x.o
... ...
... ...
当在menuconfig中配置了8250, 8250_console以及8250_orion后, 链接先后顺序依次是:
serial_core.o
8250.o
8250_orion.o
8250_early.o
这几个目标文件中一共有两个module_init,分别是8250.o中的module_init(serial8250_init);
和8250_orion.o中的module_init(serial_orion_init);
所以这两个初始化函数的先后执行顺序依次是:
serial8250_init
serial_orion_init
总结:
1.先按不同的优先级加载函数来从高优先级到低优先级这样子加载驱动
2.使用相同优先级加载函数的驱动则由其链接的顺序决定
##10.devmem
devmem 接触LINUX这么长时间了竟然不知道有这么一个寄存器读写工具的存在
在写的时候要注意,如果是32位的数据需要注意以下格式:
devmem 0x… 32 0x…要天上这个数字32,不然这个命令会报错
##11.使用本级成员获取上级成员
container_of(inode->i_cdev, struct nim_device, cdev);
##12.使用指针传递数据注意事项
如果使用指针传递数据的时候,发现传递失败,一定要打印每一层的指针地址,看这个地址传递在哪一环节出现问题。
因为malloc的数据,在malloc以后地址就定下来了,这是一个很好的debug手段
##13.关于ioctl,open,read,write的阻塞试验
对于应用程序而言,一般对于设备节点的打开有阻塞和非阻塞而言,在open的权限描述中体现,默认是阻塞的,如果不阻塞是unblock。
如果open中使用了阻塞的设计,那么后续对该节点的read write iocl等操作,均是阻塞的,反之如果open为非阻塞打开,则后续操作均是非阻塞的
但是有一个疑问,一个应用程序操作节点的阻塞和非阻塞是由应用程序的权限决定还是驱动中的阻塞来决定的?
然后做了一个试验:
1:应用 open(dev_node,O_UNBLOCK) //默认不阻塞打开
driver:对系统调用接口(open read write close ioctl 。。)做处理延5s
结果:在应用的调用处,在加有延时的地方均阻塞在了5s然后才返回,没有阻塞
2:应用 open(dev_node,O_UNBLOCK) //默认不阻塞打开
driver:对系统调用接口(open read write close ioctl 。。)做处理延5s
结果:在应用的调用处,在加有延时的地方均阻塞在了5s然后才返回
从上述可以看出,上层的对设备操作时候的阻塞和非阻塞一定是要驱动层支持的,仅仅是上层open时候的阻塞和非阻塞权限没有什么用
查看了一些对块设备的操作看到,标准的操作应该是driver应该支持去判断file->f_flags这个标志,如果file->f_flags为阻塞标志,则做相应的阻塞操作,如果是非阻塞则不做阻塞操作,
这样才可以配合上层的权限操作。open时候的权限会去设置file->f_flags这个值。
所以,所谓的阻塞和非阻塞一定要驱动层支持。
总结:支持阻塞操作的设备驱动通常会实现一组自身的等待队列如读/写等待队列用于支持上层(用户层)所需的BLOCK或NONBLOCK操作。当应用程序通过设备驱动访问该设备时(默认为BLOCK操作),若该设备当前没有数据可读或写,则将该用户进程插入到该设备驱动对应的读/写等待队列让其睡眠一段时间,等到有数据可读/写时再将该进程唤醒。
##14.在线烧录文件
使用busybox的工具:dd
在终端下敲:dd会有提示
先挂载文件:
mount -o nolock images/ /usr/
然后进入这个目录
cd /usr
查看一下分区:
cat /proc/mtd
然后执行dd命令:
dd if=main_bin.ubo of=/dev/mtdblock4
但是这个分区如果有坏块的话就不可以,这个工具会检坏块,而且不会跳坏块,遇到坏块有问题。
而且这个命令是基于mtdblock节点生成的,所以要看这个些节点有没有生成
但是有一点一定要注意。使用这个命令的时候,如果烧录的镜像太大的话,而平台ddr太小,那么烧录会失败
##15.ps和top命令的解读
ps命令可以查看系统的所有线程,包括内核线程和用户态线程。
PID USER COMMAND
1 root init
2 root [kthreadd]
3 root [ksoftirqd/0]
5 root [kworker/0:0H]
7 root [rcu_sched]
8 root [rcu_bh]
9 root [migration/0]
10 root [migration/1]
11 root [ksoftirqd/1]
13 root [kworker/1:0H]
14 root [migration/2]
15 root [ksoftirqd/2]
17 root [kworker/2:0H]
18 root [migration/3]
19 root [ksoftirqd/3]
21 root [kworker/3:0H]
22 root [kdevtmpfs]
23 root [netns]
24 root [perf]
25 root [writeback]
26 root [crypto]
27 root [kworker/0:1]
28 root [bioset]
29 root [kblockd]
30 root [ali_rpc]
31 root [ali_rpc]
32 root [ali_rpc]
33 root [ali_rpc]
34 root [ali_rpc]
35 root [rpciod]
36 root [kswapd0]
37 root [vmstat]
38 root [fsnotify_mark]
39 root [nfsiod]
50 root [bioset]
51 root [bioset]
52 root [bioset]
53 root [bioset]
54 root [bioset]
55 root [bioset]
56 root [bioset]
57 root [bioset]
58 root [bioset]
59 root [(null)]
60 root [dmx_dbg]
61 root [ali_tsg_xfer_ta]
62 root [kworker/0:2]
63 root [kworker/0:3]
64 root [pan-wq]
65 root [bioset]
66 root [bioset]
67 root [bioset]
68 root [bioset]
69 root [bioset]
70 root [bioset]
71 root [kworker/3:1]
74 root [kworker/u9:0]
75 root [otz_ipi_handler]
76 root [deferwq]
77 root [kworker/0:1H]
78 root [kworker/1:1H]
79 root [kworker/2:1H]
81 root [kworker/3:1H]
95 root [yaffs-bg-1]
103 root [yaffs-bg-1]
108 root /sbin/syslogd -n
110 root /sbin/klogd -n
116 root /usr/bin/hotplugd
119 root /usr/bin/rpcd
188 root [kworker/1:2]
189 root [kworker/0:5]
192 root /usr/sbin/lircd -u -d /dev/lirc0
255 root aui_testbench --network
256 root drv_testbench --network
295 root /tee/bin/tee-supp
300 root [kworker/2:2]
323 root -/bin/sh
1439 root [kworker/u8:2]
1442 root aui_test
1446 root [ali_hwdmx0_engi]
1458 root [kworker/u8:1]
1462 root aui_test
1463 root [nim0]
1464 root [kworker/2:0]
1484 root [kworker/3:2]
1489 root [kworker/1:0]
1492 root [kworker/2:3]
1496 root ps
#
上述就是使用ps命令后显示的系统中所有的线程,包括内核线程和用户态线程。
其中带[]的都是内核态线程,其余的都是用户态线程。
对于内核态的线程,[]中的都是线程名称。可以发现其中有很多是kworker开头的内核线程,这是什么意思?
对于多CPU的设备,系统启动的时候会为每一个cpu建立一个缺省的工作队列。每个工作队列由一个内核线程维护,我们看到的
[kworker/0:0]。[kworker/1:0][kworker/2:0][kworker/3:0]就表示一个4核系统的工作队列所对应的内核现车线程。
我们使用top命令可以看到:
62 2 root SW 0 0% 0% [kworker/0:2]
71 2 root SW 0 0% 0% [kworker/3:1]
300 2 root SW 0 0% 0% [kworker/2:2]
188 2 root SW 0 0% 0% [kworker/1:2]
这表示这4个缺省的工作队列所对应的内核线程的负载,所以如果你定一了一个任务,而你将这个任务加入了缺省的内核队列。因为这里的负载显示的,这个队列上所有的任务对这个
内核线程造成的负载,你无法知道单独一个任务造成的负载。
如果想知道,要么单独建一个工作队列,这个工作队列只加你这一个任务。然后使用top看负载。
或者单独建一个内核线程去执行你这个任务,然后看负载
上面还有一列是[kworker/u8:2],这是一个线程池。
##16.负载的小实验
之前这个4核的系统的默认缺省队列对应的内核线程负载都是0。
将一个任务加在内核缺省的工作队列中,这个任务中是一个while循环。有一个本地静态变量nu。
一直执行nu++;
static int nu = 0;
试验1.
while(1)
{
if(nu == 1000)
nu = 0;
nu++;
}
启动后,内核直接奔溃,堆栈显示在调用这个任务的时候奔溃。
试验2:
while(1)
{
if(nu == 1000)
nu = 0;
nu++;
msleep(10);
}
top显示有一个内核线程负载上升到%1
1% [kworker/0:3]
试验3:
while(1)
{
if(nu == 1000)
nu = 0;
nu++;
msleep(10);
}
top显示这个线程直接为2%
2% [kworker/0:3]
所以睡眠越长,该线程负载越少,反之越大。
#17.直接操作寄存器
#define PIN_SET(base, number, value) (*(volatile long *)((long)addr) = value)
#define PIN_GET(base, number) (*(volatile long *)((long)addr)
#18:进程调度时间片大小的设置
进程的调度周期是以tick为base周期的,是base周期的倍数
在这里配置:
Kernel Features --->
Timer frequency (1000 Hz) --->
( ) 100 Hz | |
| | ( ) 200 Hz | |
| | ( ) 250 Hz | |
| | ( ) 300 Hz | |
| | ( ) 500 Hz | |
| | (X) 1000 Hz
详细的可以看时间子系统
##19.linux下的devmem
直接make menuconfig
然后搜索,devmem即可看到依赖关系
Prompt: /dev/mem virtual device support |
| Location: |
| -> Device Drivers |
| (1) -> Character devices |
| Defined at drivers/char/Kconfig:9
选择1进入:
[*] /dev/mem virtual device support
选中该选项即可,此时devmem才可以使用。
##20.