文件IOday03(结尾)--------文件与目录属性,C库的建立

目录

一、文件和目录属性

1.文件属性

 (1).获取文件属性的函数stat ;fstat ;lsta

(2).解析文件x xxx xxx xxx的具体内容

(3).stat 结构体内部参数 

(4).根据uid获取用户信息的函数struct passwd *getpwuid(uid_t uid);

(5).根据组的id获取组的信息函数:  struct group *getgrgid(gid_t gid);

2.目录属性

(1).打开目录文件函数: DIR *opendir(const char *name);

(2).获取目录文件中的数据(目录中的文件信息)函数:struct dirent *readdir(DIR *dirp);

(3).例如: 打印目录中文件名称

    (4).练习: 用文件和目录相关知识实现:ls -l命令功能

3.实际用户和有效用户以及设置用户位和设置组位

(1).实际用户 

(2).有效用户 

二、C库的制作

1,库的概念 

2,库的分类

3,静态库的制作 

(1).静态库作用

(2).编写静态库源码----mylib.c  mylib.h

(3).将上面的静态库的源码编译成.o文件 

(4).将mylib.o制作成静态库 -------//静态库的命名规则:lib库名.a 

(5).编写一个测试代码,测试静态库是否可以使用

(6).编译上面的测试代码,因为测试代码中调用了库函数,所以编译时需要连接

(7).运行编译好的测试代码

4.动态库的制作    

(1).动态库作用

(2).编写动态库的源码----mylib.c  mylib.h

(3).将上面的动态库的源码编译成.o文件 

(4).将.o文件制作成动态库  -------//动态库命名规则: lib库名.so 

(5).编写一个测试代码,测试动态库是否可以使用        

(6).编译上面的测试代码,因为测试代码中调用了库函数,所以编译时需要连接

(7).运行编译好的测试代码

(8).装载动态库  -----//有三种方法

(9).运行测试程序

(10).升级动态库:


一、文件和目录属性

1.文件属性

 (1).获取文件属性的函数stat ;fstat ;lstat

    #include <sys/types.h>
    #include <sys/stat.h>
    #include <unistd.h>

stat ;fstat ;lstat三个的头文件

1》》        int stat(const char *pathname, struct stat *statbuf);

        作用:获取指定文件的属性,如果文件为链接文件,获取的是链接文件指向的目标文件属性

        //参数1  ---- 文件的路径

        //参数2 ----- 保存文件属性的变量的地址,文件属性用结构体描述


2》》        int fstat(int fd, struct stat *statbuf);  

        作用:获取已打开的文件的属性

        //参数1  ----文件描述符

        //参数2 ---- 保存文件属性的变量的地址,文件属性用结构体描述


3》》        int lstat(const char *pathname, struct stat *statbuf);

        //获取指定文件的属性,如果文件为链接文件,获取该链接文件的属性
        //参数1  ---- 文件的路径
        //参数2 ----- 保存文件属性的变量的地址,文件属性用结构体描述:

(2).解析文件x xxx xxx xxx的具体内容

        我们可以看到,中间set UID bit以及set GID bit 他们存储的文件uid和gid,uid是指文件拥有者的ID,是创建这个文件的用户ID,可以想象为一个身份证,gid是指文件所属组的一个id,该用户是在哪个组里的,是gid保存的,从中可以发现,他们的的作用是在32位操作系统中的以八进制为例000x000的位置,右边的三个0中,从左往右,第一个是用户权限,第二个是用户所属组的权限,第三个是其他其他用户的权限。左边的3个0是说明该文件的类型。

 

 

(3).stat 结构体内部参数 
 

    struct stat {
            unsigned long   st_dev;         /* Device.  */
            unsigned long   st_ino;         /* File serial number.  */
            unsigned int    st_mode;        /* File mode. 文件的类型和文件的权限 */
            unsigned long    st_nlink;       /* Link count.  硬链接数*/
            unsigned int    st_uid;         /* User ID of the file's owner. 文件拥有者ID */
            unsigned int    st_gid;         /* Group ID of the file's group. 文件所属组ID*/
            unsigned long   st_rdev;        /* Device number, if device.  */
            unsigned long   __pad1;
            long            st_size;        /* Size of file, in bytes.  */
            int             st_blksize;     /* Optimal block size for I/O.  */
            int             __pad2;
            long            st_blocks;      /* Number 512-byte blocks allocated. */
            long            st_atime;       /* Time of last access. 显示的是文件中的数据最后被访问的时间 */
            unsigned long   st_atime_nsec;
            long            st_mtime;       /* Time of last modification. 显示的是文件内容被修改的最后时间 */
            unsigned long   st_mtime_nsec;
            long            st_ctime;       /* Time of last status change.  上次更改文件的权限、拥有者、所属的组、链接数发生改变时的时间*/
            unsigned long   st_ctime_nsec;
            unsigned int    __unused4;
            unsigned int    __unused5;
    };

