您可以捐助,支持我们的公益事业。

1元 10元 50元





认证码:  验证码,看不清楚?请点击刷新验证码 必填



  求知 文章 文库 Lib 视频 iPerson 课程 认证 咨询 工具 讲座 Model Center   Code  
会员   
   
 
   
 
 订阅
《嵌入式linux应用程序开发详解》核心笔记
 
作者:姜源Jerry
  42  次浏览      4 次
 2024-12-31
 
编辑推荐:
本文主要介绍了嵌入式linux应用程序开发详解相关内容。 希望对您的学习有所帮助。
本文来自于CSDN,由火龙果软件Linda编辑、推荐。

【linux快速入门】

自由软件( free software)中的 free 并不是指免费,而是指自由。它赋予使用者四种自由。

· 自由之一:有使用软件的自由。

· 自由之二:有研究该软件如何运作的自由,并且得以改写该软件来符合使用者自身的需求。取得该软件的源码是达成此目的前提。

· 自由之三:有重新散布该软件的自由,所以每个人都可以藉由散布自由软件来敦亲睦另。

· 自由之四:有改善再利用该软件的自由,并且可以发表改写版供公众使用,如此一来,整个社群都可以受惠。如前项,取得该软件的源码是达成此目的前提。

GPL: GPL 协议是 GNU 组织、维护的一种版权协议,遵守这个协议的软件可以自由地获取、查看、使用其源代码。GPL 协议是整个开源世界的精神基础。

Linux 的内核版本号:

Linux 内核版本号格式是 x.y.zz-www,数字 x 代表版本类型,数字 y 为偶数时是稳定版本,为奇数时是开发版本,如 2.0.40 为稳定版本,2.3.42 为开发版本,测试版本为 3 个数字加上测试号,如 2.4.12-rc1。最新的 Linux 内核版本可从 http://www.kernel.org 上获得。

国内的一些 Linux 论坛:

http://www.linuxfans.org

http://www.linuxforum.net

http://www.linuxeden.com

http://www.newsmth.net

在 Windows 下,目录结构属于分区;Linux 下,分区属于目录结构。

在 Windows 下,路径用 \ 反斜杠分割;Linux 下,路径用 / 斜杠分割。(实际是linux最早,你懂的)

通常在 Windows 下的盘符和 Linux 设备文件的对应关系如下:

C 盘— /dev/hda1( 主分区)

D 盘—/dev/hda5(逻辑分区)

E 盘—/dev/hda6(逻辑分区)

如果想修复已经安装好的系统,请在提示符 boot:后输入“ Linux rescue”命令。

ext3 是现在 Linux(包括 Red Hat,Mandrake 下)常见的默认的文件系统,它是 ext2 的升级版本。

1./bin     bin 就是二进制(binary)英文缩写。在这里存放前面 Linux 常用操作命令的执行文件,如 mv、ls、mkdir 等。有时,这个目录的内容和/usr/bin 里面的内容一样,它们都是放置一般用户使用的执行文件
2./boot    这个目录下存放操作系统启动时所要用到的程序。如启动 grub 就会用到其下的/boot/grub子目录
3./dev    该目录中包含了所有 Linux 系统中使用的外部设备。要注意的是,这里并不是存放的外部设备的驱动程序,它实际上是一个访问这些外部设备的端口。由于在 Linux 中,所有的设备都当作文件一样进行操作,比如:/dev/cdrom 代表光驱,用户可以非常方便地像访问文件、目录一样对其进行访问
4./etc    该目录下存放了系统管理时要用到的各种配置文件和子目录。如网络配置文件、文件系统、x 系统配置文件、设备配置信息设置用户信息等都在这个目录下。系统在启动过程中需要读取其参数进行相应的配置
5./etc/rc.d    该目录主要存放 Linux 启动和关闭时要用到的脚本文件,在后面的启动详解中还会进一步地讲解
6./etc/rc.d/init    该目录存放所有 Linux 服务默认的启动脚本(在新版本的 Linux 中还用到的是/etc/xinetd.d目录下的内容)
7./home    该目录是 Linux 系统中默认的用户工作根目录。执行 adduser命令后系统会在/home 目录下为对应账号建立一个名为同名的主目录
8./lib    该目录是用来存放系统动态链接共享库的。几乎所有的应用程序都会用到这个目录下的共享库。因此,千万不要轻易对这个目录进行什么操作
9./lost+found    该目录在大多数情况下都是空的。只有当系统产生异常时,会将一些遗失的片段放在此目录下
10./media    该目录下是光驱和软驱的挂载点,Fedora Core 4 已经可以自动挂载光驱和软驱
11./misc    该目录下存放从 DOS 下进行安装的实用工具,一般为空
12./mnt    该目录是软驱、光驱、硬盘的挂载点,也可以临时将别的文件系统挂载到此目录下
13./proc    该目录是用于放置系统核心与执行程序所需的一些信息。而这些信息是在内存中由系统产生的,故不占用硬盘空间
14./root    该目录是超级用户登录时的主目录
15./sbin    该目录是用来存放系统管理员的常用的系统管理程序
16. /tmp    该目录用来存放不同程序执行时产生的临时文件。一般 Linux 安装软件的默认安装路径就是这里
17./usr    这是一个非常重要的目录,用户的很多应用程序和文件都存放在这个目录下,类似与Windows 下的 Program Files 的目录
18./usr/bin    系统用户使用的应用程序
19./usr/sbin    超级用户使用的比较高级的管理程序和系统守护程序
20./usr/src    内核源代码默认的放置目录
21./srv    该目录存放一些服务启动之后需要提取的数据
22./sys    这是 Linux 2.6 内核的一个很大的变化。该目录下安装了 2.6 内核中新出现的一个文件系统 sysfs,sysfs 文件系统集成了下面 3 种文件系统的信息:针对进程信息的 proc 文件系统、针对设备的 devfs 文件系统以及针对伪终端的 devpts 文件系统。该文件系统是内核设备树的一个直观反映。当一个内核对象被创建的时候,对应的文件和目录也在内核对象子系统中被创建
23./var    这也是一个非常重要的目录,很多服务的日志信息都存放在这里

