目录
(1).获取文件属性的函数stat ;fstat ;lsta
(4).根据uid获取用户信息的函数struct passwd *getpwuid(uid_t uid);
(5).根据组的id获取组的信息函数: struct group *getgrgid(gid_t gid);
(1).打开目录文件函数: DIR *opendir(const char *name);
(2).获取目录文件中的数据(目录中的文件信息)函数:struct dirent *readdir(DIR *dirp);
(4).练习: 用文件和目录相关知识实现:ls -l命令功能
(2).编写静态库源码----mylib.c mylib.h
(4).将mylib.o制作成静态库 -------//静态库的命名规则:lib库名.a
(6).编译上面的测试代码,因为测试代码中调用了库函数,所以编译时需要连接
(2).编写动态库的源码----mylib.c mylib.h
(4).将.o文件制作成动态库 -------//动态库命名规则: lib库名.so
(6).编译上面的测试代码,因为测试代码中调用了库函数,所以编译时需要连接
一、文件和目录属性
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
附: