glibc-2.23 puts源码分析

2024-03-06 10:59
文章标签 分析 源码 glibc 2.23 puts

本文主要是介绍glibc-2.23 puts源码分析,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

在分析puts代码之前先看一些基本的知识:
一些flag:

#define _IO_USER_BUF 1                /* User owns buffer; don't delete it on close. */
#define _IO_UNBUFFERED 2              /* 无缓冲,此时会使用_IO_FILE内部的shortbuf作为缓冲区 */
#define _IO_NO_READS 4                /* Reading not allowed */
#define _IO_NO_WRITES 8               /* Writing not allowd */
#define _IO_EOF_SEEN 0x10             //读到末尾了
#define _IO_ERR_SEEN 0x20             //出错了
#define _IO_DELETE_DONT_CLOSE 0x40    /* Don't call close(_fileno) on cleanup. */
#define _IO_LINKED 0x80               /* Set if linked (using _chain) to streambuf::_list_all.*/
#define _IO_IN_BACKUP 0x100				//不太明白这个
#define _IO_LINE_BUF 0x200            //行缓冲#define _IO_TIED_PUT_GET 0x400        /* Set if put and get pointer logicly tied. */#define _IO_CURRENTLY_PUTTING 0x800   //当前正在输出......什么时候会被设置.  
#define _IO_IS_APPENDING 0x1000       //append fopen的时候mode里面有 + 
#define _IO_IS_FILEBUF 0x2000         //不太明白
#define _IO_BAD_SEEN 0x4000           //出错了.#define _IO_USER_LOCK 0x8000          //有锁????

_IO_FILE结构 :

struct _IO_FILE {int _flags;			/* High-order word is _IO_MAGIC; rest is flags. *///高两字节是固定的magic,低两字节被用来储存flags/* The following pointers correspond to the C++ streambuf protocol. *//* Note:  Tk uses the _IO_read_ptr and _IO_read_end fields directly. *///读相关char* _IO_read_ptr;	/* Current read pointer */char* _IO_read_end;	/* End of get area. */char* _IO_read_base;	/* Start of putback+get area. *///写相关char* _IO_write_base;	/* Start of put area. */char* _IO_write_ptr;	/* Current put pointer. */char* _IO_write_end;	/* End of put area. *///缓冲区char* _IO_buf_base;	/* Start of reserve area. */char* _IO_buf_end;	/* End of reserve area. *//* The following fields are used to support backing up and undo. */ //撤销操作???//什么时候会有撤销操作char *_IO_save_base;    /* Pointer to start of non-current get area. */char *_IO_backup_base;  /* Pointer to first valid character of backup area */char *_IO_save_end;     /* Pointer to end of non-current get area. */struct _IO_marker *_markers;struct _IO_FILE *_chain;      //File 链int _fileno;                  //fd文件描述符
#if 0int _blksize;
#elseint _flags2;                  //
#endif_IO_off_t _old_offset; /* This used to be _offset but it's too small.  */#define __HAVE_COLUMN /* temporary *//* 1+column number of pbase(); 0 is unknown. */unsigned short _cur_column;     //signed char _vtable_offset;     //虚表偏移char _shortbuf[1];              //无缓冲的时候要用这个作为缓冲区./*  char* _save_gptr;  char* _save_egptr; */_IO_lock_t *_lock;              //🔒
#ifdef _IO_USE_OLD_IO_FILE
};

vtable:

