嵌入式Linux 高级阶段笔记.docx
《嵌入式Linux 高级阶段笔记.docx》由会员分享,可在线阅读,更多相关《嵌入式Linux 高级阶段笔记.docx(55页珍藏版)》请在冰豆网上搜索。
嵌入式Linux高级阶段笔记
LinuxC编程学习
1.C++list使用
list初始化,并定义一个链表。
listlist_student;
StructStudent_st*pStu=(StructStudent_st*)malloc(sizeof(StructStudent_st));
pStu->no=100;
list_student.push_back(pStu);
链表中获取一个结构体对象。
list:
:
itegratorit=list_student.begin();
StructStudent_st*pTmp=*it;//注意it存储的是指针的指针,*it才是对象指针
链表排序。
sort()和sort(compcompFun)–不建议使用排序
默认的sort()仅仅进行值比较,及<、>或者=。
sort(Comparecomp)采取的是把比较函数传入。
2.C++Hash_Map使用
引入库:
#include
及命名空间:
usingnamespace__gnu_cxx;
HashMap不能够储存重复值。
定义结构类型:
hash_map_clientMap;
添加记录:
_clientMap.insert(pair(psamNo,psam));
--注意:
必须以键值对的方式添加。
返回值是一个键值对,存储了添加是否成功标识。
pairinsert(constvalue_type&x);
例子:
ret=mymap.insert(pair(‘z’,500));
if(ret.second==true)
printf(“OK\n”);
删除记录:
可以有下列方式删除,通过位置、起始位置到终止位置及键值。
voiderase(iteratorpos);
voiderase(iteratorstart,iteratorend);
size_typeerase(constKEY_TYPE&key);
size_type删除元素个数
清除记录:
删除所有记录。
voidclear();
查询记录:
注意若没有查询到返回指向Map尾部的迭代器。
iteratorfind(constKEY_TYPE&key);
遍历记录:
使用迭代器访问每一个元素。
it->firstit->second获取key和value
P_PSAMINFOpsam=NULL;
hash_map:
:
iteratorit=_clientMap.begin();
while(it!
=_clientMap.end()){
psam=it->second;
printf("psamcode=%s,psamsocket=%d\n",psam->psamcode,psam->getFD());
it++;
free(psam);
}
3.static_cast使用
将一个值以合逻辑的方式转换。
这可看作是【利用原值重建一个临时物件,并在设立初值时使用类型转换】。
意味着有使用临时对象。
例子:
floatx=100.1;
cout<(x);//printxasint
f(static_cast("Hello"));//callf()forstringinsteadofchar*
4.dynamic_cast使用
将类型向下转换为其实际类型。
这是唯一在执行期进行检验的转换动作。
你可以用它来检验某个类型,例如:
5.socket函数
5.1socket函数
intsocket(intdomain,inttype,intprotocol);
参数1:
windows下目前仅支持AF_INET格式,也就是说ARPAInternet地址格式。
linux下支持PF_INET和AF_INET.
参数2:
新套接口的类型描述。
SOCK_STREAM(TCP模式) SOCK_DGRAM(UDP模式)SOCK_RAW(原始套接字)
参数3:
protocol:
特殊用途,默认用缺省值0
返回值:
成功则返回新的socket文件描述,失败则返回-1
例子:
intsocket_fd=socket(AF_INET,SOCK_STREAM,0);
5.2bind函数
intbind(intsockfd,structsockaddr*addr,socklen_taddrlen);
参数sockfd
指定地址与哪个套接字绑定,这是一个由之前的socket函数调用返回的套接字。
调用bind的函数之后,该套接字与一个相应的地址关联,发送到这个地址的数据可以通过这个套接字来读取与使用。
参数addr
指定地址。
这是一个地址结构,并且是一个已经经过填写的有效的地址结构。
调用bind之后这个地址与参数sockfd指定的套接字关联,从而实现上面所说的效果。
//旧的结构体
structsockaddr{
u_shortsa_family;
charsa_data[14];
};
//新的结构体
structsockaddr_in{
shortintsin_family;/*Addressfamily*/
unsignedshortintsin_port;/*Portnumber*/
structin_addrsin_addr;/*Internetaddress*/
unsignedcharsin_zero[8];/*Samesizeasstructsockaddr*/
};
sin_family指代协议族,在socket编程中只能是AF_INET
sin_port存储端口号(使用网络字节顺序)
sin_addr存储IP地址,使用in_addr这个数据结构
sin_zero是为了让sockaddr与sockaddr_in两个数据结构保持大小相同而保留的空字节。
s_addr按照网络字节顺序存储IP地址
sockaddr_in和sockaddr是并列的结构,指向sockaddr_in的结构体的指针也可以指向
sockaddr的结构体,并代替它。
也就是说,你可以使用sockaddr_in建立你所需要的信息,然后用进行类型转换就可以了
参数addrlen
正如大多数socket接口一样,内核不关心地址结构,当它复制或传递地址给驱动的时候,它依据这个值来确定需要复制多少数据。
这已经成为socket接口中最常见的参数之一了。
返回值:
成功返回0,否则失败
例子:
structsockaddr_inadr_server;
memset(&adr_server,0x00,sizeof(structsockaddr_in));
adr_server.sin_family=AF_INET;//通讯协议
adr_server.sin_port=htons(8891);//端口
adr_server.sin_addr.s_addr=htonl(INADDR_ANY);//本机IP
intz=bind(socket_fd,(structsockaddr*)&adr_server,sizeof(adr_server));
if(z!
=0)printf("funbind:
error...\n");
5.3listen函数
intlisten(intsockfd,intbacklog);
参数socketfd:
需要监听的套接字值
参数backlog:
值为需要维护的最大连接缓冲个数。
一般为5个
返回值:
成功返回0,否则失败
5.4accept函数
intaccept(intsockfd,structsockaddr*addr,socklen_t*addrlen);
参数sockfd:
服务端套接字
参数addr:
客户端地址存储空间,一般采用structsockaddr_in类型,然后强转成参数类型。
参数sockfd:
存放结构体addr的长度,可以定义变量intclient_len
返回值:
客户端套接字
注意:
accpt用于阻塞模式Socket服务,当有连接上上来时返回客户端套接字,若无连接时,等待连接。
参数中如果addr与addrlen中有一个为零NULL,将不返回所接受的套接口远程地址的任何信息。
例子:
intsocket_client=accept(socket_fd,(structsockaddr*)&adr_client,&client_len);
5.5read/write函数
read和write函数是对文件的操作。
跟文件操作一样处理。
例如:
intrSize=read(client_fd,ch,512);
intwSize=write(client_fd,ch1,sizeof(ch1));
5.6、setsockopt函数
6.select模型
select模型是非阻塞模式。
#include
intselect(intmaxfd,fd_set*readset,fd_set*writeset,fd_set*exceptset,conststructtimeval*timeout);
参数maxfd:
最大描述符+1
参数readset:
回传描述符读
参数writeset:
回传描述符写
参数exceptset:
回传描述符异常
参数timeout:
超时设置
structtimeval
{
time_ttv_sec;
time_ttv_usec;
};
有三种情况,
a、永远等待,超时参数设置为NULL
b、等待一定时间,时间自己设定
c、不等待,超时设置为0。
返回值:
错误-1、超时0、执行成功返回变动描述符个数
对描述符的处理(宏):
FD_CLR(inrfd,fd_set*set); //用来清除描述词组set中相关fd的位
FD_ISSET(intfd,fd_set*set);//用来测试描述词组set中相关fd的位是否为真
FD_SET(intfd,fd_set*set); //用来设置描述词组set中相关fd的位
FD_ZERO(fd_set*set); //用来清除描述词组set的全部位
备注:
select函数主要用于非阻塞式文件描述符监听,可以统一管理多个事件的情况。
例子:
while
(1)
{
FD_ZERO(&readfd);//初始化set
FD_SET(0,&readfd);//每次轮询都需要添加到readFd中
outTime.tv_sec=5;
outTime.tv_usec=0;//设置等待时间,必须在每次循环中设置
rlt=select(0+1,&readfd,NULL,NULL,&outTime);
if(rlt<0)
{
printf("error...\n");
break;
}
elseif(rlt==0)
{
printf("outofftime...\n");
continue;
}elseif(rlt>0)
{
printf("发送变化的句柄个数:
%d\n",rlt);
if(FD_ISSET(0,&readfd))
{
read(0,recvbuf,50);
write(1,recvbuf,strlen(recvbuf));//直接写入到标准输出屏幕上
}
}
}
7.epoll模型
epoll用到的所有函数都是在头文件sys/epoll.h中声明的,下面简要说明所用到的数据结构和函数:
所用到的数据结构
typedefunionepoll_data{
void*ptr;
intfd;//发生事件的文件描述符
__uint32_tu32;
__uint64_tu64;
}epoll_data_t;
structepoll_event{
__uint32_tevents;/*Epollevents*/
epoll_data_tdata;/*Userdatavariable*/
};
结构体epoll_event被用于注册所感兴趣的事件和回传所发生待处理的事件,其中epoll_data联合体用来保存触发事件的某个文件描述符相关的数据,例如一个client连接到服务器,服务器通过调用accept函数可以得到于这个client对应的socket文件描述符,可以把这文件描述符赋给epoll_data的fd字段以便后面的读写操作在这个文件描述符上进行。
epoll_event结构体的events字段是表示感兴趣的事件和被触发的事件可能的取值为:
EPOLLIN:
表示对应的文件描述符可以读;
EPOLLOUT:
表示对应的文件描述符可以写;
EPOLLPRI:
表示对应的文件描述符有紧急的数据可读(我不太明白是什么意思,可能是类似client关闭socket连接这样的事件);
EPOLLERR:
表示对应的文件描述符发生错误;
EPOLLHUP:
表示对应的文件描述符被挂断;
EPOLLET:
表示对应的文件描述符有事件发生;
所用到的函数:
1、epoll_create函数
函数声明:
intepoll_create(intsize)
该函数生成一个epoll专用的文件描述符。
它其实是在内核申请一空间,用来存放你想关注的socketfd上是否发生以及发生了什么事件。
size就是你在这个epollfd上能关注的最大socketfd数。
随你定好了。
只要你有空间。
2、epoll_ctl函数
函数声明:
intepoll_ctl(intepfd,intop,intfd,structepoll_event*event)
该函数用于控制某个文件描述符上的事件,可以注册事件,修改事件,删除事件。
参数:
epfd:
由epoll_create生成的epoll专用的文件描述符;
op:
要进行的操作例如注册事件,可能的取值EPOLL_CTL_ADD注册、EPOLL_CTL_MOD修
改、EPOLL_CTL_DEL删除
fd:
关联的文件描述符;
event:
指向epoll_event的指针;
如果调用成功返回0,不成功返回-1
structepoll_eventev;
//设置与要处理的事件相关的文件描述符
ev.data.fd=listenfd;
//设置要处理的事件类型
ev.events=EPOLLIN|EPOLLET;
//注册epoll事件
epoll_ctl(epfd,EPOLL_CTL_ADD,listenfd,&ev);
常用的事件类型:
EPOLLIN:
表示对应的文件描述符可以读;
EPOLLOUT:
表示对应的文件描述符可以写;
EPOLLPRI:
表示对应的文件描述符有紧急的数据可读
EPOLLERR:
表示对应的文件描述符发生错误;
EPOLLHUP:
表示对应的文件描述符被挂断;
EPOLLET:
表示对应的文件描述符有事件发生;
3、epoll_wait函数
函数声明:
intepoll_wait(intepfd,structepoll_event*events,intmaxevents,inttimeout)
该函数用于轮询I/O事件的发生;
参数:
epfd:
由epoll_create生成的epoll专用的文件描述符;
epoll_event:
用于回传代处理事件的数组;
maxevents:
每次能处理的事件数;
timeout:
等待I/O事件发生的超时值(毫秒);-1相当于阻塞,0相当于非阻塞。
一般用-1即可
返回发生事件数
唯一有点麻烦是epoll有2种工作方式:
LT和ET。
LT(leveltriggered)是缺省的工作方式,并且同时支持block和no-blocksocket.在这种做法中,内核告诉你一个文件描述符是否就绪了,然后你可以对这个就绪的fd进行IO操作。
如果你不作任何操作,内核还是会继续通知你的,所以,这种模式编程出错误可能性要小一点。
传统的select/poll都是这种模型的代表.
ET(edge-triggered)是高速工作方式,只支持no-blocksocket。
在这种模式下,当描述符从未就绪变为就绪时,内核通过epoll告诉你。
然后它会假设你知道文件描述符已经就绪,并且不会再为那个文件描述符发送更多的就绪通知,直到你做了某些操作导致那个文件描述符不再为就绪状态了(比如,你在发送,接收或者接收请求,或者发送接收的数据少于一定量时导致了一个EWOULDBLOCK错误)。
但是请注意,如果一直不对这个fd作IO操作(从而导致它再次变成未就绪),内核不会发送更多的通知(onlyonce),不过在TCP协议中,ET模式的加速效用仍需要更多的benchmark确认。
4.close(epollFd);
8.进程间通讯
进程间通讯可以采用SystemvIPC、POSIXIPC和BSDSocket方式,其中BSDSocket是基于socket方式。
socket不仅支持进程间通讯,也支持网络通信。
SystemVIPC接口是AT&T的贝尔实验室发展起来的,其通讯起到包括:
管道、FIFO、消息队列、信号灯、共享内存。
POSIX标准非常广泛,POSIXIPC标准用于进程间通信。
接口包括:
消息队列、信号灯、共享内存。
先对SystemV进程间通讯的资料做一下介绍。
Systemv进程间通讯的参数信息,请查看mansvipc7帮助文档。
下面对重要信息的概述:
简写:
svipc意思:
SystemVinterprocesscommunicationmechanisms
相关的参考头文件为:
#include
#include
#include
#include
#include
在进程通讯中创建各种机制,比如信号灯、管道、FIFO、内存共享或消息时,要求设置读写权限,即mode参数。
此参数与open函数中的mode相识。
可以采用下面方式标识它:
0400Readbyuser.
0200Writebyuser.
0040Readbygroup.
0020Writebygroup.
0004Readbyothers.
0002Writebyothers.
一般采取0666的方式。
除了权限参数外,需要或上相应的操作类型,如下:
IPC_CREATCreateentryifkeydoesn’texist.
IPC_EXCLFailifkeyexists.
IPC_NOWAITErrorifrequestmustwait.
IPC_PRIVATEPrivatekey.
IPC_RMIDRemoveresource.
IPC_SETSetresourceoptions.
IPC_STATGetresourceoptions.
比如:
消息队列的创建intmsgId=msgget((key_t)1234,0666|IPC_CREAT);
SystemV方式进程间通信的函数都采用下列格式:
创建通讯方式方式:
xxxget();
设置或删除通讯方式:
xxxctl();
接下介绍POSIXIPC的通讯方式,POSIX(PortalbleOperatingSystemInterface)已经得到Unix/Linux系统的广泛支持。
接口格式为:
intxxx_open();
intxxx_close();
intxxx_unlink();
8.1内存共享
内存共享有SystemV和POSIX两种方式。
内存共享仅仅是设置了数据的共享,数据的一致性需要通过信号灯或者其他方式实现同步。
下面简单介绍SystemV的方式。
#include
#include
intshmget(key_tkey,size_tsize,intshmflg);
功能:
创建内存共享。
参数key:
整型类型相似,用于标识共享内存名字。
比如(key_t)1234
参数size:
设置共享内存区域的大小,必须是系统页的整数倍。
通过intgetpagesize(void)函数获取系统页大小。
linux下一般是4K。
参数shmflg:
通过特定常量的按位或操作来。
IPC_CREAT:
这个标志表示应创建一个新的共享内存块。
通过指定这个标志,我们可以创建一个具有指定键值的新共享内存块。
IPC_EXCL:
这个标志只能与IPC_CREAT同时使用。
当指定这个标志的时候,如果已有一个具有这个键值的共享内存块存在,则shmget会调用失败。
也就是说,这个标志将使线程获得一个“独有”的共享内存块。
如果没有指定这个标志而系统中存在一个具有相同键值的共享内存块,shmget会返回这个已经建立的共享内存块,而不是重新创建一个。
标志:
这个值由9个位组成,分别表示属主、属组和其它用户对该内存块的访问权限。
其中表示执行权限的位将被忽略。
指明访问权限的一个简单办法是利用中指定,并且在手册页第二节stat条目中说明了的常量指定。
例如,S_IRUSR和S_IWUSR分别指定了该内存块属主的读写权限,而S_IROTH和S_IWOTH则指定了其它用户的读写权限。
下面例子中shmget函数创建了一个新的共享内存块(当shm_key已被占用时则获取对一个已经存在共享内存块的访问),且只有属主对该内存块具有读写权限,其它用户不可读写。
intsegment_id=shmget(shm_key,getpagesize(),IPC_CREAT|S_IRUSR|S_IWUSR);如果调用成功,shmget将返回一个共享内存标识符。
如果该共享内存块已经存在,系统会检查访问权限,同时会检查该内存块是否被标记为等待摧毁状态
void*shmat(intshmid,constvoid*shmaddr,