【linux基础命令】

root用户切换:

$:' sudo passwd

// 输入当前登陆密码,再根据提示输入设置的UNIX密码2遍,即为root用户切换密码

$:' su

// 输入UNIX密码,切换为root用户,提示符变为 #

设置环境变量方法如下:

1. 通过 echo 显示字符串(指定环境变量)。

2. 通过 export 设置新的环境变量。

3. 通过 env 显示所有环境变量。

4. 通过 set 命令显示所有本地定义的 Shell 变量。

5. 通过 unset 命令来清除环境变量。

Linux常见用户管理命令:

  1. useradd 添加用户账号 useradd [选项] 用户名
  2. usermod 设置用户账号属性 usermod [选项] 属性值
  3. userdel 删除对应用户账号 userdel [选项] 用户名
  4. groupadd 添加组账号 groupadd [选项] 组账号
  5. groupmod 设置组账号属性 groupmod [选项] 属性值
  6. groupdel 删除对应组账号 groupdel [选项] 组账号
  7. passwd 设置账号密码 passwd [对应账号]
  8. id 显示用户ID、组ID和用户所属组列表 id [用户名]
  9. groups 显示用户所属的组 groups [组账号]
  10. who 显示登录到系统的所有用户 who
  11. whoami 显示当前用户名 whoami

 

Linux常见系统管理命令:

  1. ps 显示当前系统中由该用户运行的进程列表 ps [选项]
  2. top 动态显示系统中运行的程序(一般为每隔 5s) top
  3. kill 输出特定的信号给指定 PID(进程号)的进程 kill [选项] 进程号(PID)
  4. uname 显示系统的信息(可加选项-a) uname [选项]
  5. setup 系统图形化界面配置 setup
  6. crontab 循环执行例行性命令 crontab [选项]
  7. shutdown 关闭或重启 Linux 系统 shutdown [选项] [时间]
  8. uptime 显示系统已经运行了多长时间 uptime
  9. clear 清除屏幕上的信息(ctrl + l) clear

 

Linux常见磁盘管理命令:

  1. free 查看当前系统内存的使用情况 free [选项]
  2. df 查看文件系统的磁盘空间占用情况 df [选项]
  3. du 统计目录(或文件)所占磁盘空间的大小 du [选项]
  4. fdisk 查看硬盘分区情况及对硬盘进行分区管理 fdisk [-l]
  5. mount 磁盘挂载命令 mount -t [选项] [文件系统类型] 设备文件名 挂载点目录

 

mount挂载举例:

$:' mount -t vfat /dev/hda1 /mnt/c

$:' umount /mnt/c

· 在 Linux 下如何使用 U 盘呢?

一般 U 盘为 SCSI 格式的硬盘,其格式为 vfat 格式,其设备号可通过“fdisk –l”进行查看,假若设备名为“/dev/sda1”,则可用如下命令就可将其挂载:

$:' mount -t vfat /dev/ sda1 /mnt/u

· 若想设置在开机时自动挂载,可在文件“ /etc/fstab”中加入该命令到一个新行即可。

正则表达式的主要参数有:

· \:忽略正则表达式中特殊字符的原有含义;

· ^:匹配正则表达式的开始行;

· $:匹配正则表达式的结束行;

· <:从匹配正则表达式的行开始;

· >:到匹配正则表达式的行结束;

· [ ]:单个字符,如[A]即 A 符合要求;

· [-]:范围,如[A-Z],即 A、B、C 一直到 Z 都符合要求;

· 。:所有的单个字符;

· *:所有字符,长度可以为 0。

ln 的链接又软链接和硬链接两种:

软链接就是上面所说的 ln -s ** **,它只会在用户选定的位置上生成一个文件的镜像,不会重复占用磁盘空间,平时使用较多的都是软链接;

硬链接是不带参数的 ln ** **,它会在用户选定的位置上生成一个和源文件大小相同的文件,无论是软链接还是硬链接,文件都保持同步变化。

Linux常见压缩打包相关命令:

  1. bzip2 .bz2 文件的压缩(或解压)程序 bzip2[选项] 压缩(解压缩)的文件名
  2. bunzip2 .bz2 文件的解压缩程序 bunzip2[选项] .bz2 压缩文件
  3. bzip2recover 用来修复损坏的.bz2 文件 bzip2recover .bz2 压缩文件
  4. gzip .gz 文件的压缩程序 gzip [选项] 压缩(解压缩)的文件名
  5. gunzip 解压被 gzip 压缩过的文件 gunzip [选项] .gz 文件名
  6. unzip 解压 winzip 压缩的.zip 文件 unzip [选项] .zip 压缩文件
  7. compress 早期的压缩或解压程序(压缩后文件名为.Z) compress [选项] 文件
  8. tar 对文件目录进行打包或解包 tar [选项] [打包后文件名]文件目录列表

 

