RT-Thread POSIX支持

发布于 2016-05-08 12:19:34
目前要比较完整的使用RT-Thread的POSIX特性需要打开:


#define RT_USING_LIBC
#define RT_USING_DFS
#define RT_USING_DFS_DEVFS
#define RT_USING_DFS_LWIP
#define RT_USING_PTHREADS
#define RT_USING_LIBDL



    RT_USING_LIBC打开系统中的libc库(针对armcc/iar/gcc,分别对应armlibc, dlib, newlib三种不同的libc库);[/*:m]
    RT_USING_DFS/DFS_DEVFS/DFS_LWIP打开系统中的文件系统相关接口支持,分别是虚拟文件系统,设备文件系统,bsd socket可按照文件描述符方式操作;[/*:m]
    RT_USING_PTHREADS打开系统中的POSIX thread支持;[/*:m]
    RT_USING_LIBDL打开系统中的dlopen等动态库的符号特性;[/*:m][/list:u]

    当前还缺少,
    1. terminal相关接口的支持,即termios、tcgetattr/tcsetattr等接口;需要做当前console设备上进行封装;
    2. pipe支持,需要进一步完善pipe驱动框架的支持,让它完全按照一个设备,并能够以文件描述符方式进行操作;
    3. select操作,能够把设备文件,pipe文件等一起加入到fd_set中以支持select操作。

    计划调整,
    DFS_LWIP更名为DFS_NET
    libdl,pthreads划归到libc文件夹下;

    查看更多

关注者
0
被浏览
2.9k
4 个回答
bernard
bernard 2016-05-10
pthreads的实现位于:
https://github.com/RT-Thread/rt-thread/ ... s/pthreads

其中不仅包括了POSIX thread的实现,也包括了clock_time, mqueue, semaphore等的实现,即POSIX方式的多线程间同步和通信机制。当把pthreads和libc合并时,clock_time相关功能应该合并到其他libc实现中,而不是放在pthreads中。

BTW, lwIP出了2.0.0 beta1版本了,相关的移植,POSIX层面的封装也需要跟上
bernard
bernard 2016-05-22
如果需要在lwip之外支持select接口(例如能够让fd_set支持到文件描述符,以及后续扩展的UNIX套接字),需要放弃lwip目前已有的select接口

lwIP的select实现类似于:

在创建一个socket时就默认设置一个事件callback函数,在一些事件触发时回调这个函数:

/**
* Callback registered in the netconn layer for each socket-netconn.
* Processes recvevent (data available) and wakes up tasks waiting for select.
*/
static void
event_callback(struct netconn *conn, enum netconn_evt evt, u16_t len)
{
int s;
struct lwip_sock *sock;
struct lwip_select_cb *scb;
int last_select_cb_ctr;
SYS_ARCH_DECL_PROTECT(lev);

LWIP_UNUSED_ARG(len);

/* Get socket */
if (conn) {
s = conn->socket;
if (s < 0) {
/* Data comes in right away after an accept, even though
* the server task might not have created a new socket yet.
* Just count down (or up) if that's the case and we
* will use the data later. Note that only receive events
* can happen before the new socket is set up. */
SYS_ARCH_PROTECT(lev);
if (conn->socket < 0) {
if (evt == NETCONN_EVT_RCVPLUS) {
conn->socket--;
}
SYS_ARCH_UNPROTECT(lev);
return;
}
s = conn->socket;
SYS_ARCH_UNPROTECT(lev);
}

sock = get_socket(s);
if (!sock) {
return;
}
} else {
return;
}

SYS_ARCH_PROTECT(lev);
/* Set event as required */
switch (evt) {
case NETCONN_EVT_RCVPLUS:
sock->rcvevent++;
break;
case NETCONN_EVT_RCVMINUS:
sock->rcvevent--;
break;
case NETCONN_EVT_SENDPLUS:
sock->sendevent = 1;
break;
case NETCONN_EVT_SENDMINUS:
sock->sendevent = 0;
break;
case NETCONN_EVT_ERROR:
sock->errevent = 1;
break;
default:
LWIP_ASSERT("unknown event", 0);
break;
}

if (sock->select_waiting == 0) {
/* noone is waiting for this socket, no need to check select_cb_list */
SYS_ARCH_UNPROTECT(lev);
return;
}

/* Now decide if anyone is waiting for this socket */
/* NOTE: This code goes through the select_cb_list list multiple times
ONLY IF a select was actually waiting. We go through the list the number
of waiting select calls + 1. This list is expected to be small. */

/* At this point, SYS_ARCH is still protected! */
again:
for (scb = select_cb_list; scb != NULL; scb = scb->next) {
if (scb->sem_signalled == 0) {
/* semaphore not signalled yet */
int do_signal = 0;
/* Test this select call for our socket */
if (sock->rcvevent > 0) {
if (scb->readset && FD_ISSET(s, scb->readset)) {
do_signal = 1;
}
}
if (sock->sendevent != 0) {
if (!do_signal && scb->writeset && FD_ISSET(s, scb->writeset)) {
do_signal = 1;
}
}
if (sock->errevent != 0) {
if (!do_signal && scb->exceptset && FD_ISSET(s, scb->exceptset)) {
do_signal = 1;
}
}
if (do_signal) {
scb->sem_signalled = 1;
/* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
lead to the select thread taking itself off the list, invalidagin the semaphore. */
sys_sem_signal(&scb->sem);
}
}
/* unlock interrupts with each step */
last_select_cb_ctr = select_cb_ctr;
SYS_ARCH_UNPROTECT(lev);
/* this makes sure interrupt protection time is short */
SYS_ARCH_PROTECT(lev);
if (last_select_cb_ctr != select_cb_ctr) {
/* someone has changed select_cb_list, restart at the beginning */
goto again;
}
}
SYS_ARCH_UNPROTECT(lev);
}


在这个其中,依据事件的类型来设置一些参数:

/* Set event as required */
switch (evt) {
case NETCONN_EVT_RCVPLUS:
sock->rcvevent++;
break;
case NETCONN_EVT_RCVMINUS:
sock->rcvevent--;
break;
case NETCONN_EVT_SENDPLUS:
sock->sendevent = 1;
break;
case NETCONN_EVT_SENDMINUS:
sock->sendevent = 0;
break;
case NETCONN_EVT_ERROR:
sock->errevent = 1;
break;
default:
LWIP_ASSERT("unknown event", 0);
break;
}


以及确实触发了事件,则发送一个信号量,这个在lwip中是以sys_sem_signal的方式进行:

if (do_signal) {
scb->sem_signalled = 1;
/* Don't call SYS_ARCH_UNPROTECT() before signaling the semaphore, as this might
lead to the select thread taking itself off the list, invalidagin the semaphore. */
sys_sem_signal(&scb->sem);
}


因为lwIP为了减少移植接口,所以基本上只采取一些最基本的任务间同步的机制,这里如果使用event flag的方式会更好。

即,如果要在外部实现对lwIP的select操作,需要对socket设置不同的event callback回调函数,并根据类似的情况来设置socket中的rcvevent/sendevent值。
bernard
bernard 2016-05-22
同理,如果要实现统一的基于fd的select接口:

int select(int nfds, fd_set *readfds, fd_set *writefds,
fd_set *errorfds, struct timeval *timeout);


可以为每个fd提供包括:read_event,write_event和error_event的参数。对于device来说,当接收到数据,会在rx_indicate和tx_complete中设置read_event和write_event。在一个任务做select时,如果fd都不满足条件时,则以select control block(select_cb)的方式挂起select_cb_list链表上。

当触发了event callback,它需要在select_cb_list链表上遍历每个select control block,如果有相应的fd,则尝试唤醒挂起的任务。

撰写答案

请登录后再发布答案,点击登录

发布
问题

分享
好友