虚拟机Linux Redhat 9与目标开发板进行系统编程方面的问题总结

来源:互联网 发布:车载音乐视频软件 编辑:程序博客网 时间:2024/06/07 10:52

mount eth0 192.168.0.5 broadcast 192.168.0.255 netmask 255.255.255.0

建立好对接后先ping下各自的IP地址看是否都连通了。

用指令mount -t nfs 192.168.0.14:/home /tmp/nfs进行挂接

3、修改文件的使用权限

chmod 777 文件名

4、在Linux系统下设置NFS共享
    网络文件系统(NFS,Network File System)是一种将远程主机上的分区(目录)经网络挂载到本地系统的一种机制,通过对网络文件系统的支持,用户可以在本地系统上像操作本地分区一样来对远程主机的共享分区(目录)进行操作。
  在嵌入式Linux 的开发过程中,开发者需要在Linux 服务器上进行所有的软件开发,交叉编译后,通用FTP 方式将可执行文件下载到嵌入式系统运行,但这种方式不但效率低下,且无法实现在线的调试。因此,可以通过建立NFS,把Linux 服务器上的特定分区共享到待调试的嵌入式目标系统上,就可以直接在嵌入式目标系统上操作Linux 服务器,同时可以在线对程序进行调试和修改,大大的方便了软件的开发。因此,NFS 的是嵌入式Linux 开发的一个重要的组成部分,本部分内容将详细说明如何配置嵌入式Linux 的NFS 开发环境。
  嵌入式Linux 的NFS 开发环境的实现包括两个方面:一是Linux 服务器端的NFS 服务器支持;二是嵌入式目标系统的NFS 客户端的支持。因此,NFS 开发环境的建立需要配置linux 服务器端和嵌入式目标系统端。
  一、Linux 服务器端NFS 服务器的配置
  以root 身份登陆Linux 服务器,编辑/etc 目录下的共享目录配置文件exports,指定共享目录及权限等。
  执行如下命令编辑文件/etc/exports:
  # vi /etc/exports
  在该文件里添加如下内容:
  /home/work 192.168.0.*(rw,sync,no_root_squash)
  然后保存退出。
  添加的内容表示:允许ip 地址范围在192.168.0.*的计算机以读写的权限来访问/home/work 目录。
  /home/work 也称为服务器输出共享目录。
  括号内的参数意义描述如下:
  rw:读/写权限,只读权限的参数为ro;
  sync:数据同步写入内存和硬盘,也可以使用async,此时数据会先暂存于内存中,而不立即写入硬盘。
  no_root_squash:NFS 服务器共享目录用户的属性,如果用户是 root,那么对于这个共享目录来说就具有 root 的权限。
  接着执行如下命令,启动端口映射:
  # /etc/rc.d/init.d/portmap start
  最后执行如下命令启动NFS 服务,此时NFS 会激活守护进程,然后就开始监听 Client 端的请求:
  # /etc/rc.d/init.d/nfs start
  用户也可以重新启动Linux 服务器,自动启动NFS 服务。
  在NFS 服务器启动后,还需要检查Linux 服务器的防火墙等设置(一般需要关闭防火墙服务),确保没有屏蔽掉NFS 使用的端口和允许通信的主机,主要是检查Linux 服务器iptables,ipchains 等选项的设置,以及/etc/hosts.deny,/etc/hosts.allow 文件。
  我们首先在Linux 服务器上进行NFS 服务器的回环测试,验证共享目录是否能够被访问。在Linux 服务器上运行如下命令:
  # mount –t nfs 192.168.0.20:/home/work /mnt
  # ls /mnt
  命令将Linux 服务器的NFS 输出共享目录挂载到/mnt 目录下,因此,如果NFS 正常工作,应该能够在/mnt 目录看到/home/work 共享目录中的内容。

    二、嵌入式目标系统NFS 客户端的配置
  在Linux 服务器设置好后,还需要对客户端进行相关配置。在配置内核时选择Load an Alternate Configuration File输入配置文件的路径和文件名添加内核对NFS的支持:
  选中networking options-》IP:kernel level auloconfiguralion项
  选中file systems-》network file systems-》下的root file system on nfs
  和nfs file system support重新编译内核下载bootloader和kernel到开发板上
  在嵌入式目标系统的Linux Shell 下,执行如下命令来进行NFS 共享目录挂载:
  # mkdir /mnt/nfs //建立Linux 服务器输出共享目录的挂载点;
  # mount –t nfs 192.168.0.20:/home/work /mnt/nfs –o nolock
  # cd /mnt/nfs
  # ls
  此时,嵌入式目标系统端所显示的内容即为Linux 服务器的输出目录的内容,即Linux 服务器的输出目。
  录/home/work 通过NFS 映射到了嵌入式目标系统的/mnt/nfs 目录。用户可以用增/删/修改文件的方式来验证实际效果。mount 命令中的192.168.0.20 为Linux 服务器的IP 地址,/home/work 为Linux 服务器端所配置的共享输出目录,/mnt/nfs 为嵌入式设备上的本地目录。
  在开发过程中,来回输入命令非常烦人,我写了两个简单的脚本来完成nfs的启动,挂载。
  host启动nfs:
  snfs
  #!/bin/bash
  ifconfig eth0 192.168.0.20
  /etc/rc.d/init.d/portmap start
  /etc/rc.d/init.d/nfs start
  嵌入式目标机挂载nfs:
  mnfs:

  #!/bin/sh
  mount -t nfs 192.168.0.20:/home/work/nfs /mnt/nfs -o nolock
  echo “nfs ok!”