Linux比较和合并文件命令:

  1. diff 比较两个不同的文件或不同目录下的两个同名文件功能,并生成补丁文件
    diff[选项] 文件1 文件2
  2. $:' diff hello1.c hello2.c > hello.patch
  3. patch 把生成的补丁文件应用到现有代码上
    patch [选项] [待 patch 的文件[patch 文件]]
  4. $:' patch ./hello1.c < hello1.patch

 

Linux常见网络相关命令:

  1. netstat 显示网络连接、路由表和网络接口信息 netstat [-an]
  2. nslookup 查询一台机器的 IP 地址和其对应的域名 nslookup [IP 地址/域名]
  3. finger 查询用户的信息 finger [选项] [使用者] [用户@主机]
  4. ping 用于查看网络上的主机是否在工作 ping [选项] 主机名/IP 地址
  5. ifconfig 查看和配置网络接口的参数 ifconfig [选项] [网络接口]
  6. ftp 利用 ftp 协议上传和下载文件 ftp [选项] [主机名/IP]
  7. telnet 利用 telnet 协议浏览信息 telent [选项] [IP 地址/域名]
  8. ssh 利用 ssh 登录对方主机 ssh [选项] [IP 地址]

 

定制linux系统服务流程步骤:

(1)查看系统的默认运行级别。

$:' cat /etc/inittab(设其结果为 N)

(2)进入到相应级别的服务脚本目录,查看哪些服务是系统启动的独立运行的服务,并做下记录。

$:' cd /etc/rc.d/rcN.d

(3)利用命令查看系统开机自启动服务,并与上次查看结果进行比较,找出其中的区别,并思考其中的原因。

$:' chkconfig –list

(4)记录 chkconfig –list 命令中由 xinet 管理的服务,并将其中启动的服务做下记录。

(5)进入 xinet 配置管理的相应目录,查看是否于 chkconfig –list 所得结果相吻合并查看相应脚本文件。

$:' cd /etc/xinetd.d

(6)将 sshd 服务停止。

$:' service sshd stop

(7)将 sshd 服务设置为开机不启动。

$:' chkconfig –level N sshd stop

(8)查看该设置是否生效。

$:' chkconfig –list

(9)查看系统中所有服务及其端口号列表。

$:' cat /etc/services

(10)将 sshd 服务端口改为 4022。

$:' vi /etc/services;转到插入模式并修改其端口号

(11)重启 sshd 服务,验证所改的端口号是否生效。

$:' service sshd start

(12)重启 Linux 系统,验证所改的服务开机启动是否生效。

【linux下的C编程】

编辑器:vim

编译器:gcc

调试器:gdb

make工程管理器:Makefile

Makefile常见预定义变量:

  1. AR 库文件维护程序的名称,默认值为 ar
  2. AS 汇编程序的名称,默认值为 as
  3. CC C 编译器的名称,默认值为 cc
  4. CPP C 预编译器的名称,默认值为$(CC) –E
  5. CXX C++编译器的名称,默认值为 g++
  6. FC FORTRAN 编译器的名称,默认值为 f77
  7. RM 文件删除程序的名称,默认值为 rm –f
  8. ARFLAGS 库文件维护程序的选项,无默认值
  9. ASFLAGS 汇编程序的选项,无默认值
  10. CFLAGS C 编译器的选项,无默认值
  11. CPPFLAGS C 预编译的选项,无默认值
  12. CXXFLAGS C++编译器的选项,无默认值
  13. FFLAGS FORTRAN 编译器的选项,无默认值

 

Makefile自动变量:

$* 不包含扩展名的目标文件名称

$+ 所有的依赖文件,以空格分开,并以出现的先后为序,可能包含重复的依赖文件

$< 第一个依赖文件的名称

$? 所有时间戳比目标文件晚的依赖文件,并以空格分开

$@ 目标文件的完整名称

$^ 所有不重复的依赖文件,以空格分开

$% 如果目标是归档成员,则该变量表示目标的归档成员名称

Makefile 中常见隐式规则:

C 编译:.c 变为.o $(CC) –c $(CPPFLAGS) $(CFLAGS)

C++编译:.cc 或.C 变为.o $(CXX) -c $(CPPFLAGS) $(CXXFLAGS)

Pascal 编译:.p 变为.o $(PC) -c $(PFLAGS)

Fortran 编译:.r 变为-o $(FC) -c $(FFLAGS)

autotools自动生成Makefile:

首先要确认系统是否装了以下工具(可以用 which 命令进行查看)。

· aclocal

· autoscan

· autoconf

· autoheader

· automake

没有安装就进行安装。$:' sudo apt-get install aclocal autoscan autoconf autoheader automake

1. 使用 aclocal 生成一个“aclocal.m4”文件,该文件主要处理本地的宏定义;

2. 改写“configure.scan”文件,并将其重命名为“configure.in”,并使用 autoconf 文件生成 configure 文件。

 

