- Linux 内核 2
- 进程管理 2
- 内存管理: 2
- 文件系统: 2
- 网络接口: 2
- 进程间通信: 3
- Ctrl+Alt+T 3
- Apt-get 3
- 查看当前目录下的内容的命令式: 3
- cd命令: 4
- 注意: 4
- 查看当前路径: 4
- 目录操作命令: 4
- 文件操作命令: 5
- 网络操作命令 7
- 安装和卸载文件系统 7
- 使用内核模块和驱动 8
- Linux环境变量 9
- Linux 文件名称 10
- Vi/Vim 编辑器 10
- 光标移动 10
- 文本编辑 11
- 配置vi 12
- Windows 环境串口登录 12
- Linux 环境串口登录 12
- 查看系统信息 12
- 加载驱动模块 13
- 网络设置 13
- 系统固件烧写 15
- Linux应用编程 15
gcc最基本的用法: 15
库文件链接: 16
创建静态库和共享库: 16
编写Makefile 17
自定义变量 17
安装 Eclipse IDE for C/C++ 18
交叉编译: 18 - 文件描述符 18
fsync()函数 19
lseek( )函数 19
ioctl()函数 19 - 进程 19
程序到进程转换的过程: 19
进程状态 19
环境变量 20 - 进程基本操作 20
创建进程 20
终止进程: 20
守护进程: 21
信号 21
信号函数: 21
Kill函数 21 - 进程间通信 22
管道: 22
1.Linux 内核
由 5 个主要子系统组成,分别是:内存管理、进程管理、进程间通信、虚拟文件系统和网络。
2.进程管理
负责控制进程对CPU的访问,任务调度是进程管理最核心的工作。
进程的状态:运行态、就绪态、可中断睡眠状态(当系统产生一个中断或释放了进程正在等待的资源,或者进程收到一个信号,就可被唤醒进入就绪态或运行态)、暂停状态(收到SIGCONT信号可进入运行态)、僵死态(进程已停止运行但其父进程还没有询问其状态)。
3.内存管理:
内存管理的主要作用是控制和管理多个进程,使之能够安全的共享主内存区域。
4.文件系统:
Linux 内核支持众多的逻辑文件系统,如 Ext2、Ext3、Ext4、btrfs、NFS、VFAT 等。,Linux 下硬盘上使用的文件系统通常是 Ext3/4 格式,而 U 盘通常 是 FAT32 格式。
5.网络接口:
网络接口提供了对各种网络标准的存取和各种网络硬件的支持,接口可分为网络协议和网络驱动程序。网络协议部分负责实现每一种可能的网络传输 协议。网络设备驱动程序负责与硬件设备通讯,每一种可能的硬件设备都有相应的设备驱动 程序。
6.进程间通信:
支持进程间各种通信机制,如管道、命名管道、信号、消息队列、内存共享、信号量和 套接字等。管道用于具有亲缘关系的进程间通信,数据先入先出,半双工通信,命名管道突破亲缘关系限制,信号是软件中断,用于多个进程间传递异步信号,消息队列可传递多个信号,内存共享用于不同进程间进行大量数据传递,信号量用于进程同步,套接字用于多个进程间通信,可基于文件或网络。
7.Ctrl+Alt+T
按"Ctrl+Alt+T"组合键也可以打开终端窗口。按"Ctrl+Shift+T"键可以在终端窗口再打开一个终端的标签。
8.Apt-get
命令安装软件,但电脑需要连接互联网。例子:sudoapt−getinstallvim(安装Vim软件)9.查看当前目录下的内容的命令式:ls,例子:sudo apt-get install vim(安装Vim软件) 9.查看当前目录下的内容的命令式: ls,例子:sudoapt−getinstallvim(安装Vim软件)9.查看当前目录下的内容的命令式:ls,例子:ls 选项 选项包括:空,按字母顺序列出当前目录下的所有非隐藏文件(包括目录) -a 按字母顺序列出当前目录下的所有文件,包括隐藏文件 -l 列出当前目录下的所有文件,包括文件长度、拥有者、权限和时间戳等信息 -t 按文件最后修改时间列出文件 -F 斜线(/)表示目录 星号()表示可执行文件 @符号表示链接文件 --color 以不同颜色显示目录,普通文件、可执行文件、压缩文件以及链接文件等
注意:
linux区分大小写,在输入的时候特别注意 各参数可以任意组合,如ls -ls;表示以详细列表查看目录下的全部内容支持通配符 、?等
10.cd命令:
/根目录 句点1(.)当前目录 句点2(...)上一层目录 ~ 当前用户的主目录,一般为/home/username,如当前登录用户为user,则表示/home/user目录,cd命令不加任何参数,将切换到用户主目录() 短横线(-)上一次工作目录,cd-可切换之前的工作目录。
11.注意:
计算机名和域名之间都是用斜线(/)分开 linux下切换目录,可用相对路径和绝对路径
12.查看当前路径:
Pwd
13.目录操作命令:
创建目录:mkdir用于创建一个或多个目录,加上选项也可以创建多级目录。
用法:mkdir选项参数目录−m创建目录的同时指定访问权限−p如果所创建目录的父目录不存在,则一同创建父目录例子:如果在当前目录创建newdir这个目录,命令如下:−mkdir 选项参数 目录 -m 创建目录的同时指定访问权限 -p 如果所创建目录的父目录不存在,则一同创建父目录 例子:如果在当前目录创建new_dir这个目录,命令如下: -mkdir选项参数目录−m创建目录的同时指定访问权限−p如果所创建目录的父目录不存在,则一同创建父目录例子:如果在当前目录创建newdir这个目录,命令如下:−mkdir new_dir 一次性创建多个目录时,命令:-mkdirdir1dir2dir3如果在dir1中创建apps子目录,同时在apps目录下再创建hello目录,命令:mkdir dir1 dir2 dir3 如果在dir1中创建apps子目录,同时在apps目录下再创建hello目录,命令:mkdirdir1dir2dir3如果在dir1中创建apps子目录,同时在apps目录下再创建hello目录,命令:mkdir -p dir1/apps、hello
删除目录:rmdir和rm可用于删除目录,rmdir命令只能删除空目录,也可删除多级空目录,用法:rmdirdir1dir2rm命令既可以删除文件,也可以删除目录而不管目录是否非空,用法:rmdir dir1 dir2 rm命令既可以删除文件,也可以删除目录而不管目录是否非空, 用法:rmdirdir1dir2rm命令既可以删除文件,也可以删除目录而不管目录是否非空,用法:rm选项 文件/目录 选项:-f 强制删除文件或目录,无需用户确认 -i 删除文件或目录钱,需用户确认
-r 递归删除,删除指定目录以及子目录下的文件
-v显示删除过程
注意:为了确保不误删文件,可使用alias别名,将rm命令设置为"rm -i"这样每次删除会有确认过程,用法:alias rm = "rm -i"
14.文件操作命令:
创建空文件:touch命令可以完成创建一个空文件的功能。例子:创建一个文件名为a的空文件:-$ touch a
创建一个有内容的文件:重定向在linux下用">"和">>"表示,">"表示输出到一个新文件中,">>"表示输出到现有文件的末尾。 echo命令将内容回显到标准输出,使用echo命令加上重定向可以创建一个带内容的非空文件,用法:$echo 内容或"内容"#输出到标准输出
KaTeX parse error: Expected 'EOF', got '#' at position 19: ...o 内容 或者"内容">文件 #̲重定向到文件,如果文件不存在则... file 文件(只能查看具有可读属性的文件) file命令支持通配符,如一次性查看当前目录下的全部文件类型,例子: file∗查看文件内容:more和less命令可用来浏览文本文件,可分页查看文件内容,空格翻页,浏览文件完毕后按q键退出,用法:file * 查看文件内容:more和less命令可用来浏览文本文件,可分页查看文件内容,空格翻页,浏览文件完毕后按q键退出,用法:file∗查看文件内容:more和less命令可用来浏览文本文件,可分页查看文件内容,空格翻页,浏览文件完毕后按q键退出,用法:more/less 文件less命令相对来说比较灵活,支持键盘的page Up和Page Down键,可任意向前先后翻页浏览,并且支持文本搜索,less命令打开文件后,输入/abc可在文本中搜索字符串abc,匹配的字符串高亮显示。 head和tail命令可分别查看文件头部和尾部,一般用于查看ascii文件,用法:head/tail选项参数文件−n数字显示数字所指定的行数−c数字显示数字所指定的字节数例子:查看文件头20行:head/tail 选项参数 文件 -n数字 显示数字所指定的行数 -c数字 显示数字所指定的字节数 例子:查看文件头20行:head/tail选项参数文件−n数字显示数字所指定的行数−c数字显示数字所指定的字节数例子:查看文件头20行:head -n 20 install.cf
查看300字节:head−c300install.cf查看文件开头的512字节:head -c 300 install.cf 查看文件开头的512字节:head−c300install.cf查看文件开头的512字节:head -c 1b install.cf(数字表示方法:b:512 kb:1000 K:1024 MB:10001000,M:1024 1024)
用cat命令查看:cat命令可将一个或多个文件输出到标准输出上,可用于文件查看,用法:$cat 文件
文件合并:如果将标准输出重定位到某个文件,则cat命令可将多个文件合并一个文件,用法:cat选项文件1文件2...\>文件3选项:−n从1开始对输出行进行编号−b类似于−n,从1开始编号但忽略空白行−s遇到连续两行或以上的空白行,就替换为一行空白行如果使用重定向符(>)可以将屏幕输出保存到另一个文件中,或者追加符(>>)可以将屏幕输出添加到某个文件末尾。例子:cat选项 文件1 文件2 ...\>文件3 选项:-n 从1开始对输出行进行编号 -b 类似于-n,从1开始编号但忽略空白行 -s 遇到连续两行或以上的空白行,就替换为一行空白行 如果使用重定向符(>)可以将屏幕输出保存到另一个文件中,或者追加符(>>)可以将屏幕输出添加到某个文件末尾。例子:cat选项文件1文件2...\>文件3选项:−n从1开始对输出行进行编号−b类似于−n,从1开始编号但忽略空白行−s遇到连续两行或以上的空白行,就替换为一行空白行如果使用重定向符(>)可以将屏幕输出保存到另一个文件中,或者追加符(>>)可以将屏幕输出添加到某个文件末尾。例子: cat -n hello.c Makefile >test
重定向符(>)可以将标准输出重定向到其他输出或者文件,文件不存在则会创建新文件;追加符(>>)则将标准输出追加到文件末尾,如果文件不存在则创建新文件;
文件压缩/解压:tar是UNIX系统的一个文件打包工具,并不具备压缩功能,但可以调用其他压缩/解压工具实现文件的压缩和解压。用法:tar选项文件选项:−c创建存档文件,与−x相斥−t列出档案文件的文件列表−x解包存档文件,与−c相斥−A合并存档文件−d比较存档文件与源文件−r追加文件到存档文件末尾−u更新存档文件−f指定存档文件,与其他选项同时使用时,必须在最后,例子:tar−xjvfa.tar.bz2−v显示详细处理信息−C转到指定目录,常用于解开存档文件−j调用bzip2程序−z调用gzip程序−Z调用compress程序−−exclued=PATH排除指定文件/目录,常用于打包文件例子:解压a.tar.bz2文件并显示详细信息:tar 选项 文件 选项: -c 创建存档文件,与-x相斥 -t 列出档案文件的文件列表 -x 解包存档文件,与-c相斥 -A 合并存档文件 -d 比较存档文件与源文件 -r 追加文件到存档文件末尾 -u 更新存档文件 -f 指定存档文件,与其他选项同时使用时,必须在最后,例子:tar -xjvf a.tar.bz2 -v 显示详细处理信息 -C 转到指定目录,常用于解开存档文件 -j 调用bzip2程序 -z 调用gzip程序 -Z 调用compress程序 --exclued=PATH 排除指定文件/目录,常用于打包文件 例子:解压a.tar.bz2文件并显示详细信息:tar选项文件选项:−c创建存档文件,与−x相斥−t列出档案文件的文件列表−x解包存档文件,与−c相斥−A合并存档文件−d比较存档文件与源文件−r追加文件到存档文件末尾−u更新存档文件−f指定存档文件,与其他选项同时使用时,必须在最后,例子:tar−xjvfa.tar.bz2−v显示详细处理信息−C转到指定目录,常用于解开存档文件−j调用bzip2程序−z调用gzip程序−Z调用compress程序−−exclued=PATH排除指定文件/目录,常用于打包文件例子:解压a.tar.bz2文件并显示详细信息:tar -xjvf a.tar.bz2
解压 b.tar.gz文件,并指定解压到/home/chenxibing/目录:
tar−xzvfb.tar.gz−C/home/chenxibing删除文件用rm命令文件改名和移动:mv命令可同时实现移动和改名,用法:tar -xzvf b.tar.gz -C/home/chenxibing 删除文件用rm命令 文件改名和移动:mv命令可同时实现移动和改名,用法:tar−xzvfb.tar.gz−C/home/chenxibing删除文件用rm命令文件改名和移动:mv命令可同时实现移动和改名,用法:mv 源文件/目录 目的文件/目录,若目的路径与源路径不同则进行移动操作,相同则改名操作。
例子:将目录other改名为newdir,再将newdir移动到上一级目录并改名为hello2:$mv other /newdir
$ls
mvnewdir/../hello2文件复制:cp命令可完成文件复制操作。用法:mv newdir/../hello2 文件复制:cp命令可完成文件复制操作。用法:mvnewdir/../hello2文件复制:cp命令可完成文件复制操作。用法:cp选项 源文件/目录 目的文件/目录
选项:
-a 保留链接、文件属性并递归复制,等同于-dpR组合,常用语复制目录
-d 复制时保留链接
-f 若目标文件已经存在,直接删除不提示
-i 若目标文件已经存在,需要用户确认操作,与-f相反
-p 除复制文件内容外,把访问权限和修改时间也复制到新文件中
-r 递归复制指定目录下的文件和目录
-v 显示文件复制过程
例子:cp−av/etc/newt/.创建链接:ln命令可创建链接,用法:cp -av /etc/newt/ . 创建链接:ln命令可创建链接,用法:cp−av/etc/newt/.创建链接:ln命令可创建链接,用法:ln 选项 源文件/目录 目标文件
Linux下的链接分为软链接和硬链接两种,默认创建硬链接,选项加上-s创建软链接。
硬链接通过索引节点进行链接,相当于源文件的镜像,占用源文件一样大小的空间,修改其中任何一个,另外一个都会进行同样的改动。
软链接是产生一个新文件,这个文件指向另一个文件的位置。软链接可以跨文件系统,且可用 于任何文件,包括目录文件。
改变文件和目录权限
chmod 用于改变或者设置文件/目录的权限。可直接用八进制表示。
用法: $chmod 参数 文件/目录
例:一个普通文件/bin/bash 的 ls -l 输出信息,输出信息第一列表示文件访问权限:
-rwxr-xr-x 1 root root 917888 2010-08-11 04:47 /bin/bash
R/w/x分别表示读/写/执行,数字表示分别为4/2/1,如rwx可用数字7表示。
更常用的是用字符方式设定文件/目录的权限,分别用 u/g/o 表示文件的拥有者/组内用户/其它用户,用 rwx 分别表示读/写/执行权限,用+/-表示增加或去除某种权限。例如,将 hello 文件的其它用户权限可执行属性去掉: vmuser@Linux-host: hello$ chmod o-x hello 如果同时设置 u/g/o,可用 a 表示,如果在运行程序的时候遇到 permission dennied 这样的错误提示,可在终端输入 chmod +x file,为将要运行的程序增加可执行权限。
15.网络操作命令
ifconfig 命令是 Linux 系统配置网卡的命令工具,可用于查看和更改网络接口的地址和参数,包括IP地址、广播地址、子网掩码和物理地址,也可激活和关闭网卡。
用法: $ifconfig 网络接口 选项 地址/参数
例子:ifconfig -a 查看系统拥有的全部网络接口
ifconfig eth0 (Broadcast)192.168.1.136 指定操作某个网口(设置网口的广播地址),需要注意:需要root权限;
选项还可包括netmask,设置网口的子网掩码;hw ether,设置网卡物理地址;up,激活指定网卡;down,关闭指定的网卡。
ping 命令,通常情况下只能 ping 同网段的主机,不能跨网段 ping 操作。
用法: KaTeX parse error: Expected 'EOF', got '#' at position 147: ...要 root 权限。 用法: #̲ mount -参数 [设...lsmod
卸载驱动模块:rmmod命令
用法: # rmmod 选项 模块
自动处理可加载模块:modprobe 命令
集加载/卸载功能于一身,并且可以自动解决模块的依赖关系,将某模块所依赖的其它模块全部加载。
用法: # modprobe 选项 模块 符号=值
选项 说明
-C<文件> 不使用默认配置文件,使用指定文件作为配置文件
-i 忽略配置文件中的加载和卸载命令
-r 卸载指定模块,包括依赖模块
-f 强制安装
-l 显示所有匹配模块
-a 安装所有匹配的模块
--show-depends 显示模块的依赖关系
-v/-V 显示执行过程/显示版本信息
-q 不显示任何信息
创建设备节点:mknod命令
用法: #mknod 设备名 设备类型 主设备号 次设备号
例:如需要创建一个字符设备 led,主设备号为 231,次设备号为 0,则命令如下: #mknod /dev/led c 231 0
重启系统用 reboot 命令,关机用 poweroff 命令,两者都需要 root 权限。
临时获取root权限:sudo命令
用法: $ sudo 命令
使用 sudo 命令需要管理员将用户添加到 sudoer 组中,一般在/etc/sudoers 文件中修改。
文件同步:sync命令
用法:只需在关闭文本编辑器后在 Shell 输入sync 即可。
文件搜索:find 命令
用法:$find 路径 --选项 其它
例如:最常用的是根据文件名来查找,加上-name 参数就可以了,还可以支持通配符,进行模糊搜索。例如只大概知道内核源码"arch/arm"目录下有文件名以 mux 开头的文件,但不知道确切文件名,
可用命令搜索: $find arch/arm/ -name mux*.c
grep 可用于字符串搜索
例子: ,想知道 pcf8563这个关键 字在"arch/arm"目录下哪些地方用到了,可以输入下列命令: grep―pcf8563‖−Rarch/arm关键字最好加上双引号,特别是包含空格的关键字,对于单个关键字倒是可以不用引号。"−R"表示进行递归查找,而不是仅仅在指定的目录下查找。执行Shell脚本有多种方式:(1)点+斜线+文件名,这种方式要求文件必须有可执行权限;(2)点+空格+文件名,这种方式不要求文件一定具有可执行权限。(3)sh+空格+文件名,这种方式不要求文件一定具有可执行权限。(4)source+空格+文件名,这种方式不要求文件一定具有可执行权限。18.Linux环境变量在Shel下通过美元符号(grep ―pcf8563‖ -R arch/arm 关键字最好加上双引号,特别是包含空格的关键字,对于单个关键字倒是可以不用引号。"-R"表示进行递归查找,而不是仅仅在指定的目录下查找。 执行 Shell 脚本有多种方式: (1)点+斜线+文件名,这种方式要求文件必须有可执行权限; (2)点+空格+文件名,这种方式不要求文件一定具有可执行权限。 (3)sh+空格+文件名,这种方式不要求文件一定具有可执行权限。 (4)source+空格+文件名,这种方式不要求文件一定具有可执行权限。 18.Linux环境变量 在 Shel下通过美元符号(grep―pcf8563‖−Rarch/arm关键字最好加上双引号,特别是包含空格的关键字,对于单个关键字倒是可以不用引号。"−R"表示进行递归查找,而不是仅仅在指定的目录下查找。执行Shell脚本有多种方式:(1)点+斜线+文件名,这种方式要求文件必须有可执行权限;(2)点+空格+文件名,这种方式不要求文件一定具有可执行权限。(3)sh+空格+文件名,这种方式不要求文件一定具有可执行权限。(4)source+空格+文件名,这种方式不要求文件一定具有可执行权限。18.Linux环境变量在Shel下通过美元符号()来引用环境变量,使用 echo 命令可以查看某个具体环境变量的值。例如,查看 TERM 的值: echo TERM (TERM☞指定系统终端)
使用 env 或者 printenv 命令可以查看系统全部的环境变量设置
(1)通过 Shell 命令设置环境变量,常用于临时设置环境变量。可直接用等号(=)为变量赋值,或者用 export 命令为变量赋值
用法: 变量=变量:新增加变量值
$ export 变量=变量:新增加变量值例如,需要为系统PATH变量增加/opt/usr/bin目录vmuser@Linux−host: 变量:新增加变量值 例如,需要为系统 PATH 变量增加/opt/usr/bin 目录 vmuser@Linux-host:~变量:新增加变量值例如,需要为系统PATH变量增加/opt/usr/bin目录vmuser@Linux−host: echo PATH/usr/lib/qt−3.3/bin:/usr/kerberos/bin:/usr/local/ruby/bin:/opt/mysql5/bin:/usr/lib/ccache:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbinchenxibing@fedora PATH=PATH /usr/lib/qt-3.3/bin:/usr/kerberos/bin:/usr/local/ruby/bin:/opt/mysql5/bin:/usr/lib/ccache:/usr/local/bin:/bin:/usr/bin:/ usr/local/sbin:/usr/sbin:/sbin chenxibing@fedora \~PATH=PATH/usr/lib/qt−3.3/bin:/usr/kerberos/bin:/usr/local/ruby/bin:/opt/mysql5/bin:/usr/lib/ccache:/usr/local/bin:/bin:/usr/bin:/usr/local/sbin:/usr/sbin:/sbinchenxibing@fedora PATH=PATH:/opt/usr/bin
修改系统配置文件。
运行文件可用"source 文件"的方式操作。通常修改/etc/profile 文件或者~/.bashrc(有 的发行版上为~/.bash_profile)文件:
修改/etc/profile 文件会影响使用本机的全部用户;
修改~/.bashrc 则仅仅影响当前用户;
推荐修改~/.bashrc 文件。
19.Linux 文件名称
Linux 的文件名保存目录文件中,文件名以点号(.)开始的文件是隐藏文件。命名应当遵循以下规则:
区分大小写;
不能以"+"和"-"开头;
不能包含< > ;| ` ―' KaTeX parse error: Unexpected character: '' at position 36: ...ll 中有特殊含义的字符; ̲不能包含空格; 长度不能超... vimdiff file1 file2 file3
vimdiff可同时进行2个以上文件的对比
linux开发环境构建,见第六章
24.Windows 环境串口登录
Windows 下的串口终端软件比较多,如:Tera Term、putty、Xshell 等。
25.Linux 环境串口登录
在 Linux 常用的串口终端软件有 minicom 和 kermit
26.查看系统信息
查看系统内核版本:
通过查看/proc/version 文件,可以获得系统内核版本信息:
例子:root@EasyARM-iMX28x ~# cat /proc/version
查看内存使用情况
使用 free 命令可以查看内存的使用情况:
例子:root@EasyARM-iMX28x ~# free
查看磁盘使用情况
使用 df -m 指令可以查看系统磁盘的使用情况:
例子:root@EasyARM-iMX28x ~# df --m
查看 CPU 等信息
通过查看/proc/cpuinfo 文件,可以获得 CPU 等信息:
例子: root@EasyARM-iMX28x ~# cat /proc/cpuinfo
27.加载驱动模块
在 shell 终端加载和使用驱动模块
加载驱动模块时需要使用 insmod 命令。例如,加载beep.ko驱动模块的命令为:
root@EasyARM-iMX28x ~# cd /root/
root@EasyARM-iMX28x ~# insmod beep.ko
28.网络设置
ifconfig 命令
使用 ifconfig 命令可以显示或配置网络设备,其常用的组合命令格式如下: #ifconfig 网络端口 IP地址 hw ether MAC 地址 netmask 掩码地址 broadcast 广播地址 up\|down
设置 IP 地址
通过 ifconfig 命令可以查看或者设置网卡的 IP 地址。操作示例: root@EasyARM-iMX28x ~# ifconfig eth0 192.168.28.236
eth0: Freescale FEC PHY driver Generic PHY (mii_bus:phy_addr=0:05, irq=-1) #表示网卡启动
PHY: 0:05 - Link is Up - 100/Full #表示网线连接
该命令把 eth0 网卡的 IP 地址设置为 192.168.28.236,同时启动网卡。 输入 ifconfig eth0 命令可以查看 eth0 网卡当前的状态。
动态获得 IP 地址
如果希望使用动态 IP 地址,可用 udhcpc 命令。
修改 MAC 地址
例子:root@EasyARM-iMX28x ~# ifconfig eth0 hw ether 00:11:22:33:44:55
设置子网掩码
例子:root@EasyARM-iMX28x ~# ifconfig eth0 netmask 255.255.255.0
设置广播地址
例子:root@EasyARM-iMX28x ~# ifconfig eth0 broadcast 192.168.28.225
关闭/启动网卡
例子:使用下面命令关闭或开启 eth0 网卡: root@EasyARM-iMX28x ~# ifconfig eth0 down/up
设置默认网关
添加、删除或查看网关参数使用"route"命令。
例子:将默认网关设置/删除 192.168.28.1
root@EasyARM-iMX28x ~# route add/del default gw 192.168.28.1
查看当前网关设置
root@EasyARM-iMX28x ~# route --n
设置 DNS
若 EasyARM-i.MX283A 需要使用域名访问互联网,则需要先设定 DNS,否则访问可能不正常。
通过 SSH 登录系统
SSH 是 Secure Shell 的缩写,是建立在应用层和传输层基础上的安全协议,能够有效防 止远程管理过程中的信息泄露问题。 SSH 实际上是一个 Shell,可以通过网络登录远程系统,当然,前提是远程系统已经开启了 SSH 服务。
在 windows 下可以使用 putty/xShell 登录开发板。在 Linux 主机可以通过 ssh 命令登录。
例子:
vmuser@Linux-host ~$ ssh root@192.168.28.236 #假设 192.168.28.236 为目标机的IP地址
TF 卡和U盘使用
输入 df --m 命令可以查看,TF 卡各分区的挂载的情况和分区的使用。
TF 卡和U盘在使用前需要格式化;TF 卡在使用完成后,在弹出 TF 卡前,需要卸载 TF 所有分区的挂载。
使用普通文件作为虚拟 U 盘的储存空间
得到的虚拟 U 盘 需要格式化
生成特定大小的普通文件可以用dd命令,其命令格式为:
dd if=file of=loop_file bs=size count=num
dd命令的执行需要几个参数:
if 参数表示生成文件的数据是从哪个文件输入;
of 参数表示要生成的 loop 文件路径;
bs 参数表示生成文件每块大小;
count 参数表示生成文件有多少个块。
触摸屏校准
触摸屏校准命令为ts_calibrate
GPIO操作
在/root/目录下有 gpio_driver.ko 驱动模块文件。输入下面命令加载驱动模块: root@EasyARM-iMX28x ~# insmod /root/gpio_driver.ko
驱动加载完成后,会为每个 gpio 端口都生成一个设备文件节点,例如可以控制P3.27接口的设备文件节点是/dev/gpio-P3.27,用户可以在shell直接操作指定的GPIO。控制P3.27输出高电平(低电平)的方法是:
root@EasyARM-iMX28x ~# echo 1 (0)>/dev/gpio-P3.27
该命令会返回0或1:0表示输入的是低电平,1表示输入的是高电平。
挂载NFS(网络文件系统)
若主机的NFS目录为/nfsroot,IP地址为192.168.28.235,在终端输入下面命令挂载主机的NFS目录:
mount --t nfs 192.168.28.235:/nfsroot/mnt --o nolock
设备NFS根文件目录
将需要的压缩根文件复制到主机的NFS目录/nfsroot/中后进行解压,解压完成后根文件系统已在主机目录下。
在目标机设置内核NFS启动参数
启动设备立即在串口终端不断键入空格键,直到进入u-boot的终端。
修改 U-boot 的 bootargs 环境变量,该变量保存了内核的启动参数。
设置内核 NFS 启动 的参数一般格式为:
setenv bootargs root=/dev/nfs rw console=(consolecfg)nfsroot=(consolecfg) nfsroot=(consolecfg)nfsroot=(serverip):(rootpath)ip=(rootpath) ip=(rootpath)ip=(ipaddr):(serverip):(serverip):(serverip):(gatewayip):(netmask):(netmask):(netmask):(hostname)::off
其中各个参数的意义如下:
consolecfg------调试串口配置;
serverip ------提供 NFS 服务的主机 IP;
ipaddr ------本机 IP(目标系统 IP);
gateway ------网关;
netmask ------子网掩码;
hostname ------目标板的主机名;
rootpath ------主机 NFS 根文件系统路径。
若主机的 IP 为 192.168.28.235,NFS 根文件系统路径为/nfsroot/rootfs;EasyARMi.MX283A 的 IP 为 192.168.28.236,
则设置内核启动参数的命令为:
MX28 U-Boot >setenv bootargs 'root=/dev/nfs rw console=ttyAM0,115200n8 nfsroot=192.168.28.235:/nfsroot/rootfs ip=192.168.28.236:192.168.28.235:192.168.28.254:255.255.255.0:epc.zlgmcu.com:eth0:off mem=64M'
设置完成后,输入 saveenv 命令保存设置,在终端输入reset命令重启系统或重新上电重启。
内存文件系统
在内存文件系统存储文件会占用系统的内存空间。
29.系统固件烧写
格式化 NAND Flash
如果板子原本就是 Linux 操作系统,仅仅进行固件恢复或者升级,则无需格式化 NAND, 除非在使用过程中安装了 WinCE,要重新安装 Linux,才需进行 NAND 格式化操作。 可通过 USB Boot 或者 SD Boot 两种方式完成 NAND 格式化。
30.Linux应用编程
gcc最基本的用法:
gcc选项文件名例子:用Vi编辑器创建一个hello.c文件后输入以下命令:gcchello.c--ohello然后在终端输入./hello可看到输出结果。也可不指定−ohello,直接输入:gcchello.c编译完成会得到a.out文件。gcc编译过程:hellc.c经过预处理到hello.i,经过编译到hello.s,经过汇编到hello.o,经过链接到hello.a/hello.out.1)预处理:在gcc命令加上−E参数,可得到预处理文件,输入以下命令:gcc--Ehello.c−ohello.i2)编译:gcc--Shello.i3)汇编:gcc--chello.s4)链接:gcchello.o链接可分为动态链接和静态链接:动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。(Linux下的动态链接库实际是共享目标文件(sharedobject),一般是.so文件,作用类似于windows下的.dll文件)静态链接库使用静态库进行链接,生成的程序包含程序运行所需要的全部库,它是汇编产生的.o文件的集合,一般以.a文件形式出现。gcc默认是动态链接,加上−static参数则采用静态链接。例:vmuser@Linux−host:hellogcc选项 文件名 例子:用Vi编辑器创建一个hello.c文件后输入以下命令: gcc hello.c --o hello 然后在终端输入./hello可看到输出结果。 也可不指定-o hello,直接输入:gcc hello.c 编译完成会得到a.out文件。 gcc编译过程: hellc.c经过预处理到hello.i,经过编译到hello.s,经过汇编到hello.o,经过链接到hello.a/hello.out. 1)预处理:在gcc命令加上-E参数,可得到预处理文件,输入以下命令: gcc --E hello.c -o hello.i 2)编译:gcc --S hello.i 3)汇编:gcc --c hello.s 4)链接:gcc hello.o 链接可分为动态链接和静态链接:动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。(Linux下的动态链接库实际是共享目标文件(shared object),一般是.so文件,作用类似于windows下的.dll文件) 静态链接库使用静态库进行链接,生成的程序包含程序运行所需要的全部库,它是汇编产生的.o文件的集合,一般以.a文件形式出现。 gcc默认是动态链接,加上-static参数则采用静态链接。 例:vmuser@Linux-host:hellogcc选项文件名例子:用Vi编辑器创建一个hello.c文件后输入以下命令:gcchello.c--ohello然后在终端输入./hello可看到输出结果。也可不指定−ohello,直接输入:gcchello.c编译完成会得到a.out文件。gcc编译过程:hellc.c经过预处理到hello.i,经过编译到hello.s,经过汇编到hello.o,经过链接到hello.a/hello.out.1)预处理:在gcc命令加上−E参数,可得到预处理文件,输入以下命令:gcc--Ehello.c−ohello.i2)编译:gcc--Shello.i3)汇编:gcc--chello.s4)链接:gcchello.o链接可分为动态链接和静态链接:动态链接使用动态链接库进行链接,生成的程序在执行的时候需要加载所需的动态库才能运行。(Linux下的动态链接库实际是共享目标文件(sharedobject),一般是.so文件,作用类似于windows下的.dll文件)静态链接库使用静态库进行链接,生成的程序包含程序运行所需要的全部库,它是汇编产生的.o文件的集合,一般以.a文件形式出现。gcc默认是动态链接,加上−static参数则采用静态链接。例:vmuser@Linux−host:hello gcc hello.o -static -o hello_static
gcc --v hello.c 加上-v可显示详细编译过程。
gcc --v hello.c -I /home/vmuser/hello 通过-I可以将 dirname 指定的目录添加到头文件搜索目录列表中。
库文件链接:
静态库文件libFOO.a/libFOO.so用法1:gcc hello.c libFOO.a/gcc hello.c libFOO.so
用法2:用"-L"指定库文件路径:将libFOO.so所在目录添加到系统库文件搜索路径中
例:$gcc hello.c -L /home/vmuser/hello
或:指定链接库文件名。在编译的时候可通过"-lFOO"参数将 libFOO.so 链接到应用中:
$gcc hello.c -L /home/vmuser/hello --lFOO
gcc优化等级由低到高:-O0,-O1,-O2,-O3,优化等级越高,编译时间越长。
代码调试时需要关闭优化选项,打开-g调试选项,例:gcc --g hello.c
创建静态库和共享库:
静态库是.o文件的集合,例如在用户主目录下创建一个lib目录,并在其中创建两个.c文件,命名为lib1,lib2,然后输入:gcc --c lib1.c lib2.c,编译完成后会得到lib1.o和lib2.o的目标文件。
用ar命令创建一个库文件,输入以下命令:
lib $ ar --r lib.a lib1.o lib2.o即可得到lib.a库文件。
创建共享库首先需要编译对象模块,在用户主目录下创建libhello目录,将lib1和lib2两个c文件复制到libhello中,
例:libhello $ cp --av .../lib/*.c.
".../lib/lib1.c"->"./lib1.c"
".../lib/lib2.c"->"./lib2.c"
在终端输入命令将两个c文件编译成一个目标文件:
Libhello $ gcc --c --fpic lib1.c lib2.c -fpic参数表示生成的对象模块是可重定位的,编译完成后得到了lib1.o和lib2.o文件,再用下列命令生成共享库:
Libhello $ gcc --shared lib1.o lib2.o --o libhello.so 即可得到libhello.so共享库文件。
也可将两条命令合并为一条,即:
vmuser@Linux-host:libhello$ gcc -fpic -shared hello1.c hello2.c -o libhello.so
编写Makefile
在hello目录下创建名为GNUmakefile、makefile或Makefile的文件,输入make出现"无目标,停止"的错误信息,说明有文件但内容不对,此时用Vi打开MakeFile文件,输入以下代码:
all(:依赖关系)
(TAB)gcc hello.c(all是目标,无依赖关系,all目标对应的命令为gcc hello.c ,)
"gcc hello.c"不能顶格,而是必须以 TAB 字符('\t')隔开,且不能以相同数量的空格代替,保存并退出后再输入make命令,会产生一个.out文件。
-f参数可指定文件名,例如将Makefile文件改为app.mk,可在终端输入"make --f app.mk"
伪目标:只执行命令,不创建目标,还可避免目标与工作目录下的实际文件名冲突。
伪目标写法:.PHONY:标签,可在末尾增加,
例:
all(:依赖关系)
(TAB)gcc hello.c
.PHONY:all
clean命令:用于清除编译产生的中间文件和可执行文件。
如果为 hello.c 的 Makefile 增加一个伪目标 clean,可以这么写:
.PHONY:clean
(TAB)-rm -v a.out
完整的Makefile文件:
.PHONY:all clean(:依赖关系)
(TAB)gcc hello.c
clean:
(TAB)-rm -v a.out
"-rm"中加上"-"的含义是如果这条命令执行失败,make将忽略这个错误继续往下执行。
自定义变量
一般Makefile编写中,通常为源文件、可执行文件以及编译参数分别定义一个变量,并予以赋值,一般通过美元符号()和括号()完成变量引用,如)和括号()完成变量引用,如)和括号()完成变量引用,如(VAR)
对于 hello.c 的 Makefile,如果定义源文件 SRC 和可执行文件 EXE 两个变量,对 Makefile 进行改写,可如下:
EXE = hello
SRC = hello.c hello1.c(变量如果要赋值多个,可直接在(=)后列出,还可使用追加符号(+=))
(
还可使用换行符()进行换行处理
SRC hello.c
Hello1.c
)
all:
gcc -o (EXE) (SRC)
.PHONY:clean
rm -v $(EXE)
安装 Eclipse IDE for C/C++
标准的 Eclipse 只支持 JAVA 开发,要进行 C/C++程序开发,必须安装 CDT 插件。
可以获得 Eclipse C/C++开发环境。
命令如下:
$sudo apt-get install eclipse-platform
$sudo apt-get install eclipse-cdt
交叉编译:
程序再主机开发完毕需要放到ARM上运行时需要重设工程的编译器为交叉编译器并重新编译。
点击Project->Properties,点击C/C++Build的Toolchain Editor,在current Toolchain栏将编译器类型设置为Cross GCC,(有时候直接编译出错将Toolchain Editor中的Current builder设置为CDT Internal Builder)再点击C/C++Build栏,在Prefix栏填写交叉编译器的前缀,如arm-none-linux-gnueabi-,在Path中填写交叉编译器的实际路径。设置完毕后点击ok按钮重新编译工程即可。
在Edlipse主界面建立远程SSH连接
点击File->New->Other,选择Remote System Explorer后选择Connection后点击Next。在跳出的界面上选择SSH Only选择建立SSH连接。在Host name栏填写目标板的IP地址(ARM板的地址),建立完成后点击Window->Open Perspective->Other,在界面上选择Remote System Explorer后点击OK即切换到远程系统试图。右键点击连接名称选择connect,填写登录名和密码,一直ok,yes即成功连接。
31.文件描述符
文件描述符fd(又叫文件句柄)是进程中代表某个文件的整数,ulimit --n 命令可查询最大打开文件数。
fsync()函数
(在<unistd.h>中)的功能是进行文件数据同步,强制把已修改过的文件数据存入持久存储设备中。Sync()函数功能也是进行数据同步,但它是针对整个系统,即直到系统中修改过的缓存数据都写入磁盘才返回。
lseek( )函数
不会读写任何数据,仅改变文件的起始读写位置,在<unistd.h>中定义:
off_t lseek(int fd,off_t offset,int whence)😭 返回值的数据类型off_t即为long int,whence有效值为SEEK_SET、SEEK_CUR、SEEK_END分别表示从开头、当前以及文件结尾计算偏移字节位数)
ioctl()函数
此函数是文件I/O的杂项函数,其函数原型在<sys/ioctl.h>中:int ioctl(int fd,int cmd,...);
Fd为打开文件的描述符,cmd为文件的操作命令,一般操作命令不同。
32.进程
程序到进程转换的过程:
1)查找命令对应程序文件的位置;
2)使用fork()函数创建新进程;
3)在新进程中调用exec族函数装载程序文件并执行main函数。
进程状态
D:不可中断的深度睡眠状态,此状态下不能响应异步信号;
R:运行态或就绪状态,只有此状态能在CPU上运行;
S:可中断的睡眠状态;
T:暂停或跟踪状态;
X:退出状态,进程即将被销毁;
Z:退出状态,进程成为僵尸进程。
以下为进程状态转换图:
Linux 系统使用"ps -aux"命令时可观察到进程的当前状态
Linux 系统 getpid()函数可以获取当前进程的进程 ID(简称PID)。Linux 下可以使用 ps 命令来查看系统各个进程的 PID
Linux 是一个多用户的操作系统,每个用户至少有一个用户 ID(简称 UID)及用户的组 ID(简称 GID)。使用 id 命令可以列出用户的 id
环境变量
进程在运行过程中可以通过以下 3 种方式来获取运行环境的环境变量:
1)通过 main()函数的第 3 个参数 env 获取;
2)通过 environ 全局变量获取; (extern char ** environ; int i = 0; while (environi) puts(environi++);)
3)通过 getenv()函数获取。(char* env; env = getenv("HOME"); 可获取到环境变量HOME的值)
33.进程基本操作
创建进程:
pid_t pid; pid = fork();
终止进程:
分为正常终止和异常终止,正常终止方式有
从main()函数return返回;
调用类exit()函数。(exit(0)等价于return 0;退出进程)
异常终止放方式有
调用abort()函数;
接收到一个信号终止。
通常在 Shell 中,可以使用?变量来获取上次运行程序的退出状态。使用"echo ? "命令获取程序退出状态。
exec 族函数用来替换调用进程的执行程序。
wait()函数用来帮助父进程获取其子进程的退出状态。waitpid 可以指定专为特定子进程等待挂起。
守护进程:
独立于控制终端并且周期性地执行某种任务或等待某些发生的事件。守护进程的父进程是init进程,linux中有多种创建守护进程的方法,其中最常用的是使用daemon()函数创建,原型如下:
#include <unistd.h>
Int daemon(int nochdir,int noclose);
Nochdir,传入0则daemon函数将调用进程的工作目录设置为根目录,否则保持原有的工作目录不变;noclose传入0则daemon函数将重定向标准输入、标准输出、标准错误输出到/dev/null文件中,否则不改变这些文件描述符。
守护进程执行后将脱离控制台,可用 ps -ef 来查看。
信号
又称软中断信号,用来通知进程发生了异步事件,进程间可互相发送软中断信号。
Linux 可以使用 kill 命令向指定进程发送指定信号,kill 命令的用法如下: kill 选项PID
kill 命令可以支持-l 列出系统支持的所有信号,例如:kill --l
信号函数:
sigaction()函数原型:
#include <signal.h>
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
参数 signum 指出需要改变处理方法的信号,如 SIGINT 信号,但 SIGKILL 和 SIGSTOP 这两个信号是不可捕捉的。参数act和oldact是一个sigaction结构体的指针,act为要设置的对信号的新处理方式,而 oldact 为原来对信号的处理方式。
Kill函数
kill()函数用来向指定的进程发送一个指定的信号,kill()函数的原型如下:
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig); (参数pid为发送sig信号的进程 PID参数sig为发送的信号。)
34.进程间通信
Linux进程间通信包括管道(匿名管道:用于进程间有父子关系的通信和命名管道:用于无父子关系的进程间通信)、信号、信号量、共享内存、消息队列和套接字等方式。
管道:
它是一个进程连接数据流到另一个进程的通道,通常用作把一个进程的输出通过管道连接到另一个进程的输入,通常使用"|"使用管道,例如:
$ps --ef | grep init
(ps和grep是独立的进程,中间的管道把本来要输出到屏幕的数据输出到grep这个进程中,ps作为grep这个进程的输入。)
pipe()函数用于创建一条匿名管道(不能在文件系统中看到的半双工通信),原型如下:
#include <unistd.h>
int pipe(int pipefd2)😭 参数pipefd即打开管道的两端,其中 pipefd0为读端,pipefd1为写端,往写端写的数据会被内核缓存起来,直到读端将数据读完。)
mkfifo()函数用于创建一个命名管道(在文件系统中以文件名形式存在,可使同一主机内的所有进程均可通信),原型如下:
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);(pathname指定文件名,mode指定了文件的读写权限。)
共享内存
它允许两个不相关的进程访问同一个逻辑内存的进程间通信方法
1)打开或创建一个共享内存区:shm_open()函数,原型如下:
#include <sys/mman.h>
#include <sys/stat.h>
#include <fcntl.h>
Int shm_open(const char *name,into flag,mode_t mode);(name为指定创建的共享内存名称,flag为打开文件的方式,只读或只写等,mode只有指定flag为O_CREATE才有效指出共享内存的权限)。
注意:已创建的共享内存大小默认为0,需设置大小才可使用。
2)删除共享内存
shm_unlink()函数在使用共享内存结束后释放系统资源,原型如下:
int shm_unlink(const char *name);
3)设置共享内存大小
ftruncate()函数:原型如下:
int ftruncate(int fd, off_t length);(fd为需要调整的共享内存或文件,length为需要调整的大小)
4)映射共享内存
创建共享内存后需要将共享内存映射到调用进程的地址空间,可通过mmap()函数完成,原型如下:
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags,int fd, off_t offset);
addr:指向映射存储区的起始地址,一般为NULL,表示由系统选择
len:映射的字节数
port:对指定映射存储区的保护要求不超过文件open模式访问权限
flag:映射标志位
fd:要被映射的文件描述符或共享内存描述符
offset:要映射字节在文件中的起始偏移量
5)取消共享内存映射
可通过munmap()函数取消,原型如下:
#include <sys/mman.h>
int munmap(void *addr,size_t length);(参数addr为mmap()函数返回的地址,length是映射的字节数)
信号量:
解决进程间的同步与互斥问题的一种进程间通信机制,它是一个特殊的变量,其值代表着关联资源的可用数量。
根据值可将信号量分为二值信号量和计数信号量:
二值信号量:信号量的值只有0和1,若资源被锁住为0,资源可用则为1。
计数信号量:信号量的值在0到一个大于1的值(最大32767),该计数表示可用资源个数。
信号量只能进行的两个原子操作:P操作:V操作
P操作:如果有可用资源,则占用一个资源,信号量值减1,反之则进程被阻塞。
V操作:若在该信号量的等待队列中有进程在等待资源,则唤醒一个阻塞进程;如果没有进程等待则释放一个资源,给信号量值加1。
POSIX提供两类信号量,有名信号量(不同的进程通过信号量的名字获取信号量)和基于内存的信号量(无名信号量:只能放置在进程间共享内存区域中。)
1)创建或打开有名信号量
可使用sem_open()函数来完成,原型如下:
#include <fcntl.h>
#include <sys/stat.h>
#include <semaphore.h>
sem_t *sem_open(const char *name, int oflag);
sem_t *sem_open(const char *name, int oflag,mode_t mode, unsigned int value);
信号量的类型为 sem_t,该结构里记录着当前共享资源的数目。
2)关闭有名信号量
可以用 sem_close()函数,原型如下:
#include <semaphore.h>
int sem_close(sem_t *sem);
初始化基于内存信号量
sem_init()函数完成初始化,原型如下:
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
参数sem为需要初始化信号量的指针,pshared若为0表示该信号量只能在线程内使用,否则为进程间使用。Value为信号量的初始值,代表的资源数。
3)P操作
P 操作由 sem_wait()函数来完成,它的函数原型如下:
#include <semaphore.h>
int sem_wait(sem_t *sem);(若信号量值大于0,函数将信号量减1并返回)
4)V操作
V 操作由 sem_post()函数来完成,它的函数原型如下:
#include <semaphore.h>
int sem_post(sem_t *sem);(当进程使用完信号灯时调用,它把指定的信号值加1,唤醒正在等待的进程)
5)销毁基于内存的信号量
sem_destroy()函数来完成,它的原型如下:
#include <semaphore.h>
int sem_destroy(sem_t *sem);
6)删除有名信号量
sem_unlink()函数,原型如下:
#include <semaphore.h>
int sem_unlink(const char *name);
35.Linux多线程编程
线程时包含在进程内部的顺序执行流,一个进程中可并发多条线程,每条线程并行执行不同的任务。
注意:
线程作为调度和分配的基本单位,进程作为拥有资源的基本单位;
进程时拥有资源的一个独立单位,线程不拥有系统资源,但可以访问隶属于进程的资源。
POSIX Threads
其定义了创建和操纵线程的一套API接口,一般应用于Unix-like POSIX系统中(如Linux、Mac OS、FreeBSD)
PThreads接口可根据功能划分四个组:线程管理、互斥量、条件变量、同步;
线程管理
编写多线程程序时源码只需包含pthread.h头文件即可使用库中所有类型及函数。在编译PThread程序时在编译和链接过程中需要加上-pthread参数:LDFLAGS += -pthread
1.线程ID:
可看作线程的句柄,用于引用一个线程。可通过调用pthread_self()函数来获取ID,原型如下:
pthread_t pthread_self(void); (pthread_t类型可能是一个结构体,可使用pthread_equal()来比较两个线程ID是否相等,原型为:int pthread_equal(pthread_t t1, pthread_t t2)😉
2.创建与终止
创建线程:创建新线程的函数为pthread_create(),线程被创建后将立即运行,原型如下:
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
参数thread为线程ID,attr表示一个封装了线程各种属性的属性对象,若为NULL,则默认属性,start_routine是线程开始执行时调用的函数名,由第四个参数arg指定值并返回一个指向void的指针,返回值被当作一个退出状态处理,arg参数为start_routine指定函数的参数。
调用pthread_exit()会使调用线程终止,而exit()函数会使整个进程终止;pthread_exit()函数原型如下:
void pthread_exit(void *retval);(retval可以将线程的返回值当作pthread_exit()的参数传入,这个值同样被 pthread_join()当作退出状态处理。)
3.连接与分离
线程可分为分离线程(DETACHED)和非分离线程(JOINABLE),分离线程指线程退出时线程将释放它的资源的线程;非分离线程退出后不会立即释放资源,需要另一个线程为它调用pthread_join函数或进程退出时才会释放资源。
只有非分离线程才是可连接的,分离线程退出时不会报告线程的退出状态。
线程分离:pthread_detach()函数可将非分离线程设置为分离线程,函数原型如下:
int pthread_detach(pthread_t thread);
线程可以自身分离,也可以由其它线程来设置分离:自身分离的例子如下:
pthread_detach(pthread_self());
线程连接:
一个非分离线程可调用pthread_join()函数对该线程进行连接,原型如下:
int pthread_join(pthread_t thread, void **retval);
此函数将调用线程挂起,直到第一个参数thread指定目标线程终止运行为止。
retval:如果不需对目标线程的返回状态进行检查可直接赋值为NULL。
4.线程属性
Pthread_create()函数中的第二个参数pthread_attr_t用于设置线程属性(包括栈大小、调度策略和线程状态)
通常先创建一个属性对象->在属性对象上设置属性值->再传给attr。
属性对象:
初始化属性对象,pthread_attr_init(),原型如下:
int pthread_attr_init(pthread_attr_t *attr);
销毁属性对象,pthread_attr_destroy()函数,原型如下:
int pthread_attr_destroy(pthread_attr_t *attr);
5.线程状态
非分离线程------PTHREAD_CREATE_JOINABLE
分离线程------PTHREAD_CREATE_DETACHED
获取线程状态:pthread_attr_getdetachstate(),原型如下:
int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate);
参数attr是一个指向已初始化的属性对象的指针,detachstate是获取结果值的指针。
设置线程状态:pthread_attr_setdetachstate(),原型如下:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);(detachstate是要设置的值)
6.线程栈
每个线程都有一个独立调用栈,linux系统线程的默认栈大小为8MB,用户可以通过属性对象来设置和获取栈大小。
获取线程栈:pthread_attr_getstacksize(),原型如下:
int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize);(stacksize是获取栈大小的指针)
设置线程栈:pthread_attr_setstacksize(),原型如下:
intpthread_attr_setstacksize(pthread_attr_t *attr, size_tstacksize);(stacksize是设置的栈大小)
线程安全
多线程编程环境中,多个线程同时调用某些函数可能会产生错误结果,这些函数称为非线程安全函数。如果库函数能在多个线程中同时执行并且不会互相干扰,那么这个库函数就是线程安全函数。
互斥量
临界区是必须以互斥方式执行的代码段。
互斥量,又称为互斥锁,是用来保护临界区的特殊变量,它可以处于锁定(locked)状态,也可处于解锁状态(unlocked):
若互斥锁锁定,就是一个特定的线程持有这个互斥锁;
若无线程持有此互斥锁,那么这个互斥锁处于解锁状态。
注意:互斥量是最简单也是最有效的线程同步机制。互斥量只能被短时间地持有,使用完临界资源后应立即释放锁。
1.创建与销毁
创建互斥量
Pthreads使用pthread_mutex_t类型的变量来表示互斥量,同时在使用互斥量进行同步时需要先对它进行初始化,可静态或动态方式对互斥量进行初始化。
静态初始化:pthread_mutex_t mutex = PTHREAD_MUTEX_INTIALIZER;(静态初始化程序通常比调用 pthread_mutex_init 更有效,而且在任何线程开始执行之前,确保变量被执行一次。)
动态初始化:调用pthread_mutex_init()函数执行初始化工作,原型如下:
int pthread_mutex_init(pthread_mutex_t *restrict mutex,const pthread_mutexattr_t *restrict attr);(参数 mutex 是一个指向要初始化的互斥量的指针;参数 attr 传递 NULL 来初始化一个带有默认属性的互斥量)
销毁互斥量
使用pthread_mutex_destroy()函数,原型如下:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
2.加锁与解锁
加锁
线程试图锁定互斥量的过程称之为加锁
Pthreads中的pthread_mutex_lock()和pthread_mutex_trylock()函数试图锁定互斥量,pthread_mutex_lock()函数会一直阻塞到互斥量可用为止,而另一个会尝试加锁后立即返回。
解锁
解锁是线程将互斥量由锁定状态变为解锁状态
Pthread_mutex_unlock()函数用来释放指定的互斥量,函数原型如下:
int pthread_mutex_unlock(pthread_mutex_t *mutex);
3.死锁和避免
死锁是指两个或两个以上的执行序在执行过程中因争夺资源而造成的一种互相等待的现象。
按照相同的顺序获得锁可避免死锁的发生,例如三个互斥锁的加锁顺序为A->B->C,则线程就按ABC的顺序。
4.条件变量:
创建条件变量
Pthreads用pthread_cond_t类型的变量表示条件变量,程序必须使用其之前进行初始化
静态初始化:将PTHREAD_COND_INITIALIZER赋值给变量来初始化默认行为的条件变量:
Pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
动态初始化:使用pthread_cond_init()初始化,函数原型如下:
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
销毁条件变量
Pthread_cond_destroy()用来销毁它参数所指出的条件变量,函数原型如下:
int pthread_cond_destroy(pthread_cond_t *cond);
5.等待与通知
等待
条件变量是与条件测试一起使用的,通常线程会对一个条件进行测试,如果条件不满足就会调用条件等待函数(pthread_cond_wait():条件不满足时一直等待和pthread_cond_timedwait():只等待一段时间)来等待条件满足。原型如下:
int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
注意:pthread_timedwait()的第三个参数 abstime 是一个指向返回时间的指针,如果条件变量通知信号没有在此等待时间之前出现,等待将超时退出,abstime 是个绝对时间,而不是时间间隔。
通知
当另一个线程修改了某参数可能使条件变量所关联的条件变成真时,它应该通知一个或多个等待在条件变量等待队列中的线程。
条件通知函数有pthread_cond_signal():可唤醒一个在条件变量等待队列等待的线程和pthread_cond_broadcast()函数:可唤醒所有在条件变量等待队列等待的线程,
嵌入式GUI编程
Qt/Embedded 交叉编译环境的搭建
安装tslib1.4:
tslib能够为触摸屏驱动获得的采样提供诸如滤波、去抖动、校准等功能,通常作为触摸屏驱动的适配层,为上层应用提供了一个统一接口。
下载得到tslib-1.4.tar.gz解压输入:$ tar --zxvf tslib-1.4.tar.gz
确保已安装autoconf、automake和libtool,可输入以下命令安装
$ sudo apt-get install autoconf
$ sudo apt-get install automake
$ sudo apt-get install libtool
配置
进入解压的目录,执行如下命令:
$cd tslib
$./autogen.sh
./configure--prefix=/安装路径--host=交叉编译器编译执行make指令:./configure --prefix = /安装路径 --host = 交叉编译器 编译 执行make指令:./configure--prefix=/安装路径--host=交叉编译器编译执行make指令:make
安装
$make install
编译生成的库,头文件等都拷贝到prefix指定的路径中。
修改ts.conf内容
进入安装目录下的/etc/文件夹,修改ts.conf文件的内容。
$vi ts.conf
找到#module_rae input去掉注释#。
移植到开发板
将安装路径下的tslib整个文件夹放置在开发板的/usr/local/下
$ls /usr/local
tslib
设置开发板环境
通过串口软件,打开开发板的环境变量文件/etc/profile
#sudo vi /etc/profile
可在末尾添加指定的路径、设备等
编译qt4.7.3-arm
下载源码包后加压进入解压缩的目录,配置相应的选项内容保存到脚本build-qt里面,然后在该目录下的mkspec/qws/linux-arm-gnueabi-g++/qmake.conf文件添加-lts参数和在文件末尾添加如下两行:
QMAKE_INCDIR = /home/vmuser/nfs_shared/tslib/include
QMAKE_LIBDIR = /home/vmuser/nfs_shared/tslib/lib
然后开始安装,命令为:
$ cd qt-everywhere-opensource-src-4.7.3
$ ./build-qt
安装成功后,则可以在安装目录下查看到相关文件夹,接下来就要移植到开发板上。
Qt SDK搭建
使用sudoapt−getupdate更新,更新成功后可使用sudo apt-get update更新,更新成功后可使用sudoapt−getupdate更新,更新成功后可使用 sudo apt-get install qt-sdk安装qt SDK。
安装完成后会在usr/bin/目录下产生两个可执行文件qmake和qmake-qt4,第一个qmake是qt5版本的,第二个是我们要使用的嵌入式的qmake指令,用qmake-arm来指定嵌入式版本的qmake,在~/.bashrc文件末尾添加如下命令:
alias qmake-arm=/home/vmuser/nfs_shared/qt-4.7.3-arm/bin/qmake
添加成功后可执行source ~/.bashrc使其生效,可以执行qmake-qt4-v和qmake-arm-v查看是嵌入式还是桌面版本。
pro文件例程:
CONFIG += qt
HEADERS += hello.h
SOURCES += hello.cpp
SOURCES += main.cpp
TARGET = helloworld
然后根据此pro文件生成Makefile,命令如下:
$ qmake--o Makefile hello.pro
如果当前目录只有一个pro文件,可直接使用$ qmake命令。
QT Creator
启动QtCreator:$ qtcreator
设置Qt Creator使用的Qt版本,点击选项中的Kits->Qt Versions,然后手动加入Qt的版本,点击Add->弹出选择qmake可执行文件的窗口->选择路径/usr/bin/qmake-qt4的执行文件->打开->在Version Name中输入版本名字->点击Apply->点击ok
在嵌入式环境运行Qt程序
将程序编译成嵌入式版本
一般只需要用交叉编译工具的qmake重新编译即可在目标板上运行,执行嵌入式的qmake然后重新交叉编译即可获得嵌入式版本的Qt程序。
在目标板上运行程序
通过nfc挂载PC主机上的目录至目标板上,便于调试开发,挂载成功后,设定鼠标设备,进入如下命令:
#export QWS_MOUSE_PROTO = tslib:/dev/input/event0
正常情况下,所需的设备文件位于/dev/input目录下,在命令行下输入如下命令:
#cat/dev/input/event0|hexdump
点击触摸屏,如果有数据输出,那么对应的设备文件就是所需的设备文件。
成功设定鼠标设备后可执行# ./hello --qws启动Qt程序,-qws指明此程序同时作为一个窗口服务器运行。
Qt帮助文档
输入$ assistant-qt4命令即可弹出帮助文档。
特殊硬件接口编程
点亮LED灯
LED操作接口位于/sys/class/leds目录下,使用#ls /sys/class/leds进入LED操作的目录,其中有beep、led-err和led-run,进入led-run目录下有多属性,例如brightness可控制LED灯亮灭,trigger写入"none"可将指示灯设为用户控制,"heartbeat"可设为"心跳灯","nand-disk"设为NAND Flash读写灯。
GPIO硬件编程
GPIO的属性文件在/sys/class/gpio目录下,属性文件中有export(导出GPIO)和unexport(将导出的GPIO从sysfs中清除),其余四个文件为符号链接(gpiochip0,gpiochip32,gpiochip64,gpio96),指向管理对应设备的目录。向export文件写入需要操作的GPIO排列序号N即可导出对应的GPIO设备目录。例如,导出序号为68的GPIO的操作接口,在shell命令下,# echo 68 > /sys/class/gpio/export
1)输入输出设置
GPIO导出后默认为输入功能:向direction文件写入"in"("out")字符串表示设置为输入(输出)功能,方向查看和设置 命令如下:
cat /sys/class/gpio/gpioN/direction #查看方向
echo out > /sys/class/gpio/gpioN/direction #设置为输出
echo in > /sys/class/gpio/gpioN/direction #设置为输入
2)输入读取
value文件记录GPIO引脚的输入电平状态:1表示高电平,0表示输入低电平。通过查看value可读取GPIO电平,命令如下:
echo in >/sys/class/gpio/gpioN/direction #设置 GPIO 排列序号为 N 的 GPIO 方向为输入 # cat /sys/class/gpio/gpioN/value #查看 GPIO 排列序号为 N 的 GPIO 电平
3)输出控制
当GPIO被设为输出时,通常向value文件写入0或1(输出高电平)设置输出电平的状态,输出命名如下:
echo out > /sys/class/gpio/gpioN/direction #设置 GPIO 排列序号为 N 的 GPIO 方向为输出 # echo 0 > /sys/class/gpio/gpioN/value #输出低电平
echo 1 > /sys/class/gpio/gpioN/value #输出高电平
用户态SPI编程
Linux的SPI总线设备文件名通常为/dev/spidevN.P,其中N表示第几路SPI总线,P表示在该路SPI总线中使用哪个CS信号线。
SPI编程接口
1)打开设备
使用SPI设备时,需要调用open()函数打开设备文件,获得文件描述符,例如:fd = open("/dev/spidev1.0",O_RDWR);
2)关闭设备
调用close(fd)函数关闭设备。
3)总线控制
调用ioctl()函数使用不同的命令,应用程序可配置SPI的极性和相位、设置总线速率、数据字长度以及实现数据收/发。
1)设置SPI总线极性和相位
是使用SPI_IOC_WR_MODE命令实现,调用方式如下:
Ret = ioctl(fd,SPI_IOC_WR_MODE,&mode);(mode可选值为SPI_MODE_0、SPI_MODE_1、SPI_MODE_2、SPI_MODE_3)
SPI_MODE_0 定义的模式为 POLARITY(极性)=0、PHASE(相位)=0
SPI_MODE_1 定义的模式为 POLARITY=0、PHASE=1
SPI_MODE_2 定义的模式为 POLARITY=1、PHASE=0
SPI_MODE_3 定义的模式为 POLARITY=1、PHASE=1
2)设置每字的数据位长度
是使用SPI_IOC_WR_BITS_PER_WORD命令实现,调用方式如下:
Ret = ioctl(fd,SPI_IOC_WR_BITS_PER_WORD,&bits);(bits为每字的二进制位数取值)
3)设置最大总线速率
是通过使用SPI_IOC_WR_MAX_SPEED_HZ命令实现,调用方式如下:
Ret = ioctl(fd, SPI_IOC_WR_MAX_SPEED_HZ,&speed);(speed为需要设置的SPI总线的最大频率,单位为HZ)
注意:SPI 总线的最大速率设置后,在使用过程并不是只能使用该频率收/发数据,而仅仅约束 收/发数据时的最大频率。
4)数据接收/发送命令
在SPI总线实现数据收/发是使用SPI_IOC_MESSAGE(n)命令实现,调用方式如下:
Ret = ioctl(fd,SPI_IOC_MESSAGE(n),&tr);(struct spi_ioc_transfer结构体用于封装要收发的数据,tr参数指定向此结构体的数组,数组长度为n)
用户态I2C编程
I2C总线的设备文件通常为/dev/I2C-n,(n=0,1,...)每个设备文件对应一组I2C总线,
I2C编程接口
1)打开设备
调用open()函数打开I2C设备获得文件描述符,例如:
int fd;
fd = open("/dev/I2C-0",O_RDWR);
2)关闭设备
close(fd);
3)配置设备
当应用程序操作I2C总线上的从机器件时,必须先调用ioctl()函数设置从机地址和从机地址的长度。
设置从机地址
它是使用I2C_SLAVE命令,其定义为:
#define I2C_SLAVE 0x0703
该命令的参数为从机地址右移一位,设置从机地址为0xA0的例子为:
if(ioctl(GiFd,I2C_SLAVE,0xA0>>1)<0)
{
pcrror("");
}
注意:地址需要右移一位,是因为地址的Bit0是读写控制位,在驱动中会将从机地址命令参数左移一位,并补上读写控制位。
设置地址长度
它使用I2C_TENBIT命令,其定义为:
#define I2C_TENBIT 0x0704
该命令的参数可选择为:1表示设置从机地址长度为10位(ioctl(fd,I2C_TENBIT,1)),0表示设置从机地址为8位。(不设置地址长度则默认为8位地址)。
发送数据
调用write()函数向I2C总线发送数据
接收数据
调用read()函数在I2C总线接收数据
按键应用层编程
按键编程
按键驱动加载完成后应用程序可读取按键事件,代码中应包含<Linux/input.h>头文件
按键事件
输入事件通常封装成input_event结构
打开设备文件
调用open()函数打开,例如:
fd = open ("/dev/input/event1", RDWR);
关闭设备文件
close(fd);
读取按键事件
调用read()函数可读取按键事件,例如:
int count;
struct input_event input_event_value;
count=read(fd, &input_event_value, sizeof(struct input_event));
Linux串口编程
串口基本操作
Linux的串口设备文件命名一般为/dev/ttySn(n=0,1,...),若串口是USB扩展的,则串口设备文件命名多为/dev/ttyUSBn(n = 0,1,...),在编写串口的c程序代码时,需要包含termios.h头文件。
打开串口
open()函数打开"dev/ttyS1"的代码示例如下:
int fd;
fd = open("/dev/ttyS1","O_RDWR|O_NOCTTY");(O_NOCTTY告诉本程序不作为串口的控制终端)
关闭串口
close(fd);
发送数据
write()函数
例子:
int Len;
char buf\[\] = "hello";
Len = write(fd,buf,sizeof(buf));
读取数据
int Len;
unsigned char buf7;
Len = read(fd,buf,7);
串口属性设置
struct termios {
tcflag_t c_cflag /* 控制标志 /
tcflag_t c_iflag; / 输入标志 /
tcflag_t c_oflag; / 输出标志 /
tcflag_t c_lflag; / 本地标志 /
tcflag_t c_ccNCCS; / 控制字符 */
};
tcflag_t的定义为typedef unsigned int tcflag_t;
控制标志:可设置串口的波特率、数据为、奇偶校验、停止位以及流控制
输入标志:负责控制串口输入数据的处理,例如INPCK(打开奇偶校验)、IXOFF(启用或停止输入控制流起作用)、IGNPAR(忽略奇偶错字符)、IXON(启动或停止输出控制流起作用)。
输出标志:管理输出过滤,例如OPOST(执行输出处理)、OCRNL(将输出的CR转换为NL)、OFILL(对于延迟使用填充符)
本地标志:影响驱动程序和用户之间的接口,例如ISIG(启用终端产生的信号)、ECHO(进行回送)、ECHOE(可见擦除字符)、ICANON(启用规范输入)
控制字符组:NCCS一般介于15-20之间,每个元素下标都用一个宏表示,例如VINTR(中断)、VQUIT(退出)、VERASE(擦除)、VEOF(行结束)。
获取和设置终端属性
使用tcgetattr()可获取串口设备的termios结构,该函数原型如下:
int tcgetattr(int fd,struct termios *termptr);
获得termios结构后,可以把串口的属性设置到termios结构中,串口属性设置完成后可通过tcsetattr()函数把新的属性设置应用到串口中,tcsetattr()函数原型如下:
int tssetattr(int fd,int opt,const struct termios *termptr);
opt参数处理缓冲区中的数据,TCSANOW(表示更改立即发生)、TCSADRAIN(发送了所有输出后更改才发生,更改输出参数则使用此选项)、TCSAFLUSH(发送了所有输出后更改才发生,在更改发生时未读的所有输入数据被删除(flush))
设置波特率
分为输入波特率和输出波特率,可通过cfsetispeed()和cfsetospeed()函数设置,这两个函数原型为:
int cfsetispeed(struct termios *termptr, speed_t speed);
int cfsetospeed(struct termios *termptr, speed_t speed);
speed 参数为需要设置的波特率,B0表示0位/秒(挂起)、B9600表示9600位/秒、B115200表示115200位/秒,通常来说串口的输入输出波特率要设置同一个值。
set_baudrate()函数设置串口输入/输出波特率为 115200 的代码为:
set_baudrate(&opt, B115200));
该函数将串口输入/ 输出设置为相同的波特率,使用时只需填写所需波特率即可。
设置数据位
设置串口的数据位为 8 位的代码为:
opt. c_cflag &= ~CSIZE;
opt. c_cflag |= CS8;(CSIZE表示数据为屏蔽,CS8表示8位数据位)
使用 set_data_bit()函数设置 8 位数据位的代码如下:(将以上串口数据位设置封装为只需要设置8,7,6,5的函数)
set_data_bit(8);
设置奇偶校验
Linux 的串口驱动支持无校验('N')、偶校验('E')和奇校验('O')。(PARENB表示进行奇偶校验,PARODD表示奇校验,否则为偶校验)
设置偶校验的方法为:
opt->c_cflag |= PARENB;
opt->c_cflag &= ~PARODD;
set_parity() 函数中,parity 参数可以取值为:'N'和'n'(无奇偶校验)、'E'和'e' (表示偶校验)、'O'和'o'(表示奇校验)。
设置停止位
需要用到的选项标志为CSTOPB(2位停止位,否则为1位)
设置 1 位停止位的方法为:
opt->c_cflag &= ~CSTOPB;
set_stopbit(struct termios *opt, const char *stopbit)函数实现串口停止位的设置。stopbit 参数可以取值为:"1"(1 位停止位)、"1.5"(1.5 位停止 位)和"2"(2 位停止位)。
其他设置
调用read()函数读取串口数据时,返回读取数据的数量需要考虑MIN(指一次read调用期望返回的最小字节数)和TIME(等待数据到达的分秒数)两个变量,对应termios结构的下标名为VMIN和VTIME。
串口属性设置函数
int set_port_attr ( int fd, int baudrate, int databit, const char *stopbit, char parity, int vtime, int vmin )
设置串口属性为"115200、8n1"的代码如下:
ret = set_port_attr (fd, B115200, 8, "1", 'N', 150, 255 );
C语言网络编程
网络基本概念
OSI模型:
物理层:负责将信息编码成电流脉冲或其他信号用于网上传输
数据链路层:通过物理网络链路提供数据传输;
数据链路层实际上由两个独立的部分组成,介质存取控制(Media Access Control,MAC) 和逻辑链路控制层(Logical Link Control,LLC)。MAC 描述在共享介质环境中如何进行调度、发送和接收数据。逻辑链路控制子层管理单一网络链路上的设备间的通信。
网络层:负责在源和终点间建立连接,一般包括网络寻径,还可能包括流量控制、错误检查。
传输层:向高层提供可靠的端到端的网络数据流服务。
会话层:建立、管理和终止表示层与实体之间的通信对话。
表示层:用于应用层数据编码和转化。
应用层:最接近终端用户,与用户之间通过应用软件相互作用。
TCP/IP 协议基本概念
网络接口层
网络接口层包括用于协作IP数据在已有网络介质上传输的协议。它定义像地址解析协议(ARP)这样的协议,提供TCP/IP 协议的数据结构和实际物理硬件之间的接口。
网间层
网间层对应于 OSI的网络层。本层包含 IP 协议、RIP协议(Routing Information Protocol,路由信息协议),负责数据的包装、寻址和路由。同时还包含网间控制报文协议(Internet Control Message Protocol,ICMP)用来提供网络诊断信息。
传输层
传输层对应于 OSI的传输层,它提供两种端到端的通信服务。其中 TCP 协议提供可靠的数据流运输服务,UDP协议提供不可靠的用户数据报服务。
应用层
应用层对应于 OSI的应用层和表达层。因特网的应用层协议包括 Finger、 Whois、FTP(文件传输协议)、Gopher、HTTP(超文本传输协议)、Telent(远程终端协议)、 SMTP(简单邮件传送协议)、IRC(因特网中继会话)、NNTP(网络新闻传输协议)等。
TCP/IP常用协议
ICMP
(网络控制消息协议)是 TCP/IP的核心协议之一,用于在 IP 网络中发送控制消息,提供通信过程中的各种问题反馈。 ICMP 直接使用 IP 数据包传输,但 ICMP 并不被视为 IP 协议的子协议。常见的联网状态诊断工具比如 ping、traceroute 都依赖于 ICMP 协议。
TCP
(传输控制协议)是一种面向连接的,可靠的,基于字节流传输的通信协议。TCP 具有端口号的概念,用来标识同一个地址上的不同应用。
UDP
(用户数据报协议)是一个面向数据报的传输层协议。UDP 的传输是不可靠的,它同 TCP 一样有用来标识本地应用的端口号。
ECHO
(回声协议)是一个简单的调试和检测工具。服务器会原样回发它收到的任何数据,既可以使用 TCP 传输,也可以使用 UDP 传输。使用端口号 7。
ARP 和 RARP
(地址解析协议;逆向地址解析协议)其中 ARP 负责根据 IP 地址查找 MAC 地址。ARP 在 IPv6中已经被NDP取代。RARP 可以根据 MAC 地址转换为 IP 地址,但已经被其他协议如 DHCP/BOOTP 取代功能。
DHCP
(动态主机配置协议)是用于局域网自动分配 IP 地址和主机配置的协议。
DNS
(域名系统)是互联网的一项服务,可以简单的将用"." 分隔的一般会有意义的域名转换成IP 地址。一般使用 UDP 协议传输,也可以使用 TCP,默认服务端口号 53。
RIP
(路由信息协议)是一种让路由器自动维护链路和路由状态的协议。使用 UDP 多播传输,RIPv2 使用端口号 520,RIPng 使用端口号 521。
FTP
(文件传输协议)是用来进行文件传输的标准协议。FTP 基于TCP使用端口号 20来传输数据,21来传输控制信息。
TFTP
(简单文件传输协议)是一个简化的文件传输协议,在传输大量文件时建议不要使用 TFTP。NTP
(网络时间协议)用来在网络上对主机进行时间的协议,它被设计为可以尽量抵消网络传输延时,采用 UDP 协议,端口号 123。
TELNET
(远程网络),是最初网络远端登录的协议和主要方式,使用 TCP,默认端口 23。此协议虽然方便,但是 安全性上有缺陷,登录服务基本上已经大量被新的协议 SSH 所取代。
SSH
(安全 Shell),它其实是一个协议框架,有大量的扩展冗余能力,并且提供了加密压缩的通道可以为其他协议使用。
POP
(邮局协议)是支持通过客户端访问电子邮件的服务,现在版本是 POP3,也有加密的版本 POP3S。协议使用 TCP,端口 110。
SMTP
(简单邮件传输协议)是现在在互联网上发送电子邮件的事实标准。使用 TCP 协议传输,端口号 25。
HTTP
(超文本传输协议)是现在广为流行的 WEB 网络的基础,HTTPS 是 HTTP 的加密安全版本。协议通过 TCP 传输,HTTP 默认使用端口80,HTTPS 使用 443。
字节序
如果最低有效字节在最高有效字节的前面,则称小端序;反之则称大端序。
注意:
在网络编程中不应该假设自己程序运行的主机的字节序,应当使用 htonl/htons/ntohs /ntohl 之类的函数来在网络字节序和主机字节序之间进行转换。其中 h 代表 host,就是cbf表示形式;n 代表 network,表示网络上传输的字节序,s 和 l 代表类型 short 和 long。后文将对这几个函数进行更为详细的说明。 ARM 的字节序实际上是可配置的,但是一般都配置为小端。
编程接口BSD Socket
用 Socket 能够实现网络上的不同主机之间或同一主机的不同对象之间的数据通信。
大的类型可以分为网络 Socket 和本地 Socket 两种:本地 Socket 在 Linux 上包括 Unix Domain Socket 和 Netlink 两种。Unix Domain Socket 主要用于进程间通信,NetLink 用于用户空间和内核空间通讯,这里暂不做讨论;
地址表示数据结构
IP协议使用的地址描述数据结构,需要包括头文件<netinet/in.h>
网络字节序和本地字节序之间的转换
包含在头文件<arpa/inet.h>中;
uint32_t htonl(uint32_t hostlong);(表示32位整数从主机字节序转换为网络字节序)
uint16_t htons(uint16_t hostshort);(表示16位整数从主机字节序转换为网络字节序)
uint32_t ntohl(uint32_t netlong); (表示32位整数从网络字节序转为主机字节序)
uint16_t ntohs(uint16_t netshort);(表示16位整数从网络字节序转为主机字节序)
主机名和地址转换函数
需要包含头文件<netinet/in.h>和<arpa/inet.h>
in_addr_t inet_addr(const char *cp)
此函数将点分十进制的IP地址字符串转换成in_addr_t类型,例如192.168.0.1 在 PC 上会 被转换成 0x0100A8C0。
char *inet_ntoa(struct in_addr in)
此函数可将结构struct in_addr中的二进制IP地址转换为点分十进制表示的字符串。例如会将一个网络字节序二进制无符号32位整数表示的 IP地址0x0100A8C0 转换为点分十进制表示"192.168.0.1"。
通过主机获取IP地址
是 gethostbyname()函数,原型如下:
struct hostent *gethostbyname(const char *name); 直接根据主机名字符串返回一个 struct hostent 结构。
函数 gethostbyname2()原型如下:
struct hostent *gethostbyname2(consts char *name, in af); 相对gethostbyname(),多一个af 参数,可以指明需要解析的地址协议类型,对于IPv4 就是 AF_INET。
BSD Socket常用操作
创建Socket
一般调用socket()函数来创建一个Socket通信端点,原型如下:
int socket(int domain,int type,int protocol);
domain代表此Socket使用的地址类型,IPv4协议的IP地址可使用AF_INET/PF_INET;
type代表Socket的类型,面向流的(TCP)Socket(可取值SOCK_STREAM)和面向数据报的(UDP)Socket(可取值SOCK_DGRAM)
protocol是协议类型,取0即可。
创建TCP Socket:sock_fd = socket(AF_INET,SOCK_STREAM,0);
绑定地址和端口
调用bind()函数绑定到特定的地址和端口进行通信,函数原型如下:
int bind(int socket,const struct sockaddr address,socklent address_len);
address参数是一个指向struct sockaddr的指针,对于IP地址就是struct sockaddr_in,但调用时需强制转换避免警告。
address_len表示使用的地址数据结构的长度,编程时直接取sizeof(struct sockaddr_in)即可。
服务器程序绑定端口流程如下:
Struct sockaddr_in server_addr;
Memset(&server_addr,0,sock_len);
server_addr.sin_family = AF_INET;
server_addr.sin_addr.s_addr = htol(INADDR_ANY);
server_addr.sin_port = htons(80);
bind(server_sock,(struct sockaddr )&server_addr,sizeof(server_addr));
连接服务器
调用connect()函数连接到需要通信的服务器的特定通信端点后才能正确进行通信。
使用TCP协议通讯前必须调用connect()函数才能通信,UDP协议客户机这个步骤可选,在此之后可以不需要指定数据报的目的地址直接发送,否则每次发送数据均需要指定。
函数原型如下:
int connect(int socket, const struct sockaddr *address, socklent address_len);(参数及含义与bind函数相同)
设置Socket为监听模式
基于TCP协议的服务器需调用listen()函数将其Socket设置成被动模式,等待客户的连接,该函数原型如下:
int listen(int socket,int backlog);
backlog是指等待连接的队列长度,通常取5。
接受连接
TCP服务器还需要调用accept()函数来处理到来的客户机连接请求,函数原型如下:
int accept(int socket,struct sockaddr *restrict address,socklen_t *restrict address_len);