书签 分享 收藏 举报 版权申诉 / 43
上传文档赚钱

类型嵌入式系统原理与技术资料:linux串口编程-1.doc

  • 上传人(卖家):罗嗣辉
  • 文档编号:2047671
  • 上传时间:2022-01-21
  • 格式:DOC
  • 页数:43
  • 大小:119KB
  • 【下载声明】
    1. 本站全部试题类文档,若标题没写含答案,则无答案;标题注明含答案的文档,主观题也可能无答案。请谨慎下单,一旦售出,不予退换。
    2. 本站全部PPT文档均不含视频和音频,PPT中出现的音频或视频标识(或文字)仅表示流程,实际无音频或视频文件。请谨慎下单,一旦售出,不予退换。
    3. 本页资料《嵌入式系统原理与技术资料:linux串口编程-1.doc》由用户(罗嗣辉)主动上传,其收益全归该用户。163文库仅提供信息存储空间,仅对该用户上传内容的表现方式做保护处理,对上传内容本身不做任何修改或编辑。 若此文所含内容侵犯了您的版权或隐私,请立即通知163文库(点击联系客服),我们立即给予删除!
    4. 请根据预览情况,自愿下载本文。本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
    5. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007及以上版本和PDF阅读器,压缩文件请下载最新的WinRAR软件解压。
    配套讲稿:

    如PPT文件的首页显示word图标,表示该PPT已包含配套word讲稿。双击word图标可打开word文档。

    特殊限制:

    部分文档作品中含有的国旗、国徽等图片,仅作为作品整体效果示例展示,禁止商用。设计者仅对作品中独创性部分享有著作权。

    关 键  词:
    嵌入式 系统 原理 技术 资料 linux 串口 编程
    资源描述:

    1、linux 串口编程http:/ Linux 中,串口是一个字设备,访问具体的串行端口的编程与读写文件的操作类似,只需打开相应的设备文件即可操作。串口编程特殊在于串口通信时相关参数与属性的设置。嵌入式 Linux 的串口编程时应注意,若在根文件中没有串口设备文件,应使用mknod 命令创建,这这里假设串口设备是/dev/ttyS0,介绍一下串口的编程过程。mknod /dev/ttyS0 c 4 641、打开串口打开串口设备文件的操作与普通文件的操作类似,都采用标准的 I/O 操作函数 open()。fd = open(/dev/ttyS0,O_RDWR|O_NDELAY|O_NOCTTY);

    2、open()函数有两个参数,第一个参数是要打开的文件名(此处为串口设备文件/dev/ttyS0);第二个参数设置打开的方式,O_RDWR表示打开的文件可读/写,O_NDELAY 表示以非阻塞方式打开,ONOCTTY 表示若打开的文件为终端设备,则不会将终端作为进程控制终端。2、设置串口属性串口通信时的属性设置是串口编程的关键问题,许多串口通信时的错误都与串口的设置相关,所以编程时应特别注意这些设置,最常见的设置包括波特率、奇偶校验和停止位以及流控制等。在 Linux 中,串口被作为终端 I/O,它的参数设置需要使用 struct termios 结构体,这个结构体在 termio.h 文件中定

    3、义,且应在程序中包含这个头文件。typedef unsigned charcc_t ;typedef unsigned intspeed_t ;typedef unsigned inttcflag_t ;struct termiostcflag_tc_iflag ;/*输入模式标志*/tcflag_tc_oflag ;/*输出模式标志*/tcflag_tc_cflag ;/*控制模式标志*/tcflag_tc_lflag ;/*本地模式标志*/tcflag_tc_line ;/*行规程类型,一般应用程序不使用*/cc_tc_ccNCC;/*控制字符*/speed_tc_ispeed ;/*输入

    4、数据波特率*/speed_tc_ospeed ;/*输出数据波特率*/;串口的设置主要是设置这个结构体的各成员值,然后利用该结构体将参数传给硬件驱动程序。在 Linux 中,串口以串行终端的方式进行处理,因而,可以使用 tcgetattr()/tcsetattr()函数获取/设置串口的参数。int tcgetattr( int fd, struct termios *termios_p );int tcsetattr( int fd, int optional_actions , struct termios *termios_p );这两个参数都有一个批向 termios 结构体的指针作为参

    5、数, 用于返回当前终端的属性或设置该终端的属性。 参数 fd 就是用 open()函数打开的终端文件句柄,而串口就是用 open()打开的串口设备文件句柄。tcsetattr()函数的 optional_action 参数用于指定新设定的参数起作用的时间,其设定值可以为:TCSANOW改变立即生效TCSADRAIN在所有的输出都被传输后改变生效,适用于更改影响输出参数的情况。TCSAFLUSH在所有输出都被传输后改变生效,丢弃所有末读入的输入(清空输入缓存)。(1)设置波特率使用 cfsetospeed()/cfsetispeed()函数设置波特率, 它们分别用于在 termios 结构体中设

    6、置输出和输入的波特率。 设置波特率可以使用波特率常数,其定义为字母“B速率”,如 B19200 就是波特率为 19200bps,B115200 就是波特率为 115200bps。int cfsetispeed( struct termios *termios_p, speed_t speed );/speed 为波特率常数int cfsetospeed( struct termios *termios_p, speed_t speed );例 :cfsetispeed( ttys0_opt, B115200 );cfsetospeed( ttys0_opt, B115200 );(2)设置控制

    7、模式标志控制模式标志 c_cflag 主要用于设置串口对 DCD 信号状态检测、 硬件流控制、 字符位宽、 停止位和奇偶校验等, 常用标志位如下:CLOCAL忽略 DCD 信号,若不使用 MODEM,或没有串口没有 CD 脚就设置此标志CREAD启用接收装置,可以接收字符CRTSCTS 启用硬件流控制,对于许多三线制的串不应使用,需设置CRTCTSCSIZE字符位数掩码,常用 CS8CSTOPB使用两个停止位,若用一位应设置CSTOPBPARENB启用奇偶校验例如, 下面的代码将串口设置为忽略 DCD 信号, 启用接收装置, 关闭硬件流控制, 传输数据时使用 8 位数据位和一位停止位 (8N1

    8、) ,不使用奇偶校验。struct temios ttys0ttyso_opt.c_cflag |= CLOCAL | CREAD ;/将 CLOCAL 与 CREAD 位设置为 1ttys0_opt.c_cflag &= CRTSCTS ;/将硬件流控制位 CRTSCTS 清 0,其他位不变ttys0_opt.c_cflag & CSIZE ;/清除数据位掩码ttys0_opt.c_cflag |= CS8 ;/设置 8 位数据位标志 CS8ttys0_opt.c_cflag &= (PARENB|CSTOPB);/使用 1 位停止位,停用奇偶校验(3)设置本地模式标志本地模式标志 c_lf

    9、lag 主要用于设置终端与用户的交互方式, 常见的设置标志位有 ICANON, ECHO 和 ECHOE 等。 其中, ICANON标志位用于实现规范输入,即 read()读到行结束符后返回,常用于终端的处理;若串口用于发送/接收数据,则应清除此标志,使用非规范模式(raw mode)。非规范模式中,输入数据不组成行,不处规范模式中的特殊字符。在规范模式中,当设置 ECHO 标志位时,用户向终端输入的字符将被回传给用户;当设置 ECHOE 标志位时,用户输入退格键时,则回传“退格空格退格”序列给用户,使得退格键覆盖的字符从显示中消失,这样更符合用户的习惯(若未设置此标志,输入退格键时,则光标回

    10、退一个字符,但原有的字符未从显示中消失)。(4)设置输入模式标志输入模式标志 c_iflag 主要用于控制串口的输入特性,常用的设置有 IXOFF 和 IXON,分别用于软件流控制。其中,IXOFF 用于防止输入缓冲区溢出;IXON 则是在输入数据中识别软件流控制标志。由于许多嵌入式系统无法使用硬件流控制,因此,只能使用软件流控制数据传输的速度,但是,它可能降低串口数据传输效率。启用软件流控制的代码如下:ttys0_opt.c_iflag |= IXOFF|IXON ;(5)设置输出模式标志输出模式标志 c_oflag 主要用于对串口在规范模式时输出的特殊字符处理,而对非规范模式无效。(6)设

    11、置控制字符在非规范模式中,控制字符数组 c_cc中的变量 c_ccVMIN和 c_ccVTIME用于设置 read()返回前读到的最少字节数和读超时时间,其值分为四种情况:(a)c_ccVMIN0,c_ccVTIME0读到一个字节后,启动定时器,其超时时间为 c_ccVTIME,read()返回的条件为至少读到 c_ccVMIN个字符或定时器超期。(b)c_ccVMIN0, c_ccVTIME =0只要读到数据的字节数大于等于 c_ccVMIN,则 read()返回;否则,将无限期阻塞等待。(c)c_ccVMIN = 0, c_ccVTIME0只要读到数据,则 read()返回;若定时器超期(

    12、定时时间 c_ccVTIME)却未读到数据,则 read()返回 0;(d)c_ccVMIN = 0, c_ccVTIME = 0若有数据,则 read()读取指定数量的数据后返回;若没有数据,则 read()返回 0;在 termios 结构体中填写完这些参数后,接下来就可以使用 tcsetattr()函数设置串口的属性。tcsetattr( fd, &old_opt );/将原有的设置保存到 old_opt,以便程序结束后恢复tcsetattr( fd, TCSANOW, &ttsy0_opt );3、清空发送/接收缓冲区为保证读/写操作不被串口缓冲区中原有的数据干拢,可以在读/写数据前用

    13、 tcflush()函数清空串口发送/接收缓冲区。tcflush()函数的参数可为:TCIFLUSH清空输入队列TCOFLUSH清空输出队列TCIOFLUSH同时清空输入和输出队列4、从串口读写数据串口的数据读/写与普通文件的读/写一样,都是使用 read()/write()函数实现。n = write( fd, buf, len );/将 buf 中 len 个字节的数据从串口输出,返回输出的字节数n = read( fd, buf, len );/从串口读入 len 个字节的数据并放入 buf, 返回读取的字节数5、关闭串口关闭串口的操作很简单,将打开的串口设备文件句柄关闭即可。close

    14、(fd);Linux 串口读写串口读写(二二)例子下面是一个简单的读取串口数据的例子,使用了上面定义的一些函数和头文件/* 代码说明:使用串口二测试的,发送的数据是字符,但是没有发送字符串结束符号,* 所以接收到后, 后面加上了结束符号。 我测试使用的是单片机发送数据到第二个串口, 测试通过。*/#define FALSE-1#define TRUE0/*/int OpenDev(char *Dev)/Dev 就是设备,设备就是文件,就是给出该设备文件的路径int fd = open(Dev, O_RDWR ); /| O_NOCTTY | O_NDELAYif (-1 = fd)perror

    15、(Cant Open Serial Port);return -1;elsereturn fd;int main(int argc, char *argv)int fd;int nread;char buff512;char *dev = /dev/ttyS1; /串口二fd = OpenDev(dev);set_speed(fd, 19200);if (set_Parity(fd, 8, 1, N) = FALSE)printf(Set Parity Errorn);exit (0);while (1) /循环读取数据while (nread = read(fd, buff, 512)0)p

    16、rintf(nLen %dn, nread);buffnread+1 = 0;printf(n%s, buff);/close(fd);/ exit (0);1、虚拟机下使用串口的方法使用 vmwave,默认串口设备是没有添加的,通过 vmwave 将设备加入即可正常使用串口。虚拟机串口打开后,可能会占用 windows 下的串口。另外,虚拟机的串口收发比正常的速度的确要慢许多。2、消除 Linux 串口收发的一些规则Linux 串口收发有许多模式,如:(1) 接收返回模式: 如果串口没有接收到数据,read()函数不返回。(2) 数据接收n 才返回接收的数据,否则 read()函数返回 0(

    17、3) 特殊字符解析问题,部分特殊字符接收/发送时,会被屏蔽或者转义。如发送 0 x0A 接收变为0 x0A 0 x0A ,0 x0D 被屏蔽等。(4) 接收反馈:如串口接收到数据,立即将该数据发送出去。3、解决问题的方法是,消除这些默认规则,关键是 struct termios 的参数影响。struct termiostcflag_t c_iflag;/*/* 输入模式旗标 */tcflag_t c_oflag;/*/* 输出模式旗标 */tcflag_t c_cflag;/*/* 控制模式旗标 */tcflag_t c_lflag;/*/* 区域模式旗标 */cc_t c_line;/*/*

    18、 行控制 (line discipline) */cc_t c_ccNCCS;/*/* 控制特性 */;可如下处理的struct termios options;串口打开方式:open (dev/ttyS0 , O_RDWR|O_NOCTTY| O_NDELAY );消除收发模式规则:options.c_lflag= 0;options.c_oflag= 0;options.c_iflag= 0;消除字符屏蔽规则:options.c_ccVINTR= 0;/*/* Ctrl-c */options.c_ccVQUIT= 0;/*/* Ctrl- */options.c_ccVERASE= 0;

    19、/*/* del */options.c_ccVKILL= 0;/*/* */options.c_ccVEOF= 0;/*/* Ctrl-d */options.c_ccVTIME= 1;/*/*/options.c_ccVMIN= 0;/*/*/options.c_ccVSWTC= 0;/*/* */options.c_ccVSTART= 0;/*/* Ctrl-q */options.c_ccVSTOP= 0;/*/* Ctrl-s */options.c_ccVSUSP= 0;/*/* Ctrl-z */options.c_ccVEOL= 0;/*/* */options.c_ccVRE

    20、PRINT = 0;/*/* Ctrl-r */options.c_ccVDISCARD = 0;/*/* Ctrl-u */options.c_ccVWERASE= 0;/*/* Ctrl-w */options.c_ccVLNEXT= 0;/*/* Ctrl-v */options.c_ccVEOL2= 0;/*/* */以上设置,在其它参数串口设置前执行,如果你需要保留部分参数,请参阅= = = = = = = = = = = 非阻塞 read= = = = = = = = = = =Q:在调用串口 read(fd,buff,len);时,如果串口没有数据,会停在 read 处,请问有没

    21、有办法让这个read 动作中止?A:使用非阻塞方式 select 函数(I/O 多工机制)或者 open 的时候加 O_NONBLOCK 参数。int select(int n,fd_set * readfds,fd_set * writefds,fd_set *exceptfds,struct timeval * timeout);关于这个函数的使用我会在下篇 blog 中整理。= = = = = = = = = = = 串口收发源码= = = = = = = = = = = = = = = = receive.c= = = = = =#include#include#include#inc

    22、lude#include#include#include#include#include#define TRUE 1/初始化串口选项:void setTermios(struct termios * pNewtio, int uBaudRate)bzero(pNewtio, sizeof(struct termios); /* clear struct for new portsettings */8N1pNewtio-c_cflag = uBaudRate | CS8 | CREAD | CLOCAL;pNewtio-c_iflag = IGNPAR;pNewtio-c_oflag = 0;

    23、pNewtio-c_lflag = 0; /non ICANON/*initialize all control charactersdefault values can be found in /usr/include/termios.h, andare given in the comments, but we dont need them here*/pNewtio-c_ccVINTR = 0; /* Ctrl-c */pNewtio-c_ccVQUIT = 0; /* Ctrl- */pNewtio-c_ccVERASE = 0; /* del */pNewtio-c_ccVKILL

    24、= 0; /* */pNewtio-c_ccVEOF = 4; /* Ctrl-d */pNewtio-c_ccVTIME = 5; /* inter-character timer, timeout VTIME*0.1*/pNewtio-c_ccVMIN = 0; /* blocking read until VMIN character arrives*/pNewtio-c_ccVSWTC = 0; /* 0 */pNewtio-c_ccVSTART = 0; /* Ctrl-q */pNewtio-c_ccVSTOP = 0; /* Ctrl-s */pNewtio-c_ccVSUSP

    25、= 0; /* Ctrl-z */pNewtio-c_ccVEOL = 0; /* 0 */pNewtio-c_ccVREPRINT = 0; /* Ctrl-r */pNewtio-c_ccVDISCARD = 0; /* Ctrl-u */pNewtio-c_ccVWERASE = 0; /* Ctrl-w */pNewtio-c_ccVLNEXT = 0; /* Ctrl-v */pNewtio-c_ccVEOL2 = 0; /* 0 */#define BUFSIZE 512int main(int argc, char *argv)int fd;int nread;char buff

    26、BUFSIZE;struct termios oldtio, newtio;struct timeval tv;char *dev =/dev/ttyS1;fd_set rfds;if (fd = open(dev, O_RDWR | O_NOCTTY)0)if (FD_ISSET(fd, &rfds)nread=read(fd, buff, BUFSIZE);printf(readlength=%dn, nread);buffnread=0;printf(%sn, buff);tcsetattr(fd, TCSANOW, &oldtio);close(fd);= = = = = send.c

    27、= = = = = =#include#include#include#include#include#include#include#include#include/初始化串口选项:void setTermios(struct termios * pNewtio, int uBaudRate)bzero(pNewtio, sizeof(struct termios); /* clear struct for new portsettings */8N1pNewtio-c_cflag = uBaudRate | CS8 | CREAD | CLOCAL;pNewtio-c_iflag = IG

    28、NPAR;pNewtio-c_oflag = 0;pNewtio-c_lflag = 0; /non ICANON/*initialize all control charactersdefault values can be found in /usr/include/termios.h, andare given in the comments, but we dont need them here*/pNewtio-c_ccVINTR = 0; /* Ctrl-c */pNewtio-c_ccVQUIT = 0; /* Ctrl- */pNewtio-c_ccVERASE = 0; /*

    29、 del */pNewtio-c_ccVKILL = 0; /* */pNewtio-c_ccVEOF = 4; /* Ctrl-d */pNewtio-c_ccVTIME = 5; /* inter-character timer, timeout VTIME*0.1*/pNewtio-c_ccVMIN = 0; /* blocking read until VMIN character arrives*/pNewtio-c_ccVSWTC = 0; /* 0 */pNewtio-c_ccVSTART = 0; /* Ctrl-q */pNewtio-c_ccVSTOP = 0; /* Ct

    30、rl-s */pNewtio-c_ccVSUSP = 0; /* Ctrl-z */pNewtio-c_ccVEOL = 0; /* 0 */pNewtio-c_ccVREPRINT = 0; /* Ctrl-r */pNewtio-c_ccVDISCARD = 0; /* Ctrl-u */pNewtio-c_ccVWERASE = 0; /* Ctrl-w */pNewtio-c_ccVLNEXT = 0; /* Ctrl-v */pNewtio-c_ccVEOL2 = 0; /* 0 */int main(int argc, char *argv)int fd;int nCount, n

    31、Total, i;struct termios oldtio, newtio;char *dev =/dev/ttyS1;if (argc!=3) | (sscanf(argv1, %d, &nTotal) != 1)printf(err: need tow arg!n);return -1;if (fd = open(dev, O_RDWR | O_NOCTTY)0)printf(err: cant open serial port!n);return -1;tcgetattr(fd, &oldtio); /* save current serial port settings */setT

    32、ermios(&newtio, B115200);tcflush(fd, TCIFLUSH);tcsetattr(fd, TCSANOW, &newtio);for (i=0; inTotal; i+)nCount=write(fd, argv2, strlen(argv2);printf(send datan);sleep(1);tcsetattr(fd, TCSANOW, &oldtio);close(fd);return 0;= = = = = =.makefile= = = = = =CC = /usr/local/arm/2.95.3/bin/arm-linux-gccall:rec

    33、eive sendreceive: receive.c$(CC) receive.c -oreceivesend: send.c$(CC) send.c -osendclean:-rm -rf testCOM receive send到此基本就结束了,可能代码注释比较少些,写的太着急了,等有时间整理一下。最好再看看上一篇 blog 这样能更好的理解串口。#include#include#include#define STDIN 0 /* file descriptor for standard input */main()struct timeval tv;fd_set readfds;tv.

    34、tv_sec = 2;tv.tv_usec = 500000;FD_ZERO(&readfds);FD_SET(STDIN,&readfds);/* don&apost care about writefds and exceptfds: */select(STDIN+1,&readfds, NULL, NULL,&tv);if (FD_ISSET(STDIN,&readfds)printf(Akey was pressed!n);elseprintf(Timed out.n);如果你是在一个 line buffered 终端上,那么你敲的键应该是回车 (RETURN),否则无论如何它都会超时

    35、。现在,你可能回认为这就是在数据报套接字上等待数据的方式-你是对 的:它可能是。有些 Unix 系统可以按这种方式,而另外一些则不能。你 在尝试以前可能要先看看本系统的 man page 了。最后一件关于 select() 的事情:如果你有一个正在侦听 (listen() 的套 接字,你可以通过将该套接字的文件描述符加入到 readfds 集合中来看是 否有新的连接。这就是我关于函数 select() 要讲的所有的东西。http:/ UNIX 编程:一个简单聊天室的两种实现 (fcntl 和 select)-http:/ 2004-12-3 互联网在互联网相当普及的今天,在互联网上聊天对很多“

    36、网虫”来说已经是家常便饭了。聊天室程序可以说是网上最简单的多点通信程序。聊天室的实现方法有很多,但都是利用所谓的“多用户空间”来对信息进行交换,具有典型的多路 I/O 的架构。一个简单的聊天室, 从程序员的观点来看就是在多个 I/O 端点之间实现多对多的通信。其架构如图一所示。这样的实现在用户的眼里就是聊天室内任何一个人输入一段字符之后,其他用户都可以得到这一句话。这种“多用户空间”的架构在其他多点通信程序中应用的非常广泛,其核心就是多路 I/O通信。多路 I/O 通信又被称为 I/O 多路复用(I/O Multiplexing)一般被使用在以下的场合:客户程序需要同时处理交互式的输入和同服务

    37、器之间的网络连接时需要处理 I/O 多路复用问题;客户端需要同时对多个网络连接作出反应(这种情况很少见) ;TCP 服务器需要同时处理处于监听状态和多个连接状态的 socket;服务器需要处理多个网络协议的 socket;服务器需要同时处理不同的网络服务和协议。聊天室所需要面对的情况正是第一和第三两种情况。我们将通过在 TCP/IP 协议之上建立一个功能简单的聊天室让大家更加了解多路 I/O 以及它的实现方法。 我们要讨论的聊天室功能非常简单, 感兴趣的朋友可以将其功能扩展, 发展成一个功能比较完整的聊天室, 如加上用户认证, 用户昵称, 秘密信息, semote 等功能. 首先它是一个 cl

    38、ient/server 结构的程序, 首先启动 server, 然后用户使用 client 进行连接. client/server 结构的优点是速度快, 缺点是当 server 进行更新时, client 也必需更新.网络初始化首先是初始化 server, 使 server 进入监听状态: (为了简洁起见,以下引用的程序与实际程序略有出入, 下同)sockfd = socket( AF_INET,SOCK_STREAM, 0);/ 首先建立一个 socket, 族为 AF_INET, 类型为 SOCK_STREAM./AF_INET = ARPAInternet protocols 即使用 T

    39、CP/IP 协议族/ SOCK_STREAM 类型提供了顺序的, 可靠的, 基于字节流的全双工连接./ 由于该协议族中只有一个协议, 因此第三个参数为 0bind( sockfd, ( struct sockaddr *)&serv_addr, sizeof( serv_addr);/ 再将这个 socket 与某个地址进行绑定./ serv_addr 包括 sin_family =AF_INET 协议族同 socket/ sin_addr.s_addr = htonl( INADDR_ANY) server 所接受的所有其他/ 地址请求建立的连接./ sin_port = htons( SE

    40、RV_TCP_PORT) server 所监听的端口/ 在本程序中, server 的 IP 和监听的端口都存放在 config 文件中.listen( sockfd, MAX_CLIENT);/ 地址绑定之后, server 进入监听状态./ MAX_CLIENT 是可以同时建立连接的 client 总数.server 进入 listen 状态后, 等待 client 建立连接。Client 端要建立连接首先也需要初始化连接:sockfd = socket( AF_INET,SOCK_STREAM,0);/ 同样的, client 也先建立一个 socket, 其参数与 server 相同.

    41、connect( sockfd, ( struct sockaddr *)&serv_addr, sizeof( serv_addr);/ client 使用 connect 建立一个连接./ serv_addr 中的变量分别设置为:/ sin_family = AF_INET 协议族同 socket/ sin_addr.s_addr = inet_addr( SERV_HOST_ADDR) 地址为 server/ 所在的计算机的地址./ sin_port = htons( SERV_TCP_PORT) 端口为 server 监听的端口.当 client 建立新连接的请求被送到 Server

    42、端时, server 使用 accept 来接受该连接:accept( sockfd, (struct sockaddr*)&cli_addr,&cli_len);/ 在函数返回时, cli_addr 中保留的是该连接对方的信息/ 包括对方的 IP 地址和对方使用的端口./ accept 返回一个新的文件描述符.在 server 进入 listen 状态之后, 由于已有多个用户在线,所以程序需要同时对这些用户进行操作,并在它们之间实现信息交换。这在实现上称为I/O 多路复用技术。多路复用一般有以下几种方法:非阻塞通信方法:将文件管道通过 fcntl()设为非阻塞通信方式,每隔一端时间对他们实行

    43、一次轮询,以判断是否可以进行读写操作。这种方式的缺点是费用太高,大部分资源浪费在轮询上。子进程方法:应用多个子进程,每一个对一个单工阻塞方式通信。所有子进程通过 IPC 和父进程进行通信。父进程掌管所有信息。这种方式的缺点是实现复杂,而且由于 IPC 在各个操作系统平台上并不完全一致,会导致可移植性降低。信号驱动(SIGIO)的异步 I/O 方法:首先,异步 I/O 是基于信号机制的,并不可靠。其次单一的信号不足以提供更多的信息来源。还是需要辅助以其他的手段,实现上有很高的难度。select ()方法:在 BSD 中提供了一种可以对多路 I/O 进行阻塞式查询的方法select()。它提供同时

    44、对多个 I/O 描述符进行阻塞式查询的方法,利用它,我们可以很方便的实现多路复用。根据统一 UNIX 规范的协议,POSIX 也采用了这种方法,因此,我们可以在大多数操作系统中使用 select 方法。使用专门的 I/O 多路复用器:在“UNIX? SYSTEM V Programmer&aposs Guide: STREAMS”一书中详细的说明了构造和使用多路复用器的方法。这里就不再详述了。我们下面分别讨论多路 I/O 的两种实现方法:1. 非阻塞通信方法对一个文件描述符指定的文件或设备, 有两种工作方式: 阻塞与非阻塞。所谓阻塞方式的意思是指, 当试图对该文件描述符进行读写时, 如果当时没

    45、有东西可读,或者暂时不可写, 程序就进入等待状态, 直到有东西可读或者可写为止。而对于非阻塞状态, 如果没有东西可读, 或者不可写, 读写函数马上返回, 而不会等待。 缺省情况下, 文件描述符处于阻塞状态。 在实现聊天室时, server 需要轮流查询与各 client 建立的 socket, 一旦可读就将该 socket 中的字符读出来并向所有其他 client 发送。并且, server 还要随时查看是否有新的 client 试图建立连接,这样, 如果 server 在任何一个地方阻塞了, 其他client 发送的内容就会受到影响,得不到服务器的及时响应。新 client 试图建立连接也会

    46、受到影响。所以我们在这里不能使用缺省的阻塞的文件工作方式,而需要将文件的工作方式变成非阻塞方式。在 UNIX 下,函数 fcntl()可以用来改变文件 I/O 操作的工作方式,函数描述如下:fcntl( sockfd, F_SETFL, O_NONBLOCK);/ sockfd 是要改变状态的文件描述符./ F_SETFL 表明要改变文件描述符的状态/ O_NONBLOCK 表示将文件描述符变为非阻塞的.为了节省篇幅我们使用自然语言描述聊天室 server :while ( 1)if 有新连接 then 建立并记录该新连接;for ( 所有的有效连接)beginif 该连接中有字符可读 the

    47、nbegin读入字符串;for ( 所有其他的有效连接)begin将该字符串发送给该连接;end;end;end;end.由于判断是否有新连接, 是否可读都是非阻塞的, 因此每次判断,不管有还是没有, 都会马上返回. 这样,任何一个 client 向 server 发送字符或者试图建立新连接, 都不会对其他 client 的活动造成影响。对 client 而言, 建立连接之后, 只需要处理两个文件描述符, 一个是建立了连接的 socket 描述符, 另一个是标准输入. 和 server 一样, 如果使用阻塞方式的话, 很容易因为其中一个暂时没有输入而影响另外一个的读入. 因此将它们都变成非阻塞

    48、的, 然后 client 进行如下动作:while ( 不想退出)beginif ( 与 server 的连接有字符可读)begin从该连接读入, 并输出到标准输出上去.End;if ( 标准输入可读)Begin从标准输入读入, 并输出到与 server 的连接中去.End;End.上面的读写分别调用这样两个函数:read( userfdi, line, MAX_LINE);/ userfdi 是指第 i 个 client 连接的文件描述符./ line 是指读出的字符存放的位置./ MAX_LINE 是一次最多读出的字符数./ 返回值是实际读出的字符数.write( userfdj, lin

    49、e, strlen( line);/ userfdj 是第 j 个 client 的文件描述符./ line 是要发送的字符串./ strlen( line) 是要发送的字符串长度.分析上面的程序可以知道, 不管是 server 还是 client, 它们都不停的轮流查询各个文件描述符, 一旦可读就读入并进行处理. 这样的程序, 不停的在执行,只要有 CPU 资源, 就不会放过。因此对系统资源的消耗非常大。server 或者 client 单独执行时, CPU 资源的 98% 左右都被其占用。极大的消耗了系统资源。select 方法因此,虽然我们不希望在某一个用户没有反应时阻塞其他的用户,但我

    50、们却应该在没有任何用户有反应的情况之下停止程序的运行,让出抢占的系统资源,进入阻塞状态。有没有这种方法呢?现在的 UNIX 系统中都提供了 select 方法,具体实现方式如下:select 方法中, 所有文件描述符都是阻塞的. 使用 select 判断一组文件描述符中是否有一个可读(写), 如果没有就阻塞, 直到有一个的时候就被唤醒. 我们先看比较简单的 client 的实现:由于 client 只需要处理两个文件描述符, 因此, 需要判断是否有可读写的文件描述符只需要加入两项:FD_ZERO( sockset);/ 将 sockset 清空FD_SET( sockfd, sockset);

    展开阅读全文
    提示  163文库所有资源均是用户自行上传分享,仅供网友学习交流,未经上传用户书面授权,请勿作他用。
    关于本文
    本文标题:嵌入式系统原理与技术资料:linux串口编程-1.doc
    链接地址:https://www.163wenku.com/p-2047671.html

    Copyright@ 2017-2037 Www.163WenKu.Com  网站版权所有  |  资源地图   
    IPC备案号:蜀ICP备2021032737号  | 川公网安备 51099002000191号


    侵权投诉QQ:3464097650  资料上传QQ:3464097650
       


    【声明】本站为“文档C2C交易模式”,即用户上传的文档直接卖给(下载)用户,本站只是网络空间服务平台,本站所有原创文档下载所得归上传人所有,如您发现上传作品侵犯了您的版权,请立刻联系我们并提供证据,我们将在3个工作日内予以改正。

    163文库