【嵌入式系统基础】

RAM(随机存取存储器),内存,掉电丢失。

ROM(只读存储器),硬盘,掉电不丢失。

RAM 又可分为 SRAM(静态存储器)和 DRAM(动态存储器)。

SDRAM 是 DRAM 的一种,它是同步动态存储器。

Flash 也是一种非易失性存储器(掉电不会丢失),它擦写方便,访问速度快。

NOR Flash 的特点是芯片内执行(Execute In Place),这样应用程序可以直接在 flash 闪存内运行,而不必再把代码读到系统 RAM 中。

NAND Flash 结构能提供极高的单元密度,可以达到高存储密度,NAND 读和写操作采用 512 字节的块,单元尺寸几乎是 NOR 器件的一半,同时由于生产过程更为简单,大大降低了生产的成本。

常见的 CPU 指令集分为 CISC 和 RISC 两种:

CISC(Complex Instruction Set Computer)是“复杂指令集”。

RISC(Reduced Instruction Set Computing)是“精简指令集”。

MMU 是内存管理单元,它把内存以“页(page)”为单位来进行处理。一页内存是指一个具有一定大小的连续的内存块,通常为 4096B 或 8192B。

 

【嵌入式Linux开发环境搭建】

交叉编译器下载: ftp://gcc.gnu.org/pub/

多个编译包: cross-3.3.2.bar.bz2

超级终端:115200,8,N,1

Minicom:

CTRL+A Z,来查看 minicom 帮助;

CTRL-A O,来进行配置串口参数,同样是 115200,8,N,1

下载映像到开发板:

>> tftp

$:' vi /etc/xinetd.d/tftp

// 主要要将“disable=yes”改为“no”,另外,从“server_args”可以看出,tftp服务器端的默认根目录为“/tftpboot”。

$:' service xinetd restart

$:' netstat -au

// 确认tftp服务是否已经开启

接下来,用直连线(注意:不可以使用网线)把目标板和宿主机连起来,并且将其配置成一个网段的地址。

#:' tftpboot 0x30200000 zImage

// 进行下载。

编译linux内核、制作文件系统镜像、u-boot移植,这些基本操作:略。

【文件I/O编程】

系统调用:操作系统提供给用户程序调用的一组“特殊”接口,用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务。

 

Linux 中的文件主要分为 4 种: 普通文件、目录文件、链接文件和设备文件。

一个进程启动时,都会打开 3 个文件:标准输入、标准输出和标准出错处理。这 3 个文件分别对应文件描述符为 0、1 和 2(也就是宏替换 STDIN_FILENO、STDOUT_FILENO 和 STDERR_FILENO,鼓励使用这些宏替换)。

不带缓存的文件 I/O 操作,主要用到 5 个函数: open、 read、 write、 lseek和 close。

函数较简单,用法:略。

不带缓存是指每一个函数都只调用系统中的一个函数。

在文件已经共享的情况下如何操作,也就是当多个用户共同使用、操作一个文件的情况,这时,Linux 通常采用的方法是给文件上锁,来避免共享的资源产生竞争的状态。

fcntl 函数:

  1. /* fcntl 使用实例 lock_set 函数 */
  2. void lock_set(int fd, int type)
  3. {
  4. struct flock lock;
  5. lock.l_whence = SEEK_SET;//赋值 lock 结构体
  6. lock.l_start = 0;
  7. lock.l_len =0;
  8. while(1)
  9. {
  10. lock.l_type = type;
  11. /*根据不同的 type 值给文件上锁或解锁*/
  12. if((fcntl(fd, F_SETLK, &lock)) == 0){
  13. if( lock.l_type == F_RDLCK )
  14. printf("read lock set by %d\n",getpid());
  15. else if( lock.l_type == F_WRLCK )
  16. printf("write lock set by %d\n",getpid());
  17. else if( lock.l_type == F_UNLCK )
  18. printf("release lock by %d\n",getpid());
  19. return;
  20. }
  21. /*判断文件是否可以上锁*/
  22. fcntl(fd, F_GETLK,&lock);
  23. /*判断文件不能上锁的原因*/
  24. if(lock.l_type != F_UNLCK){
  25. /*/该文件已有写入锁*/
  26. if( lock.l_type == F_RDLCK )
  27. printf("read lock already set by %d\n",lock.l_pid);
  28. /*该文件已有读取锁*/
  29. else if( lock.l_type == F_WRLCK )
  30. printf("write lock already set by %d\n",lock.l_pid);
  31. getchar();
  32. }
  33. }
  34. }

 

  1. /*fcntl_write.c 测试文件写入锁主函数部分*/
  2. #include <unistd.h>
  3. #include <sys/file.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. int main(void)
  9. {
  10. int fd;
  11. /*首先打开文件*/
  12. fd=open("hello",O_RDWR | O_CREAT, 0666);
  13. if(fd < 0){
  14. perror("open");
  15. exit(1);
  16. }
  17. /*给文件上写入锁*/
  18. lock_set(fd, F_WRLCK);
  19. getchar();
  20. /*给文件接锁*/
  21. lock_set(fd, F_UNLCK);
  22. getchar();
  23. close(fd);
  24. return 0;
  25. }

 

终端一:

[root@localhost file]# ./fcntl_write