#ifdef _LIBC
versioned_symbol (libc, _IO_new_do_write, _IO_do_write, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_attach, _IO_file_attach, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_close_it, _IO_file_close_it, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_finish, _IO_file_finish, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_fopen, _IO_file_fopen, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_init, _IO_file_init, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_setbuf, _IO_file_setbuf, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_sync, _IO_file_sync, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_overflow, _IO_file_overflow, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_seekoff, _IO_file_seekoff, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_underflow, _IO_file_underflow, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_write, _IO_file_write, GLIBC_2_1);
versioned_symbol (libc, _IO_new_file_xsputn, _IO_file_xsputn, GLIBC_2_1);
#endifconst struct _IO_jump_t _IO_file_jumps =
{JUMP_INIT_DUMMY,JUMP_INIT(finish, _IO_file_finish),JUMP_INIT(overflow, _IO_file_overflow),JUMP_INIT(underflow, _IO_file_underflow),JUMP_INIT(uflow, _IO_default_uflow),JUMP_INIT(pbackfail, _IO_default_pbackfail),JUMP_INIT(xsputn, _IO_file_xsputn),JUMP_INIT(xsgetn, _IO_file_xsgetn),JUMP_INIT(seekoff, _IO_new_file_seekoff),JUMP_INIT(seekpos, _IO_default_seekpos),JUMP_INIT(setbuf, _IO_new_file_setbuf),JUMP_INIT(sync, _IO_new_file_sync),JUMP_INIT(doallocate, _IO_file_doallocate),JUMP_INIT(read, _IO_file_read),JUMP_INIT(write, _IO_new_file_write),JUMP_INIT(seek, _IO_file_seek),JUMP_INIT(close, _IO_file_close),JUMP_INIT(stat, _IO_file_stat),JUMP_INIT(showmanyc, _IO_default_showmanyc),JUMP_INIT(imbue, _IO_default_imbue)
};

puts函数
在这里插入图片描述
接着puts函数进入了_IO_sputn,看一下它的定义
在这里插入图片描述
是调用了vtable里面的 xsputn函数,从上面的那个结构体初始化可以看出,就是_IO_file_xsputn函数,也就是_IO_new_file_xsputn函数

转到_IO_new_file_xsputn函数看一下:

_IO_size_t _IO_new_file_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{const char *s = (const char *) data;_IO_size_t to_do = n;             //to_do是剩余要输出的长度int must_flush = 0;               //标是否要输出缓冲区内容_IO_size_t count = 0;             //输出缓冲区剩余部分大于 所要输出的长度if (n <= 0)return 0;if ((f->_flags & _IO_LINE_BUF) && (f->_flags & _IO_CURRENTLY_PUTTING)){count = f->_IO_buf_end - f->_IO_write_ptr;//看一下缓冲区剩余的空间还有多少,如果无法把data拷贝进去,那么会输出缓冲区内容,//这里是看一下把data可以全部拷贝到缓冲区的情况,若指定了行缓冲,那么就找一下是否存在\n,//存在的话就需要flush 输出缓冲区。if (count >= n){const char *p;for (p = s + n; p > s; ){if (*--p == '\n'){count = p - s + 1;must_flush = 1;break;}}}}else if (f->_IO_write_end > f->_IO_write_ptr)count = f->_IO_write_end - f->_IO_write_ptr; /* Space available. *//* Then fill the buffer. */if (count > 0){if (count > to_do)                      //先尽可能的把数据拷贝到缓冲区count = to_do;
#ifdef _LIBCf->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
#elsememcpy (f->_IO_write_ptr, s, count);f->_IO_write_ptr += count;            
#endifs += count;to_do -= count;}if (to_do + must_flush > 0)                         //还有数据没有拷贝到缓冲区或者遇到了行缓冲,这时需要flush缓冲区,{_IO_size_t block_size, do_write;/* Next flush the (full) buffer. */if (_IO_OVERFLOW (f, EOF) == EOF)                  //flush 缓冲区,并且重置write_base,write_ptr,write_end指针./* If nothing else has to be written we must not signal thecaller that everything has been written.  */return to_do == 0 ? EOF : n - to_do;/* Try to maintain alignment: write a whole number of blocks.  */block_size = f->_IO_buf_end - f->_IO_buf_base;do_write = to_do - (block_size >= 128 ? to_do % block_size : 0);//block_size = 128;//剩余部分的大小 拆成 block_size * k + r.//do_write = block_size * kif (do_write){count = new_do_write (f, s, do_write);          //大块输出,直接sys_write.to_do -= count;if (count < do_write)return n - to_do;}//除去整块大小剩余的部分,调用_IO_default_xsputn if (to_do)to_do -= _IO_default_xsputn (f, s+do_write, to_do); //把剩余的r 输出,(一定是小于 block_size )}return n - to_do;
}

