在linux下,printf输出到控制台经历了app->libc(stdio.h)->syscall->console驱动 下面是<<linux内核完全注释>>一书中的一段描述: 继续看下write函数的实现: 以下是write()函数的基本定义:
#include <unistd.h> ssize_t write(int fd, const void *buf, size_t count); // ssize_t是一个有符号整数类型,通常用来表示读取或者写入的字节数。c中ssize_t通常被定义为typedef long int ssize_t,也就是long int类型来表示
- fd:表示要写入的文件描述符。在UNIX系统中,标准输出流(终端屏幕显示)的文件描述符是1,标准错误流的文件描述符是2,标准输入流的文件描述符是0。
- buf:指向要写入数据的缓冲区的指针。
- count:表示要写入的字节数。
#include <unistd.h> #include <fcntl.h> int main() { int fd = open("output.txt", O_WRONLY | O_CREAT | O_TRUNC, 0644); // 打开文件以便写入 if (fd == -1) { // 处理文件打开失败的情况 return 1; } const char *str = "Hello, world!\n"; ssize_t bytes_written = write(fd, str, strlen(str)); // 将字符串写入到文件 if (bytes_written == -1) { // 处理写入失败的情况 } close(fd); // 关闭文件 return 0; }在上述代码中: 使用open()函数打开output.txt文件,如果文件不存在则创建,O_WRONLY表示以只写方式打开文件,O_CREAT表示如果文件不存在则创建,O_TRUNC表示清空文件内容。 在成功打开文件后,使用write()函数将字符串"Hello, world!\n"写入到文件中。最后,使用close()函数关闭文件描述符 当运行上述代码时,字符串将会被写入到名为output.txt的文件中。 write函数会系统调用到内核,内核uart驱动将其在串口输出,也就是我们看到的屏幕打印。 延伸出来介绍下android c++打印:
#include LOG_TAG #undef LOG_TAG #endif #define LOG_TAG "lethe_DEBUG" #include <android/log.h> // 在此处等价于 #include<utils/Log.h> // 在使用可变参数列表的宏时,如果可变参数列表为空,那么在宏展开时可能会导致语法错误。为了避免这种情况,##操作符可以用于将可变参数列表与前面的标识符分开,以确保在没有可变参数时不会产生语法错误。 #define ALOGE(...) __android_log_print(ANDROID_LOG_ERROR,LOG_TAG,## __VA_ARGS__) #define ALOGI(...) __android_log_print(ANDROID_LOG_INFO,LOG_TAG, ## __VA_ARGS__) #define ALOGD(...) __android_log_print(ANDROID_LOG_DEBUG,LOG_TAG, ## __VA_ARGS__) fun() { ALOGE("debug log"); }以ALOGE打印为例,ALOGE是android平台上用于打印错误级别日志信息的宏定义。在android源码中,ALOGE通常被定义为__android_log_print函数的一个简化形式 知识点一、...和##和__VA_ARGS__ __VA_ARGS__是c语言中的一个特殊宏,用于可变参数列表。在C语言中,有一类宏称为可变参数宏(variadic macros),允许在宏展开时接受不定数量的参数。而 __VA_ARGS__就是用于代表这个可变参数列表的占位符。 ##是一种处理器操作符,称为连接操作符或者粘贴操作符,它的作用是将两个标识符连接成一个标识符,下面举例:
#define PRINT_COORD(x, y) printf("Coordinates: " #x ", " #y "\n"); PRINT_COORD(10, 20); // 打印出 "Coordinates: 10, 20"。在c语言中,...是可变参数列表。在宏定义中,...表示可变参数的部分,即宏可以接受任何数量的参数 缓冲区: 行缓冲、全缓冲、无缓冲以及用户缓冲区、内核缓冲区介绍 不可不知的三种缓冲类型 全缓冲:写磁盘文件一般是全缓冲 还可以使用fflush函数,它可以将缓冲区中的内容写入到磁盘中(终端驱动设备表示丢弃缓冲区的数据)。所以将fwrite下面一行的注释去掉后,就可以发现,写入之后,就可以直接在文件中看到内容 #include<stdio.h> #include<unistd.h> int main(void) { /*以可读可写的方式打开*/ FILE *fp = fopen("./test.txt","w+"); if(NULL == fp) { perror("open file failed"); return -1; } /*写入内容*/ char buf[] = "--------------------\n"; fwrite(buf,sizeof(char),sizeof(buf),fp); //fflush(fp); /*sleep一段时间,以便观察*/ sleep(20); fclose(fp); return 0; } 行缓冲:行缓冲指的是当遇到换行符时,或者缓冲区已经满了(一般1024字节),标准I/O库执行I/O操作。printf执行完了之后,内容并没有马上输出到终端,而是在程序运行完之后才输出。举例子如下: #include<stdio.h> #include<unistd.h> int main(void) { printf("-----------------"); sleep(10); return 0; } 无缓冲:stderr是无缓冲 #include<stdio.h> #include<unistd.h> int main(void) { fprintf(stderr,"wechat:shouwangxiansheng"); sleep(10); return 0; } 疑问解析 android中的打印为啥不会出现不出现打印的情况呢? logd守护进程是Android系统中负责接收、过滤和处理日志信息的组件之一。它在内部使用了缓冲机制来管理和处理日志数据。 具体来说,logd守护进程的缓冲机制包括以下几个方面:
- 日志缓冲区:logd维护了多个日志缓冲区,用于存储不同级别的日志信息。这些缓冲区以环形缓冲区的形式组织,可以容纳一定数量的日志条目。
- 缓冲区分级:logd将不同级别的日志信息分别存储在不同的缓冲区中。常见的缓冲区级别包括Main、System、Events等,每个缓冲区都有对应的大小限制。
- 日志条目格式化:当日志信息通过__android_log_printf函数或其他方式提交给logd时,logd会将其格式化为一定的结构体格式,并将该结构体放入相应的缓冲区中。
- 缓冲区切换:当一个缓冲区已满时,logd会自动切换到下一个可用的缓冲区。这样可以确保日志信息的连续记录,并且不会因为缓冲区满导致丢失日志。
- 日志写入:logd会定期将缓冲区中的日志信息写入到日志文件中,以便长期存储和查看。写入操作可以由定时触发、缓冲区满触发或手动触发。