《linux从0到99》九基础io(代码片段)

RONIN_WZ RONIN_WZ     2022-11-28     505

关键词:

基础IO

1. 回顾c语言文件操作接口

点此回顾

2. 系统调用文件操作系统

01 open函数

open函数用于打开一个文件。
函数原型: int open(const char *pathname, int flags, mode_t mode);
头文件: #include<unistd.h>
参数:

  • pathname:打开文件或创建文件的名字.
  • flags:表示选项,用|连接多个选项
    flags选项宏的定义文件在每个系统中有所不同,Linux中定义在fcntl-linux.h文件中
  • mode参数仅在使用部分选项时才用到,例如O_CREAT在mode中需要给定文件初始权限

返回值: 成功返回打开的文件描述符,失败返回-1
flogs参数:

必选参数(有且仅有一个):

  • O_RDONLY : 以只读的方式打开
  • O_WRONLY : 以只写的方式打开
  • O_RDWR : 以可读可写的方式打开

可选参数:

  • O_TRUNC : 截断文件,清空文件内容
  • O_CREAT : 若文件不存在,则创建文件
  • O_APPEND : 以追加的方式打开
  • O_EXCL | O_CREAT : 如文件存在,则打开失败

mod参数:

当新打开一个文件时,给文件设置权限。设置权限时,传递一个8进程数就可以了

02 read函数

read函数用于从文件中读取数据。
函数原型: ssize_t read(int fildes, void *buf, size_t nbyte);
头文件: #include<unistd.h>
参数:

  • fildes : 读取的文件描述符
  • buf : 数据存放的目标缓冲区
  • nbyte : 最多读取的数据长度,16位无符号整型,一次读取最多为65535个字节

返回值: 返回实际读取的数据长度。

  • 如果是管道或者套接字目前暂无数据则会阻塞
  • 如果是普通文件,读到文件结尾返回0
  • 可以设置非阻塞读取,如果暂无数据则不会阻塞而回返回-1并将errno置为EAGAIN

03 write函数

write函数用于向文件内写入。
函数原型: int write(int fildes, void *buf, int nbyte)
头文件: #include<unistd.h>
参数:

  • fildes :文件描述符
  • buf :写入数据存放的缓冲区
  • nbyte :最大写入字节数

返回值: 写入成功返回实际写入的数据长度,若写入失败,返回-1.

如果数据长度小于nbyte则在后补’\\0’;如果文件剩余容量小于nbyte则返回能写入的最大数据长度 。

04 lseek函数

lseek函数用于修改文件当前偏移量。
函数原型: off_t lseek(int fd, off_t offset, int whence);
头文件: #include<unistd.h>
参数:

  • fd:操作的文件描述符
  • whence:可以有三种参数,
    SEEK_SET :文件开头
    SEEK_CUR :当前文件偏移量
    SEEK_END :文件结尾
  • offset:表示移动距离,offset可正可负

返回值: 成功返回当前文偏移量,失败返回-1。

如果当前fd是一个管道,套接字等不可修改的会将errno置为ESPIPE

05 close函数

close函数用于关闭打开的文件。
函数原型: int close(int fd);
头文件: #include<unistd.h>
参数:

  • fd :待删除文件的文件描述符

返回值: 成功返回0,失败返回-1.

3. 文件描述符

文件描述符就是一个整数。

0 & 1 & 2

  • Linux进程默认情况下会有3个缺省打开的文件描述符,分别是标准输入0, 标准输出1, 标准错误2.
  • 0,1,2对应的物理设备一般是:键盘,显示器,显示器

所以输入输出还可以采用如下方式:

#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>

int main()

	char buf[1024];
	ssize_t s = read(0, buf, sizeof(buf));
	if(s > 0)
	
		buf[s] = 0;
		write(1, buf, strlen(buf));
		write(2, buf, strlen(buf));
	
	return 0;

文件描述符的分配规则(最小未分配原则)

文件描述符的分配规则:在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。

4. 文件描述符与文件流指针的区别

01 文件流指针的本质

02 c标准库对应的缓冲区

读缓冲区 & 写缓冲区。

03 文件流指针与文件描述符的关系

文件流指针中包含文件描述符。

5. 重定向

#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <stdlib.h>

int main()

    close(1);//鍏抽棴鎵撳紑鐨勬爣鍑嗚緭鍏ユ枃浠?
    int fd = open("myfile",O_WRONLY | O_CREAT,0644);
    if(fd < 0)
    
        perror("open:");
        return 1;
    
    printf("fd: %d\\n",fd);
    fflush(stdout);
    close(fd);
    return 0;

此时,我们发现,本来应该输出到显示器上的内容,输出到了文件 myfile 当中,其中,fd=1。这种现象叫做输出重定向。