总结:

1、在配置NFS服务器之前用ping命令确保两个linux系统正常连接,如果无法连接关闭图形界面中的防火墙#service iptables stop
2、在配置中确保输入的命令是正确的
3、更改完“exports”文件后要输入exportfs –rv ,使得“exports”文件生效。
4、检查nfs服务是否开启,默认是关闭的。
5、卸载的时候不能在/mnt目录中卸载,必须注销系统后卸载。
卸载系统中已挂载的NFS共享目录:命令# umount /mnt/

5、tar压缩、解压用法(platinum)
解压:x
压缩:c
针对gz:z
针对bz2:j
用于显示:v

解压实例
gz文件:tar xzvf xxx.tar.gz
bz2文件:tar xjvf xxx.tar.bz2

压缩实例
gz文件:tar czvf xxx.tar.gz /path
bz2文件:tar cjvf xxx.tar.bz2 /path

6、NFS网络文件系统的设置

    NFS本身不提供信息传输的协议和功能,但它的确能够透过网络实现信息的分享,它是Linux系统中广泛使用的文件系统,可以方便地实现异构网络系统的文件共享、数据交换,以及其他分布式的应用。

Linux会默认安装NFS文件系统,查看NFS是否安装,可以执行以下命令:

rpm -qa |grep nfs

启动NFS服务器主要分为以下两步:

portmap start

/etc/rc.d/init.d/nfs start

NFS服务器的共享配置保存在/etc/exports文件中,如果用户修改了这个文件,可以运行以下的命令行,以便马上生效。

/etc/rc.d/init.d/nfs restart

挂接可以使用mount进行,挂接前必须创建好要挂接到的目标目录

例如:mount -t nfs 192.168.8.188:/install /home/install

