Linux-基础IO

库函数IO接口

fopen

file *fopen(cosnt char *path, const char *mode);

path:要打开的文件路径名称; mode:“r”,“r+”,“w”,“w+”,“a”,“a+”,“b”
r:只读; r+:可读可写;
w:只写,文件不存在则创建新文件; w+:可读可写,不存在则创建新文件;
a:追加只写(总是将数据写入文件末尾(其他则是覆盖写),不存在则创建新文件);a+:读和追加写,打开文件后从起始读,但是写总是在末尾,不存在则创建新文件;
b:对文件数据进行二进制操作
返回值:失败返回NULL;成功返回一个文件流指针FILE*

fwrite

size_t *fwrite(cosnt void *ptr, size_t size, size_t nmemb, FILE *stream);

ptr:要写入文件的数据的空间地址;
size:块大小;nmemb:块个数;两个相乘为总体要操作数据大小。
stream:操作句柄–fopen返回的文件流指针;
返回值:实际写入文件的块个数

fread

size_t *fread(void *ptr, size_t size, size_t nmemb, FILE *stream);

ptr:一块缓冲区内存空间的地址,用于存放读取的数据
size:块大小;nmemb:块个数;
stream:操作句柄–fopen返回的文件流指针
返回值:成功返回读取到完整块个数;出错返回0;读取到文件末尾返回0,读取的数据不足一块也返回0;
建议:块大小为1,块个数为操作数据长度,否则若读取一块数据50字节,但是实际只读取了40字节的时候也会返回0,通过返回值无法确定是否已经读取到的数据
需要通过其他判断是哪个出错,feof判断读取到文件末尾,ferror判断读取数据是否出错。

fseek

int fseek(FILE *stream, long offset, int whence);

–跳转文件读写位置
stream:操作句柄-打开文件返回的文件流指针
offset:相对起点开始偏移量(偏移量可以是负数–正数向后,负数向前)
whence:相对起点–SEEK_SET-起始位置;SEEK_CUR-当前位置;SEEK_END-末尾位置
返回值:成功返回0;失败返回-1

fclose

int fclose(FILE *fp);

–关闭文件,释放资源

实例

#include <stdio.h>
#include <string.h>

int main()
{
        //fopen(lujingming, dakaifangshi)
        FILE *fp = fopen("./test.txt", "w+");
        if(fp == NULL)
        {
                perror("fopen error");
                return -1;
        }
        char *ptr = "nihaoya!\n";
        //fwrite(dizhi, daxiao, geshu, jubing)
        int ret = fwrite(ptr, strlen(ptr), 1, fp);
        if(ret == 0)
        {
                perror("fwrite error");
                return -1;
        }
        printf("ret:%d\n", ret);

        char buf[1024] = {0};
        ret = fread(buf, 1, 1023, fp);
        if(ret == 0)
        {
                printf("not read data");
        }
        printf("buf:%s----%d\n", buf, ret);

        //fseek(jubing, pianyiliang, xiangduiqidian)
        fseek(fp, 0, SEEK_SET);

        fclose(fp);
        return 0;
}

系统调用IO接口

open

int open(const char *pathname, int flags, mode_t mode);

pathname:文件路径名
flags:标志位
必选其一:O_RDONLY,O_WRONLY,O_RDWR
可选项:
O_CREAT:如果文件不存在则创建
O_EXCL:跟O_CREAT一起使用,如果文件已经存在报错返回
O_TRUNC:打开文件时截断文件长度为0–清空内容
O_APPEND:将写入设置为追加写
例:w+:O_RDWR | O_CREAT | O_TRUNC ----- 可读可写,文件不存在时创建文件,文件存在时清空内容
mode:文件的权限设置,0777;–如果使用了O_CREAT就一定要指定
返回值:成功返回非负整数–操作句柄–文件描述符;失败返回-1

write

size_t write(int fd, const void *buf, size_t count);

fd:open返回的操作句柄,用于明确操作的是哪个打开的文件
buf:要写入文件的数据
count:要写入的数据长度
返回值:成功返回实际写入文件的数据长度;失败返回-1;

read

size_t read(int fd, const void *buf, size_t count);

