在進行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_strtoulkstrtoul ,它的替代者是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);