(4).根据uid获取用户信息的函数struct passwd *getpwuid(uid_t uid);

        参数:uid的值

        返回一个passwd结构体,内容如下

struct passwd *getpwuid(uid_t uid);   //根据uid获取用户信息
            struct passwd {
               char   *pw_name;       /* username */
               char   *pw_passwd;     /* user password */
               uid_t   pw_uid;        /* user ID */
               gid_t   pw_gid;        /* group ID */
               char   *pw_gecos;      /* user information */
               char   *pw_dir;        /* home directory */
               char   *pw_shell;      /* shell program */
           };

(5).根据组的id获取组的信息函数:  struct group *getgrgid(gid_t gid);

参数:gid的值

返回:group结构体,内容如下

   struct group *getgrgid(gid_t gid);  //根据组的id获取组的信息
          struct group {
               char   *gr_name;        /* group name */
               char   *gr_passwd;      /* group password */
               gid_t   gr_gid;         /* group ID */
               char  **gr_mem;         /* NULL-terminated array of pointers
                                          to names of group members */
           };

    例:使用lstat()函数实现ls -l命令的功能
        
    参考: lstat.c

        

#include <stdio.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <grp.h>
#include <pwd.h>


#define perr(str) ({perror(str); exit(1);})

int main(int argc,char **argv)
{
    struct stat st;

    if(lstat(argv[1],&st) < 0)
	perr("lstat");

    //显示文件属性

    //显示文件类型
    if(S_ISREG(st.st_mode))
	printf("-");
    else if(S_ISDIR(st.st_mode))
	printf("d");
    else if(S_ISCHR(st.st_mode))
	printf("c");
    else if(S_ISBLK(st.st_mode))
	printf("b");
    else if(S_ISFIFO(st.st_mode))
	printf("p");
    else if(S_ISSOCK(st.st_mode))
	printf("s");
    else if(S_ISLNK(st.st_mode))
	printf("l");

    //显示文件权限
    int i;
    char str[] = "xwr";
    for(i = 8; i>=0; i--){
	if(st.st_mode & 1<<i)
	    printf("%c",str[i%3]);
	else
	    printf("-");
    }

    printf(" %lu ",st.st_nlink);

    //显示文件拥有者和所属组
    printf("%s ",getpwuid(st.st_uid)->pw_name);
    printf("%s ",getgrgid(st.st_gid)->gr_name);

    printf("%ld ",st.st_size);
    //文件修改的最后时间和日期
    struct tm *tmp;
    tmp = localtime(&st.st_mtime);
    char *month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec"};
    printf("%s %02d %02d:%02d ",month[tmp->tm_mon],tmp->tm_mday,tmp->tm_hour,tmp->tm_min);

    printf("%s\n",argv[1]);

    return 0;
}

2.目录属性


(1).打开目录文件函数: DIR *opendir(const char *name);

参数:目录名

返回值:成功:DIR类型的指针,失败;NULL

涉及的头文件:
        #include <sys/types.h>
        #include <dirent.h>


(2).获取目录文件中的数据(目录中的文件信息)函数:struct dirent *readdir(DIR *dirp);

参数:DIR类型的地址

返回值:dirent类型的结构体

涉及头文件:#include <dirent.h>

注意://显示目录中所有文件信息
           //这里要特别注意的是,readdir函数每次只能读取一个p所指向的文件名,也就是一个字符串
          //每次读取之后,会把P指向的文件名往后移动一个管辖范围
          //所以得用循环来把所有的文件名读取完,直到p指出范围之外,才会使得readdir返回一个0

dirent类型结构体内容:
 

          struct dirent {
               ino_t          d_ino;       /* Inode number */
               off_t          d_off;       /* Not an offset; see below */
               unsigned short d_reclen;    /* Length of this record */
               unsigned char  d_type;      /* Type of file; not supported
                                              by all filesystem types */
               char           d_name[256]; /* Null-terminated filename 目录下的文件名字*/
           };

(3).例如: 打印目录中文件名称

         int main(int argc,char **argv)
        {
            DIR * p;
            struct dirent *dp;

            //打开目录
            if((p = opendir(argv[1])) == NULL)
                perr("opendir");

            //显示目录中所有文件信息
            while((dp = readdir(p))){
                printf("%s\n",dp->d_name);
            }

            return 0;
        }


    