常见的重定向有:>, >>, <
重定向的本质

使用dup2系统调用

函数原型: int dup2(int oldfd, int newfd);
头文件: #include <unistd.h>
作用: newfd拷贝oldfd的值,将newfd重定向为oldfd
如果成功: 1. 关闭newfd; 2. 让newfd指向oldfd。
如果失败: 1. oldfd是一个非法的文件描述符或者不存在的文件描述符,函数调用失败并且没有关闭newfd。 2. newfd与oldfd值相等,则dup2函数什么事都不干。

实例:

#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/types.h>

int main()

    int fd = open("text",O_WRONLY | O_CREAT,0644);
    if(fd<0)// 打开失败
    
        perror("open:");
        return 1;
    
    close(1);
    dup2(fd,1);
    while(1)
    
        char buf[1024]=0;
        ssize_t read_size = read(0,buf,sizeof(buf)-1);
        if(read_size<0)
        
            perror("read:");
            break;
        
        printf("%s",buf);
        fflush(stdout);
    
    return 0;


6. 动态库和静态库

动态库和静态库都是代码的集合,将代码集合封装在库文件当中,提供给调用者使用。

  • 静态库(.a):程序在编译链接的时候把库的代码链接到可执行文件中。程序运行的时候将不再需要静态库
  • 动态库(.so):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。
  • 一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码
  • 在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)
  • 动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间

测试程序:

//add.h
#ifndef __ADD_H__
#define __ADD_H__
int add(int a,int b);
#endif //__ADD_H__ 


//add.c
#include "add.h"
int add(int a,int b)

    return a+b;



//sub.h
#ifndef __SUB_H__
#define __SUB_H__ 
int sub(int a,int b);
#endif //__SUB_H__


//sub.c
#include "sub.h"
int sub(int a,int b)

    return a-b;



//main.c
#include <stdio.h>
#include "sub.h"
#include "add.h"

int main()

     int a=10;
     int b=20;
     printf("%d + %d = %d\\n",a,b,add(a,b));
     printf("%d - %d = %d\\n",a,b,sub(a,b));
     return 0;

静态库

生成静态库:

测试目标文件生成后,静态库删掉,程序照样可以运行。

  • -L 指定库路径
  • -l 指定库名

    库搜索路径
  • 从左到右搜索-L指定的目录。
  • 由环境变量指定的目录 (LIBRARY_PATH)
  • 由系统指定的目录
    • /usr/lib
    • /usr/local/lib

动态库

  • shared: 表示生成共享库格式
  • fPIC:产生位置无关码(position independent code)
  • 库名规则:libxxx.so

实例:

  1. 生成动态库
  2. 使用动态库
  • l : 链接动态库,只要库名即可(去掉lib以及版本号)
  • L: 链接库所在的路径
  1. 运行动态库

1、拷贝.so文件到系统共享库路径下, 一般指/usr/lib
2、更改 LD_LIBRARY_PATH

使用外部库
系统中其实有很多库,它们通常由一组互相关联的用来完成某项常见工作的函数构成。比如用来处理屏幕显示情况的函数(ncurses库)

#include <math.h>
#include <stdio.h>
int main(void)

	double x = pow(2.0, 3.0);
	printf("The cubed is %f\\n", x);
	return 0;

gcc -Wall calc.c -o calc -lm

  • -lm表示要链接libm.so或者libm.a库文件

以上就是这篇文章的所有内容啦,感谢老铁有耐心看完。有啥错误请多多指正哈!码字不易,希望大佬们点个赞

《linux从0到99》八进程控制(代码片段)

LINUX进程控制1.进程创建(fork)2.进程终止01_exit函数02exit函数03atexit函数04return退出3.进程等待01进程等待的必要性02进程等待的方法a)wait函数b)waitpid函数03获取子进程status5.进程替换01进程替换原理02进程替换函数6.利用所学... 查看详情

《linux从0到99》十一进程信号(代码片段)

进程信号一、信号的概念二、信号的种类1.非可靠信号(非实时信号)2.可靠信号(实时信号)三、信号的产生方式1.硬件产生2.软件产生四、信号的注册五、信号的注销六、信号的处理方式七、信号的捕捉流程1.内... 查看详情

《linux从0到99》十一进程信号(代码片段)

进程信号一、信号的概念二、信号的种类1.非可靠信号(非实时信号)2.可靠信号(实时信号)三、信号的产生方式1.硬件产生2.软件产生四、信号的注册五、信号的注销六、信号的处理方式七、信号的捕捉流程1.内... 查看详情

《linux从0到99》六进程概念下(代码片段)

