一、函数类型划分
在C语言中以行参划分,可将函数分为两类:
固定参数函数和可变参数函数(variable argument function,VA函数)。
1.1 固定参数函数:
定义之后,使用过程中参数个数不可变。在实际代码编写中,这种函数是使用最多的一类。
例如,
-
/*
-
此函数是linux内核源码
-
其作用是在接口监听任务检测到接口载波发生变化是对接口状态进行通知
-
*/
-
void netdev_state_change(struct net_device *dev)
-
{
-
if (dev->flags & IFF_UP)
-
{
-
call_netdevice_notifiers(NETDEV_CHANGE, dev);
-
rtmsg_ifinfo(RTM_NEWLINK, dev, 0);
-
}
-
}
1.2 可变参数函数:
定义之后,使用过程中参数个数(最少为1)可变。定义时,至少有一个类型确定的参数,之后形参利用“...”代表可变参数。
例如内核调试函数printk,
点击(此处)折叠或打开
-
asmlinkage int printk(const char *fmt, ...)
-
{
-
va_list args;
-
int r;
-
-
#ifdef CONFIG_KGDB_KDB
-
if (unlikely(kdb_trap_printk)) {
-
va_start(args, fmt);
-
r = vkdb_printf(fmt, args);
-
va_end(args);
-
return r;
-
}
-
#endif
-
va_start(args, fmt);
-
r = vprintk(fmt, args);
-
va_end(args);
-
-
return r;
-
}
PS:
形参为空( int main() )时,表示函数参数不确定,这和形参为void( int main(void) )时代表使用时不需要参数是有区别的。
二、可变参数函数实现解析
2.1 需要用到的宏
点击(此处)折叠或打开
-
include\acpi\actypes.h
-
-
#if ACPI_MACHINE_WIDTH == 64
-
...
-
typedef s64 acpi_native_int;
-
...
-
#elif ACPI_MACHINE_WIDTH == 32
-
............
typedef s32 acpi_native_int;
...
-
#endif
根据系统定义acpi_native_int 类型,64位定义为 signed long long类型,32位定义为 signed int类型。
-
include\acpi\platform\acenv.h
-
-
#ifndef _VALIST
-
#define _VALIST
-
typedef char *va_list;
-
#endif /* _VALIST */
-
-
/*
-
* Storage alignment properties
-
*/
-
#define _AUPBND (sizeof (acpi_native_int) - 1)
-
#define _ADNBND (sizeof (acpi_native_int) - 1)
-
/*
-
* Variable argument list macro definitions
-
*/
-
#define _bnd(X, bnd) (((sizeof (X)) + (bnd)) & (~(bnd)))
-
#define va_arg(ap, T) (*(T *)(((ap) += (_bnd (T, _AUPBND))) - (_bnd (T,_ADNBND))))
-
#define va_end(ap) (void) 0
-
#define va_start(ap, A) (void) ((ap) = (((char *) &(A)) + (_bnd (A,_AUPBND))))
_AUPBND 和 _ADNBND 为对齐变量,
_AUPBND用在上移地址时的对齐,_ADNBND用在下移地址时的对齐。
_bnd(X, bnd) 将类型X做32位(int)或者64位(long long)对齐。
理解最后的三个宏定义,就需要知道函数参数在系统中的存放方式。
系统运行函数时,会将函数的参数从最后一个向前逐个压栈,第一个参数在栈顶(低位),最后一个参数在栈底(高位)。因此,如果我们知道其中一个参数位置及所有参数的类型,就能推算出所有的参数所在的位置。
va_start(ap, A) 将ap赋值为参数A后面参数的位置。
va_arg(ap, T) 取值。首先,ap向下一个位置移动,之后,返回移动过位置的参数值。
va_end(ap) 取消ap定义。
这几个宏是可变参数函数的核心部分。
2.2 实例解析
以printk的实现为例,看一下内核开发人员是怎样实现可变参数。
-
asmlinkage int printk(const char *fmt, ...)
-
{
-
va_list args;
-
int r;
-
-
#ifdef CONFIG_KGDB_KDB
-
if (unlikely(kdb_trap_printk)) {
-
va_start(args, fmt);
-
r = vkdb_printf(fmt, args);
-
va_end(args);
-
return r;
-
}
-
#endif
-
/*将args初始化为,fmt之后的第一个参数的地址*/
-
va_start(args, fmt);
-
r = vprintk(fmt, args);
-
va_end(args);
-
-
return r;
-
}
vprintk函数仅解析其中有关可变参数的内容(红色部分)。
-
asmlinkage int vprintk(const char *fmt, va_list args)
-
{
-
int printed_len = 0;
-
int current_log_level = default_message_loglevel;
-
unsigned long flags;
-
int this_cpu;
-
char *p;
-
-
...
-
/* Emit the output into the temporary buffer */
-
printed_len += vscnprintf(printk_buf + printed_len,
-
sizeof(printk_buf) - printed_len, fmt, args);
-
...
-
}
-
int vscnprintf(char *buf, size_t size, const char *fmt, va_list args)
-
{
-
int i;
-
-
i = vsnprintf(buf, size, fmt, args);
-
-
return (i >= size) ? (size - 1) : i;
-
}
-
struct printf_spec {
-
u8 type; /* format_type enum */ 输出类型,int、char等
-
u8 flags; /* flags to number() */ 输出标识
-
u8 base; /* number base, 8, 10 or 16 only */ 输出进制
-
u8 qualifier; /* number qualifier, one of 'hHlLtzZ' */
-
s16 field_width; /* width of output field */
-
s16 precision; /* # of digits/chars */
-
};
-
int vsnprintf(char *buf, size_t size, const char *fmt, va_list args)
-
{
-
unsigned long long num;
-
char *str, *end;
-
struct printf_spec spec = {0};
-
-
-
/* Reject out-of-range values early. Large positive sizes are
-
used for unknown buffer sizes. */
-
if (WARN_ON_ONCE((int) size < 0))
-
return 0;
-
-
-
str = buf;
-
end = buf + size;
-
-
-
/* Make sure end is always >= buf */
-
if (end < buf) {
-
end = ((void *)-1);
-
size = end - buf;
-
}
-
-
/*循环第一个参数,查找输出格式*/
-
while (*fmt) {
-
const char *old_fmt = fmt;
-
/*查找参数中格式,并将格式保存在spec中,方便之后的处理*/
-
int read = format_decode(fmt, &spec);
-
-
fmt += read;
-
-
switch (spec.type) {
-
...
-
/*仅看几个常用的类型*/
-
default:
-
switch (spec.type) {
-
case FORMAT_TYPE_LONG_LONG:
-
num = va_arg(args, long long);
-
break;
-
case FORMAT_TYPE_ULONG:
-
num = va_arg(args, unsigned long);
-
break;
-
case FORMAT_TYPE_LONG:
-
num = va_arg(args, long);
-
break;
-
case FORMAT_TYPE_SIZE_T:
-
num = va_arg(args, size_t);
-
break;
-
case FORMAT_TYPE_PTRDIFF:
-
num = va_arg(args, ptrdiff_t);
-
break;
-
case FORMAT_TYPE_UBYTE:
-
num = (unsigned char) va_arg(args, int);
-
break;
-
case FORMAT_TYPE_BYTE:
-
num = (signed char) va_arg(args, int);
-
break;
-
case FORMAT_TYPE_USHORT:
-
num = (unsigned short) va_arg(args, int);
-
break;
-
case FORMAT_TYPE_SHORT:
-
num = (short) va_arg(args, int);
-
break;
-
case FORMAT_TYPE_INT:
-
/*类型为int类型时,取值将其赋给num*/
-
num = (int) va_arg(args, int);
-
break;
-
default:
-
num = va_arg(args, unsigned int);
-
}
-
/*拼接字符,数字时用number函数*/
-
str = number(str, end, num, spec);
-
}
-
}
-
-
if (size > 0) {
-
if (str < end)
-
*str = '\0';
-
else
-
end[-1] = '\0';
-
}
-
}
-
阅读(1185) | 评论(0) | 转发(0) |