首页 > 其他分享 >printf函数与缓冲区 --20240310

printf函数与缓冲区 --20240310

时间:2024-03-10 16:46:42浏览次数:33  
标签:__ 20240310 -- 缓冲 写入 logd printf 缓冲区 日志

在linux下,printf输出到控制台经历了app->libc(stdio.h)->syscall->console驱动   下面是<<linux内核完全注释>>一书中的一段描述: 0 继续看下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:表示要写入的字节数。
write()函数会尝试将count个字节的数据从buf指向的缓冲区写入到文件描述符fd所代表的文件中。如果成功,它会返回实际写入的字节数,如果失败,则返回-1,并设置全局变量errno来标识具体的错误原因。   write函数使用示例: 当使用write()函数将数据写入到文件中时,需要首先使用open()函数打开文件,并获取文件描述符,然后使用write()函数向文件描述符所代表的文件中写入数据。
#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守护进程的缓冲机制包括以下几个方面:
  1. 日志缓冲区:logd维护了多个日志缓冲区,用于存储不同级别的日志信息。这些缓冲区以环形缓冲区的形式组织,可以容纳一定数量的日志条目。
  2. 缓冲区分级:logd将不同级别的日志信息分别存储在不同的缓冲区中。常见的缓冲区级别包括Main、System、Events等,每个缓冲区都有对应的大小限制。
  3. 日志条目格式化:当日志信息通过__android_log_printf函数或其他方式提交给logd时,logd会将其格式化为一定的结构体格式,并将该结构体放入相应的缓冲区中。
  4. 缓冲区切换:当一个缓冲区已满时,logd会自动切换到下一个可用的缓冲区。这样可以确保日志信息的连续记录,并且不会因为缓冲区满导致丢失日志。
  5. 日志写入:logd会定期将缓冲区中的日志信息写入到日志文件中,以便长期存储和查看。写入操作可以由定时触发、缓冲区满触发或手动触发。
需要注意的是,以上是对logd守护进程缓冲机制的一般描述,具体的实现可能会因Android系统版本和配置而有所不同。此外,Android系统还提供了一些配置选项和接口,允许开发者进行一定的定制和操作,以满足特定需求和场景下的日志输出要求。 logd守护进程的缓冲机制并不是严格的行缓冲方式。虽然logd会将日志信息存储在缓冲区中,但它并不是按照每行进行缓冲和刷新的。 具体来说,当应用程序调用__android_log_printf函数或其他方式提交日志信息给logd时,logd将接收到的日志信息格式化为一定的结构体,并将该结构体放入相应的日志缓冲区中。这里的缓冲区是以环形缓冲区的形式组织的,可以容纳一定数量的日志条目。 logd并不会立即将日志缓冲区中的内容写入到日志文件中,而是在一定的条件下触发写入操作。这些条件包括缓冲区大小达到上限、定时触发、系统事件触发等。因此,logd的缓冲机制更倾向于批量写入而非每次都进行行缓冲和刷新操作。   总之,android日志系统并不是简单的行缓冲方式,具体不用去深究。 也给到我们一种跟性能有关的方法,kill -9 logd    

标签:__,20240310,--,缓冲,写入,logd,printf,缓冲区,日志
From: https://www.cnblogs.com/lethe1203/p/18064338

相关文章

  • 站点主动信息收集
    1.二层主机发现-arping/netdiscovet命令探索1.1利用osi中链路层的协议进行发现,一般使用arp协议1.2优缺点:速度快可靠性高(优)不可路由(缺)1.3使用apring命令查看局域网中的主机是否存活arping192.168.10.12-c5-c参数表示发送的次数1.4使用Netdiscover一个主动/被动的arp......
  • [BalticOI 2017] Toll
    做法很多,本人使用线段树。原图可以看作分层DAG,每层结点有\(k\)个,而\(k\le5\)。假设每层的点编号\(0\simk-1\)。从\(l\)到\(r\)层的路径,在线段树上用区间\([l,r-1]\)表示。线段树上每个结点都存储表示最段路的矩阵,合并时使用Floyd。另外,需要特判询问中是否两个点......
  • CF763E Timofey and our friends animals
    使用回滚莫队可以有效降低思维含量。对于回滚莫队和可撤销并查集,本文不再赘述。假设当前询问是\([L,R]\),目前加入了\([l,r]\)的所有点和边。将\(r\)增加\(1\)时,连边\((r+1,v\in[l,r])\)。然后需要处理左边的散块。对于所有零散的\(l\),连边\((l,v\in[L,R])\)。上述两......
  • java.net.UnknownHostException: api.weixin.qq.com解决办法
    java.net.UnknownHostException: api.weixin.qq.comat java.net.AbstractPlainSocketImpl.connect(AbstractPlainSocketImpl.java:175)at java.net.SocksSocketImpl.connect(SocksSocketImpl.java:384)at java.net.Socket.connect(Socket.java:546)at sun.security.ssl.SSLSo......
  • linux查看资源使用情况
    linux查看资源使用情况top-c#查看资源使用情况top输出如下内容top-14:54:21up95days,20:03,3users,loadaverage:2072.21,1241.33,1244.76Tasks:1071total,459running,610sleeping,2stopped,0zombie%Cpu(s):12.4us,36.1sy,0.0ni,51......
  • NetCore 依赖注入
    .AddTransient<IFoo,Foo>()////ImplementationType根据类型.AddScoped<IBar>(_=>newBar())//ImplementationFactory通过工厂创建.AddSingleton<IBaz>(newBaz());//ImplementationInstance实现实例添加对象注册并注册不同作用域,并遍历注册到容器内。1Implementat......
  • ssh进阶,免密登录
    免密登录(重点)公钥:一串字符串,在非对称加密里面用来加密数据,随意公开。私钥:一串字符串,在非对称加密里面用来解密数据,不能泄露。你原本是用root的密码进行身份验证,登录该服务器客户端更换为公钥形式登录普通密钥:一串字符串。在对称加密里面,加密和解密都用它基于公私钥的......
  • Case.1云霄飞车杀人事件
    Case.1云霄飞车杀人事件File.1平成年代的福尔摩斯基本信息:《名侦探柯南》第一话开启了该作品长达30余年的连载登场人物:工藤新一毛利兰毛利小五郎目暮警官琴酒伏特加在本话中,工藤新一的出场伴随着一起案子的圆满解决,他不断展现出了对于推理的热情与当一名侦探的追求,而问......
  • 数据类型的遍历
    数据类型的遍历:for循环语句1.for循环用来遍历序列2.通过不使用下标的方式来实现对序列中每一个元素的访问3.遍历的对象:列表,元组,字符串,字典,集合遍历列表:#遍历列表a=[1,2,3,4,5]forelementina:print(element,end="")print()#遍历列表的元素数据,......
  • Gym-101915D 题解
    D给定一张图,分为左右各\(P\)个点,左右各自内部是一个完全图,左右之间有\(m\)条边。求这个图的最大团。\(P\le20,m\leP^2\)。对于每个右部点,求出一个长度为\(20\)的二进制数,第\(i\)位是\(1\)表示它与左部第\(i\)点有连边。枚举右部点的子集\(S\),将它们的二进制数......