进程概念下1.僵尸进程01僵尸进程的概念03解决僵死状态/僵尸进程04僵尸进程的模拟实现05僵尸进程的危害2.孤儿进程01孤儿进程的概念02孤儿进程的模拟实现03孤儿进程的危害3.进程优先级01PRIandNI02PRIvsNI查看进程优先级的命令4.环境... 查看详情

《linux从0到99》五进程概念上(代码片段)

进程概念上1.冯·诺依曼体系结构2.操作系统01概念02设计OS的目的03OS的定位04对OS所谓管理的理解3.进程01基本概念02描述进程PCB1)task_struct-PCB的一种2)task_struct内容分类03查看进程04通过系统调用获得进程标识符05通过系统... 查看详情

《linux从0到99》十进程间通信(代码片段)

进程间通信一、什么是进程间通信1.进程间通信的目的2.进程间通信的分类二、管道1.管道01管道的符号02管道的本质2.匿名管道与命名管道01匿名管道02命名管道三、消息队列四、共享内存1.共享内存的原理2.共享内存接口01shmget02shm... 查看详情

linux运维基础(九):linux的引导过程(代码片段)

计算机从开启电源到用户可以登录,主要经历了四个阶段:初始化BIOS执行启动加载程序载入内核启动systemd服务BIOS    计算机通电后,首先由BIOS进行POST自检,然后依据BIOS内置的引导顺序从引导设备中读取引导... 查看详情

《linux从0到99》三yum与vim编辑器(代码片段)

...软件包在Linux下安装软件,最常用的办法是下载到程序的源代码,并进行编译 查看详情

《linux从0到99》四linux编译器(gcc/g++)和调试器(gdb)(代码片段)

...项目清理1.Linux编译器gcc/g++使用01预处理主要处理源代码文件中的以“#”开头的预编译指令。删除所有的#define并展 查看详情

linux内存从0到1学习笔记(九,内存优化调试之三-内存拆解)---持续更新(代码片段)

写在前面我们在日常的工作当中需要各种手段来调试内存,尤其是在内存泄漏的情况下,我们需要一种手段来统计内存的使用去向,以确定内存使用不合理的方向。或者物理内存有限的情况下,需要对内存进行优... 查看详情

《linux从0到99》八进程控制(代码片段)

LINUX进程控制1.进程创建(fork)2.进程终止01_exit函数02exit函数03atexit函数04return退出3.进程等待01进程等待的必要性02进程等待的方法a)wait函数b)waitpid函数03获取子进程status5.进程替换01进程替换原理02进程替换函数6.利用所学... 查看详情

《linux从0到99》十一进程信号(代码片段)

进程信号一、信号的概念二、信号的种类1.非可靠信号(非实时信号)2.可靠信号(实时信号)三、信号的产生方式1.硬件产生2.软件产生四、信号的注册五、信号的注销六、信号的处理方式七、信号的捕捉流程1.内... 查看详情

《c++从0到99》六模板(代码片段)

c++模板一、模板初阶1.函数模板01函数模板的概念02函数模板的格式03函数模板的原理04函数模板的实例化05模板参数的匹配原则2.类模板01类模板的格式02类模板的实例化二、模板进阶1.非类型模板参数2.模板特化01概念02函数... 查看详情

《c++从0到99》六模板(代码片段)

c++模板一、模板初阶1.函数模板01函数模板的概念02函数模板的格式03函数模板的原理04函数模板的实例化05模板参数的匹配原则2.类模板01类模板的格式02类模板的实例化二、模板进阶1.非类型模板参数2.模板特化01概念02函数... 查看详情

《linux从0到99》十进程间通信(代码片段)

进程间通信一、什么是进程间通信1.进程间通信的目的2.进程间通信的分类二、管道1.管道01管道的符号02管道的本质2.匿名管道与命名管道01匿名管道02命名管道三、消息队列四、共享内存1.共享内存的原理2.共享内存接口01shmget02shm... 查看详情

《c++从0到99》一c++入门(代码片段)

c++入门1.c++的认识2.c++关键字3.c++的命名空间01命名空间的定义02命名空间的使用4.c++的输入输出5.缺省参数01概念:02缺省参数分类:6.函数重载01概念02实现03函数命名规则04extern"c"7.引用01... 查看详情

《c++从0到99》三类和对象中(代码片段)

类和对象中0.类的六个默认构造函数1.构造函数2.拷贝构造函数3.析构函数4.赋值运算符重载5.const修饰的成员函数6.日期类的实现0.类的六个默认构造函数定义一个空类:classDate经过编译器处理之后,类Date不在为空,它... 查看详情

linux从青铜到王者第二十二篇:linux高级io(代码片段)

...结构体5.select函数返回值6.select函数优缺点7.select函数监控代码 查看详情