代码逻辑比较简单,这里就不多说明了。
该函数里面调用了三个函数:

	_IO_OVERFLOW (f, EOF) == EOFnew_do_write (f, s, do_write);_IO_default_xsputn (f, s+do_write, to_do);

继续分析这三个函数:

_IO_OVERFLOW :
这个实际上是调用了_IO_file_overflow函数,代码里面的名称是_IO_new_file_overflow,
可以由函数名大概猜出,该函数的就是用于当输出缓冲区满了的时候,继续写会溢出,所以要flush输出缓冲区,同样还有_IO_file_underflow函数是read时候缓冲区内容不够,重新往缓冲区读入数据

//溢出.
int _IO_new_file_overflow (_IO_FILE *f, int ch)
{//如果不可写的话直接返回,这个是flag是根据打开文件的方式而设置的if (f->_flags & _IO_NO_WRITES) /* SET ERROR */{f->_flags |= _IO_ERR_SEEN;__set_errno (EBADF);return EOF;}///* If currently reading or no buffer allocated. */if ((f->_flags & _IO_CURRENTLY_PUTTING) == 0 || f->_IO_write_base == NULL){//这里目前还不太明白什么时候会调用他.........../* Allocate a buffer if needed. */if (f->_IO_write_base == NULL){_IO_doallocbuf (f);_IO_setg (f, f->_IO_buf_base, f->_IO_buf_base, f->_IO_buf_base);}/* Otherwise must be currently reading.If _IO_read_ptr (and hence also _IO_read_end) is at the buffer end,logically slide the buffer forwards one block (by setting theread pointers to all point at the beginning of the block).  Thismakes room for subsequent output.Otherwise, set the read pointers to _IO_read_end (leaving thatalone, so it can continue to correspond to the external position). */if (__glibc_unlikely (_IO_in_backup (f))){size_t nbackup = f->_IO_read_end - f->_IO_read_ptr;_IO_free_backup_area (f);f->_IO_read_base -= MIN (nbackup,f->_IO_read_base - f->_IO_buf_base);f->_IO_read_ptr = f->_IO_read_base;}if (f->_IO_read_ptr == f->_IO_buf_end)f->_IO_read_end = f->_IO_read_ptr = f->_IO_buf_base;f->_IO_write_ptr = f->_IO_read_ptr;f->_IO_write_base = f->_IO_write_ptr;f->_IO_write_end = f->_IO_buf_end;f->_IO_read_base = f->_IO_read_ptr = f->_IO_read_end;f->_flags |= _IO_CURRENTLY_PUTTING;if (f->_mode <= 0 && f->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))f->_IO_write_end = f->_IO_write_ptr;}if (ch == EOF)return _IO_do_write (f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base);//把缓冲区现有的数据输出.if (f->_IO_write_ptr == f->_IO_buf_end ) /* Buffer is really full */if (_IO_do_flush (f) == EOF)return EOF;*f->_IO_write_ptr++ = ch;if ((f->_flags & _IO_UNBUFFERED)|| ((f->_flags & _IO_LINE_BUF) && ch == '\n'))if (_IO_do_write (f, f->_IO_write_base,f->_IO_write_ptr - f->_IO_write_base) == EOF)return EOF;return (unsigned char) ch;
}

_IO_do_write代码:


int _IO_new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{return (to_do == 0|| (_IO_size_t) new_do_write (fp, data, to_do) == to_do) ? 0 : EOF;
}static _IO_size_t new_do_write (_IO_FILE *fp, const char *data, _IO_size_t to_do)
{_IO_size_t count;if (fp->_flags & _IO_IS_APPENDING)fp->_offset = _IO_pos_BAD;else if (fp->_IO_read_end != fp->_IO_write_base){_IO_off64_t new_pos = _IO_SYSSEEK (fp, fp->_IO_write_base - fp->_IO_read_end, 1);if (new_pos == _IO_pos_BAD) return 0;fp->_offset = new_pos;}count = _IO_SYSWRITE (fp, data, to_do);if (fp->_cur_column && count)fp->_cur_column = _IO_adjust_column (fp->_cur_column - 1, data, count) + 1;_IO_setg (fp, fp->_IO_buf_base, fp->_IO_buf_base, fp->_IO_buf_base);fp->_IO_write_base = fp->_IO_write_ptr = fp->_IO_buf_base;fp->_IO_write_end = (fp->_mode <= 0&& (fp->_flags & (_IO_LINE_BUF | _IO_UNBUFFERED))? fp->_IO_buf_base : fp->_IO_buf_end);return count;
}

_IO_default_xsputn 代码 (这个函数会把数据拷贝到缓冲区,然后在缓冲区满的时候flush缓冲区,直到把全部数据拷贝到缓冲区):