(4).练习: 用文件和目录相关知识实现:ls -l命令功能

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <dirent.h>
#include <sys/stat.h>
#include <unistd.h>
#include <time.h>
#include <grp.h>
#include <pwd.h>


#define perr(str) ({perror(str); exit(1);})

void show_fileinfo(char *path,char *name);
int main(int argc,char **argv)
{
    DIR * p;
    struct dirent *dp;
    char dirpath[100];
    struct stat st;

    //如果没有文件或者目录传入,则将当前路径.给到dirpath路径
    if(argc == 1){  
	strcpy(dirpath,".");	
    }
    //如果有目录传入,则再进一步判断
    else if(argc == 2){
    //对传进来的文件或者目录名进行获取信息操作
	    if(lstat(argv[1],&st) < 0)
	        perr("lstat");
    //S_ISREG(st.st_mode;这个宏是为了检测该名字是否是文件类型,是返回1,否返回0
    	if(S_ISREG(st.st_mode)){
    //进入if后确定是文件类型,直接在当前目录对文件进行文件属性获取
	        show_fileinfo(".",argv[1]);
	        exit(0);
    	}
    //S_ISDIR(st.st_mode);这个宏是为了检测该名字是否是目录类型,是返回1,否返回0
        else if(S_ISDIR(st.st_mode))
    //确定是目录类型,将目录名字以字符串类型复制存放路径的数组内
	        strcpy(dirpath,argv[1]);
    }

    //打开目录
    if((p = opendir(dirpath)) == NULL)
	perr("opendir");

    //显示目录中所有文件信息
    //这里要特别注意的是,readdir函数每次只能读取一个p所指向的文件名,也就是一个字符串
    //每次读取之后,会把P指向的文件名往后移动一个管辖范围
    //所以得用循环来把所有的文件名读取完,直到p指出范围之外,才会使得readdir返回一个0

    while((dp = readdir(p))){
	//printf("%s\n",dp->d_name);

    //将隐藏文件.开头的不进行获取文件操作

	if(dp->d_name[0] != '.')
	    show_fileinfo(dirpath,dp->d_name);
    }

    return 0;
}

//把显示文件内容函数封装
//传入路径名称和文件名称
void show_fileinfo(char *path,char *name)
{
    struct stat st;
    char pathname[100];
//将路径名称和文件名称输出到新数组,并用/来隔开形成一个路径

    sprintf(pathname,"%s/%s",path,name);

//将路径和接受文件属性结构体参数传入lstat函数,若成功则返回0,失败返回-1

    if(lstat(pathname,&st) < 0)
	perr("lstat");

    //显示文件属性

    //显示文件类型
    if(S_ISREG(st.st_mode))
	printf("-");
    else if(S_ISDIR(st.st_mode))
	printf("d");
    else if(S_ISCHR(st.st_mode))
	printf("c");
    else if(S_ISBLK(st.st_mode))
	printf("b");
    else if(S_ISFIFO(st.st_mode))
	printf("p");
    else if(S_ISSOCK(st.st_mode))
	printf("s");
    else if(S_ISLNK(st.st_mode))
	printf("l");

    //显示文件权限
    int i;
    char str[] = "xwr";
    for(i = 8; i>=0; i--){
	if(st.st_mode & 1<<i)
	    printf("%c",str[i%3]);
	else
	    printf("-");
    }

    printf(" %lu ",st.st_nlink);

    //显示文件拥有者和所属组
    printf("%s ",getpwuid(st.st_uid)->pw_name);
    printf("%s ",getgrgid(st.st_gid)->gr_name);

    printf("%ld ",st.st_size);
    //文件修改的最后时间和日期
    struct tm *tmp;
    tmp = localtime(&st.st_mtime);
    char *month[] = {"Jan","Feb","Mar","Apr","May","Jun","Jul","Aug","Sept","Oct","Nov","Dec"};
    printf("%s %02d %02d:%02d ",month[tmp->tm_mon],tmp->tm_mday,tmp->tm_hour,tmp->tm_min);

    printf("%s\n",name);

}

3.实际用户和有效用户以及设置用户位和设置组位

(1).实际用户 


        对于一个可执行程序来说,实际用户指的是该可执行文件的拥有者


(2).有效用户 

        能够执行程序的用户,也就是程序的使用者当某个用户执行程序时,都是以使用者身份执行例如: 

    #include <unistd.h>
    #include <sys/types.h>
    uid_t getuid(void);   //获取实际用户ID
    uid_t geteuid(void);  //获取有效用户ID 
    
    int main(int argc,char **argv)
    {
        printf("euid = %d\n",geteuid());

        return 0;
    }

    //在运行程序时,都是以程序的有效用户身份运行,但是,有时候希望运行程序时都是程序拥有者的身份运行。
    
    