fd:open返回的操作句柄,用于明确操作的是哪个打开的文件
buf:存放读取到的数据的缓冲区
count:要读取的数据长度
返回值:成功返回实际读取到的数据长度;失败返回-1;

lseek

off_t lseek(int fd, off_t offset, int whence);

fd:open返回的操作句柄
offset:偏移量
whence:相对起始偏移位置-SEEK_SET / SEEK_CUR / SEEK_END
返回值:跳转后的位置,相对于文件起始位置的偏移量,失败返回-1

close

int close(int fd);

实例

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

int main()
{
        //jiang dang qian jincheng wenjianquanxianyanma shezhiwei 0
        umask(0);
        //open(name, flag, mode)
        int fd = open("test.txt", O_RDWR|O_CREAT|O_APPEND, 0777);
        if(fd < 0)
        {
                perror("open error");
                return -1;
        }

        char *ptr = "hello\n";
        //write(jubing, data, length)
        int ret = write(fd, ptr, strlen(ptr));
        if(ret < 0)
        { 
                perror("write error");
                return -1;
        }

        lseek(fd, 0, SEEK_SET);

        char buf[1024] = {0};
        ret = read(fd, buf, 1023);
        if(ret < 0)
        { 
                perror("read error");
                return -1;
        }
        printf("%d-%s\n", ret, buf);

        close(fd);
        return 0;
}

探讨IO操作句柄

库函数的IO接口和系统调用的IO接口–都是用于完成IO的;
文件描述符:系统调用IO接口的操作句柄–非负整数

文件描述符本质:内核中进程打开的文件描述信息表中的下标。
也就是fd,当前进程pcb有一个指针指向文件结构体file_struct,该结构体内有一个数组保存每个文件的描述(地址),每个文件在数组内的下标即为文件描述符fd。当需要进行修改哪个文件时,通过查找fd进而去修改对应的文件。

重定向实现

重定向原理:通过改变文件描述符这个下标位置所保存的文件描述信息,进而改变这个描述符所操作的文件,实现改变数据的流向。
文件描述符分配规则:最小未使用
举例:0:标准输入文件;1:标准输出文件;2:标准错误文件
当close(1)时,则清空数组下标1里面的结构体,即断开文件描述符1与标准输出文件之间的联系。
这时文件描述符1为空,此时打开fopen一个文件,如test.txt,则该文件分配的文件描述符为1;
此时进行printf,则打印出来的信息出现在test.txt文件中,而并非标准输出中。
printf本质上是write(1 …),将信息写入文件描述符1中。

dup2

int dup2(int oldfd, int newfd);

让newfd从oldfd中拷贝文件描述信息地址,把newfd重定向到oldfd对应的文件上

在自己实现的minishell中加入重定向功能:>> / >

功能:将原本要写入标准输出的数据,写入到指定文件中
echo “nihao” >> a.txt
解释:>>:追加重定向,保持文件原内容不变,将新数据追加到文件末尾;>:清空重定向,清空文件原有内容,将新数据写入文件中。

ls -a > test.txt
1.捕捉键盘输入
1.1解析重定向(判断有没有>符号)
1.2将重定向符号之前的信息按照以前的命令处理方式进行处理
1.3取出重定向的新的文件名称
2.解析输入(得到命令名称+参数)
3.创建子进程
3.1在程序替换之前进行标准输出重定向到指定的文件,open,dup2
4.在子进程中进行程序替换
5.父进程进行进程等待

#include <stdio.h>
#include <unistd.h>
#include <stdlib.h>
#include <string.h>
#include <ctype.h>
#include<sys/wait.h>
#include<fcntl.h>

