Toggle navigation
首页
问答
文章
积分商城
专家
专区
更多专区...
文档中心
返回主站
搜索
提问
会员
中心
登录
注册
ART-Pi
ART-Pi Smart
RIL(Radio Interface Layer)
[ART smart 开发板] 移植RIL到ART-Smart(二)共享内存
发布于 2022-05-15 11:52:28 浏览:751
订阅该版
[tocm] # 背景简介 本次移植的linux ril中,rild作为一个client与modem通过AT命令交互,同时也作为一个server与应用通信。在`/libd/ril.c`中,已经将各个服务封装为C接口,应用可以```include
```调用接口实现各个功能,无需关心其与rild直接的通信部分。 ```C int Dial(char *pCallingNumber); int Answer(); int HangUp(); int SendDTMF(char cDtmf); int SendSMS(char *pNumber, char *pMessage, int nType); int SetSpeakerVolume(int nVol); ... int GetLastError(void); int GetModemVolume(void); int GetModemMicGain(void); int GetLTEBands(void); int GetWCDMABands(void); int GetNWScanMode(void); ``` 这些接口与rild直接的通信主要通过两种方式,一种是在应用启动初期,通过一个固定的key拿到一块共享内存,并将其映射到自己的虚拟内存空间。这块共享内存主要包含一些modem的基本信息、rild版本号以及接下来消息队列通信中用到的mqID。而之后,应用的接口调用和主动上报监听则主要通过消息队列的机制传递。 这里有一个比较有意思的操作,在client端,共享内存的加载不是在main中,而是位于```void _attach()```函数,这个attach函数在程序中并没有显式调用,而是进行了如下定义: ```c static void _attach() __attribute__((constructor)); ``` 经过查阅资料,这是gcc对C语言的一种扩展,constructor就是要先于main函数被调用,而对应的destructor这是要在main函数结束或者exit的时候执行。 rild共享内存中存放的数据结构如下: ```C typedef struct{ int agentPID; int agentQue; // for client -> server int tcpipQue; char strAgentVer[MAX_VERSION_LENGTH]; ModemInfo modemInfo; UiccInfo simInfo; IPAddrT modemIP; int nRSSI; int nRSRQ; int nRSRP; int nRadioTech; int nRegistration; int eRASState ; int eATDState ; int eSIMState ; int eSMSState ; int eGPSState ; int nRejectCode; int nModemVolume; int nModemMicGain; int nWCDMABands; int nLTEBands; int nNwScanMode; } SharedData; ``` # 共享内存 不知道这样说合不合适,目前为止,共享内存是最快速的进程间通信方式,原因在于进行通信的进程pcb的地址空间的共享区同时映射到了一块物理内存上,通过虚拟地址直接找到访问上面的数据;这些进程间数据传递不再涉及到内核的系统接口的调用。 共享内存区按标准可分为Posix共享内存和System V共享内存,两者在概念上类似。 Posix 表示可移植操作系统接口(Portable Operating System Interface ,缩写为 POSIX ),POSIX标准定义了操作系统应该为应用程序提供的接口标准,是IEEE为要在各种UNIX操作系统上运行的软件而定义的一系列API标准的总称,其正式称呼为IEEE 1003,而国际标准名称为ISO/IEC 9945。 System V,曾经也被称为 AT&T System V,是Unix操作系统众多版本中的一支。它最初由 AT&T 开发,在1983年第一次发布。一共发行了4个 System V 的主要版本:版本1、2、3 和 4。 还有一个与之相关的概念叫做内存映射mmap,mmap与共享内存最大的区别在于,mmap的底层支撑实体是文件,而共享内存的支撑实体是物理内存。因此二者的差异就是,mmap大小不受物理内存大小限制,关机可保存但是效率相对共享内存较低。而共享内存效率非常高,但是不能大于物理内存,且断电后其中存储的信息会丢失。  ## POSIX中定义的共享内存方法 POSIX中有关共享内存的接口定义如下: ```C //创建/获取共享内存 int shm_open(const char *name, int oflag, mode_t mode); //删除共享内存 int shm_unlink(const char *name); //修改共享内存大小 int ftruncate(int fd, off_t length); //获取共享内存状态 int fstat(int fd, struct stat *buf); //映射共享内存 void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset); //卸载共享内存 int munmap(void *addr, size_t length); ``` 与SystemV信号相比,POSIX使用了文件系统来标识共享内存,并且调用操作文件的接口来操作共享内存。每创建一个POSIX共享内存,挂载在`/dev/shm`下的tmpfs文件系统中就会新增一个文件。 POSIX共享内存的大小可以动态调整,因为POSIX共享内存是基于文件的,所以可以很方便地通过ftruncate函数来调整共享内存的大小。 ## System V定义的共享内存方法 system V中有关共享内存的接口定义如下: ```C //创建/获取共享内存 int shmget(key_t key, size_t size, int shmflg); //控制共享内存 int shmctl(int shmid, int cmd, struct shmid_ds *buf); //映射共享内存到用户空间 void *shmat(int shmid, const void *shmaddr, int shmflg); //将共享内存从用户空间解绑 int shmdt(const void *shmaddr); ``` SystemV共享内存的大小在创建时就已经确定,无法再做调整。 ## RT smart中的共享内存方法 RT-smart中,shm方法在```lwp_shm.h```中定义: ```C int lwp_shmget(size_t key, size_t size, int create); int lwp_shmrm(int id); void* lwp_shmat(int id, void* shm_vaddr); int lwp_shmdt(void* shm_vaddr); void *lwp_shminfo(int id); int lwp_shm_ref_inc(struct rt_lwp *lwp, void *shm_vaddr); int lwp_shm_ref_dec(struct rt_lwp *lwp, void *shm_vaddr); ``` 这部分的代码实现是开放出来的,有兴趣的同学可以研究一下。 # Linux Ril中使用共享内存交互 ## Rild rild中主要使用共享内存和client交互,在Initialize client interface的时候创建共享内存并映射到用户空间 ```C void Initialize(int pid, int qid, int tcp_qid) { ... sharedMemID = shmget((key_t)SHARED_MEMORY_KEY, sizeof(SharedData), 0666 | IPC_CREAT); if(sharedMemID == -1){ DEBUG(MSG_ERROR, "Initialize shmget error\r\n"); perror("shmget failed : "); exit(0); } g_pSharedData = (SharedData *)shmat(sharedMemID, NULL, 0); if((void *)g_pSharedData ==(void * )-1){ DEBUG(MSG_ERROR, "Initialize shmat error\r\n"); perror("shmat failed : "); exit(0); } memset(g_pSharedData, 0x0, sizeof(SharedData)); ... } ``` 在Uninitialize client interface的时候将共享内存从用户空间解绑。 ```C void Uninitialize(void) { struct shmid_ds ds; ... shmdt(g_pSharedData); shmctl(sharedMemID, IPC_STAT, &ds); if (0 == ds.shm_nattch) { shmctl(sharedMemID, IPC_RMID, NULL); } ... msgctl(clientQueID, IPC_RMID, NULL); } ``` ## Client接口 在Client端,主要是通过constructro和deconstructor机制,在应用启动前和退出后对共享内存绑定和解绑。 ```C void _attach() { int shm_id; shm_id = shmget((key_t)SHARED_MEMORY_KEY, sizeof(SharedData), 0666 | IPC_CREAT); if(shm_id == -1){ perror("shmget failed : "); return; } s_pSharedData = (SharedData *)shmat(shm_id, NULL, 0); if((void *)s_pSharedData ==(void *)-1){ perror("shmat failed : "); return; } s_sharedID = shm_id; printf("### _attach(%d) ###\r\n", shm_id); } ``` ```C void _detach() { ... shmdt(s_pSharedData); shmctl(shm_id, IPC_STAT, &ds); if (0 == ds.shm_nattch) { shmctl(shm_id, IPC_RMID, NULL); } } ``` 解绑这一步非常关键,如果多次将同一块共享内存在用户空间进行绑定,会在用户空间生成多块一样的引用,造成空间浪费,甚至导致用户空间地址耗尽! 可以发现,当前的linux ril使用的是system V定义的共享内存接口,这在RT smart中是没有实现的,但是其与lwp中定义的共享内存接口非常像,我们很轻松就可以移植进去。 # 调试 替换成功后,我们把rild和telephony两个应用push到开发板 查看当前系统中的共享内存, 当前还没有共享内存被创建。  启动rild ``` cd bin rild.elf -d /dev/uart22 -v & ``` 再次查看系统中的共享内存,可以看到rild创建共享内存成功,key为0x4d2,与代码中的key是对应的 ```C #define SHARED_MEMORY_KEY 1234 ```  启动telephony ``` telephony.elf & ``` 运行telephony获取modem信息的功能,可以得到结果  IMEI 和ICCID都已经是空值,说明已经被rild进行过初始化 而如果不执行rild,直接运行telephony,则会显示很多乱码  以上说明两个应用已经链接到了同一块共享内存,并且rild的初始化信息可以同步到telephony进程中。 # 代码 [Linux ril开源组件 github](https://github.com/keipy/linux-rild/) [我的RT-Thread Smart开发仓库 gitee](https://gitee.com/yu-wang-yy/ART-Pi-smart/) # 总结 1. RT smart中提供的shm接口和POSIX / systemV 接口的兼容度还是很高的,移植起来不怎么废力。 2. 本次主要是验证了创建、绑定共享内存,对于共享内存的control接口并没有太多的研究,lwp也提供了共享内存rm和getinfo接口,有时间可以试一试。 3. 共享内存在使用上,如何同步,避免竞争其实是个大问题,这部分一般要依赖于其他的IPC机制实现,linux ril中是通过消息队列实现的,这个问题在之后的移植中还要多多注意。 4. 时间仓促,很快就要归还板子了,我这种移植方法比较粗暴,直接修改了源码其实是不太好的,后期全部功能验证ok了以后,可以考虑一下如何修改更加优雅。可以从预编译替换,或者最起码要区分平台,用宏控制一下。
1
条评论
默认排序
按发布时间排序
登录
注册新账号
关于作者
AngerCoke
这个人不懒,主要是真的不想写什么
文章
10
回答
2
被采纳
0
关注TA
发私信
相关文章
1
RT-Thread Studio 编译ART-Pi 的SDK报错 求助!-
2
ART-Pi 在 Studio 中使用TouchGFX Library 编译报错
3
RT-Thread Studio 更新错误
4
基于ART-Pi开发板创建的工程,RT-Thread Settings打不开
5
在 ART-Pi 平台使用 openocd 直接烧录到外部 spi flash?
6
ART-Pi工程用mdk5打开后提示文件找不到
7
ART-PI demo是否能够增加demo流程框图或者详细设计文档
8
ART-Pi 的ST_Link无法识别
9
请把ART-Pi仓库放到gitee上一份。
10
仅有ART-Pi开发包时,RTT Studio无法创建ART-Pi工程
推荐文章
1
RT-Thread应用项目汇总
2
玩转RT-Thread系列教程
3
国产MCU移植系列教程汇总,欢迎查看!
4
机器人操作系统 (ROS2) 和 RT-Thread 通信
5
五分钟玩转RT-Thread新社区
6
【技术三千问】之《玩转ART-Pi》,看这篇就够了!干货汇总
7
关于STM32H7开发板上使用SDIO接口驱动SD卡挂载文件系统的问题总结
8
STM32的“GPU”——DMA2D实例详解
9
RT-Thread隐藏的宝藏之completion
10
【ART-PI】RT-Thread 开启RTC 与 Alarm组件
热门标签
RT-Thread Studio
串口
Env
LWIP
SPI
AT
Bootloader
Hardfault
CAN总线
FinSH
ART-Pi
DMA
USB
文件系统
RT-Thread
SCons
RT-Thread Nano
线程
MQTT
STM32
RTC
rt-smart
FAL
I2C_IIC
UART
ESP8266
cubemx
WIZnet_W5500
ota在线升级
PWM
BSP
flash
freemodbus
packages_软件包
潘多拉开发板_Pandora
定时器
ADC
GD32
flashDB
socket
编译报错
中断
Debug
rt_mq_消息队列_msg_queue
keil_MDK
ulog
SFUD
msh
C++_cpp
MicroPython
本月问答贡献
RTT_逍遥
10
个答案
3
次被采纳
xiaorui
3
个答案
2
次被采纳
winfeng
2
个答案
2
次被采纳
三世执戟
8
个答案
1
次被采纳
KunYi
8
个答案
1
次被采纳
本月文章贡献
catcatbing
3
篇文章
5
次点赞
lizimu
2
篇文章
9
次点赞
swet123
1
篇文章
4
次点赞
Days
1
篇文章
4
次点赞
YZRD
1
篇文章
2
次点赞
回到
顶部
发布
问题
投诉
建议
回到
底部