(4).用来测试文件权限函数: int access(const char *pathname, int mode); 

 

参数1: 要测试的文件
参数2: 要测试的文件权限: 

        参数2填入需要测试的权限选择:  

                        F_OK   ------测试文件是否存在
                        R_OK   ------测试文件是否可读
                        W_OK   ------测试文件是否可写
                        X_OK   ------测试文件是否可执行

涉及的头文件:#include <unistd.h>


    例如: 

        int main(int argc,char **argv)
        {

            int fd;

            if(access(argv[1],F_OK) < 0)
                fd = open(argv[1],O_WRONLY|O_CREAT,0666);
            else
                fd = open(argv[1],O_WRONLY);

            return 0;
        }

二、C库的制作

1,库的概念 


        本质上来说库是一种可执行代码的二进制形式,可以被操作系统载入内存执行。由于windows和linux的本质不同,因此二者库的二进制是不兼容的。 库是别人写好的现有的,成熟的,可以复用的代码,你可以使用但要记得遵守许可协议。

        现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。

        共享库的好处是,不同的应用程序如果调用相同的库,那么在内存里只需要有一份该共享库的实例。 

2,库的分类

         linux下的库有两种:静态库和共享库(动态库)。二者的不同点在于代码被载入的时刻不同。 

    1》静态库在程序编译时会被连接到目标代码中,程序运行时将不再需要该静态库,因此体积较大。
    
    2》动态库在程序编译时并不会被连接到目标代码中,而是在程序运行是才被载入,因此在程序运行时还需要动态库存在,因此代码体积较小。 

 

3,静态库的制作 

(1).静态库作用


        ① 静态库对函数库的链接是放在编译时期(compile time)完成的。
        ② 程序在运行时与函数库再无瓜葛,移植方便
        ③ 浪费空间和资源,因为所有相关的对象文件(object file)与牵涉到的函数库(library)被链接合成一个可执行文件(executable file)。

(2).编写静态库源码----mylib.c  mylib.h


        mylib.c:


       int myfun01(int n)
        {
            if(n == 0)
                return 0;
            else
                return myfun01(n-1) + n ;
        }

        int myfun02(int n)
        {
            if(n == 0)
                return 1;
            else
                return myfun02(n-1) * n;
        }
    


mylib.h :

        #ifndef __MYLIB_H__
        #define __MYLIB_H__

        extern int myfun01(int n);
        extern int myfun02(int n);

        #endif

(3).将上面的静态库的源码编译成.o文件 


gcc -c mylib.c -o mylib.o 

(4).将mylib.o制作成静态库 -------//静态库的命名规则:lib库名.a 

ar crs libmylib.a mylib.o

(5).编写一个测试代码,测试静态库是否可以使用

	    #include <stdio.h>
		#include "mylib.h"

		int main(int argc,char **argv)
		{
			int n;

			printf("请输入一个自然数n:");
			scanf("%d",&n);
			printf("%d个自然数和为:%d\n",n,myfun01(n));

			return 0;
		}

(6).编译上面的测试代码,因为测试代码中调用了库函数,所以编译时需要连接

gcc test.c -o test -lmylib -L.

//-l 链接   后面跟的是掐头去尾后的静态库,也就是原始的库名

// -L.        链接后面跟的是该静态库所处的路径,.意思当前目录下

编译操作也可使用Makefile直接对当前的路径进行编译

app:test.c mylib.c mylib.h
    gcc $^ -o $@ -Wall -O0 -g

(7).运行编译好的测试代码

        farsight@ubuntu:~/22071/IO/day03_code/mylib/static_lib$ ./test
        请输入一个自然数n:100
        100个自然数和为:5050

4.动态库的制作    

(1).动态库作用

        ① 动态库把对一些库函数的链接载入推迟到程序运行的时期(runtime)。
        ② 可以实现进程之间的资源共享。
        ③ 将一些程序升级变得简单。
        ④ 甚至可以真正做到链接载入完全由程序员在程序代码中控制。

(2).编写动态库的源码----mylib.c  mylib.h

mylib.c:

		mylib.c
		int myfun01(int n)
		{
			if(n == 0)
				return 0;
			else
				return myfun01(n-1) + n ;
		}

		int myfun02(int n)
		{
			if(n == 0)
				return 1;
			else
				return myfun02(n-1) * n;
		}

mylib.h

		#ifndef __MYLIB_H__
		#define __MYLIB_H__

		extern int myfun01(int n);
		extern int myfun02(int n);

		#endif