int main(int argc, char *argv[])
{
        while(1)
        {
                printf("[user@host path]$ ");

                fflush(stdout);
                char buf[1024] = {0};
                fgets(buf, 1023, stdin);
                buf[strlen(buf) - 1] = '\0';

                char *str = buf;
                int reflag = 0;
                char *refile = NULL;
                while(*str != '\0')
                {
                        if(*str == '>')
                        {
                                reflag++;
                                *str = '\0';
                                str++;
                                if(*str == '>')
                                {
                                        reflag++;
                                        *str = '\0';
                                        str++;
                                }
                                while(*str != '\0' && *str == ' ') str++;
                                refile = str;
                                while(*str != '\0' && *str != ' ') str++;
                                *str = '\0';
                        }
                        str++;
                }

                int myargc = 0;
                char *ptr = buf, *myargv[32] = {NULL};
                while(*ptr != '\0')
                {
                        if(*ptr != ' ')
                        {
                                myargv[myargc] = ptr;
                                myargc++;
                                while(*ptr != '\0' && *ptr != ' ')
                                ptr++;
                                *ptr = '\0';
                        }
                        ptr++;
                }
                myargv[myargc] = NULL;

                for(int i = 0; i < myargc; i++)
                {
                        printf("[%s]\n", myargv[i]);
                }

                pid_t pid = fork();
                if(pid < 0)
                {
                        perror("fork error");
                        continue;
                }
                else if(pid == 0)
                {
                        if(reflag == 1)
                        {
                                int fd;
                                fd = open(refile, O_RDWR|O_CREAT|O_TRUNC,0664);
                                dup2(fd, 1);
                        }
                        else if(reflag == 2)
                        {
                                int fd;
                                fd = open(refile, O_RDWR|O_CREAT|O_APPEND,0664);
                                dup2(fd, 1);

                        }
                        execvp(myargv[0], myargv);
                        perror("execvp error");
                        exit(-1);
                }
                wait(NULL);
        }
        return 0;
}

文件描述符与文件流指针

文件流指针与文件描述符之间的关系
库函数:fopen,fwrite,fread,fseek,fclose,printf,fprintf,fgets…
系统调用接口:open,write,read,lseek,close
文件描述符:int,系统调用IO接口的操作句柄
文件流指针:FILE*,库函数IO接口的操作句柄
库函数与系统调用接口的关系:库函数封装了系统调用接口
通过库函数进行IO操作,最终是会归纳到使用系统调用接口完成IO操作。
库函数句柄:文件流指针
系统调用接口句柄:文件描述符

文件流指针是一个结构体,其中内部有一个成员变量就是文件描述符。当通过库函数操作文件流指针,最终会演变为通过系统调用操作描述符。

库函数IO与系统调用IO的区别:
缓冲区:通常所说的换行刷新缓冲区的该缓冲区,是文件流指针所有的,对于文件描述符或者系统调用来说,是没有的。

动态库与静态库的生成与使用

生成可执行程序的链接方式:
动态链接:链接动态库,只是在生成的程序中记录库中的函数信息表,并没有将具体代码实现写入到程序中。因此运行动态链接生成的程序的时候需要依赖动态库的存在,好处是内存中共享一份。
静态链接:链接静态库,在生成的程序中直接将库中所需函数的实现写入到可执行程序中,生成的程序比较大,但是没有依赖性。

库的生成:库中的函数不能有main函数
1.将源码经过编译汇编之后,解释成为二进制指令
gcc -c -fPIC testlib.c -o testlib.o
其中:-FPIC是产生位置无关代码
2.将编译完成后的二进制指令组织打包成为库文件
动态库:gcc --shared testlib.o … -o libtestlib.so
其中:–share表示生成的是库文件,不包含main函数。最后是生成库的名称,lib是前缀,.so是后缀,中间为库的名字。
静态库:ar -cr libtestlib.a testlib.o …

库的使用:
生成可执行程序时链接使用:使用gcc的 -l 选项指定要链接的库名称:gcc main.c -o main -ltestlib
其中:testlib为库文件名称
但是链接器在链接库文件生成可执行程序的时候,会到指定的一些路径下去找库文件,找到了就链接,找不到就会报错
生成可执行程序时链接使用:
1.库文件放在指定路径下:/usr/lib64
2.设置环境变量:export LIBRARY_PATH=$LIBRARY_PATH:./
其中:./为库所在路径
3.使用gcc的-L选项指定库路径:gcc main.c -o main -L./ -ltestlib
运行可执行程序时加载使用:仅针对动态链接生成的程序。

要生成一个可执行程序时用到了库函数,使用动态库好还是用静态库好?
动态库的优点:共享,代码冗余小,便于模块代码替换一些模块化,便于功能替换的一些接口使用动态库。
静态库的优点:依赖性低,功能改动性小,并且只有当前程序使用的时候使用静态库

  • 7
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值