1.linux系统调用和文件IO(ppt)
- 格式:pdf
- 大小:339.29 KB
- 文档页数:18
linux操作系统的组成1.内核(Kernel)Linux内核是整个Linux操作系统的核心,它负责管理系统资源,包括硬件、内存、进程、文件系统等。
内核提供了一系列系统调用,用户空间程序可以通过这些系统调用来访问内核提供的功能。
2.用户空间(User Space)用户空间是操作系统中除内核之外的部分。
用户空间包括Shell、图形界面、应用程序等。
用户空间通过系统调用来访问内核提供的功能。
用户空间和内核之间有一个保护机制,保证用户空间程序不能直接访问内核资源,只能通过系统调用。
3.ShellShell是Linux系统中的命令解释器,它充当了用户和内核之间的接口。
用户可以在Shell中输入命令,Shell解析命令并通过系统调用调用内核提供的功能。
Linux操作系统中常用的Shell有Bash、Zsh、Fish等。
4.文件系统(File System)Linux操作系统支持多种文件系统,包括Ext2、Ext3、Ext4、Btrfs、XFS等。
文件系统是管理文件和目录的机制,它负责在硬盘上分配空间,存储文件内容和元数据。
文件系统还提供了一些额外的功能,如权限管理、链接、快速查找等。
5.设备驱动程序(Device Driver)设备驱动程序是连接硬件设备和内核的桥梁,它转换设备的IO请求为内核能够理解的形式,并向内核提供设备的状态信息。
Linux操作系统支持多种设备驱动程序,包括字符设备驱动程序、块设备驱动程序、网络设备驱动程序等。
6.命令行工具(Command-Line Tool)Linux操作系统提供了丰富的命令行工具,可以轻松地完成各种任务。
常见的命令行工具有ls、cp、mv、mkdir、rm等,还有一些高级工具,如awk、sed、grep等。
7.图形界面(Graphical User Interface)Linux操作系统提供了多种图形界面,如GNOME、KDE、Xfce、LXDE等。
图形界面提供了一种更加友好的交互方式,用户可以通过鼠标点击、拖拽等方式完成操作,极大地提高了用户的工作效率。
c语言加快文件拷贝速度的方法文件拷贝作为计算机程序中常见的操作之一,效率对于大文件操作尤为重要。
以下是提高C语言文件拷贝速度的几种方法:1. 使用操作系统提供的系统调用:C语言可以使用操作系统提供的系统调用函数来进行文件拷贝。
例如,使用Linux系统的open()、read()和write()系统调用,或者使用Windows系统的CreateFile()、ReadFile()和WriteFile()系统调用。
这些系统调用是直接与操作系统内核交互的,因此可以更高效地操作文件。
2. 使用缓冲区:在C语言中,使用缓冲区可以减少硬盘读取和写入的次数,从而加快文件拷贝速度。
可以使用标准库函数如fread()和fwrite(),通过设置合适大小的缓冲区,一次读取或写入多个数据块,减少文件操作的次数。
3. 多线程或异步IO:使用多线程或异步IO可以实现并行的文件拷贝操作。
通过将文件分成多个块,并使用多个线程或异步IO来同时读取和写入文件,可以利用多核处理器的优势,加快文件拷贝速度。
4. 优化算法:对于大文件的拷贝操作,可以考虑使用基于块的拷贝算法,如循环拷贝和内存拷贝。
循环拷贝算法逐个字节地从源文件读取,并写入目标文件,直到拷贝完成。
内存拷贝算法则可以使用内存操作函数如memcpy(),一次性拷贝大块内存数据。
5. 避免额外的文件操作:尽量避免在拷贝文件过程中进行不必要的文件打开、关闭和定位操作。
这些额外操作会浪费时间,降低文件拷贝效率。
在拷贝前,可以预先创建目标文件,并仅执行必要的读取和写入操作。
通过使用操作系统提供的系统调用、合理使用缓冲区、多线程或异步IO、优化算法,并避免额外的文件操作,可以极大地提高C语言文件拷贝速度。
不同的方法可以结合使用,根据实际情况选择最适合的方式来优化文件拷贝操作,从而提高程序的效率。
linux选择ioctl命令在为 ioctl 编写代码之前, 你需要选择对应命令的数字. 许多程序员的第⼀个本能的反应是选择⼀组⼩数从0或1 开始, 并且从此开始向上. 但是,有充分的理由不这样做. ioctl 命令数字应当在这个系统是唯⼀的, 为了阻⽌向错误的设备发出正确的命令⽽引起的错误. 这样的不匹配不会不可能发⽣, 并且⼀个程序可能发现它⾃⼰试图改变⼀个⾮串⼝输⼊系统的波特率, 例如⼀个 FIFO 或者⼀个⾳频设备. 如果这样的 ioctl 号是唯⼀的, 这个应⽤程序得到⼀个 EINVAL 错误⽽不是继续做不应当做的事情.为帮助程序员创建唯⼀的 ioctl 命令代码, 这些编码已被划分为⼏个位段. Linux 的第⼀个版本使⽤ 16-位数: ⾼ 8 位是关联这个设备的"魔"数,低 8 位是⼀个顺序号, 在设备内唯⼀. 这样做是因为 Linus 是"⽆能"的(他⾃⼰的话); ⼀个更好的位段划分仅在后来被设想. 不幸的是, 许多驱动仍然使⽤⽼传统. 它们不得不: 改变命令编码会破坏⼤量的⼆进制程序,并且这不是内核开发者愿意见到的.根据 Linux 内核惯例来为你的驱动选择 ioctl 号, 你应当⾸先检查 include/asm/ioctl.h 和 Documentation/ioctl-number.txt. 这个头⽂件定义你将使⽤的位段: type(魔数), 序号, 传输⽅向, 和参数⼤⼩. ioctl-number.txt ⽂件列举了在内核中使⽤的魔数,[] 因此你将可选择你⾃⼰的魔数并且避免交叠. 这个⽂本⽂件也列举了为什么应当使⽤惯例的原因.定义 ioctl 命令号的正确⽅法使⽤ 4 个位段, 它们有下列的含义. 这个列表中介绍的新符号定义在 <linux/ioctl.h>.但是, 这个⽂件的维护在后来有些少见了.魔数. 只是选择⼀个数(在参考了 ioctl-number.txt 之后)并且使⽤它在整个驱动中. 这个成员是 8 位宽(_IOC_TYPEBITS).number序(顺序)号. 它是 8 位(_IOC_NRBITS)宽. direction数据传送的⽅向,如果这个特殊的命令涉及数据传送. 可能的值是 _IOC_NONE(没有数据传输), _IOC_READ, _IOC_WRITE, 和_IOC_READ|_IOC_WRITE (数据在 2 个⽅向被传送). 数据传送是从应⽤程序的观点来看待的; _IOC_READ 意思是从设备读, 因此设备必须写到⽤户空间. 注意这个成员是⼀个位掩码, 因此 _IOC_READ 和_IOC_WRITE 可使⽤⼀个逻辑 AND 操作来抽取.size涉及到的⽤户数据的⼤⼩. 这个成员的宽度是依赖体系的, 但是常常是 13 或者 14 位. 你可为你的特定体系在宏 _IOC_SIZEBITS 中找到它的值. 你使⽤这个 size 成员不是强制的 - 内核不检查它 -- 但是它是⼀个好主意. 正确使⽤这个成员可帮助检测⽤户空间程序的错误并使你实现向后兼容, 如果你曾需要改变相关数据项的⼤⼩. 如果你需要更⼤的数据结构, 但是, 你可忽略这个 size 成员. 我们很快见到如何使⽤这个成员.头⽂件 <asm/ioctl.h>, 它包含在 <linux/ioctl.h> 中, 定义宏来帮助建⽴命令号, 如下: _IO(type,nr)(给没有参数的命令), _IOR(type, nre, datatype)(给从驱动中读数据的), _IOW(type,nr,datatype)(给写数据), 和 _IOWR(type,nr,datatype)(给双向传送). type 和 number 成员作为参数被传递, 并且 size 成员通过应⽤ sizeof 到 datatype 参数⽽得到.这个头⽂件还定义宏, 可被⽤在你的驱动中来解码这个号: _IOC_DIR(nr),_IOC_TYPE(nr), _IOC_NR(nr), 和 _IOC_SIZE(nr). 我们不进⼊任何这些宏的细节, 因为头⽂件是清楚的, 并且在本节稍后有例⼦代码展⽰.这⾥是⼀些 ioctl 命令如何在 scull 被定义的. 特别地, 这些命令设置和获得驱动的可配置参数./* Use 'k' as magic number */#define SCULL_IOC_MAGIC 'k'/* Please use a different 8-bit number in your code */#define SCULL_IOCRESET _IO(SCULL_IOC_MAGIC, 0)/** S means "Set" through a ptr,* T means "Tell" directly with the argument value* G means "Get": reply by setting through a pointer* Q means "Query": response is on the return value* X means "eXchange": switch G and S atomically* H means "sHift": switch T and Q atomically*/#define SCULL_IOCSQUANTUM _IOW(SCULL_IOC_MAGIC, 1, int)#define SCULL_IOCSQSET _IOW(SCULL_IOC_MAGIC, 2, int)#define SCULL_IOCTQUANTUM _IO(SCULL_IOC_MAGIC, 3)#define SCULL_IOCTQSET _IO(SCULL_IOC_MAGIC, 4)#define SCULL_IOCGQUANTUM _IOR(SCULL_IOC_MAGIC, 5, int)#define SCULL_IOCGQSET _IOR(SCULL_IOC_MAGIC, 6, int)#define SCULL_IOCQQUANTUM _IO(SCULL_IOC_MAGIC, 7)#define SCULL_IOCQQSET _IO(SCULL_IOC_MAGIC, 8)#define SCULL_IOCXQUANTUM _IOWR(SCULL_IOC_MAGIC, 9, int)#define SCULL_IOCXQSET _IOWR(SCULL_IOC_MAGIC,10, int)#define SCULL_IOCHQUANTUM _IO(SCULL_IOC_MAGIC, 11)#define SCULL_IOCHQSET _IO(SCULL_IOC_MAGIC, 12)#define SCULL_IOC_MAXNR 14 真正的源⽂件定义⼏个额外的这⾥没有出现的命令.我们选择实现 2 种⽅法传递整数参数: 通过指针和通过明确的值(尽管, 由于⼀个已存在的惯例, ioclt 应当通过指针交换值). 类似地, 2 种⽅法被⽤来返回⼀个整数值:通过指针和通过设置返回值. 这个有效只要返回值是⼀个正的整数; 如同你现在所知道的, 在从任何系统调⽤返回时,⼀个正值被保留(如同我们在 read 和 write 中见到的), ⽽⼀个负值被看作⼀个错误并且被⽤来在⽤户空间设置 errno.[]"exchange"和"shift"操作对于 scull 没有特别的⽤处. 我们实现"exchange"来显⽰驱动如何结合独⽴的操作到单个的原⼦的操作, 并且"shift"来连接"tell"和"query". 有时需要象这样的原⼦的测试-和-设置操作, 特别地, 当应⽤程序需要设置和释放锁.命令的明确的序号没有特别的含义. 它只⽤来区分命令. 实际上, 你甚⾄可使⽤相同的序号给⼀个读命令和⼀个写命令, 因为实际的 ioctl 号在"⽅向"位是不同的, 但是你没有理由这样做. 我们选择在任何地⽅不使⽤命令的序号除了声明中, 因此我们不分配⼀个返回值给它. 这就是为什么明确的号出现在之前给定的定义中. 这个例⼦展⽰了⼀个使⽤命令号的⽅法, 但是你有⾃由不这样做.除了少数⼏个预定义的命令(马上就讨论), ioctl 的 cmd 参数的值当前不被内核使⽤, 并且在将来也很不可能. 因此, 你可以, 如果你觉得懒, 避免前⾯展⽰的复杂的声明并明确声明⼀组调整数字. 另⼀⽅⾯, 如果你做了, 你不会从使⽤这些位段中获益, 并且你会遇到困难如果你曾提交你的代码来包含在主线内核中. 头⽂件<linux/kd.h> 是这个⽼式⽅法的例⼦, 使⽤ 16-位的调整值来定义 ioctl 命令. 那个源代码依靠调整数因为使⽤那个时候遵循的惯例, 不是由于懒惰. 现在改变它可能导致⽆理由的不兼容.。
Linux高性能网络编程之系统调用过程简析第一部分:基础A PI1、主机字节序和网络字节序我们都知道字节序分位大端和小端:•大端是高位字节在低地址,低位字节在高地址•小端是顺序字节存储,高位字节在高地址,低位字节在低地址既然机器存在字节序不一样,那么网络传输过程中必然涉及到发出去的数据流需要转换,所以发送端会将数据转换为大端模式发送,系统提供API实现主机字节序和网络字节序的转换。
#include < netinet/in.h >// 转换长整型unsigned long htonl(unsigned long int hostlong);unsigned long ntohl(unsigned long int netlong);// 转换短整型unsigned short htonl(unsigned short inthostshort);unsigned short ntohl(unsigned short int netshort);2、socket地址(1)socket地址包含两个部分,一个是什么协议,另一个是存储数据,如下:struct sock ad dr{sa_family_t sa_family; // 取值:PF_UNIX(UNIX本地协议簇),PF_INET(ipv4),PF_INET6(ipv6)char sa_data[14]; // 根据上面的协议簇存储数据(UNIX本地路径,ipv4端口和IP,ipv6端口和IP)};(2)各个协议簇专门的结构体// unix本地协议簇struct sockaddr_un{sa_family_t sin_family; // AF_UNIXchar sun_path[18];};// ipv4本地协议簇struct sockaddr_in{sa_family_t sin_family; // AF_INETu_int16_t sin_port;struct in_addr sin_addr;};// ipv6本地协议簇struct sockaddr_in6{sa_family_t sin_family; // AF_INET6u_int16_t sin6_port;u_int32_t sin6_flowinfo;...};3、socket创建socket,bind,listen,ac cept,connect,close和shutdown作为linux网络开发必备知识,大家应该都都耳熟能详了,所以我就简单介绍使用方式,重点介绍参数注意事项。
文件操作的一般步骤(1)在linux系统中要操作一个文件,一般是先open打开一个文件,得到一个文件描述符,然后对文件进行读写操作(或其他操作),最后close关闭文件即可(2)强调一点:我们对文件进行操作时,一定要先打开文件,打开成功后才能去操作(如果打开本身失败,后面就不用操作了);最后读写完成之后一定要close关闭文件,否则可能会造成文件损坏。
(3)文件平时是存在块设备中的文件系统中的,我们把这种文件叫静态文件。
当我们去open打开一个文件时,linux内核在进程中建立了一个打开文件的(进程控制块PCB (process control block)),记录下我们打开的这个文件;内核在内存中申请一段内存,将静态文件的内容从块设备中读取到内存中(叫动态文件)。
(4)打开文件后,以后对这个文件的读写操作,都是针对内存中这一份动态文件的,而并不是针对静态文件的。
当我们对动态文件进行读写后,此时内存中的动态文件和块设备中的静态文件就不同步了,当我们close关闭动态文件时,close内部内核将内存中的动态文件的内容去更新(同步)块设备中的静态文件。
(5)常见的一些现象:第一个:打开一个大文件时比较慢第二个:我们写了一半的文件,如果没有点保存直接关机/断电,重启后文件内容丢失。
文件描述符:文件描述符就是用来区分一个程序打开的多个文件的。
作用域:当前进程,出了当前进程这个文件描述符就没有意义了ssize_t read(int fd, void *buf, size_t count) 特点:非阻塞,但是设备文件(键盘鼠标)会阻塞文件操作函数fd表示要读取哪个文件,fd一般由前面的open返回得到buf是应用程序自己提供的一段内存缓冲区,读到缓存区里面count是我们要读取的字节数ssize_t write(int fd, const void *buf, size_t count);buf是输入型参数,写到文件里int open(const char *path, int oflag, ... );1) 读写权限:O_RDONLY O_WRONLY O_RDWR2) 打开存在并有内容的文件时:O_APPEND、O_TRUNC3)如果O_APPEND和O_TRUNC同时出现,会忽略O_APPEND 去执行 O_TRUNC4)打开不存在的文件时:O_CREAT、O_EXCL联合使用open中加入O_CREAT后,不管原来这个文件存在与否都能打开成功,如果原来这个文件不存在则创建一个空的新文件,如果原来这个文件存在则会重新创建这个文件,原来的内容会被消除掉(有点类似于先删除原来的文件再创建一个新的)5)O_NONBLOCK打开一个文件默认就是阻塞式的,如果你希望以非阻塞的方式打开文件,则flag中要加O_NONBLOCK标志。
文件操作主讲人主讲人::李奎本章学习目标•文件操作在linux linux系统编程中系统编程中系统编程中,,通过linux linux系统调用系统调用操作文件操作文件,,完成本章学习应该能够通过linux linux系统调用系统调用操作文件以下部分:•创建文件•读和写文件•更新文件内容文件操作理论及原理介绍•Linux Linux系统调用系统调用所谓系统调用是操作系统提供给用户程序的一组所谓系统调用是操作系统提供给用户程序的一组““特殊特殊””接口接口,,用户程序可以通过这组用户程序可以通过这组““特殊特殊””接口来获得操作系统内核提供的特殊服务统内核提供的特殊服务。
在linux linux中用户程序不能直接访问中用户程序不能直接访问内核提供的服务内核提供的服务,,为了更好的保护内核空间为了更好的保护内核空间,,将程序的运行空间分为内核空间和用户空间行空间分为内核空间和用户空间,,它们运行在不同的级别上,在逻辑上是相互隔离的在逻辑上是相互隔离的。
用户程序接口用户程序接口((API API))在linux linux中用户编程接口中用户编程接口中用户编程接口((API API))遵循了在遵循了在UNIX UNIX UNIX中最流行的中最流行的应用编程界面标准应用编程界面标准——POSIX POSIX标准标准标准。
这些系统调用编程接口主要通过要通过C C 库(libc.so libc.so))实现的实现的。
系统调用系统调用、、API API与系统命令之间的关系与系统命令之间的关系文件文件I/O I/O I/O函数函数•可用的文件可用的文件I / O I / O I / O函数函数打开文件打开文件、、读文件读文件、、写文件等等写文件等等。
大多数大多数linux linux linux文件文件文件I / O I / O I / O只需用到只需用到只需用到55个函数个函数::open open、、read read、、write write、、lseek lseek 以及以及以及close close close。
open open函数函数#include <sys/types.h>#include <sys/stat.h>#include <fcntl.h>int open(const char *pathname, int oflag, …/*, mode_t mode * / ) ;返回返回::若成功为文件描述符若成功为文件描述符,,若出错为若出错为--1文件描述符对于内核而言对于内核而言,,所有打开文件都由文件描述符引用所有打开文件都由文件描述符引用。
文件描述符是一个非负整数描述符是一个非负整数。
当打开一个现存文件或创建一个新文件时新文件时,,内核向进程返回一个文件描述符内核向进程返回一个文件描述符。
当读当读、、写一个文件时个文件时,,用o p e n 返回的文件描述符标识该文件返回的文件描述符标识该文件,,将其作为参数传送给r e a d 或w r i t e 。
在P O S I X . 1P O S I X . 1应用程序中应用程序中应用程序中,,整数整数00、1、2应被代换成符号常数号常数::STDIN_FILENOSTDOUT_FILENOSTDERR_FILENO这些常数都定义在头文件<unistd.h><unistd.h>中中。
文件描述符的范围是文件描述符的范围是0 ~ OPEN_MAX 0 ~ OPEN_MAX 0 ~ OPEN_MAX 。
早期的早期的UNIX UNIX UNIX版本版本采用的上限值是采用的上限值是1 9 (1 9 (1 9 (允许每个进程打开允许每个进程打开允许每个进程打开2 02 02 0个文件个文件个文件)),现在很多系统则将其增加至很多系统则将其增加至256256。
open open函数说明函数说明参数参数pathname pathname pathname 指向想要打开的文件路径字符串指向想要打开的文件路径字符串指向想要打开的文件路径字符串。
参数参数flags flags flags 表示打开文件的方式表示打开文件的方式表示打开文件的方式,,例如例如::O_RDONLY 以只读方式打开文件O_WRONLY 以只写方式打开文件O_RDWR 以读写方式打开文件以上三种打开方式是互斥的以上三种打开方式是互斥的,,即flags flags只能选择一种只能选择一种只能选择一种,,但可以利用以利用’’|’运算符组合运算符组合。
O_APPEND 每次写时都加到文件的尾端每次写时都加到文件的尾端。
O_CREAT 若此文件不存在则创建它若此文件不存在则创建它。
使用此选择项时使用此选择项时,,需同时说明第三个参数同时说明第三个参数mode mode mode,,用其说明该新文件的存取许可权位权位。
O_EXCL 如果同时指定了如果同时指定了O_CREAT O_CREAT O_CREAT,,而文件已经存在而文件已经存在,,则出错。
这可测试一个文件是否存在这可测试一个文件是否存在,,如果不存在则创建此文件成为一个原子操作件成为一个原子操作。
O_TRUNC 如果此文件存在如果此文件存在,,而且为只读或只写成功打开而且为只读或只写成功打开,,则将其长度截短为则将其长度截短为00。
perror perror函数说明函数说明#include <stdio.h>void perror(const char *s);函数说明函数说明::–perror perror perror函数用来将上一个函数发生的错误的原因输出到函数用来将上一个函数发生的错误的原因输出到标准错误标准错误((stderr stderr)。
)。
)。
参数参数参数s s 所指的字符串会先打印所指的字符串会先打印,,后面再输出错误原因的字符串面再输出错误原因的字符串。
此错误原因依照全局此错误原因依照全局errno errno 的值来决定要输出的字符串的值来决定要输出的字符串。
–返回值返回值无close close函数函数close 函数用于关闭一个的打开文件#include <unistd.h>int close (int filedes );返回返回::若成功为若成功为00,若出错为若出错为--1当一个进程终止时当一个进程终止时,,它所有的打开文件都由内核自动关闭关闭。
很多程序都使用这一功能而不显式地用很多程序都使用这一功能而不显式地用c l o s e c l o s e c l o s e关关闭打开的文件闭打开的文件。
read read函数函数#include <unistd.h>ssize_t read(int fd,void* buf,sszie_t count);函数说明read read函数由以打开的文件读取数据函数由以打开的文件读取数据函数由以打开的文件读取数据,,read read函数会把参数函数会把参数函数会把参数fd fd fd所指的文所指的文件传送件传送count count count个字节到个字节到个字节到buf buf buf指针所指的内存中指针所指的内存中指针所指的内存中。
若参数若参数count count count为为0,则read read不会有作用并返回不会有作用并返回不会有作用并返回00。
返回值为实际从文件中读到的字节数返回值为实际从文件中读到的字节数,,如果返回如果返回00,表示已到达文件尾或是无可读取的数据表示已到达文件尾或是无可读取的数据,,此外文件读写位置会随读取到的字节移动写位置会随读取到的字节移动。
•备注–如果顺利如果顺利如果顺利read read read会返回实际读到的字节数会返回实际读到的字节数会返回实际读到的字节数,,最好能将返回值与参数值与参数count count count做比较做比较做比较,,若返回的字节数比若返回的字节数比count count count的值小的值小,则有可能读到了文件尾则有可能读到了文件尾、、从管道或是终端读取从管道或是终端读取,,或是read read被信号中断了读取动作被信号中断了读取动作被信号中断了读取动作。
当有错误发生时当有错误发生时,,返回返回--1,错误原因存入错误原因存入errno errno errno中中,发生这类错误时发生这类错误时,,文件读写位置无法预期置无法预期。
write write函数函数#include <unistd.h>ssize_t write ssize_t write((int fd int fd,,const void* buf const void* buf,,size_t count size_t count););函数说明–函数函数函数write write write会把参数会把参数会把参数buf buf buf所指的内存写入所指的内存写入所指的内存写入count count count个字节到参个字节到参数fd fd所指的文件中所指的文件中所指的文件中,,读写位置会随之移动读写位置会随之移动。
–返回值返回值如果顺利如果顺利write write write会返回实际写入的字节数会返回实际写入的字节数会返回实际写入的字节数。
当有错误发生时错误发生时,,则返回则返回--1;错误代码存放入错误代码存放入errno errno errno中中。
fdopen fdopen函数函数#include <stdio.h>FILE *fdopen(int fildes,const char *mode);函数说明fdopen fdopen将将fildes fildes文件描述符转换为文件指针后返回文件描述符转换为文件指针后返回文件描述符转换为文件指针后返回,,mode mode表示文件表示文件指针的流形态指针的流形态,,与原来文件描述符的读写形式相同与原来文件描述符的读写形式相同。
返回值正确返回指向该流的文件指针正确返回指向该流的文件指针,,错误错误NULL NULLfgets fgets函数说明函数说明#include <stdio.h>char* fgets(char* s,int size,FILE* stream);•函数说明函数函数fgets fgets fgets用来从参数用来从参数用来从参数stream stream stream所指的文件内读入字符并存到参数所指的文件内读入字符并存到参数所指的文件内读入字符并存到参数s s 所指的内存空间指的内存空间,,直到出现换行字符直到出现换行字符、、读到文件尾或是已读了读到文件尾或是已读了size size size--1个字符为止个字符为止,,最后会加上最后会加上NULL NULL NULL作为字符串结束作为字符串结束返回值成功返回成功返回s s 指针指针,,错误返回错误返回NULL NULL。