(3).将上面的动态库的源码编译成.o文件 

        gcc -fPIC -Wall -c mylib.c

//   意义:-fPIC 创建与地址无关的编译程序

//    即使不加fPIC也可以生成.so文件,但是对于源文件有要求:

        不加fPIC编译的.so文件必须要在加载到用户程序的地址空间时重定向所有表目,所以在它里面不能引用其它地方的代码(即该.so文件内部程序不能出现引用其他地方的代码,如include....)

//    加了fPIC才能实现真正意义上的多个进程共享so文件。多个进程引用同一个 PIC 动态库时,可以共用内存。这一个库在不同进程中的虚拟地址不同,但操作系统显然会把它们映射到同一块物理内存上。

//    不加fPIC,则加载so文件时,需要对代码段引用的数据对象需要重定位,重定位会修改代码段的内容,这就造成每个使用这个.so文件代码段的进程在内核里都会生成这个.so文件代码段的copy.每个copy都不一样,取决于这个.so文件代码段和数据段内存映射的位置。可见,这种方式更消耗内存。但是不加fPIC编译的 so文件的优点是加载速度比较快。

目前计算机来说:大部分都得加-fPIC

(4).将.o文件制作成动态库  -------//动态库命名规则: lib库名.so 

        gcc -shared -fPIC -o libmylib.so mylib.o 

        -shared  //动态编译     生成的是.so文件

        //通常会给动态库建立软链接,在编写程序时,都是使用链接文件
        ln -s /home/farsight/22071/IO/day03_code/mylib/auto_lib/libmylib.so  libmylib_1.so
        ln -s 软链接命令    + .so文件当前路径   生成的新的动态库文件名,遵守  lib库名.so 格式

(5).编写一个测试代码,测试动态库是否可以使用        

		#include <stdio.h>
		#include "mylib.h"

		int main(int argc,char **argv)
		{
			int n;

			printf("请输入一个自然数n:");
			scanf("%d",&n);
			printf("%d个自然数和为:%d\n",n,myfun01(n));

			return 0;
		}

(6).编译上面的测试代码,因为测试代码中调用了库函数,所以编译时需要连接
 

        gcc test.c -o test -lmylib_1 -L.

(7).运行编译好的测试代码

        farsight@ubuntu:~/22071/IO/day03_code/mylib/auto_lib$ ./test
        ./test: error while loading shared libraries: libmylib_1.so: cannot open shared object file: No such file or directory
        
        //上面的错误时由于动态库在程序运行时才需要加载到内存中,这是,需要去系统库的目录下找动态库,如果没有就报错。    

(8).装载动态库  -----//有三种方法

        方法一:将动态库直接拷贝到系统库的目录下: /lib  或 /usr/lib 
            sudo cp libmylib_1.so /lib

        方法二:将动态库的路径添加到库的配置文件中: /etc/ld.so.conf.d/libc.conf
        
            打开配置文件:
                sudo vim /etc/ld.so.conf.d/libc.conf
            添加库的路径:
                /home/farsight/22071/IO/day03_code/mylib/auto_lib
            使之生效: 
                 sudo ldconfig

        方法三:将动态库的路径添加到环境变量: LD_LIBRARY_PATH   (环境变量)                               export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:

/home/farsight/22071/IO/day03_code/mylib/auto_lib
       如果还是不能使用,则执行:sudo ldconfig 
            

(9).运行测试程序

        farsight@ubuntu:~/22071/IO/day03_code/mylib/auto_lib$ ./test
        请输入一个自然数n:100
        100个自然数和为:5050

(10).升级动态库:

① 修改动态库的源码:

            #include <stdio.h>
            int myfun01(int n)
            {
                printf("这是我写的动态库!\n");
                if(n == 0)
                    return 0;
                else
                    return myfun01(n-1) + n ;
            }

            int myfun02(int n)
            {
                if(n == 0)
                    return 1;
                else
                    return myfun02(n-1) * n;
            }


② 重新制作动态库


        gcc -fPIC -Wall -c mylib.c
        gcc -shared -fPIC -o libmylib_v2.so mylib.


③ 将链接文件的目标库文件改为新的动态库文件


        sudo ln -s /home/farsight/22071/IO/day03_code/mylib/auto_lib/libmylib_v2.so /lib/libmylib_1.so


④ 再次运行测试程序:test 


        farsight@ubuntu:~/22071/IO/day03_code/mylib/auto_lib$ ./test
        请输入一个自然数n:10
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        这是我写的动态库!
        10个自然数和为:55


 附:

​​​​​​​

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值