另外如果用户希望开机时自动挂接,也可以用以下方法编辑/etc/fstab文件,在/etc/fstab文件中追加一行:(以IP为192.168.8.188为例

192.168.8.188:/install /home/install nfs defaults,auto 0 0

192.168.8.188:/Downloads /home Downloads   nfs ro 0 0

/etc/fstab文件格式如下:

fs_spec fs_file fs_type fs_options fs_dump fs_pass

fs_spec-该字段定义要加载的文件系统所在的设备或远程文件系统,对于NFS文件系统一般配置为:192.168.8.188:/install的格式

fs_file-本地的挂载目标路径

fs_type-对于NFS来说文件系统为nfs

fs_options-挂载的参数,这个参数可以参考mount命令行

fs_dump-该选项可以设置为0

fs_pass-该选项可以设置为0

7、修改外壳脚本的权限
chmod u+x filename 只用自己可以执行,其他人不能执行

chmod ug+x filename 只有自己以及同一工作组的人可以执行,其他人不能执行

chmod +x filename 或者 chmod 777 filename 所有人都可以执行

8、内核的编译和移植(以PXA270为例)

 

 

    针对目前嵌入式系统中最流行的ARM平台,它的这部分工作就是由英国ARM公司的工程师完成的,我们所要做的就是从其网站上下载相关版本Linux内核的补丁(Patch)。把它打到我们的Linux内核上,再进行交叉编译就行。
其基本过程是这样的(以Linux2.6.9为例):
1)、到
ftp://ftp.arm.linux.org.uk 上下载Linux2.6.9 内核及其关于ARM 平台的补丁
2)、给Linux2.6.9 打补丁
3)、准备交叉编译环境。交叉编译环境工具链一般包括binutils(含AS 汇编器,LD 链接器等),arm-gcc,glibc 等。交叉编译环境的搭建也是个复杂的过程,后面我将做进一步介绍。
4)、修改内核目录下的makefile 文件,主要是以下几行:
注释掉ARCH:=$(shell uname –m | sed –e s/i.86/i386/-e s/sun4u/sparc64/ -e s/arm.
\ */arm/ - e s/sa110/arm/)这一行。
ARCH := 改为 ARCH := arm
CROSS_COMPILE := 改为 CROSS_COMPILE = 您的交叉编译工具中arm-linux 所在目录/arm-linux-
例如:CROSS_COMPILE = /opt/crosstool/arm-linux/bin/arm-linux-
5)此后就可以进行编译。
6)以上可以使您的目标板上跑起linux 系统,但是您的pxa270 目标板上的一些硬件还没有驱
动起来,这需要您编写相应的硬件驱动程序。

修改Linux的内核源码:

    在Linux启动的第一阶段,内核与体系结构相关部分(arch目录下)首先执行,它完成硬件寄存器设置,内存映像等初始化工作。然后把控制权转给内核中与系统结构无关部分。而我们移植工作中要改动的代码主要集中在与体系结构相关部分。

    在arch 目录中我们可以看到有许多子目录,它们往往是用芯片命名的,表示是针对该芯片体系结构的代码。为arm 系列芯片编译内核,就应修改ARM目录下的相关文件。在ARM的子目录下我们可以找到一个boot/bootp目录,在bootp下有一个init.S 的文件,.S表示它是汇编语言文件。这里init.S是用ARM汇编写成的。这个init.S就是引导Linux内核在Arm平台上启动的初始化代码。它里头定义了一个全局符号_start,它定义了默认的起始地址。同时它也是整体内核二进制镜像的起始标志。Init.S主要完成以下功能:(具体的代码可以查阅UP-TECHPXA270A-LINUX实验指导书V5.0
1) 定义数据段、代码段、bbs(未初始化数据段)起始地址变量并对bbs段进行初始化。
2) 设置寄存器以初始化系统硬件。
3) 关闭中断。
4) 初始化LCD显示。
5) 将数据段数据复制到内存。
6) 跳转到内核起始函数start_kernel继续执行。
7) 对主寄存器的修改。

Linux 内核的编译菜单有好几个版本,您运行:
1)make config:进入命令行,可以一行一行的配置,这不好使用所以我们不具体介绍.
2)make menuconfig:进入我们熟悉的menuconfig 菜单,相信很多人对此都不陌生.
3)make xconfig:在2.4.X 以及以前版本中xconfig 菜单是基于TCL/TK 的图形库的。(本人推荐使用这种)

编译内核:
进入目录
tar xjvf up-linux-2.6.9-release.tar.bz2 -C /up-techpxa270/kernel/
ln –s linux-2.6.9 linux
cd linux
make clean
make dep
make bzImage(或者make zImage)
生成的二进制文件为zImage 存储在./arch/arm/boot/zImage。

把生成的zImage 文件拷贝到PC 机的tftp 服务目录/tftpboot/下,在进行以下操作前请确保已配置好您的PC 机TFTP 服务器。(安装软件tftp32)