write lock set by 4994

release lock by 4994

终端二:

[root@localhost file]# ./fcntl_write

write lock already set by 4994

write lock set by 4997

release lock by 4997

  1. /*fcntl_read.c 测试文件读取锁主函数部分*/
  2. #include <unistd.h>
  3. #include <sys/file.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. int main(void)
  9. {
  10. int fd;
  11. fd=open("hello",O_RDWR | O_CREAT, 0666);
  12. if(fd < 0){
  13. perror("open");
  14. exit(1);
  15. }
  16. /*给文件上读取锁*/
  17. lock_set(fd, F_RDLCK);
  18. getchar();
  19. /*给文件接锁*/
  20. lock_set(fd, F_UNLCK);
  21. getchar();
  22. close(fd);
  23. return 0;;
  24. }

 

}终端一:

[root@localhost file]# ./fcntl2

read lock set by 5009

release lock by 5009

终端二:

[root@localhost file]# ./fcntl2

read lock set by 5010

release lock by 5010

I/O 处理的模型有 5 种:

· 阻塞 I/O 模型:在这种模型下,若所调用的 I/O 函数没有完成相关的功能就会使进程挂起,直到相关数据到才会出错返回。如常见对管道设备、终端设备和网络设备进行读写时经常会出现这种情况。

· 非阻塞模型:在这种模型下,当请求的 I/O 操作不能完成时,则不让进程睡眠,而且返回一个错误。非阻塞 I/O 使用户可以调用不会永远阻塞的 I/O 操作,如 open、write和 read。如果该操作不能完成,则会立即出错返回,且表示该 I/O 如果该操作继续执行就会阻塞。

· I/O 多路转接模型:在这种模型下,如果请求的 I/O 操作阻塞,且它不是真正阻塞 I/O,而是让其中的一个函数等待,在这期间,I/O 还能进行其他操作。如 select函数和 poll 函数,就是属于这种模型。

· 信号驱动 I/O 模型:在这种模型下,通过安装一个信号处理程序,系统可以自动捕获特定信号的到来,从而启动 I/O。这是由内核通知用户何时可以启动一个 I/O 操作决定的。

· 异步 I/O 模型:在这种模型下,当一个描述符已准备好,可以启动 I/O 时,进程会通知内核。现在,并不是所有的系统都支持这种模型。

select函数:

select 的 I/O 多路转接模型是处理 I/O 复用的一个高效的方法。

int select(int numfds, fd_set *readfds, fd_set *writefds, fd_set *exeptfds, struct timeval *timeout);

功能:I/O 多路转接,具体设置每一个所关心的文件描述符的条件、希望等待的时间等。

参数:

@numfds:需要检查的号码最高的文件描述符加 1

@readfds:由 select()监视的读文件描述符集合

@writefds:由 select()监视的写文件描述符集合

@exeptfds:由 select()监视的异常处理文件描述符集合

@timeout:

NULL:永远等待,直到捕捉到信号或文件描述符已准备好为止

具体值:struct timeval 类型的指针,若等待为 timeout 时间还没有文件描符准备好,就立即返回

0:从不等待,测试所有指定的描述符并立即返回

函数返回值:成功:准备好的文件描述符,-1:出错

select 函数根据希望进行的文件操作对文件描述符进行了分类处理,这里,对文件描述符的处理主要涉及到 4 个宏函数:

  1. FD_ZERO(fd_set *set) 清除一个文件描述符集
  2. FD_SET(int fd,fd_set *set) 将一个文件描述符加入文件描述符集中
  3. FD_CLR(int fd,fd_set *set) 将一个文件描述符从文件描述符集中清除
  4. FD_ISSET(int fd,fd_set *set) 测试该集中的一个给定位是否有变化
  5. // 一般来说,在使用 select 函数之前,首先使用 FD_ZERO 和 FD_SET
    来初始化文件描述符集,在使用了 select 函数时,可循环使用
    FD_ISSET 测试描述符集,在执行完对相关后文件描述符后,使用 FD_CLR 来清楚描述符集。

 

