Linux文件(系统)IO(含动静态库的链接操作)

这篇具有很好参考价值的文章主要介绍了Linux文件(系统)IO(含动静态库的链接操作)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。


Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

Linux文件(系统)IO(含动静态库的链接操作)

1、C语言文件IO操作

  • fopen打开文件,fclose关闭文件

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • fwrite操作(写文件)

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

fwrite函数从ptr里将nitems个大小为size字节的数据写进定stream

  • hello_w.c文件

    #include <stdio.h>                                                              
    #include <string.h>    
    
    int main(){    
        FILE* pf = fopen("myfile.txt","w");    
        if(!pf){    
            perror("fopen");    
            return 1;    
        }    
    
        const char* msg = "hello fopen\n";    
        int count = 5;    
        while(count--){    
            fwrite(msg,strlen(msg),1,pf);    
        }    
    
        fclose(pf);    
        return 0;    
    }
    

    这里我们将会在此路径得到一个写有5hello fopenmyfile.txt文件,如下gif:

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • fread操作(写文件)

fread 不会在字符串末尾添加 null 终止符(\0),fgets会。

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

fread函数从定stream里读取nitemssize字节大小的数据存到ptr

  • hello_r.c文件

    #include <stdio.h>
    #include <string.h>
    
    int main(){
        FILE* pf = fopen("myfile.txt","r");
        if(!pf){
            perror("fopen");
            return 1;
        }
    
        char buffer[1024];
    
        while(1){
            //fgets
            // 每次读1个字节,读1024次(文件结束就不读)
            size_t s = fread(buffer,1,sizeof(buffer),pf);
            if(s > 0){
                buffer[s] = 0;
                printf("%s",buffer);
            }
            if(feof(pf)){
                break;
            }
        }
        fclose(pf);
        return 0;
    }
    

    这里我们将会在此路径读到myfile.txt文件,看到打印出来的数据,如下gif:

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • fgets操作(每次读文件的一行))

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

fgets函数从给定stream中读取size个字节放到str中,当发现换行符、文件结束符或发生错误时,读取将停止。

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

int main(){
    FILE* pf = fopen("myfile.txt","r");
    if(!pf){
        perror("fopen");
        return 1;
    }

    char buffer[1024];

    const char* msg = "hello xxxxx\n";    

    while(1){    
        char* r = fgets(buffer,sizeof(msg),pf);                                                  
        if(!r)    
            break;    
        printf("%s",buffer);    
    }

    fclose(pf);
    return 0;
}

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • 打开文件的方式:
			 r      打开文本文件,用于读。流被定位于文件的开始。

       r+     打开文本文件,用于读写。流被定位于文件的开始。

       w      将文件长度截断为零,或者创建文本文件,用于写。流被定位于文件的开始。

       w+     打开文件,用于读写。如果文件不存在就创建它,否则将截断它。流被定位于文件的开始。

       a      打开文件,用于追加
              (在文件尾写)。如果文件不存在就创建它。流被定位于文件的末尾。

       a+     打开文件,用于追加
              (在文件尾写)。如果文件不存在就创建它。读文件的初始位置是文件的开始,但是输出总是被追加到文件的末尾。

2、三个数据流stdin、stdout、stderr

  • stdin(标准输入流):我们在C语言常用的scanf函数,其默认就是指定了标准输入流stdin
#include <stdio.h>

int main(){
  int x = 0;
  scanf("%d",&x);
  return 0;
}

fread使用标准输入流:

#include <stdio.h>    
#include <string.h>    
    
int main(){ 
		char buffer[10];    
    // 这里要注意,这里的sizeof(buffer)-1是多少,fread就会去读多少数据,直到读到这么多数据    
    size_t bytesRead = fread(buffer, sizeof(char), sizeof(buffer)-1, stdin);    
    buffer[bytesRead] = '\0';  // 字符串结束符    
    printf("buff:%s",buffer);
    return 0;
}

如果我们想指定输入流,我们可以使用fscanf函数:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

#include <stdio.h>    
    
int main(){    
    
    FILE* pf = fopen("myfile","r");    
    if(!pf){    
        perror("fopen error");    
        return 1;    
    }    
    
    int in = 0;    
    fscanf(pf,"%d",&in);    
    printf("%d\n",in);
  	fclose(pf);
    
    return 0;    
}

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • stdout(标准输出流):我们在C语言常用的printf函数,其默认就是指定了标准输入流stdout
#include <stdio.h>

int main(){
  printf("%d\n",2);
  return 0;
}

fwrite使用标准输出流:

#include <stdio.h>    
#include <string.h>                                                                              
    
int main(){    
    
    FILE* pf = fopen("myfile","w");    
    if(!pf){    
        perror("fopen");    
        return 1;    
    }    
    
    const char* msg = "hello fprintf\n";       
    
    fwrite(msg,strlen(msg),1,stdout);
  	fclose(pf);	
    return 0;    
}

如果我们想指定输出流,我们可以使用fprintf函数:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

#include <stdio.h>    
    
int main(){    
    
    FILE* pf = fopen("myfile","w");    
    if(!pf){    
        perror("fopen error");    
        return 1;    
    }    
  
    const char* msg = "hello fprintf\n";    
    fprintf(pf,"%s",msg);                                                                       
    fclose(pf);
    return 0;    
}

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • stderr(标准输入流)(后面讲)

3、系统文件IO

3.1、相关系统调用接口的使用

  • 类比C标准库中的函数fopen、fwrite、fread等,我们称之为库函数(权限低)(底层就是使用系统调用接口)。

  • 而open、write、read等属于系统提供的接口,我们称之为系统调用接口(权限高)。

  • 参考下图的lib和系统调用接口

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • open打开文件,close关闭文件

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

