linux开发打印及格式化
在进行android或者linux开发的过程中,打印和格式化使我们经常使用的函数,有时候有某种想法,可是不知道有哪些函数可以去实现,就算你知道是有函数的,但你可能记不住名字,参数个数,以及顺序,快年底了,趁现在有空,赶紧整理出来,我可能侧重内核空间部分,但对于内核空间和用户空间的打印、格式化一般都有一一对应的函数的,可能就是名字稍微不一样罢了,比如内核空间打印用printk,而用户空间用print
在进行android或者linux开发的过程中,打印和格式化使我们经常使用的函数,有时候有某种想法,可是不知道有哪些函数可以去实现,就算你知道是有函数的,但你可能记不住名字,参数个数,以及顺序,快年底了,趁现在有空,赶紧整理出来,我可能侧重内核空间部分,但对于内核空间和用户空间的打印、格式化一般都有一一对应的函数的,可能就是名字稍微不一样罢了,比如内核空间打印用printk,而用户空间用printf。
内核空间打印:
1. printk
kernel\kernel\printk.c
kernel\include\linux\printk.h
printk是内核中最主要的打印函数了,其他的一些打印基本都是基于此的,对应于用户空间的printf,参数区别在于,printk多了个打印等级。
原型: int printk(const char *fmt, ...);返回值为打印的长度
例如,printk(KERN_INFO "%s[%d]\n",__FUNCTION__,__LINE__) ;
在printk.h中有定义等级:
#define KERN_EMERG "<0>" /* system is unusable */
#define KERN_ALERT "<1>" /* action must be taken immediately */
#define KERN_CRIT "<2>" /* critical conditions */
#define KERN_ERR "<3>" /* error conditions */
#define KERN_WARNING "<4>" /* warning conditions */
#define KERN_NOTICE "<5>" /* normal but significant condition */
#define KERN_INFO "<6>" /* informational */
#define KERN_DEBUG "<7>" /* debug-level messages */
/* printk's without a loglevel use this.. */
#define DEFAULT_MESSAGE_LOGLEVEL CONFIG_DEFAULT_MESSAGE_LOGLEVEL
/* We show everything that is MORE important than this.. */
#define MINIMUM_CONSOLE_LOGLEVEL 1 /* Minimum loglevel we let people use */
#define DEFAULT_CONSOLE_LOGLEVEL 7 /* anything MORE serious than KERN_DEBUG */
int console_printk[4] = {
DEFAULT_CONSOLE_LOGLEVEL, /* console_loglevel */
DEFAULT_MESSAGE_LOGLEVEL, /* default_message_loglevel */
MINIMUM_CONSOLE_LOGLEVEL, /* minimum_console_loglevel */
DEFAULT_CONSOLE_LOGLEVEL, /* default_console_loglevel */
};
数组第一项:为控制台打印级别,更高优先级(数值更小的)将被输出到控制台
数组第二项:为默认消息打印级别,即printk不指定级别时的打印级别值
数组第三项:为控制台打印级别可设置的最小值(最高优先级)
数组第四项:为控制台打印缺省级别值
这数组四项默认值为7,4,1,7
通过 cat /proc/sys/kernel/printk
能够获取当前的4个值,一般你的printk没有打印出来,就是要echo改第一个值即可,当然如果你的printk没有设置打印级别,你也可以调整第二值
我的设备获取的值为6,6,1,7,由于第一个值为6,那么小于6的打印才能输出,debug,info的打印就不会输出了。
所有的printk打印实际上都是放到ring buffer中的,这个环形buffer大小默认是64K,当然可以编译的时候在General setup --->Kernel log buffer size中修改,默认是16,即2^16=64K,或者在启动参数中增加log_buf_len=2M。当打印超出比如64K,后面的内容会把最先打印的从buffer中挤出去,类似于FIFO。
cat /proc/kmsg和dmesg打印的内容实际上就是从ring buffer中获取,所以不存在打印级别的限制,打印级别限制只是在console上才会有效。console我们先肤浅的当做就是所谓的串口吧(串口要register_console才能算console,而且能在串口中输入shell命令,而如果没有注册console的串口是不会相应你的shell命令的)
比如编译user版本安卓系统时,内核打印就是无权限的,kmsg和dmesg无权限反馈如下
/system/bin/sh: cat: /proc/kmsg: Permission denied
klogctl: Operation not permitted
只有root权限的才能看内核打印。
2. dev_xxx
设备打印也是8个类型,对应于8种打印级别,且这8个函数都是基于printk打印的。我们先说前7个,最后一个事DEBUG级别,下面单独说明。
int dev_emerg(const struct device *dev, const char *fmt, ...);
int dev_alert(const struct device *dev, const char *fmt, ...);
int dev_crit(const struct device *dev, const char *fmt, ...);
int dev_err(const struct device *dev, const char *fmt, ...);
int dev_warn(const struct device *dev, const char *fmt, ...);
int dev_notice(const struct device *dev, const char *fmt, ...);
int dev_info(const struct device *dev, const char *fmt, ...);
实质上这7个函数先调用的是
int __dev_printk(const char *level, const struct device *dev,
struct va_format *vaf)
{
if (!dev)
return printk("%s(NULL device *): %pV", level, vaf);
return printk("%s%s %s: %pV",
level, dev_driver_string(dev), dev_name(dev), vaf);
}
然后__dev_printk调用printk
这7个函数是直接用的,只要包含linux\device.h即可,但是能否通过console打印出来,要看/proc/sys/kernel/printk的默认console打印级别了。这7个函数唯一需要说明的就是第二个参数dev,这个参数是你当前设备指针(可能是自己自定义的结构体或者平台结构体)展开到struct device的指针,那么这个参数会打印出什么呢?dev是这样处理的
drv = ACCESS_ONCE(dev->driver);
return drv ? drv->name :
(dev->bus ? dev->bus->name :
(dev->class ? dev->class->name : " "));
即驱动名>设备总线名>设备类名,从这个优先级中取出一个作为打印字符串
那么dev_xxx打印的字段依次为:打印级别,驱动名,设备名,格式化的字符串
接下来得说说dev_dbg,因为它要打印出来,还有DEBUG开关的。必须在直接或者间接包含linux\device.h之前定义DEBUG才能使用
比如:
#define ....
#define DEBUG 1
#include <linux/platform_device.h>
......
dev_dbg(.....);
......
另外还有一个冗余打印dev_vdbg,实质上他就是dev_dbg函数,要是使用dev_vdbg,需要在直接或者间接包含linux\device.h之前定义DEBUG和VERBOSE_DEBUG
#define DEBUG
#define VERBOSE_DEBUG
...
#include <linux/device.h>
...
内核空间格式化
len_3 = snprintf(tlist_3,10,
"this is a overflow test!\n"
);
printf
(
"len_3 = %d,tlist_3 = %s\n"
,len_3,tlist_3);//结果是len_3=25,但是tlist_3="this is a"
此处提一下strlcpy和strlcat问题,但是这2个函数不是标准的c函数,但是linux是支持的,使用优先级strcpy<strncpy<strlcpy,strncpy有性能问题,strcpy有越界问题,strlcpy最优先用,strlcat类同。具体详情见文章《Strlcpy和strlcat——一致的、安全的字符串拷贝和串接函数》
2. sscanf
int sscanf
(const char * buf, const char * fmt, ... ...);
返回值为Unformat 参数的个数。详细见《C语言函数sscanf的用法》,能执行复杂的去格式转换,简单的一次去格式转换下面会有介绍。
例如sscanf(buf, "%x", &parsed_rate);//将buf还原为16进制整数。返回值为1
sscanf("iios/12DDWDFF@122", "%*[^/]/%[^@]", buf);
printf("%s\n", buf);
结果为:12DDWDFF
3. strtoxxx
unsigned long simple_strtoul(const char *cp, char **endp, unsigned int base) ;
返回:返回转换后数据。
参数:cp指向字符串的开始,endp指向分析的字符串末尾的位置,base为要用的基数(进制数),base为0表示通过cp来自动判断基数,函数自动可识别的基数:‘0x’表示16进制,‘0’表示8进制,其它都认定为10进制。函数可转换成数字的有效字符为:[0,f]。
举例:cp = “0x12str”,base = 0,则返回unsigned long long为18,*endp = “str”。对于待处理字符串没有严格的要求。
此类函数有以下几种:
unsigned long simple_strtoul(const char *,char **,unsigned int);
long simple_strtol(const char *,char **,unsigned int);
unsigned long long simple_strtoull(const char *,char **,unsigned int);
long long simple_strtoll(const char *,char **,unsigned int);
在linux\kernel.h中有这么一句:/* Obsolete, do not use. Use kstrto<foo> instead */说明上述simple_strtoxx函数在内核中已经不推荐使用了,将来应该会踢出linux中的,又有#define strict_strtoul
kstrtoul ,它的替代者是strict_strtoxxx。
int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
功能:将一个字符串转换成unsigend long型。
返回:转换成功返回0,否则返回负。res指向转换后的unsigned long数据。
说明:该函数对cp指向的字符串严格要求,cp指向的字符串必须为真正的unsigned long形式的字符串。字符串必须以“0x”、“0”、[0,f]开始,中间全部为有效的字符[0,f],否则返回为负。它会处理字符串最后的“\n”字符。
此类函数有以下几种:
<span style="font-size:14px;">int strict_strtoul(const char *cp, unsigned int base, unsigned long *res)
int strict_strtol(const char *cp, unsigned int base, long *res)
int strict_strtoull(const char *cp, unsigned int base, unsigned long long *res)
int strict_strtoll(const char *cp, unsigned int base, long long *res) </span>
用户空间打印
用户空间的打印无非就是printf了,没什么可说的。
用户空间的格式转化
如atoi,它对应于内核空间的simple_strtoul或者strict_strtoul
linux好像没有itoa函数,如果你要使用itoa,ltoa,ultoa这类意义的函数去将各种整形转换为字符串,只要用sprintf就通吃了。
字符串转化为数
/*字符串转化为数*/
#inclue <stdlib.h>
//跳过前面空格,从遇到数字或符号开始转换,再次碰到非数字或者'\0'停止。atof等价于strtod(const char *start, NULL)
double atof(const char *str);
int atoi(const char *str);
//atol等价于strtol(const char *start, NULL, 10);
long atol(const char *str);
double strtod(const char *start, char **end);
long int strtol(const char *start, char **end, int radix);
unsigned long int strtoul(const char *start, char **end, int radix);
更多推荐
所有评论(0)