本实例中主要实现将文件 hello1 里的内容读出,并将此内容每隔 10s 写入 hello2 中去。在这里建立了两个描述符集,其中一个描述符集 inset1 是用于读取文件内容,另一个描述符集 inset2 是用于写入文件的。两个文件描述符 fds[0]和 fds[1]分别指向这一文件描述符。在首先初始化完各文件描述符集之后,就开始了循环测试这两个文件描述符是否可读写,由于在这里没有阻塞,所以文件描述符处于准备就绪的状态。这时,就分别对文件描述符 fds[0]和fsd[1]进行读写操作。

  1. /*select.c*/
  2. #include <fcntl.h>
  3. #include <stdio.h>
  4. #include <unistd.h>
  5. #include <stdlib.h>
  6. #include <time.h>
  7. int main(void)
  8. {
  9. int fds[2];
  10. char buf[7];
  11. int i,rc,maxfd;
  12. fd_set inset1,inset2;
  13. struct timeval tv;
  14. /*首先按一定的权限打开 hello1 文件*/
  15. if((fds[0] = open ("hello1", O_RDWR|O_CREAT,0666))<0)
  16. perror("open hello1");
  17. /*再按一定的权限打开 hello2 文件*/
  18. if((fds[1] = open ("hello2", O_RDWR|O_CREAT,0666))<0)
  19. perror("open hello2");
  20. if((rc = write(fds[0],"Hello!\n",7)))
  21. printf("rc=%d\n",rc);
  22. lseek(fds[0],0,SEEK_SET);
  23. /*取出两个文件描述符中的较大者*/
  24. maxfd = fds[0]>fds[1] ? fds[0] : fds[1];
  25. /*初始化读集合 inset1,并在读集合中加入相应的描述集*/
  26. FD_ZERO(&inset1);
  27. FD_SET(fds[0],&inset1);
  28. /*初始化写集合 inset2,并在写集合中加入相应的描述集*/
  29. FD_ZERO(&inset2);
  30. FD_SET(fds[1],&inset2);
  31. tv.tv_sec=2;
  32. tv.tv_usec=0;
  33. /*循环测试该文件描述符是否准备就绪,并调用 select 函数对相关文件描述符做对应操作*/
  34. while(FD_ISSET(fds[0],&inset1)||FD_ISSET(fds[1],&inset2))
  35. {
  36. if(select(maxfd+1,&inset1,&inset2,NULL,&tv)<0)
  37. perror("select");
  38. else
  39. {
  40. if(FD_ISSET(fds[0],&inset1))
  41. {
  42. rc = read(fds[0],buf,7);
  43. if(rc>0)
  44. {
  45. buf[rc]='\0';
  46. printf("read: %s\n",buf);
  47. }
  48. else
  49. perror("read");
  50. }
  51. if(FD_ISSET(fds[1],&inset2))
  52. {
  53. rc = write(fds[1],buf,7);
  54. if(rc>0)
  55. {
  56. buf[rc]='\0';
  57. printf("rc=%d,write: %s\n",rc,buf);
  58. }
  59. else
  60. perror("write");
  61. sleep(10);
  62. }
  63. }
  64. }
  65. return 0;
  66. }

 

运行结果:

[root@(none) 1]#:' ./select

rc=7

read: Hello!

rc=7,write: Hello!

rc=7,write:Hello!

rc=7,write:Hello!

[root@(none) 1]#:' cat hello1

Hello!

[root@(none) 1]#:' cat hello2

Hello!

Hello!

使用 select 可以很好地实现 I/O 多路复用,在有阻塞的情况下更能够显示出它的作用。

Linux 串口应用开发 page203

在 Linux 中,所有的设备文件一般都位于“/dev”下,其中串口一、串口二对应的设备名依次为“/dev/ttyS0”、“/dev/ttyS1”,可以查看在“/dev”下的文件以确认。

标准 I/O 开发 - 基于流缓冲

标准 I/O 提供流缓冲的目的: 尽可能减少使用 read 和 write 系统调用的次数。

打开文件函数: fopen、 fdopen和 freopen

fopen 可以指定打开文件的路径和模式,fdopen可以指定打开的文件描述符和模式,而 freopen 除可指定打开的文件、模式外,还可指定特定的 IO 流。

关闭文件函数: fclose

文件读写函数: fread 和 fwrite

输入输出函数:

字符输入, getc/ fgetc/ getchar

字符输出, putc/ fputc/ putchar

// 这几个函数功能类似,其区别仅在于 getc 和 putc 通常被实现为宏,而 fgetc 和 fputc 不能实现为宏,因此,函数的实现时间会有所差别。

行输入, gets/ fgets

行输出, puts/ fputs

// 行输入输出函数一次操作一行。

格式化输入, scanf/ fscanf/ sscanf

格式化输出, printf/ fprintf/ sprintf、vprintf/vfprintf/vsprintf

// 格式化输入输出函数使用的是可变参数。

  1. /* 打开一个文件,然后将该文件上写入锁,并写入 hello 字符串。
    接着在解锁后再将该文件上读取锁,并读取刚才写入的内容。
    最后模拟多进程,同时读写一个文件时的情况。 expr1.c 实验源码*/
  2. #include <unistd.h>
  3. #include <sys/file.h>
  4. #include <sys/types.h>
  5. #include <sys/stat.h>
  6. #include <stdio.h>
  7. #include <stdlib.h>
  8. #include <string.h>
  9. void lock_set(int fd, int type)
  10. {
  11. struct flock lock;
  12. lock.l_whence = SEEK_SET;//赋值 lock 结构体
  13. lock.l_start = 0;
  14. lock.l_len =0;
  15. while(1)
  16. {
  17. lock.l_type = type;
  18. /*根据不同的 type 值给文件上锁或解锁*/
  19. if((fcntl(fd, F_SETLK, &lock)) == 0){
  20. if( lock.l_type == F_RDLCK )
  21. printf("read lock set by %d\n",getpid());
  22. else if( lock.l_type == F_WRLCK )
  23. printf("write lock set by %d\n",getpid());
  24. else if( lock.l_type == F_UNLCK )
  25. printf("release lock by %d\n",getpid());
  26. return;
  27. }
  28. /*判断文件是否可以上锁*/
  29. fcntl(fd, F_GETLK,&lock);
  30. /*判断文件不能上锁的原因*/
  31. if(lock.l_type != F_UNLCK){
  32. /*/该文件已有写入锁*/
  33. if( lock.l_type == F_RDLCK )
  34. printf("read lock already set by %d\n",lock.l_pid);
  35. /*该文件已有读取锁*/
  36. else if( lock.l_type == F_WRLCK )
  37. printf("write lock already set by %d\n",lock.l_pid);
  38. getchar();
  39. }
  40. }
  41. }
  42. int main(void)
  43. {
  44. int fd,nwrite,nread,len;
  45. char *buff="Hello\n";
  46. char buf_r[100];
  47. len=strlen(buff);
  48. fd=open("hello",O_RDWR | O_CREAT, 0666);
  49. if(fd < 0){
  50. perror("open");
  51. exit(1);
  52. }
  53. /*加上写入锁*/
  54. lock_set(fd, F_WRLCK);
  55. if((nwrite=write(fd,buff,len))==len){
  56. printf("write success\n");
  57. }
  58. getchar();
  59. /*解锁*/
  60. lock_set(fd, F_UNLCK);
  61. getchar();
  62. /*加上读取锁*/
  63. lock_set(fd, F_RDLCK);
  64. lseek(fd,0,SEEK_SET);
  65. if((nread=read(fd,buf_r,len))==len){
  66. printf("read:%s\n",buf_r);
  67. }
  68. getchar();
  69. /*解锁*/
  70. lock_set(fd, F_UNLCK);
  71. getchar();
  72. close(fd);
  73. return 0;
  74. }
  75. // 同一个终端内运行测试,开两个终端运行测试多进程情况,测试结果均不会收到影响。

 

 

