《Linux从0到99》九 基础IO

Posted AURORA_CODE

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了《Linux从0到99》九 基础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》九 基础IO的主要内容,如果未能解决你的问题,请参考以下文章

《Linux从0到99》九 基础IO

Linux运维基础(九):Linux的引导过程

Linux运维基础(九):Linux的引导过程

《Linux从0到99》三 yum与vim编辑器

《Linux从0到99》三 yum与vim编辑器

《Linux从0到99》四 Linux编译器(gcc/g++)和调试器(gdb)