_IO_size_t _IO_default_xsputn (_IO_FILE *f, const void *data, _IO_size_t n)
{const char *s = (char *) data;_IO_size_t more = n;if (more <= 0)return 0;for (;;){/* Space available. */if (f->_IO_write_ptr < f->_IO_write_end)          {_IO_size_t count = f->_IO_write_end - f->_IO_write_ptr;if (count > more)count = more;//数量不同的时候使用不同的拷贝方式if (count > 20){
#ifdef _LIBCf->_IO_write_ptr = __mempcpy (f->_IO_write_ptr, s, count);
#elsememcpy (f->_IO_write_ptr, s, count);f->_IO_write_ptr += count;
#endifs += count;}else if (count){char *p = f->_IO_write_ptr;_IO_ssize_t i;for (i = count; --i >= 0; )*p++ = *s++;f->_IO_write_ptr = p;}more -= count;}//if (more == 0 || _IO_OVERFLOW (f, (unsigned char) *s++) == EOF)break;more--;}return n - more;
}

emmmmmm貌似只是把代码抄了一次…突然发现C语言的文件操作有很多不明白的地方…还是去看一下的C语言的文件操作,之后应该就能看懂代码为什么要这样写了…

这篇关于glibc-2.23 puts源码分析的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/779846

相关文章

性能分析之MySQL索引实战案例

文章目录 一、前言二、准备三、MySQL索引优化四、MySQL 索引知识回顾五、总结 一、前言 在上一讲性能工具之 JProfiler 简单登录案例分析实战中已经发现SQL没有建立索引问题,本文将一起从代码层去分析为什么没有建立索引? 开源ERP项目地址:https://gitee.com/jishenghua/JSH_ERP 二、准备 打开IDEA找到登录请求资源路径位置

JAVA智听未来一站式有声阅读平台听书系统小程序源码

智听未来,一站式有声阅读平台听书系统 🌟&nbsp;开篇:遇见未来,从“智听”开始 在这个快节奏的时代,你是否渴望在忙碌的间隙,找到一片属于自己的宁静角落?是否梦想着能随时随地,沉浸在知识的海洋,或是故事的奇幻世界里?今天,就让我带你一起探索“智听未来”——这一站式有声阅读平台听书系统,它正悄悄改变着我们的阅读方式,让未来触手可及! 📚&nbsp;第一站:海量资源,应有尽有 走进“智听

Java ArrayList扩容机制 (源码解读)

结论:初始长度为10,若所需长度小于1.5倍原长度,则按照1.5倍扩容。若不够用则按照所需长度扩容。 一. 明确类内部重要变量含义         1:数组默认长度         2:这是一个共享的空数组实例,用于明确创建长度为0时的ArrayList ,比如通过 new ArrayList<>(0),ArrayList 内部的数组 elementData 会指向这个 EMPTY_EL

如何在Visual Studio中调试.NET源码

今天偶然在看别人代码时,发现在他的代码里使用了Any判断List<T>是否为空。 我一般的做法是先判断是否为null,再判断Count。 看了一下Count的源码如下: 1 [__DynamicallyInvokable]2 public int Count3 {4 [__DynamicallyInvokable]5 get

SWAP作物生长模型安装教程、数据制备、敏感性分析、气候变化影响、R模型敏感性分析与贝叶斯优化、Fortran源代码分析、气候数据降尺度与变化影响分析

查看原文>>>全流程SWAP农业模型数据制备、敏感性分析及气候变化影响实践技术应用 SWAP模型是由荷兰瓦赫宁根大学开发的先进农作物模型,它综合考虑了土壤-水分-大气以及植被间的相互作用;是一种描述作物生长过程的一种机理性作物生长模型。它不但运用Richard方程,使其能够精确的模拟土壤中水分的运动,而且耦合了WOFOST作物模型使作物的生长描述更为科学。 本文让更多的科研人员和农业工作者

MOLE 2.5 分析分子通道和孔隙

软件介绍 生物大分子通道和孔隙在生物学中发挥着重要作用,例如在分子识别和酶底物特异性方面。 我们介绍了一种名为 MOLE 2.5 的高级软件工具,该工具旨在分析分子通道和孔隙。 与其他可用软件工具的基准测试表明,MOLE 2.5 相比更快、更强大、功能更丰富。作为一项新功能,MOLE 2.5 可以估算已识别通道的物理化学性质。 软件下载 https://pan.quark.cn/s/57

工厂ERP管理系统实现源码(JAVA)

工厂进销存管理系统是一个集采购管理、仓库管理、生产管理和销售管理于一体的综合解决方案。该系统旨在帮助企业优化流程、提高效率、降低成本,并实时掌握各环节的运营状况。 在采购管理方面,系统能够处理采购订单、供应商管理和采购入库等流程,确保采购过程的透明和高效。仓库管理方面,实现库存的精准管理,包括入库、出库、盘点等操作,确保库存数据的准确性和实时性。 生产管理模块则涵盖了生产计划制定、物料需求计划、

衡石分析平台使用手册-单机安装及启动

单机安装及启动​ 本文讲述如何在单机环境下进行 HENGSHI SENSE 安装的操作过程。 在安装前请确认网络环境,如果是隔离环境,无法连接互联网时,请先按照 离线环境安装依赖的指导进行依赖包的安装,然后按照本文的指导继续操作。如果网络环境可以连接互联网,请直接按照本文的指导进行安装。 准备工作​ 请参考安装环境文档准备安装环境。 配置用户与安装目录。 在操作前请检查您是否有 sud

线性因子模型 - 独立分量分析(ICA)篇

序言 线性因子模型是数据分析与机器学习中的一类重要模型,它们通过引入潜变量( latent variables \text{latent variables} latent variables)来更好地表征数据。其中,独立分量分析( ICA \text{ICA} ICA)作为线性因子模型的一种,以其独特的视角和广泛的应用领域而备受关注。 ICA \text{ICA} ICA旨在将观察到的复杂信号

Spring 源码解读:自定义实现Bean定义的注册与解析

引言 在Spring框架中,Bean的注册与解析是整个依赖注入流程的核心步骤。通过Bean定义,Spring容器知道如何创建、配置和管理每个Bean实例。本篇文章将通过实现一个简化版的Bean定义注册与解析机制,帮助你理解Spring框架背后的设计逻辑。我们还将对比Spring中的BeanDefinition和BeanDefinitionRegistry,以全面掌握Bean注册和解析的核心原理。