烧录内核:
1. 启动pxa270 开发板,按ctl+c 组合键进入blob
2. 配置tftp 服务:
打开PC 机的tftp 服务,关闭防火墙;
在blob 下使用ifconfig server 192.168.0.43(192.168.0.43 为您的tftp 服务器ip)配置服务器IP,
注意只能一次配置有效,若重启开发板需要再次配置。
3. 可以输入help 查看blob 下的命令,输入help command 查看命令参数。
4. 按照以下命令烧写zImage 到Ram:
tdownload kernel
5. 按照以下命令烧写zImage 到flash:
flash kernel

至此,内核映象文件的烧写完成。

另外,若在vivi的Bootloader引导下,还可以通过串口的方式烧写内核和根文件系统(分别为zImage和root.cramfs两个文件)

vivi>load flash kernel x

vivi>load flash root x

9、编写Red hat内核驱动程序前需要注意的

    需要在usr/src/ 目录下建立指向linux内核的符号连接。若PC机的linux系统内核路径是/usr/src/linux-2.4.xxx,那么执行建立符号连接的命令是:
#ln -s /usr/src/linux-2.4.xxx /usr/src/linux

before make , please cd /up-techpxa270/kernel/linux-2.6.9/ directory and run " make zImage"
then go back to this directory ,run make

 

10、调试过程中利用printk跟踪

    printk是一个很有用的函数,利用它可以实现从内核向Linux控制台的格式化输出。其用法与标准C的printf类似。在调试驱动程序时,依靠printk输出信息跟踪程序,也是很有效的方法。

    与标准C的printf不同的是,printk支持分级输出。在include/linux/kernel.h中,为输出定义了如下8个级别:

#define    KERN_EMERG       "<0>"    /* system is unusable,紧急情况 */
#define    KERN_ALERT        "<1>"    /* action must be taken immediately,需要立即被注意到的 */
#define    KERN_CRIT           "<2>"    /* critical conditions,临界情况 */
#define    KERN_ERR            "<3>"    /* error conditions,错误 */
#define    KERN_WARNING "<4>"    /* warning conditions,警告 */
#define    KERN_NOTICE      "<5>"    /* normal but significant condition,普通的,需要注意 */
#define    KERN_INFO            "<6>"    /* informational,非正式的 */
#define    KERN_DEBUG      "<7>"    /* debug-level messages,一般的调试信息 */

未指定优先级的默认级别定义在/kernel/printk.c中:
#define DEFAULT_MESSAGE_LOGLEVEL 4 /* KERN_WARNING */

可以用这种方式,即使用:printk(KERN_INFO"your message\n"); 在指定的级别下输出。未使用输出级别定义的printk调用,则用DEFAULT_MESSAGE_LOGLEVEL作为默认的输出级别,它在kernel/printk.c中被定义为第4级的输出,即KERN_WARNING。在include/linux/kernel.h中,定义了宏console_loglevel.任何小于console_loglevel级别的输出,都会被显示在Linux控制台(console)上。console_loglevel的初始值为DEFAULT_CONSOLE_LOGLEVEL(在kernel/printk.c中被定义为7),而且该值可通过sys_syslog系统调用修改。如命令:klogd -C 4 就是利用sys_syslog系统调用把console_loglevel的值改成了4。

    为了调试方便,建议在驱动程序中定义DPRINTK宏实现在调试过程中的跟踪。

其命令如下:#define DPRINTK(x... )    printk("s3c2410 - led: " ##x)

用法如下:DPRINTK("write: led=0x%x,count=%d\n",ledstatus,count);

DPRINTK("open\n");

DPRINTK("release\n");

 

 

11、编写Makefile文件需要注意的

 

编写hello.c文件
利用vi编辑器,我们键入下面的代码,并保存为hello.c文件。
/**
* hello.c
*     ------Test for kernel module
*/
#i nclude <linux/init.h>         
#i nclude <linux/module.h>       
                               
MODULE_LICENSE("MYGPL");                      //(1)     
                                
static int hello_init(void)                   //(2)
{                               
    printk(KERN_ALERT "Hello, world\n");      //(3)
    return 0;                           
}                                       

static void hello_exit(void)    
{
    printk(KERN_ALERT "Goodbye, cruel world\n");
}

