目标: 看明白在uclibc中网络系统调用是如何实现的,和普通的Linux有什么区别
0. 将uClibc-0.9.30.1/libc/inet目录下的文件按文件大小排列一下,会发出100多字节的文件有很多,都是调用的。呵呵。
1. 代码文件:uClibc-0.9.30.1/libc/inet/socketcall.c 代码如下:
#ifdef L_recv
extern __typeof(recv) __libc_recv;
#ifdef __NR_recv
#define __NR___libc_recv __NR_recv
_syscall4(ssize_t, __libc_recv, int, sockfd, __ptr_t, buffer, size_t, len,
int, flags)
#elif defined(__NR_socketcall)
/* recv, recvfrom added by bir7@leland.stanford.edu */
ssize_t __libc_recv(int sockfd, __ptr_t buffer, size_t len, int flags)
{
unsigned long args[4];
args[0] = sockfd;
args[1] = (unsigned long) buffer;
args[2] = len;
args[3] = flags;
return (__socketcall(SYS_RECV, args));
}
#elif defined(__NR_recvfrom)
libc_hidden_proto(recvfrom)
ssize_t __libc_recv(int sockfd, __ptr_t buffer, size_t len, int flags)
{
return (recvfrom(sockfd, buffer, len, flags, NULL, NULL));
}
#endif
libc_hidden_proto(recv)
weak_alias(__libc_recv,recv)
libc_hidden_weak(recv)
#endif
#ifdef L_connect
extern __typeof(connect) __libc_connect;
#ifdef __NR_connect
#define __NR___libc_connect __NR_connect
_syscall3(int, __libc_connect, int, sockfd, const struct sockaddr *, saddr, socklen_t, addrlen)
#elif defined(__NR_socketcall)
int __libc_connect(int sockfd, const struct sockaddr *saddr, socklen_t addrlen)
{
unsigned long args[3];
args[0] = sockfd;
args[1] = (unsigned long) saddr;
args[2] = addrlen;
return __socketcall(SYS_CONNECT, args);
}
#endif
libc_hidden_proto(connect)
weak_alias(__libc_connect,connect)
libc_hidden_weak(connect)
#endif
#ifdef L_getpeername
#ifdef __NR_getpeername
_syscall3(int, getpeername, int, sockfd, struct sockaddr *, addr, socklen_t *,paddrlen)
#elif defined(__NR_socketcall)
int getpeername(int sockfd, struct sockaddr *addr, socklen_t * paddrlen)
{
unsigned long args[3];
args[0] = sockfd;
args[1] = (unsigned long) addr;
args[2] = (unsigned long) paddrlen;
return __socketcall(SYS_GETPEERNAME, args);
}
#endif
#endif
2. 对上面代码的分析:
A. 将网络系统调用传送过来的参数放到一个unsigned long 型参数数组中, 再将这些参数传递给内核的__开头的函数库。
B. 注意:为什么是unsigned long ? 因为unsigned long 可以存储指针的地址。 还有这里有不同的recv版本的,这个和当时编译的环境相关
C. 不过这里没有对入口参数进行判断,会不会被黑客利用?
3. ntohl.c
#if __BYTE_ORDER == __BIG_ENDIAN
uint32_t ntohl (uint32_t x)
{
return x;
}
uint16_t ntohs (uint16_t x)
{
return x;
}
uint32_t htonl (uint32_t x)
{
return x;
}
uint16_t htons (uint16_t x)
{
return x;
}
#elif __BYTE_ORDER == __LITTLE_ENDIAN
uint32_t ntohl (uint32_t x)
{
return __bswap_32(x);
}
uint16_t ntohs (uint16_t x)
{
return __bswap_16(x);
}
uint32_t htonl (uint32_t x)
{
return __bswap_32(x);
}
uint16_t htons (uint16_t x)
{
return __bswap_16(x);
}
#else
#error "You seem to have an unsupported byteorder"
#endif
4. 对上面代码分析: 三种模式: 大端,小端,不可识别的。
大端的时候是uclinux默认的,而小端就需要用函数转换一下了
5. getaddrinfo.c
libc_hidden_proto(getaddrinfo)
int
getaddrinfo(const char *name, const char *service,
const struct addrinfo *hints, struct addrinfo **pai)
{
int i = 0, j, last_i = 0;
struct addrinfo *p = NULL, **end;
const struct gaih *g = gaih, *pg = NULL;
struct gaih_service gaih_service, *pservice;
struct addrinfo default_hints;
if (name != NULL && name[0] == '*' && name[1] == 0)
name = NULL;
if (service != NULL && service[0] == '*' && service[1] == 0)
service = NULL;
if (name == NULL && service == NULL)
return EAI_NONAME;
if (hints == NULL) {
memset(&default_hints, 0, sizeof(default_hints));
if (AF_UNSPEC)
default_hints.ai_family = AF_UNSPEC;
hints = &default_hints;
}
if (hints->ai_flags & ~(AI_PASSIVE|AI_CANONNAME|AI_NUMERICHOST|
AI_ADDRCONFIG|AI_V4MAPPED|AI_NUMERICSERV|AI_ALL))
return EAI_BADFLAGS;
if ((hints->ai_flags & AI_CANONNAME) && name == NULL)
return EAI_BADFLAGS;
if (service && service[0]) {
char *c;
gaih_service.name = service;
gaih_service.num = strtoul(gaih_service.name, &c, 10);
if (*c != '/0') {
if (hints->ai_flags & AI_NUMERICSERV)
return EAI_NONAME;
gaih_service.num = -1;
} else {
/*
* Can't specify a numerical socket unless a protocol
* family was given.
*/
if (hints->ai_socktype == 0 && hints->ai_protocol == 0)
return EAI_SERVICE;
}
pservice = &gaih_service;
} else
pservice = NULL;
end = NULL;
if (pai)
end = &p;
j = 0;
while (g->gaih) {
if (hints->ai_family == g->family || hints->ai_family == AF_UNSPEC) {
if ((hints->ai_flags & AI_ADDRCONFIG) && !addrconfig(g->family)) {
++g;
continue;
}
j++;
if (pg == NULL || pg->gaih != g->gaih) {
pg = g;
i = g->gaih(name, pservice, hints, end);
if (i != 0) {
last_i = i;
if (hints->ai_family == AF_UNSPEC && (i & GAIH_OKIFUNSPEC))
continue;
if (p)
freeaddrinfo(p);
return -(i & GAIH_EAI);
}
if (end)
while (*end)
end = &((*end)->ai_next);
}
}
++g;
}
if (j == 0)
return EAI_FAMILY;
if (p) {
*pai = p;
return 0;
}
if (pai == NULL && last_i == 0)
return 0;
/* if (p) - never happens, see above */
/* freeaddrinfo(p); */
return last_i ? -(last_i & GAIH_EAI) : EAI_NONAME;
}
libc_hidden_def(getaddrinfo)
6. 对上面代码的分析:
A. 比较复杂
B. 太懒了,不想看,也看不懂
7. 对这些代码阅读之后的感想。
怎样才是一个合格或者是熟练的uclinux 上的软件开发工程师?这里只说C。。 我想应该要用熟练这个库里提供的所有函数,这样应该就OK了,有心人的话,也可以把这些函数库全部读一遍,有个一两年Linux平台下C开发经验的人,读这个应该很快的吧,而且会得心应手,发现uclibc的库真简单,难点不是太多,但是全部掌握这个库也要花点心血的。
应该没有必要去重写这个库,不过,用这个库去做一些实事,倒是最为重要的。这个目标和大部分库的目标是一样的。 为了让更多人使用