这里注意有两个open系统调用接口,第一个不带第三个参数mode,这个mode就是给创建的文件设置权限的比如0666,就是让这个文件的root、自己、其他人的权限都设置为只能读写,当然,这个还要看权限掩码umask是多少,假如umask是0002这最后文件的权限为0664(前面的博客有讲权限掩码,自行查阅)。

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • 系统调用接口write写文件

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

write接口试图从buf中取nbyte字节的数据写到到文件描述符fildes指向的空间中

#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
    
int main(){    
        
    int fd = open("myfile",O_WRONLY|O_TRUNC|O_CREAT,0666);                      
    		 printf("%d\n",fd);// 3
  	 const char* msg = "hello write\n";    
    write(fd,msg,strlen(msg));    
    
    close(fd);    
    return 0;    
}
  • 系统调用接口read读文件

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

read接口试图从文件描述符fildes指向的空间中读取nbyte字节放到buf

#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    
#include <string.h>    
    
int main(){    
        
    int fd = open("myfile",O_RDONLY);    
        		 printf("%d\n",fd);// 3
    char buff[64];        
    const char* msg = "hello write\n";    
        
    read(fd,buff,strlen(msg));    
    printf("%s",buff);
    
    close(fd);    
    return 0;    
}

3.2、文件描述符fd

  • 经过上面对文件myfile的写读,我们知道这个open的返回值fd是3,那么为什么返回值是3?我们再打开几个文件试试它们的文件描述符到底是多少:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    
    #include <string.h>    
        
    int main(){
    		int fd1 = open("myfile1",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd2 = open("myfile2",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd3 = open("myfile3",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd4 = open("myfile4",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd5 = open("myfile5",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        
        printf("%d\n",fd1);    
        printf("%d\n",fd2);    
        printf("%d\n",fd3);                                                         
        printf("%d\n",fd4);    
        printf("%d\n",fd5);
     
      	//close省时间不写了
      	return 0;
    }
    

    我们得出结果是:

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    这像是什么?是不是数组的下标!那么问题又来了,前面的0,1,2去哪了?这时我们想到还有三个数据流,分别是stdin、stdout、stderr。我们继续来验证一下:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    
    #include <string.h>    
        
    int main(){
    		int fd1 = open("myfile1",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd2 = open("myfile2",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd3 = open("myfile3",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd4 = open("myfile4",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        int fd5 = open("myfile5",O_WRONLY|O_TRUNC|O_CREAT,0666);    
        
        printf("%d\n",stdin->_fileno);    
        printf("%d\n",stdout->_fileno);    
        printf("%d\n",stderr->_fileno);    
        printf("%d\n",fd1);    
        printf("%d\n",fd2);                                                         
        printf("%d\n",fd3);    
        printf("%d\n",fd4);    
        printf("%d\n",fd5);
     
      	//close省时间不写了
      	return 0;
    }
    

    这个打印出来的结果是:

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    因此验证了我们的猜想!

  • 而现在知道,文件描述符就是从0开始的小整数。当我们打开文件时,操作系统在内存中要创建相应的数据结构来描述目标文件。于是就有了file结构体,表示一个已经打开的文件对象。而进程执行open系统调用,所以必须让进程和文件关联起来。每个进程都有一个指针*files, 指向一张表files_struct,该表最重要的部分就是包涵一个指针数组,每个元素都是一个指向打开文件的指针!所以,本质上,文件描述符就是该数组的下标。所以,只要拿着文件描述符,就可以找到对应的文件。如下图:

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维


3.3、文件描述符的分配规则

  • 来看下列代码:
#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    
                                                                                
int main(){    
    
    int fd = open("myfile",O_RDONLY);    
    
    if(fd < 0){    
        perror("open");    
        return 1;    
    }    
    printf("%d\n",fd);    
    
    close(fd);    
    return 0;    
}

输出是3,关闭0或2试试(这里不能关闭1,因为1是标准输出流,关闭了我们就看不到输出结果,想看到输出结果我们需要进行输出重定向,下面会讲)

#include <stdio.h>    
#include <fcntl.h>    
#include <unistd.h>    
                                                                                
int main(){    
    
  	close(0);
  	//close(2);
    int fd = open("myfile",O_RDONLY);    
    
    if(fd < 0){    
        perror("open");    
        return 1;    
    }    
    printf("%d\n",fd);    
    
    close(fd);    
    return 0;    
}

输出是0或者2。说明了什么?说明文件描述符的分配规则是在files_struct数组当中,找到当前没有被使用的最小的一个下标,作为新的文件描述符。


3.3、重定向

  • 重定向分为输入重定向和输出重定向

  • 针对上面关闭1的情况,关闭1后看不到输出结果了,那么我们可以进行输出重定向,看下面代码:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    
                                                                                    
    int main(){    
        
      	close(1);
      	
        int fd = open("myfile",O_WRONLY|O_CREAT,0666);    
        
        if(fd < 0){    
            perror("open");    
            return 1;    
        }    
        printf("%d\n",fd);    
        
      	fflush(stdout);// 	刷新缓冲区
        close(fd);    
        return 0;    
    }
    

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    我们发现,本来应该输出到显示器上的数据,输出到了myfile文件中!这种现象叫做输出重定向。输入重定向类似(从myfile文件中读取数据)。

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

  • 那么重定向的本质是什么?看下图:

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    也就是将file*的指针重新指向新的文件。

  • 除了使用关闭标准输入输出流的方法实现重定向功能,还可以使用dup2系统调用。

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    dup2系统调用,fildes2文件描述符是指定的,fildes是需要重定向的文件描述符。

    使用dup2系统调用代码:

    #include <stdio.h>    
    #include <fcntl.h>    
    #include <unistd.h>    
       
    int main(){    
       
       int fd = open("myfile",O_RDWR|O_CREAT,0666);                                   
       if(fd < 0){    
           perror("open");    
           return 1;    
       }    
       
       close(1);    
       dup2(fd,1);    
       
       printf("hello dup2\n");    
       fflush(stdout);    
       return 0;    
    }
    

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    #include <stdio.h>
    #include <unistd.h>
    #include <fcntl.h>
    
    int main() {
       int fd = open("./log", O_CREAT | O_RDWR);
       if (fd < 0) {
           perror("open");
           return 1;
       }
       close(1);
       dup2(fd, 1);
       for (;;) {
           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;
    }
    

3.4、自制shell加入重定向功能

  • myshell.c文件

    #include <stdio.h>
    #include <stdlib.h>
    #include <string.h>
    #include <unistd.h>
    #include <sys/types.h>
    #include <sys/wait.h>
    #include <fcntl.h>
    #include <sys/stat.h>
                                        
    #define SIZE 1024
    #define MAX_LEN 128
    #define SEP " "
                                        
    // 下面的都和重定向有关
    #define NoneRedir  -1
    #define StdinRedir  0
    #define StdoutRedir 1
    #define AppendRedir 2
                                        
    //#define IgnSpace(buf,pos) do{ while(isspace(buf[pos])) pos++; }while(0)
                                        
    int redir_type = NoneRedir;
    char *filename = NULL;
                                        
    char* argv[MAX_LEN];//命令行字符串数组
    char pwd[SIZE];
    char envtemp[SIZE];
    int lastcode = 0;//退出码
                                        
    const char* HostName(){
        char* hostname = getenv("HOSTNAME");
        if(hostname) return hostname;
        else return "None";
    }
                                        
    const char* UserName(){
        char* hostname = getenv("USER");
        if(hostname) return hostname;
        else return "None";
    }
                                        
    const char* CurrentWorkDir(){
        char* hostname = getenv("PWD");
        if(hostname) return hostname;
        else return "None";
    }
                                        
    char* Home(){
        char* hostname = getenv("HOME");
        if(hostname) return hostname;
        else return "None"; 
    }
                                        
    int Interactive(char* commandline, int size){
        printf("[%s@%s %s]$ ",UserName(),HostName(),CurrentWorkDir());
        fgets(commandline,SIZE,stdin);
        commandline[strlen(commandline)-1] = '\0';
                                        
        return strlen(commandline);//空串返回0
    }
                                        
    void Check_redir(char in[])
    {
         // ls -a -l
        // ls -a -l > log.txt
        // ls -a -l >> log.txt
        // cat < log.txt
        redir_type = NoneRedir;
        filename = NULL;
        int pos = strlen(in) - 1;
        while( pos >= 0 )
        {
            if(in[pos] == '>')
            {
                if(in[pos-1] == '>')
                {
                    redir_type = AppendRedir;
                    in[pos-1] = 0;
                    pos++;
                    //IgnSpace(in, pos);
                                        
                    while(in[pos] == ' '){
                        pos++;
                                                    }
                                        
                    filename = in+pos;
                    break;
                                }
                else
                {
                    redir_type = StdoutRedir;
                    in[pos++] = 0;
                    //IgnSpace(in, pos);
                    while(in[pos] == ' '){
                        pos++;
                    }
                    filename = in+pos;
                    //printf("debug: %s, %d\n", filename, redir_type);
                    break;
                }
            }
            else if(in[pos] == '<')
            {
                redir_type = StdinRedir;
                in[pos++] = 0;
                //IgnSpace(in, pos);
                while(in[pos] == ' '){
                    pos++;
                }
                                        
                filename = in+pos;
                //printf("debug: %s, %d\n", filename, redir_type);
                                            break;
            }
            else
            {
                pos--;
            }
        }
    }
                                        
    void Split(char* commandline){
                                        
        Check_redir(commandline);
                                        
        int i = 0;
        argv[i++] = strtok(commandline,SEP);
        while(argv[i++] = strtok(NULL,SEP));
        //解决ls无彩色问题
        if(strcmp(argv[0],"ls") == 0){
            argv[i-1] = (char*)"--color";
            argv[i] = NULL;
        }
    }
                                        
    int BuildingCmd(){
        int ret = 0;
        if(strcmp(argv[0],"cd") == 0){
            ret = 1;
            char* target = argv[1];
            //cd XXX 和cd
            if(!target) target = Home();//第二个参数为NULL
            //改变当前工作目录
            chdir(target);
            //处理target为..的情况
            //重新获取当前路径
            char temp[1024];
            getcwd(temp,1024);
            //更新当前环境变量PWD
            snprintf(pwd,SIZE,"PWD=%s",temp);
            //导出环境变量
            putenv(pwd);
        }
        else if(strcmp(argv[0],"export") == 0){
            ret = 1;
            if(argv[1]){
                strcpy(envtemp,argv[1]);
                putenv(envtemp);
            }
        }
        else if(strcmp(argv[0],"echo") == 0){
            ret = 1;
            if(argv[1] == NULL){
                printf("\n");
            }else{
                if(argv[1][0] == '$'){
                    if(argv[1][1] == '?'){
                        printf("%d\n",lastcode);
                        lastcode = 0;
                    }else{
                        char* e = getenv(argv[1]+1);
                        if(e) printf("%s\n",e);
                    }
                }
                else{
                    printf("%s\n",argv[1]);
                }
            }
        }
        return ret;
    }
                                        
    void Execute(){
        //只能交给子进程,如果用父进程执行命令行,执行一次就结束了
        pid_t id = fork();
        if(id < 0) perror("fork\n");
        else if(id == 0){
        int fd = -1;
        if(redir_type == StdinRedir)
        {
            fd = open(filename, O_RDONLY);
            dup2(fd, 0);
        }
        else if(redir_type == StdoutRedir)
        {
            umask(0);
            fd = open(filename, O_CREAT | O_WRONLY | O_TRUNC,0666);
            dup2(fd, 1);
        }
        else if(redir_type == AppendRedir)
        {
            fd = open(filename, O_CREAT | O_WRONLY | O_APPEND);
            dup2(fd, 1);
        }
        else
        {
            // do nothing
        }
                                        
            execvp(argv[0],argv);
            exit(1);//执行完退出
        }
        //父进程等待子进程
        int status = 0;
        pid_t rid = waitpid(id,&status,0);
        if(rid == id) lastcode = WEXITSTATUS(status);//等待成功
    }
                                        
    int main(){
                                        
        while(1){
            char commandline[SIZE];
            //1. 打印命令行提示符,获取用户的命令字符串
            int n = Interactive(commandline,SIZE);
            if(!n) continue;//返回值为0就是空串,下面代码不再执行
            //2. 对命令行字符串进行切割
            Split(commandline);
            //3. 处理内建命令
            n = BuildingCmd();
            if(n) continue;
            //4. 执行这个命令
            Execute();
        }
        //int i;
        //for(i=0;argv[i];++i){
        //    printf("argv[%d]:%s\n",i,argv[i]);
        //}
        return 0;
    }
    

4、FILE

  • 因为IO相关函数与系统调用接口对应,并且库函数封装系统调用,所以本质上,访问文件都是通过fd访问的。所以C库当中的FILE结构体内部,必定封装了fd。

  • 研究以下代码看输出结果

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

int main(){    

   //int fd  = open("myfile",O_RDWR|O_CREAT|O_TRUNC,0666);    

   const char* msg1 = "hello printf\n"; // \n自带刷新    
   const char* msg2 = "hello fwrite\n";    
   const char* msg3 = "hello write\n";    

   write(1,msg3,strlen(msg3));    
   printf("%s",msg1);    
   fwrite(msg2,strlen(msg2),1,stdout);    

   fork();    
   return 0;    
}

结果如下:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

但如果对进程实现输出重定向呢? ./buffer > buffile , 我们发现结果变成了:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

我们发现 printf 和 fwrite (库函数)都输出了2次,而 write 只输出了一次(系统调用)。为什么呢?肯定和fork有关!

  • 一般C库函数写入文件时是全缓冲的,而写入显示器是行缓冲

  • printf和``fwrite `库函数会自带缓冲区(之前的进度条例子就可以说明),当发生重定向到普通文件时,数据的缓冲方式由行缓冲变成了全缓冲。而我们放在缓冲区中的数据,就不会被立即刷新,甚至fork之后。但是进程退出之后,会统一刷新,写入文件当中。但是fork的时候,父子数据会发生写时拷贝,所以当你父进程准备刷新(刷新完相当于清空数据,就会发生写时拷贝)的时候,子进程也就有了同样的一份数据,随即产生两份数据。

  • write 没有变化,说明没有所谓的缓冲。

  • 图示:

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • 那么为什么会有缓冲区这个概念呢?
  • 可以提高使用者的效率
  • **减少系统调用次数:**调用系统调用是有时间和空间上的消耗的,增加缓冲区可以在数据填满过后再调用系统调用,减少调用系统调用的次数
  • **减少磁盘访问次数:**直接将数据写入磁盘会导致频繁的磁盘访问,而磁盘访问是一种相对较慢的操作。通过使用缓冲区,可以将多个写入操作合并到一起,减少磁盘访问次数,提高写入性能。
  • 模拟实现FILE结构体
  • mystdio.h文件

    #include<stdio.h>                                                                                                                                                     
                
    #define NONE_FLUSH (1<<1)
    #define LINE_FLUSH (1<<2)
    #define FULL_FLUSH (1<<3)
    #define SIZE 1024
                
    typedef struct _myFILE{
        int fileno;
        int pos;// 当前写入位置
        int cap;// 文件容量
        int flush_mode; // 刷新模式
        char buff[SIZE];
    }myFILE;
                
    myFILE* my_fopen(const char *path, const char *mode);
                
    int my_fwrite(const char *ptr, int size,myFILE *stream);
                
    //int my_fread(void *ptr, int size, myFILE *stream);
                
    void my_fclose(myFILE* stream);
                
    const char *toString(int flag);
                
    void DebugPrint(myFILE *fp);
    
  • mystdio.c文件

    #include "mystdio.h"                                                                                                                                                                                                                                                                                                                                                                   
    #include <fcntl.h>           
    #include <string.h>          
    #include <sys/types.h>       
    #include <sys/stat.h>        
    #include <stdlib.h>      
    #include <unistd.h>      
                      
    myFILE* my_fopen(const char *path, const char *mode){      
        int flag = 0;      
        if(strcmp(mode,"r") == 0){      
            flag |= O_RDONLY;      
        }else if(strcmp(mode,"w") == 0){      
            flag |= (O_WRONLY|O_CREAT|O_TRUNC);      
        }else if(strcmp(mode,"a") == 0){      
            flag |= (O_WRONLY|O_CREAT|O_APPEND);      
        }else{      
            return NULL;      
        }      
                      
        int fd = 0;      
        if(flag & O_WRONLY){      
            umask(0);      
            fd = open(path,flag,0666);      
        }else{      
            fd = open(path,flag);      
        }      
                      
        if(fd < 0) return NULL;      
                      
        myFILE* fp = (myFILE*)malloc(sizeof(myFILE));      
        if(!fp){      
            perror("malloc");      
            return NULL;      
        }      
                      
        fp->fileno = fd;      
        fp->pos = 0;      
        fp->cap = SIZE;      
        fp->flush_mode = LINE_FLUSH;      
                      
        return fp;      
    }      
                      
    void my_fflush(myFILE* stream){      
        if(stream->pos == 0) return;      
        write(stream->fileno,stream->buff,stream->pos);      
        stream->pos = 0;// 刷新后pos到最初位置      
    }      
                      
    int my_fwrite(const char *ptr, int size,myFILE *stream){      
        memcpy(stream->buff+stream->pos,ptr,size);// buff从pos开始       
        stream->pos += size;      
        if((stream->flush_mode & LINE_FLUSH) && (stream->buff[stream->pos-1] == '\n')){      
            my_fflush(stream);      
        }else if((stream->flush_mode & FULL_FLUSH) && (stream->pos == stream->cap)){      
            my_fflush(stream);      
        }      
                      
        return size;      
    }      
                      
    void my_fclose(myFILE* stream){      
        my_fflush(stream);      
        close(stream->fileno);      
        free(stream);      
    }      
                      
    const char *toString(int flag)      
    {      
        if(flag & NONE_FLUSH) return "None";      
        else if(flag & LINE_FLUSH) return "Line";      
        else if(flag & FULL_FLUSH) return "FULL";      
        return "Unknow";      
    }      
                      
    void DebugPrint(myFILE *fp)      
    {      
        printf("outbufer: %s\n", fp->buff);      
        printf("fd: %d\n", fp->fileno);      
        printf("pos: %d\n", fp->pos);      
        printf("cap: %d\n", fp->cap);      
        printf("flush_mode: %s\n", toString(fp->flush_mode));      
    } 
    
  • main.c文件

    #include "mystdio.h"    
    #include <string.h>    
    #include <unistd.h>    
                    
    int main(){    
        myFILE* fp = my_fopen("myfile","w");    
                    
        if(!fp) return 1;    
                    
        const char* msg = "hello my_fwrite\n";    
                    
        my_fwrite(msg,strlen(msg),fp);                                                                                                                                    
                    
        //int cnt = 5;    
        //char buffer[64];    
        //while(cnt)    
        //{    
        //    snprintf(buffer, sizeof(buffer), "helloworld,hellobit,%d  ", cnt--);    
        //    my_fwrite( buffer, strlen(buffer),fp);    
        //    DebugPrint(fp);    
        //    sleep(2);    
        //    //my_fflush(fp);    
        //}    
        //    
        //fork();    
                    
        my_fclose(fp);    
        return 0;    
    }
    

5、文件系统

我们在命令行输入ls -l ,除了看见文件名,还看见了一些文件的其他属性

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

其中还包含:

  • 模式
  • 硬链接数
  • 文件所有者
  • 文件所属组
  • 大小
  • 最后修改时间
  • 文件名

还可以通过stat命令来获取文件的更多信息

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

上面的执行结果有几个信息我们可以通过inode来解释一下


5.1、inode

为了能解释清楚inode,我们需要来理解一下文件系统,我们先看以下这张磁盘文件系统图:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

磁盘是一个典型的块设备,磁盘分区被划分为一个个的block(块)。一个block的大小是在格式化的时候确定的,后面不可更改。

解释一下上述图片各名词的含义:

  • 块组(Block Group):文件系统会根据分区的大小划分为数个块组,一个块组就是一个分区,并且每个块组都会有相同的结构组成。

  • 超级块(Super Block):存放文件系统本身的信息。记录的信息主要有:bolck 和 inode的总量,未使用的block和inode的数量,一个block和inode的大小,最近一次挂载的时间,最近一次写入数据的时间,最近一次检验磁盘的时间等其他文件系统的相关信息。Super Block的信息被破坏,可以说整个文件系统结构就被破坏了,不过一般磁盘不止一个扇区存放有超级块,可能会有三四个扇区存放有超级块,以便于第一个扇区的超级块损坏可以通过其他扇区的超级块来恢复。

  • 块组描述(Group Discriptor Table):描述块组属性信息,也就是管理磁盘块位图、inode为徒、inode表、数据块的使用情况。

  • 块位图(Block Bitmap):块位图中记录着数据块中哪个数据块已经被占用,哪个数据块没有被占用。

  • inode位图(inode Bitmap):每个bite表示该inode编号是否已经被使用,比如0表示没有被使用,1表示已经被使用。

  • inode表(inode Table):存放文件属性。如文件大小,所有者,最近修改时间,使用的块情况等

  • 数据块(Data Block):存放文件内容。

将属性和数据分开存放的想法看起来很简单,但实际上是如何工作的呢?我们通过touch一个新文件来看看如何工作的。

查看inode编号ls -li

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

从整体上来看,创建一个文件所经历的步骤如下:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

为了说明问题,我们将上图简化:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

创建文件一般需要4个操作:

  1. 存储属性

    内核先找到一个空闲的i节点(这里是263466)。内核把文件信息记录到其中。

  2. 存储数据

    该文件需要存储在三个磁盘块,内核找到了三个空闲块:300,500,800。将内核缓冲区的第一块数据复制到300,下一块复制到500,以此类推。

  3. 记录分配情况

    文件内容按顺序300,500,800存放。内核在inode上的磁盘分布区记录了上述块列表。

  4. 添加文件名到目录

    新的文件名abc。linux如何在当前的目录中记录这个文件?内核将入口(263466,abc)(映射关系)添加到目录文件。文件名和inode之间的对应关系将文件名和文件的内容及属性连接起来。


插写一个文件系统操作:

使用dd命令创建大文件并将其写入文件系统,然后将文件系统挂载到系统中,可以按照以下步骤进行:

  1. 创建目录 /path/to

    mkdir -p /path/to
    
  2. 使用 dd 命令创建大文件

    以下命令创建一个大小为1GB的空文件:

    dd if=/dev/zero of=/path/to/largefile bs=1M count=1024
    

    这将在指定路径创建名为 largefile 的大小为1GB的空文件。

  3. 使用 mkfs 创建文件系统

    在上面创建的文件中,可以使用适当的文件系统类型(例如ext4)来创建文件系统。假设要创建ext4文件系统:

    mkfs.ext4 /path/to/largefile
    

    这将在 /path/to/largefile 中创建一个ext4文件系统。

  4. 挂载文件系统

    创建文件系统后,您可以将其挂载到系统中的某个目录。首先,创建一个用于挂载的目录:

    mkdir /mnt/largefile
    

    然后将文件系统挂载到此目录:

    mount -o loop /path/to/largefile /mnt/largefile
    

    这将将 /path/to/largefile 中的文件系统挂载到 /mnt/largefile 目录中。


5.2、硬链接(Hard Link)

  • 硬链接是指在文件系统中,多个文件可以指向同一个inode,这样多个文件实际上指向了相同的数据块
  • 硬链接是文件系统级别的链接,它们只是指向了相同的inode,不保留路径信息,因此即使原文件移动或重命名,硬链接仍然有效。
  • 删除原文件并不会影响硬链接的可用性,只有当所有链接都被删除后,文件的inode和数据块才会被释放

使用硬链接:ln 原文件名 硬链接文件名,如下

ln file.txt file.hard

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

我们前面有讲到,文件的一行属性信息,有一个是硬链接数,我们能看到,一开始没给file.txt建立硬链接的时候,它的硬链接数1,就是只有自己,当给它建立硬链接后,硬链接的文件和原文件的硬链接数都变为2。

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

因此我们得出结论:普通文件的硬链接数等于该文件的建立的硬链接文件个数+1(可以自行再给file.txt文件再建立一个硬链接文件,看是不是硬链接数都变为3)。

我们再看目录的硬链接数:

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

我们看到,新创建的目录就有2个硬链接数!为什么是两个?我们看图可以知道,一个是自己的目录文件的,还有一个是该目录下的.目录的,我们知道目录.就是指当前目录。我们再来测试,在该目录下再创建5个目录,再看看硬链接数是多少?

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

能看到硬链接数变为7了!为什么呢?我们发现其中2个是自己和.目录,还有5个是该目录下的目录(a、b、c、d、e)下的..目录,显而易见..目录就是上级目录。因此我们得出结论:目录文件的硬链接数等于2+该目录下的目录个数。

注意:用户不能自己给目录建立硬链接,只能由系统创建!因为自己创建目录的硬链接会导致路径回路。

假如当前目录是/user/home,你给user目录创建一个硬链接/,哪当前创建的硬链接目录变为了/user/home/user,这就导致了路径回路。

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维


5.3、软链接(Symbolic Link)

软链接也叫符号链接。

  • 软链接是一个特殊的文件,它包含了指向另一个文件的路径,相当于一个快捷方式
  • 软链接可以跨越文件系统,因为它们保存的是路径信息而不是inode号。
  • 软链接不像硬链接那样指向相同的数据块,而是指向目标文件的路径,因此即使原文件移动或重命名,软链接也可能失效。
  • 删除原文件后,软链接将成为死链接(即指向一个不存在的目标),如果不再需要,可以手动删除

使用软链接:ln -s 原文件 软链接文件。如:

ln -s file.txt file.symb

Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维


6、动态库和静态库

下面讲解动静态库可能会用到的文件

  • mystdio.h文件

    #include<stdio.h>
        
    #define NONE_FLUSH (1<<1)
    #define LINE_FLUSH (1<<2)
    #define FULL_FLUSH (1<<3)
    #define SIZE 1024
        
    typedef struct _myFILE{
        int fileno;
        int pos;// 当前写入位置
        int cap;// 文件容量
        int flush_mode; // 刷新模式
        char buff[SIZE];
    }myFILE;
        
    myFILE* my_fopen(const char *path, const char *mode);
        
    int my_fwrite(const char *ptr, int size,myFILE *stream);
        
    //int my_fread(void *ptr, int size, myFILE *stream);
        
    void my_fclose(myFILE* stream);
        
    const char *toString(int flag);
        
    void DebugPrint(myFILE *fp);
    
  • mystdio.c文件

    #include "mystdio.h"
    #include <fcntl.h>
    #include <string.h>
    #include <sys/types.h>
    #include <sys/stat.h>
    #include <stdlib.h>
    #include <unistd.h>
        
    myFILE* my_fopen(const char *path, const char *mode){
        int flag = 0;
        if(strcmp(mode,"r") == 0){
            flag |= O_RDONLY;
        }else if(strcmp(mode,"w") == 0){
            flag |= (O_WRONLY|O_CREAT|O_TRUNC);
        }else if(strcmp(mode,"a") == 0){
            flag |= (O_WRONLY|O_CREAT|O_APPEND);
        }else{
            return NULL;
        }
        
        int fd = 0;
        if(flag & O_WRONLY){
            umask(0);
            fd = open(path,flag,0666);
        }else{
            fd = open(path,flag);
        }
        
        if(fd < 0) return NULL;
        
        myFILE* fp = (myFILE*)malloc(sizeof(myFILE));
        if(!fp){
            perror("malloc");
            return NULL;
        }
        
        fp->fileno = fd;
        fp->pos = 0;
        fp->cap = SIZE;
        fp->flush_mode = LINE_FLUSH;
        
        return fp;
    }
        
    void my_fflush(myFILE* stream){
        if(stream->pos == 0) return;
        write(stream->fileno,stream->buff,stream->pos);
        stream->pos = 0;// 刷新后pos到最初位置
    }
        
    int my_fwrite(const char *ptr, int size,myFILE *stream){
        memcpy(stream->buff+stream->pos,ptr,size);// buff从pos开始 
        stream->pos += size;
        if((stream->flush_mode & LINE_FLUSH) && (stream->buff[stream->pos-1] == '\n')){
            my_fflush(stream);
        }else if((stream->flush_mode & FULL_FLUSH) && (stream->pos == stream->cap)){
            my_fflush(stream);
        }
        
        return size;
    }
        
    void my_fclose(myFILE* stream){
        my_fflush(stream);
        close(stream->fileno);
        free(stream);
    }
        
    const char *toString(int flag)
    {
        if(flag & NONE_FLUSH) return "None";
        else if(flag & LINE_FLUSH) return "Line";
        else if(flag & FULL_FLUSH) return "FULL";
        return "Unknow";
    }
        
    void DebugPrint(myFILE *fp)
    {
        printf("outbufer: %s\n", fp->buff);
        printf("fd: %d\n", fp->fileno);
        printf("pos: %d\n", fp->pos);
        printf("cap: %d\n", fp->cap);
        printf("flush_mode: %s\n", toString(fp->flush_mode));
    }
    
  • cal.h文件

    #pragma once                                                                       
            
    int Add(int a, int b);
    
  • cal.c文件

    #include "cal.h"    
            
    int Add(int a ,int b){    
        return a + b;                                                                  
    }
    
  • main.c文件

    #include <stdio.h>    
    #include "cal.h"    
    #include "mystdio.h"    
            
    int main(){    
        int res = Add(10,20);    
        printf("%d+%d=%d\n",10,20,res);    
            
        myFILE* fp = my_fopen("log1.txt","w");    
        if(fp) return 1;                                                               
        return 0;    
    }
    

6.1、静态库

静态库(.a文件):程序编译的时候把哭的代码链接到可执行文件中,程序运行时不再需要静态库

  • 生成静态库:将.o文件打包生成静态库

  • 使用归档工具:ar -rc 静态库名 .o文件

  • ar是gnu归档工具,rc表示(replace and create)

  • 这里的.o文件是用命令gcc -o 生成的(和动态库不一样,下面动态库会讲)

  • 需要注意的是:静态库名是有规范的,不能写成XXX.a,必须写成libXXX.a

  • 如:ar -rc libmylib.a *.o

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • 查看静态库列表ar -tv 静态库名

  • -t:列出静态库中的文件

  • -v:verbose 详细信息

  • 如:ar -tv libmylib.a

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • 使用静态库

  • 先将main.c生成main.o文件

    • 这里我们使用一个新命令 gcc -c .c文件,可以生成同名.o文件。如gcc -c main.c
  • 再使用命令:gcc - o 可执行文件 带main函数的目标文件(如main.o) -L 静态库路径 -l静态库名(去除前缀lib和后缀.a,如mylib) -static

  • gcc -o myexe main.c -L. -lmylib -static

  • 解释一下选项:

    • -L:指定静态库路径(当前路径可以使用.,也可以使用绝对路径)
    • -l:指定静态库
    • -static:指定是静态链接

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维


6.2、动态库

动态库相对静态库的使用更复杂,原理也更复杂。

动态库(.so文件):程序在运行的时候才去链接动态库的代码,多个程序共享使用库的代码。

一个与动态库链接的可执行文件仅仅包含它用到的函数入口地址的一个表,而不是外部函数所在目标文件的整个机器码(不像静态库那样直接在代码里面展开)。

在可执行文件开始运行以前,外部函数的机器码由操作系统从磁盘上的该动态库中复制到内存中,这个过程称为动态链接(dynamic linking)

动态库可以在多个程序间共享,所以动态链接使得可执行文件更小,节省了磁盘空间。操作系统采用虚拟内存机制允许物理内存中的一份动态库被要用到该库的所有进程共用,节省了内存和磁盘空间。

  • 生成动态库:动态库的生成也需要目标文件,但是动态库的目标文件和静态库的目标文件的生成方式不一样,动态库目标文件必须使用选项fPIC(产生位置无关码)。

    • 先将.c文件生成.o文件:gcc -c -fPIC *.c,将.c文件生成同名.o文件

      Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    • 再使用选项shared.o文件打包成动态库:gcc -shared -o 动态库名 .o文件

    • gcc -shared -o libmylib.so *.o

    • 需要注意的是:这个动态库名也有规则:不能是XXX.so,必须是libXXX.so

      Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • 使用动态库

    • 先用main.c文件生成main.o文件

    • 再使用命令gcc -o 可执行文件 带main函数的目标文件 -L动态库路径 -l(动态库名去掉前缀lib,去掉后缀.so)

    • gcc -o myexe main.o -L. -lmylib

      Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

  • 现在有一个问题:如果动态库不在当前目录下呢?

    我们来测试一下,将动态库和头文件放到一个目录下

    假设我们得到了一个压缩文件,文件解压后得到一个目录文件,目录文件里面包含所需所有的头文件和静态库

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    这时候我们怎么运行这个main函数呢?

    这里我们又会使用到一个选项I,这个选项是用来指明头文件的路径

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    现在出现一个新的问题!直接运行myexe会报错!

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    报错的原因是找不到动态库libmyib.so。我们来看看该可执行文件所依赖的动态库,使用命令:ldd 可执行文件名。ldd即list dynamic dependence。

    ldd myexe

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    也就是没找到该动态库。有下面四种解决办法:

    1、将该文件所需的动态库放到系统里的动态库中(不建议使用,风险高)。

    如:sudo cp mylib/lib/* /lib64

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    2、使用环境变量

    使用命令:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:动态库文件路径。这样导入环境变量的话,重启shell又需要重新配置。

    如:export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/home/xp2/Linux_Primary/day_32/demo_02/dynamiclib/user/mylib/lib

    还可以去系统文件添加环境变量

    在这两个文件中:.bash_profile.bashrc文件。查找到这两个文件的方式就是先cd到用户路径cd ~,再ls -al查看这个路径下所有文件就可以看到这两个文件。

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    3、使用软链接的方式(推荐,简单好用)

    在lib64目录下新建一个同动态库名的动态库文件的软链接。

    如:sudo ln -s /home/xp2/Linux_Primary/day_32/demo_02/dynamiclib/user/mylib/lib/libmylib.so /lib64/libmylib.so

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维

    4、在系统目录下(/etc/ld.so.conf.d/)新建一个(文件名.conf)配置文件(推荐)

    Linux文件(系统)IO(含动静态库的链接操作),Linux,linux,服务器,运维


那么好,Linux基础IO就到这里,如果你对Linux和C++也感兴趣的话,可以看看我的主页哦。下面是我的github主页,里面记录了我的学习代码和leetcode的一些题的题解,有兴趣的可以看看。

Xpccccc的github主页文章来源地址https://www.toymoban.com/news/detail-851587.html

到了这里,关于Linux文件(系统)IO(含动静态库的链接操作)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 【linux深入剖析】深入理解软硬链接 | 动静态库的制作以及使用

    🍁你好,我是 RO-BERRY 📗 致力于C、C++、数据结构、TCP/IP、数据库等等一系列知识 🎄感谢你的陪伴与支持 ,故事既有了开头,就要画上一个完美的句号,让我们一起加油 软链接和硬链接是在Linux系统中常见的文件链接方式。 软链接(Symbolic Link): 软链接是一个指向目标文

    2024年04月13日
    浏览(45)
  • Linux基础IO【软硬链接与动静态库】

    ✨个人主页: 北 海 🎉所属专栏: Linux学习之旅 🎃操作环境: CentOS 7.6 阿里云远程服务器 假设你下载了一款游戏,你是否会跑到游戏所在目录中双击 .exe 打开游戏?答案是不会,大多数人都会通过桌面的快捷方式直接打开文件,而这个快捷方式实际就是对 .exe 的 软链接 文

    2024年02月02日
    浏览(41)
  • 【Linux初阶】基础IO - 动静态库 | 初识、生成、链接、加载

    🌟hello,各位读者大大们你们好呀🌟 🍭🍭系列专栏:【Linux初阶】 ✒️✒️本篇内容:动静态库初识,库的含义,静态库的生成与链接,gcc/g++默认链接方式,动态库的生成与动态链接,查看动态链接的方法,动静态库的加载原理 🚢🚢作者简介:计算机海洋的新进船长一

    2024年02月14日
    浏览(35)
  • [Linux]理解文件系统!动静态库详细制作使用!(缓冲区、inode、软硬链接、动静态库)

            hello,大家好,这里是bang___bang_,今天来谈谈的文件系统知识,包含有缓冲区、inode、软硬链接、动静态库。本篇旨在分享记录知识,如有需要,希望能有所帮助。 目录 1️⃣缓冲区 🍙缓冲区的意义 🍙常见缓冲区刷新策略 🍙缓冲区位置猜想 🍥现象猜测 🍥现象解

    2024年02月13日
    浏览(50)
  • 【探索Linux】—— 强大的命令行工具 P.13(文件系统 | 软硬链接 | 动态库和静态库)

    在计算机科学领域中,Linux 系统一直以来都是备受推崇的操作系统之一。其中,文件系统、软硬链接、动态库和静态库是 Linux 系统中非常重要的概念,在实际应用中扮演着不可或缺的角色。 在上一篇文章中,我们了解了 Linux 系统中文件描述符、重定向以及基础 IO 操作的相关

    2024年02月04日
    浏览(55)
  • 【Linux操作系统】举例解释Linux系统编程中文件io常用的函数

    在Linux系统编程中,文件IO操作是非常常见和重要的操作之一。通过文件IO操作,我们可以打开、读取、写入和关闭文件,对文件进行定位、复制、删除和重命名等操作。本篇博客将介绍一些常用的文件IO操作函数。 1.1 原型、参数及返回值说明 1.1.1 原型: open()函数是Linux系统

    2024年02月12日
    浏览(48)
  • 详解Linux下静态库/动态库的生成和使用(含代码示例和操作流程)&&动态库和静态库的区别

    关于gcc的使用方法可以参考下方链接博客: Linux下详解gcc编译过程(含代码示例) gcc使用教程 库是一种组件技术。 库里封装了数据和函数,提供给用户程序调用。 库只执行到第三阶段编译,没有链接。 库的使用可以使程序模块化,提高程序的编译速度,实现代码复用。

    2024年02月15日
    浏览(45)
  • 静态链接库和动态链接库的区别

    在编译时将库的代码( .lib文件 )和应用程序的代码合并在一起,生成一个单独的可执行文件。 发布时非常方便,直接一个.exe文件即可 库的代码在编译时和链接时与应用程序分开。应用程序包含对库的引用( .lib文件 ,在这里被称为导入库),但不包含库的实际代码。 在 运行

    2024年02月11日
    浏览(44)
  • Windows下C++静态链接库的生成以及使用

    这篇文章简单讨论一下Windows下如何使用VS生成和使用C++静态链接库,示例使用VS2022环境。 先创建C++项目-静态库 然后将默认生成的.h和.cpp文件清理干净,当然你也可以选择保留。 然后创建需要的.h和.cpp文件。 看下代码 很简单的代码,就是提供一个打印字符串的接口。编译一

    2024年02月05日
    浏览(37)
  • 【Linux】详解动静态库的制作和使用&&动静态库在系统中的配置步骤

    1、提高开发效率,让开发者所有的函数实现不用从零开始。 2、隐藏源代码。          库其实就是所有的.o文件用特定的方式进行打包形成一个文件,各个.o文件包含了源代码中的机器语言指令。 先将我们的.c文件或者是.cpp文件形成.o文件,指令为: gcc/g++ -c 要形成的.o文

    2024年04月17日
    浏览(48)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包