module_init(hello_init);                      //(4)
module_exit(hello_exit);                      //(5)
对上面这段代码中有5个地方需要加以说明:
    (1):本句代码可以不要,但不要的话,运行时会出现"hello: module license 'unspecified' taints kernel.",词典上对taints的解释是"感染,污点".
    (2):我们可以看出,对模块内的函数一般需要加上static关键字进行修饰,这样可防止模块外访问该函数,这是应该养成的一个好习惯。
    (3):printk函数相当于C标准库函数printf, KERN_ALERT是指的输出消息的优先级别,且KERN_ALERT优先级别最高。
    (4)(5):模块初始化和模块退出时有专门的函数,我们只需要为这两个函数指定初始化和退出时的具体调用的函数名即可。

编写下面的makefile:
# Makefile for hello module
# If KERNELRELEASE is defined, we've been invoked from the
# kernel build system and can use its language.

ifneq($(KERNELRELEASE),)
obj-m := hello.o

# Otherwise we were called directly from the command
# line; invoke the kernel build system.

else
KERNELDIR ?= /lib/modules/$(shell uname -r)/build
PWD := $(shell pwd)

default:
$(MAKE) -C $(KERNELDIR) SUBDIRS=$(PWD) modules

endif

clean:
$(shell rm -f *.bak)
$(shell rm -f hello.o)
$(shell rm -f hello.ko)
$(shell rm -f hello.mod.c)
$(shell rm -f hello.mod.o)
对这段脚本需要说明下面几点:
    (1)、将上面的脚本保存为Makefile,注意必须保存为M为大写的Makefile。这是因为编译的时候首先看环境变量KERNELRELEASE是否定义,如果没定义则调用Linux内核编译build脚本。该脚本会首先编译内核,其间会创建环境变量KERNELRELEASE,接着编译当前工作目录下的hello模块,此时会第二遍读取Makefile,再次判断环境变量KERNELRELEASE是否定义,已经定义的情况下开始编译hello模块。
如果将该脚本保存为小写m开头的makefile,编译时会出现这样的提示:
scripts/Makefile.build:13: /root/zhou/Makeifle: No such file or directory.
    (2)、因为build脚本会首先判断有无必要重新编译内核,所以如果需要先重新编译内核的话,编译过程会运行一段时间。
    (3)、在Makefile中行首缩进需要用Tab键,否则会出问题(这个问题曾经折磨我很长一段时间,后来改用Tab键缩进,问题一下子就得到了解决)。
    (4)、在default部分,原书中原来的语句是:
             $(MAKE) -C $(KERNELDIR) M=$(PWD) modules
         利用man make命令查看make的使用手册发现,对-m的说明:
             -m   These options are ignored for compatibility with other version of make.     
       即-m参数被忽略掉了,所以编译时会出现:
           Nothing to be done for '/root/zhou'.
       为了避免上述问题,可将M=$(PWD)改成SUBDIRS=$(PWD),即重新定义环境变量SUBDIRS,供编译时使用。
编译完成后,在当前工作目录下会多了几个文件:hello.o, hello.ko, hello.mod.c, hello.mod.o。其中hello.ko就是我们需要的。

.ko是2.6内核使用的动态连接文件的后缀名,也就是模块文件。

另:在编译内核并安装时,系统会改变或新增哪些文件?
会在/boot/下增加新的内核文件,新的initrd和map文件,修改grub的配置
文件/boot/grub/grub.conf

运行
    必须有root用户的权限,才能进行内核模块操作。
    (1)、挂载模块
         执行insmod hello.ko命令,即可挂载并运行hello模块。insmod意思是insert module。成功挂载上hello模块后,会输出:
             Hello, world
    (2)、卸载模块
         执行rmmod hello命令(也可以执行rmmod hello.ko命令),即可卸载模块。rmmod意思是remove module。成功卸载模块后,会输出:
             Goodbye, cruel world

    在内核上成功挂载和卸载了hello模块。知道在执行rmmod hello命令后,hello模块已经卸载了。如果此时我们再执行一遍rmmod hello命令,则会出现类似下面的提示:
      ERROR: Module hello does not exist in /proc/modules
    从这句提示中,可以知道,挂载模块时,hello被放在了/proc/modules文件中,卸载模块时,从/proc/modules文件中删除了对应的记录行。为了验证这一点,利用insmod hello.ko命令重新挂载hello模块,然后,我们利用more /proc/modules命令来查看该文件的内容,可以看到在第一行就是我们刚挂上的hello模块:
     hello 1152 0 - Live 0xca8c9000
    关闭该文件。我们再利用rmmod hello命令卸载掉hello模块,然后我们再次利用more /proc/modules命令再次查看该文件,可以发现已经没有了这一行内容。其实每次执行lsmod命令,就是读取和显示/proc/modules文件中的内容