【进程控制开发】

进程创建: fork

进程中启动进程: exec函数族(execve为系统调用,其他都为库函数)

进程退出: exit/ _exit

进程阻塞等待: wait/ waitpid

守护进程Daemon编写 5 步骤:

1. 创建子进程,父进程退出; fork/ exit

  1. /*父进程退出*/
  2. pid=fork();
  3. if(pid>0){
  4. exit(0);
  5. }

 

2. 在子进程中创建新会话; setsid

3. 改变当前目录为根目录; chdir

4. 重设文件权限掩码; umask

5. 关闭文件描述符; close

  1. /* 守护进程需要关闭标准I/O MAXFILE==3 */
  2. for(i=0;i<MAXFILE;i++)
  3. close(i);

 

汇总:开始 >> fork()/exit() >> setsid() >> chdir("/") >> umask(0) >> close >> 结束(守护进程创建)

  1. /* 实现守护进程的一个完整实例 */
  2. /*dameon.c 创建守护进程实例*/
  3. #include<stdio.h>
  4. #include<stdlib.h>
  5. #include<string.h>
  6. #include<fcntl.h>
  7. #include<sys/types.h>
  8. #include<unistd.h>
  9. #include<sys/wait.h>
  10. #define MAXFILE 65535
  11. int main()
  12. {
  13. pid_t pc;
  14. int i,fd,len;
  15. char *buf="This is a Dameon\n";
  16. len =strlen(buf);
  17. pc=fork(); // 第一步
  18. if(pc<0)
  19. {
  20. printf("error fork\n");
  21. exit(1);
  22. }else if(pc>0)
  23. exit(0);
  24. /*第二步*/
  25. setsid();
  26. /*第三步*/
  27. chdir("/");
  28. /*第四步*/
  29. umask(0);
  30. /*第五步*/
  31. for(i=0;i<MAXFILE;i++)
  32. close(i);
  33. /*这时创建完守护进程,以下开始正式进入守护进程工作*/
  34. while(1)
  35. {
  36. if((fd=open("/tmp/dameon.log",O_CREAT|O_WRONLY|O_APPEND,0600))<0)
  37. {
  38. perror("open");
  39. exit(1);
  40. }
  41. write(fd, buf, len+1);
  42. close(fd);
  43. sleep(10);
  44. }
  45. }
  46. // 该程序每隔 10s 就会在对应的文件中输入相关内容。

守护进程的进程要如何调试呢?

一种通用的办法是使用 syslog服务,将程序中的出错信息输入到" /var/log/messages "系统日志文件中,需要root权限,从而可以直观地看到程序的问题所在。

系统日志函数: openlog、 syslog和 closelog

openlog 函数用于打开系统日志服务的一个连接;syslog 函数是用于向日志文件中写入消息,在这里可以规定消息的优先级、消息输出格式等;closelog 函数是用于关闭系统日志服务的连接。

【进程间通信】

1. UNIX 进程间通信(IPC)方式包括管道、FIFO、信号。

2. System V 进程间通信(IPC)包括 System V 消息队列、System V信号灯(信号量集)、System V共享内存区。

3. Posix 进程间通信(IPC)包括 Posix 消息队列、Posix 信号灯(信号量集)、Posix 共享内存区。

4. 基于socket的进程间通信。

管道的创建和关闭: pipe/ close

管道的读写: read/ write

标准流管道: popen/ pclose

 

信号的发送: kill / raise / alarm / pause

信号的处理: signal / 信号量集函数组

信号量集功能模块:

创建信号集合, sigemptyset / sigfillset / sigaddset / sigdelset / sigismember

登记信号处理器, sigprocmask / sigaction

检测信号, sigpending

 

共享内存: shmget / shmat/ shmdt

消息队列: msgget / msgsnd/ msgrcv/ msgctl

【多线程通信】