12、设备文件和设备文件系统

    在Linux系统中,字符设备和块设备是通过文件节点访问的。习惯上,这些设备文件存在于系统的/dev目录下。因为系统不是靠路径去关联设备文件和对应的驱动程序的,所以也可通过mknod命令把设备文件创建在其他位置。通过如下命令:ls -l /dev 可以列出系统的设备文件。

    其中:对应的字母表示的是:字符设备文件(c),准予读取(r),准予写入(w),块设备文件(b),所有者(root),组(floppy);第一个数字对应的是主设备号,第二个数字对应的是次设备号。

    Linux系统是依靠主次设备号来联系驱动程序和设备文件节点的(而不是设备文件的路径)。系统依靠主设备号标识不同的驱动程序,因此,在同一个系统中,一类设备的主设备号是惟一的。每一个Linux驱动程序都对应了一个主设备号。可以使用以下命令:cat /proc/devices 列出系统内核支持的设备驱动程序和对应的主设备号。

    同一个驱动程序可以管理多个设备,它们依靠次设备号来区别。次设备号只在驱动程序内部使用,系统内核直接把次设备号传递给驱动程序,由驱动程序去管理。

    对于字符设备和块设备,Linux内核对这些操作进行了统一 的抽象,把它们定义在结构体file_operations中。

Linux下的设备驱动程序通常放在/dev目录下。如果我们进入到/dev目录,然后执行ls -l|more命令,我们会发现下面几点:
(1)、以b开头的代表block device
(2)、以c开头的代表character device
(3)、以l开头的代表link文件
(4)、以d开头的代表directory
(5)、在last modified的前面有两个数字,用逗号隔开,分别是该设备的major number和minor number,即主设备号和次设备号。
例如:
brw-rw---- 1 root disk 34, 70 Dec 29 2005 hdh6

13、Linux驱动程序的编写细节点
    嵌入式Linux操作系统下通过fd=open(DEVICE_NAME,O_RDER)把驱动程序和应用程序连接起来,便可以通过应用层程序操控硬件设备。

    通常,程序员会通过一个判断语句来观察设备是否被正确打开,如果被正确打开会进行所要的操作,否则便会报错。实现方法如下面的代码所示:

if(fd==-1) {printf("open device %s error \n",DEVICE_NAME);}

else{//文件打开成功,进行具体的文件操作}

driver_register是2.6内核中提供的注册驱动程序的方法,其原型在drivers/base/driver.c中定义:

int driver_register(struct device_driver * drv)

它使得系统加载、检测、卸载驱动程序有一个统一的接口。

程序在最外一层的local_irq_restore调用才把中断打开,而__cli__sti宏则不能嵌套调用。

14、嵌入式根文件系统

目录 习惯用法
bin:用户命令所在目录
dev:硬件设备文件及其它特殊文件
etc:系统配置文件,包括启动文件等
home:多用户主目录
lib:链接库文件目录
mnt:装配点,用于装配临时文件系统或其他的文件系统
opt:附加的软件套件目录
proc:虚拟文件系统,用来显示内核及进程信息
root:root 用户主目录
sbin:系统管理员命令目录
tmp:临时文件目录
usr:用户命令目录
var:监控程序和工具程序所存放的可变数据

对于用途单一的嵌入式系统,上边的一些用于多用户的目录可以省略,例如/home、/opt、/root目录等。而/bin、/dev、/etc、/lib、/proc、/sbin 和/usr 目录,是几乎每个系统必备的目录,也是不可或缺的目录。

15、查看/dev目录下的设备的主次设备号的命令

查看/dev目录下的设备的主次设备号使用如下的命令:ls /dev -l
在系统/dev下查看设备的命令:
#pwd
/dev
#ll