线程创建和退出: pthread_create / pthread_exit / pthread_join

修改线程属性: pthread_attr_init / pthread_attr_setscope
/ pthread_attr_setdetachstate/ pthread_attr_getschedparam/ pthread_attr_setschedparam

这些属性主要包括绑定属性、分离属性、堆栈地址、堆栈大小、优先级。

线程同步:

· 互斥锁初始化: pthread_mutex_init

· 互斥锁上锁: pthread_mutex_lock

· 互斥锁判断上锁: pthread_mutex_trylock

· 互斥锁接锁: pthread_mutex_unlock

· 销毁互斥锁: pthread_mutex_destroy

信号量线程控制:PV操作

· sem_init 用于创建一个信号量,并能初始化它的值。

· sem_wait 和 sem_trywait相当于 P 操作,它们都能将信号量的值减一,两者的区别在于若信号量小于零时,sem_wait 将会阻塞进程,而 sem_trywait 则会立即返回。

· sem_post 相当于 V 操作,它将信号量的值加一同时发出信号唤醒等待的进程。

· sem_getvalue 用于得到信号量的值。

· sem_destroy 用于删除信号量。

经典例程:生产者与消费者模型。

【网络编程】

TCP 对话通过三次握手来初始化的。

三次握手的目的: 使数据段的发送和接收同步,告诉其他主机其一次可接收的数据量,并建立虚连接。

socket三种类型:

(1)流式 socket( SOCK_STREAM)

流式套接字提供可靠的、面向连接的通信流;它使用 TCP 协议,从而保证了数据传输的正确性和顺序性。

(2)数据报 socket( SOCK_DGRAM)

数据报套接字定义了一种无连接的服务,数据通过相互独立的报文进行传输,是无序的,并且不保证是可靠、无差错的。它使用数据报协议 UDP。

(3)原始 socket

原始套接字允许对底层协议如 IP 或 ICMP 进行直接访问,它功能强大但使用较为不便,主要用于一些协议的开发。

网络字节序与主机字节序的转化: htons、 ntohs、 htonl、 ntohl

// h 代表 host,n 代表 network,s 代表 short,l 代表 long。通常 16 位的 IP 端口号用 s 代表,而 IP 地址用 l 来代表。

地址格式转化:IPv4, inet_aton、 inet_addr、 inet_ntoa;IPv4和IPv6兼容函数, inet_pton和 inet_ntop

主机名和地址的转化: gethostbyname、 gethostbyaddr、 getaddrinfo

  1. Struct hostent{
  2. char *h_name;/*正式主机名*/
  3. char **h_aliases;/*主机别名*/
  4. int h_addrtype;/*地址类型*/
  5. int h_length;/*地址长度*/
  6. char **h_addr_list;/*指向 IPv4 或 IPv6 的地址指针数组*/
  7. }
  8. struct addrinfo{
  9. int ai_flags;/*AI_PASSIVE,AI_CANONNAME;*/
  10. int ai_family;/*地址族*/
  11. int ai_socktype;/*socket 类型*/
  12. int ai_protocol;/*协议类型*/
  13. size_t ai_addrlen;/*地址长度*/
  14. char *ai_canoname;/*主机名*/
  15. struct sockaddr *ai_addr;/*socket 结构体*/
  16. struct addrinfo *ai_next;/*下一个指针链表*/
  17. }

 

socket编程基本函数: socket、 bind、 listen、 accept、 send、 sendto、 recv、 recvfrom

网络高级编程: fcntl

· 非阻塞 I/O:可将 cmd 设置为 F_SETFL,将 lock 设置为 O_NONBLOCK。

· 信号驱动 I/O:可将 cmd 设置为 F_SETFL,将 lock 设置为 O_ASYNC。

网络高级编程: select

使用 select 函数还可以设置等待的时间,减少CPU资源浪费。详解可单独查阅专题。

   
42 次浏览       4
 
相关文章

CMM之后对CMMI的思考
对软件研发项目管理的深入探讨
软件过程改进
软件过程改进的实现
 
相关文档

软件过程改进框架
软件过程改进的CMM-TSP-PSP模型
过程塑造(小型软件团队过程改进)
软件过程改进:经验和教训
 
相关课程

以"我"为中心的过程改进(iProcess )
iProcess过程改进实践
CMMI体系与实践
基于CMMI标准的软件质量保证

最新活动计划
C++高级编程 12-25 [线上]
白盒测试技术与工具实践 12-24[线上]
LLM大模型应用与项目构建 12-26[特惠]
需求分析最佳实践与沙盘演练 1-6[线上]
SysML建模专家 1-16[北京]
UAF架构体系与实践 1-22[北京]
 
 
最新文章
iPerson的过程观:要 过程 or 结果
基于模型的需求管理方法与工具
敏捷产品管理之 Story
敏捷开发需求管理(产品backlog)
Kanban看板管理实践精要
最新课程
基于iProcess的敏捷过程
软件开发过程中的项目管理
持续集成与敏捷开发
敏捷过程实践
敏捷测试-简单而可行
更多...   
成功案例
英特尔 SCRUM-敏捷开发实战
某著名汽车 敏捷开发过程与管理实践
北京 敏捷开发过程与项目管理
东方证券 基于看板的敏捷方法实践
亚信 工作量估算
更多...