如何制作简易 网站,做二手车广告推广哪家网站好,建网站维护要多少钱,wordpress分类函数三、文件系统#xff1a;
1、目录和文件
2、系统数据文件和信息
3、进程环境 类ls的实现#xff0c;如myls
/etc/group
/etc/passwd 一、目录和文件
1、获取文件属性
stat: 通过文件路径获取属性#xff0c;面对符号链接文件时获取的是所指向的目标文件的属性。
fst…三、文件系统
1、目录和文件
2、系统数据文件和信息
3、进程环境 类ls的实现如myls
/etc/group
/etc/passwd 一、目录和文件
1、获取文件属性
stat: 通过文件路径获取属性面对符号链接文件时获取的是所指向的目标文件的属性。
fstat通过文件描述符获取属性。
lstat面对符号链接文件时获取符号链接文件的属性。 查看文件的属性将参数path所指的文件状态复制到参数buf所指的结构中
int stat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
int lstat(const char *path, struct stat *buf);
返回值成功返回0; 失败 返回-1设置errno. #include stdio.h
#include stdlib.h
#include sys/types.h
#include sys/stat.h
#include unistd.h off_t flen(const char *path)
{
struct stat statres; if(stat(path,statres) 0)
{
perror(stat());
exit(1);
} return statres.st_size;
} int main(int argc ,char **argv)
{ if(argc 2)
{
fprintf(stderr,Usage....\n);
exit(1);
} printf(%lld\n,flen(argv[1])); exit(0);
} 文件类型
普通文件 -
目录文件 d
字符设备文件 c
块设备文件 b
符号链接 l
网络套接字 s
管道(FIFO) p 2、文件访问权限
st_mode是一个16位的位图用于表示文件类型文件访问权限以及特殊权限位。 3、umask用来设定系统中新创建文件的默认权限的
文件掩码是linux系统中维护的一个全局设置
0666 umask
作用为进程设置文件权限(模式)创建掩码(屏蔽字)防止产生权限过松的文件。 修改进程的文件模式创建屏蔽字:mode_t umask(mode_t mask);
返回值之前的文件模式创建屏蔽字。
修改屏蔽字为 mask0777,返回就的屏蔽字
注意:进程的屏蔽字是继承于父进程,umask 不会改变父进程的屏蔽字 4、现有文件权限的更改/管理 chmod fchmod 把文件的权限设置为 mode
int chmod(const char *pathname, mode_t mode);
pathname参数为绝对路径
返回值成功返回0; 出错 返回-1. 修改打开文件的访问权限
int fchmod(int fd, mode_t mode);
返回值成功返回0; 出错 返回-1. 注意:chmod函数不受文件模式屏蔽字的约束,要想改变一个文件的权限位,进程的有效用户ID必须等于文件所有者ID或者进程的有效用户ID是0. 5、粘住位 t位
文件的文件模式中可以设置一个位此位为粘住位S_ISVTX。即文件模式0777中的0。
如果在一个执行文件设置了该位则执行该文件且进程结束后系统会把该进程的正文部分放置磁盘的交换区中
(在交换区中文件是连续存放的不像非交换区一样一个文件的内容分散在磁盘的几个块中.)
所以在加载该执行文件时就可以加快速度启动直接从交换区中把进程的正文部分取至内存中运行。 6、文件系统FATUFS
文件系统文件或数据的存储和管理
FAT16/32 静态存储单链表 7、硬链接符号链接 link ulink remove rename 区别与联系
硬链接与目录是同义词且建立硬链接有限制;不能给分区建立不能给目录建立。
符号链接优点可跨分区 可以给目录建立。 硬链接ln bigfile bigfile_link (给一个文件做了一个硬链接)
硬链接其实就是指向 i 节点的一个目录项,如果有多个目录项指向同一个i节点,
那么该i节点表示的文件就有多个链接,删除一个文件其实就是删除了一个目录项,
也就是解除了一个链接,当一个文件的链接数为 0 的时候,这个文件的实际内容才会被删除。 符号链接符号链接是一个独立的文件,这个文件的内容是指向的文件的路径名。
ln -s bigfile_link bigfile_s 创建一个现有文件的链接: int link(const char *oldpath, const char *newpath);
返回值成功返回0; 出错 返回-1.
参数oldpath表示现有文件参数newpath表示新目录项。 解除一个已有文件的链接: int unlink(const char *pathname);
返回值成功返回0; 出错 返回-1.
删除目录项并将pathname所引用文件的链接计数减1. unlink创建匿名文件
方法open一个文件然后unlink colse可以释放。 删除一个文件或目录int remove(count char *pathname)
相当于rm 改变一个文件的名字或位置int renamecount char *oldpath, count char *newpath
返回值成功返回0; 出错 返回-1.
相当于mv 8、utime可以更改的最后一次读的时间和写的时间 9、目录的创建和销毁 mkdir rmdir 创建目录int mkdir(const char *pathname, mode_t mode);
删除目录int rmdir(const char *pathname);
打开目录DIR *opendir(const char *name); /DIR *fdopendir(int fd);
关闭目录int closedir(DIR *dirp);
读目录struct dirent *readdir(DIR *dirp);
写目录void rewinddir(DIR *dirp); 10、更改当前工作路径 chdir fchdir 1更改工作路径
int chdir(const char *path);
int fchdir(int fd);
参数path或fd为新的当前工作目录。
返回值成功返回0;出错返回-1. 2获得当前工作路径
char *getcwd(char *buf, size_t size);
参数buf表示缓冲区地址参数size表示缓冲区长度
返回值成功返回buf; 失败返回NULL。
char *getwd(char *buf);
char *get_current_dir_name(void); 注意:如果 buf 的空间不够大,则会出错返回 NULL
注意:如果 getcwd 中的 buf 为 NULL,系统会分配 size 大小的内存,用户需要 free
注意:用户需要 free get_current 函数返回的内存
注意:工作路径是进程的属性,因此子进程调用 chdir 不会改变父进程的工作路径 11、分析目录/读取目录内容
du可以分析出来一个目录或一个文件所占磁盘空间的大小以K为单位。
如du 文件名/目录名 12、glob():解析模式/通配符
glob库函数用于Linux文件系统中路径名称的模式匹配即查找文件系统中指定模式的路径。
注意这不是正则表达式匹配虽然有些相似但还是有点差别。 glob函数原型 #include glob.h int glob(const char *pattern, int flags, int errfunc(const char *epath, int eerrno), glob_t *pglob);
glob函数搜索匹配 函数pattern中的参数
如:/*是匹配根文件下的所有文件不包括隐藏文件要找的隐藏文件需要从新匹配然后会将匹配出的结果存放到 pglob即第4个参数中
第二个参数能选择匹配模式如:是否排序或者在函数第二次调用时是否将匹配的内容追加到pglob中等第3个参数是查看错误信息用一般置为NULL #include stdio.h
#include stdlib.h
#include glob.h #define PAT /etc/a*.conf int err_func(const char *epath, int eerrno)
{
printf(%s:%s\n,epath,strerror(eerrno));
} int main(int argc,char *argv[])
{
int i,err;
glob_t globres; err glob(PAT,0,NULL/*err_func*/,globres);
if(err)
{
printf(err %d\n,err);
exit(1);
} for(i 0 ; i globres.gl_pathc; i)
puts(globres.gl_pathv[i]); globfree(globres); exit(0);
} 一个变量它的使用是单纯的在递归点之前那么在这个变量可以优化到静态区去存放甚至可以放到全局位置也没有关系。 opendir();
closedir();
readdir(3);
rewinddir();
seekdir();
telldir();
二、系统数据文件和信息 1、/etc/passwd账户信息
不同系统环境中的口令文件支持不一样。
1读取口令文件(查询用户信息)
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
返回值成功返回指针; 出错返回NULL。
注意:这两个函数不可重入 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 */ }; [组信息函数和加密原理讲解]
2、/etc/group组信息
1查询组的相关信息
struct group *getgrnam(const char *name);
struct group *getgruid(uid_t uid);
返回值成功返回指针; 出错返回NULL。 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 */ }; 3、/etc/shadow加密口令
普通用户下cat /etc/shadow(无法查看权限不够)
(root下)
阴影文件格式
用户名:密码:最近修改时间:::
zhiyong:$6$mrFIWNAI$Iy788XD2BtJn9gImM9cyZZCQPHP7.OE8RzLzyalwM1WfAcuiP20nW
4Zfrackk6Qzq10eOze5BEWAMb1NsSA371:16295:0:99999:7::: 6表示加密方式 mrFIWNAI表示杂字串(原串或上杂字经过加密的到Iy788XD2BtJn9gImM9cyZZCQPHP7.OE8RzLzyalwM1WfAcuiP20nW
4Zfrackk6Qzq10eOze5BEWAMb1NsSA371) 1通过阴影密码文件API
struct spwd *getspnam(const char *name);
返回值成功返回指针; 出错返回NULL。 2密码和数据加密
char *crypt(const char *key, const char *salt);
key表示口令原文
返回值成功返回加密密码的指针; 出错返回NULL。 hash 混淆(不可逆) 如果原串相同所得串也相同需防备管理员监守自盗
加密安全攻击成本大于收益
安全 穷举口令随机校验 //[密码校验实例]
#includestdio.h
#includestdlib.h
#includeunistd.h
#includeshadow.h
#includestring.h
int main(int argc, char **argv)
{ char *input_pass; struct spwd *shadowline; char *crypted_pass; if(argc 2) { fprintf(stderr, Usage...\n); exit(1); } input_pass getpass(Password:);//获取口令 password提示符 shadowline getspnam(argv[1]);//访问口令文件获取所有信息 crypted_pass crypt(input_pass, shadowline-sp_pwdp);//加密 if(strcmp(shadowline-sp_pwdp,crypted_pass) 0) puts(OK!); else puts(faile!); exit(0);
} 4、时间戳
[时间函数精讲]
1得到以秒为单位的时间
time_t time(time_t *t);
返回值返回 UTC 时间(从 1970-01-01 00:00:00 开始的秒数); 出错 返回-1 2将日历时间转换成协调统一时间的年月日十分秒周日分解结构
struct tm *gmtime(const time_t *timep);
返回值指向分解的tm结构的指针; 出错 返回NULL。 3将日历时间转换成本地时间
struct tm *localtime(const time_t *timep);
返回值指向分解的tm结构的指针; 出错 返回NULL。 4以本地时间的年月日等作为参数将其变换成time_t值
time_t mktime(struct tm *tm);
返回值成功返回日历时间出错返回-1. struct tm { int tm_sec; /* Seconds (0-60) */ int tm_min; /* Minutes (0-59) */ int tm_hour; /* Hours (0-23) */ int tm_mday; /* Day of the month (1-31) */ int tm_mon; /* Month (0-11) */ int tm_year; /* Year - 1900 */ int tm_wday; /* Day of the week (0-6, Sunday 0) */ int tm_yday; /* Day in the year (0-365, 1 Jan 0) */ int tm_isdst; /* Daylight saving time */ }; 5类似于printf的时间值函数。可以通过可用的多个参数来制定产生的字符串。
size_t strftime(char *s, size_t max, const char *format,const struct tm *tm);
s表示所放内容的位置空间大小为maxformat为格式串从tm中抽取
返回值若有空间返回存入数组的字符数; 否则返回0. [时间实例1]
//获取一个时间一秒钟向终端打印一次
//同时具有过一段时间依然能获取当前时间输出
#include stdio.h
#include stdlib.h
#include time.h
#include unistd.h
#define FNAME /tmp/out
#define BUFSIZE 1024
int main()
{
struct tm *tm;
time_t stamp;
int no;
char buf[BUFSIZE];
FILE *fp;
fp fopen(FNAME,a);//打开FANME文件 用追加读写
if(fp NULL)
{
perror(fopen());
exit(1);
}
while(fgets(buf,BUFSIZE,fp) ! NULL)//从fp读取内容到buf中字节大小BUFSIZE
no;
while(1)
{
time(stamp);//获取时戳
tm localtime(stamp);//转换成结构体
fprintf(fp,%-4d%d-%d-%d %d:%d:%d\n,
no,tm-tm_year1900,tm-tm_mon1,
tm-tm_mday,tm-tm_hour,tm-tm_min,tm-tm_sec);
//no自增换行
fflush(fp);
sleep(1);
}
fclose(fp);
exit(0);
}
[]$ ./timelog
切换终端执行
[]$ tail -f /tmp/out [时间函数实例2]
//一年任意一天的一百天后是什么时间
#include stdio.h
#include stdlib.h
#include time.h #define BUFSIZE 1024 int main()
{
struct tm *tm;
time_t stamp;
char buf[BUFSIZE];//缓冲区 stamp time(NULL);//获取时戳
tm localtime(stamp);//转换成结构体
strftime(buf,BUFSIZE,Now:%Y-%m-%d,tm);
//显示当前年月日从tm中抽取放到buf中空间大小为BUFSIZE。
puts(buf);//标准输出 tm-tm_mday 100;
mktime(tm);//可以自动将时间进位。
strftime(buf,BUFSIZE,100 days later :%Y-%m-%d,tm); puts(buf); exit(0);
} 三、进程环境
[进程终止方式]
1、main函数
int main(int argc, char *argv[])
argc是命令行参数的数目argv是指向参数的各个指针所构成的数组 2、进程的终止
正常终止
1从main函数返回;
注意:在 main 函数中调用 return 0 和 exit(0)是等价的 2调用exit;
使当前进程正常终止void exit(int status); 3调用_exit或_Exit;
void _exit(int status);
void _Exit(int status);
注意:exit 函数会调用 fclose 关闭所有打开的流,并且会调用由 atexit 安装的函数,另外两个函数会直接进入内核,不会清理缓存。 4最后一个线程从其启动例程返回; 5最后一个线程调用pthread_exit。 异常终止:
1调用abort;
2接到一个信号并终止;
3最后一个线程对其取消请求作出响应。 钩子函数atexit()
int atexit(void (*function)(void));
安装若干个在调用 exit 函数时候调用的函数 function注册一个在正常进程终止时调用的函数。
注意:每个进程可以安装多达 32 个函数,同一个函数也可以被安装多次,执行的顺序和安装的顺序相反。 [钩子函数]
#includestdlib.h
#includestdio.h static void f1(void)
{ puts(f1() is working!);
} static void f2(void)
{ puts(f2() is working!);
} static void f3(void)
{ puts(f3() is working!);
} int main()
{ puts(Begin!); atexit(f1); atexit(f2); atexit(f3); puts(End!); exit(0);
}
/***************** Begin! End! f3() is working! f2() is working! f1() is working!
******************/ 3、[命令行参数的分析] 解析命令行参数int getopt(int argc, char * const argv[],const char *optstring); [命令行实例2]
//解析时间
#include stdio.h
#include stdlib.h
#include string.h
#include unistd.h
#include time.h #define SIZE 1024 int main(int argc,char **argv)
{
struct tm *tm;
time_t stamp;
char buf[SIZE];
int ch;
char fmt[SIZE] {\0};//格式串 空串
FILE *fp stdout; stamp time(NULL);//获取时间戳
tm localtime(stamp);//转换为结构体形式 while(1)
{
ch getopt(argc,argv,-y:mdH:MS);
//解析命令行参数-y:mdH:MS -表示识别非选项的传参 表示这个字母在终端上使用传参的话它有自己的参数这叫带参数的选项
if(ch 0)
break; switch(ch)
{
case 1:
if(fp stdout)
{
fp fopen(argv[optind-1],w);
if(fp NULL)
{
perror(fopen());
fp stdout;
}
}
break;
case y:
if(strcmp(optarg,2) 0)
strncat(fmt,%y ,SIZE);
else if(strcmp(optarg,4) 0)
strncat(fmt,%Y ,SIZE);
else
fprintf(stderr,Invalid argument of -y\n);
break; case m:
strncat(fmt,%m ,SIZE); break; case d:
strncat(fmt,%d ,SIZE); break; case H: if(strcmp(optarg,12) 0) strncat(fmt,%I(%P) ,SIZE); else if(strcmp(optarg,24) 0) strncat(fmt,%H ,SIZE);//向fmt中连进去一个大小为SIZE的%H覆盖fmt中的\0。 else fprintf(stderr,Invalid argument of -H\n); break; case M:
strncat(fmt,%M ,SIZE);//向fmt中连进去一个大小为SIZE的%M覆盖fmt中的\0。 break; case S:
strncat(fmt,%S ,SIZE);//向fmt中连进去一个大小为SIZE的%S覆盖fmt中的\0。 break; default:
break;
}
} strncat(fmt,\n,SIZE);
strftime(buf,SIZE,fmt,tm);//按照fmt格式从tm中拿出内容放到大小为SIZE的buf中。
fputs(buf,fp); if(fp ! stdout)
fclose(fp); exit(10);
}
4、[环境变量]
每个进程都有一组环境变量,进程的环境变量继承于父进程
指向进程环境表的指针是 char **environ
环境表是一个指针数组,数组中的每一个指针都指向一个字符串(环境变量)
查看环境变量export
KEY VAILE
environ存放所有的环境变量 1获得环境变量char *getenv(const char *name);
返回值指向与name关联的value的指针;若未找到返回NULL
注意:如果在环境变量当中没有 name,那么该函数可能会导致段错误。
(获取环境变量)
#include stdio.h
#include stdlib.h
extern char **environ;
int main()
{
puts(getenv(PWD));//获取当前路径
/*
int i;
for(i 0 ; environ[i] ! NULL ; i)//获取所有环境变量
puts(environ[i]);
*/
exit(0);
} 2修改或添加一个环境变量的值int putenv(char *string);
返回值成功返回0;出错返回非0.
putenv取形式为namevalue的字符串将其放到环境表中。如果name已经存在则先删除其原来的定义。 3int setenv(const char *name, const char *value, int overwrite);
返回值成功返回0;出错返回-1.
name表示环境变量的名字 name存在相当与addname不存在相当于change valuename overwrite表示是否覆盖。
setenv将name设置为value,如果在环境中name已经存在那么:
a若overwrite非0,则首先删除其现有的定义;
b若overwrite为0,则不删除其现有定义name不能设置为新的value而且也不出错。 注意:用 putenv 添加或者修改环境变量时,系统并不分配空间给 name,只是把 name 添加或者更新到环境表中,
所以只要修改了 name 指向的字符串就修改了环境变量,如果 name指向的空间是在某个函数的栈中分配的,一旦函数返回就可能出现问题. 4删除环境变量
int unsetenv(const char *name);
返回值成功返回0;出错返回-1. 5、c程序的存储空间布局
[程序空间和手工装载库]
pmap1
内存分配
void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size); 6、库
动态库
静态库
手工装载库 void *dlopen(const char *filename, int flags);
filename表示要手工装载库的名字 flags表示打开的模式
返回值成功返回一个非空指针;出错返回NULL。 int dlclose(void *handle);
返回值成功返回0;出错返回非零值。 为动态加载的应用程序界面中的函数获取错误诊断:char *dlerror(void); 在共享对象或可执行文件中获得一个符号的地址:void *dlsym(void *handle, const char *symbol);
返回值成功返回与符号关联的地址。失败时返回NULL。 [手工装载库并打印cosine2.0]
#include stdio.h
#include stdlib.h
#include dlfcn.h
#include gnu/lib-names.h
int main(void) { void *handle; double (*cosine)(double); char *error; handle dlopen(LIBM_SO, RTLD_LAZY); if (!handle) { fprintf(stderr, %s\n, dlerror()); exit(EXIT_FAILURE); } dlerror(); /* Clear any existing error */ cosine (double (*)(double)) dlsym(handle, cos); //*(void **) (cosine) dlsym(handle, cos); error dlerror(); if (error ! NULL) { fprintf(stderr, %s\n, error); exit(EXIT_FAILURE); } printf(%f\n, (*cosine)(2.0)); dlclose(handle); exit(EXIT_SUCCESS); } 7、函数跳转
goto不能跨函数跳转
长跳转
用 setjmp 设立一个标志,用 longjmp 跳到该标志处,setjmp 调用时返回 0,longjmp 的调用
会使 setjmp 再一次返回,返回值是 val。 1设置跳转点int setjmp(jmp_buf env);
返回值若直接调用返回0;若从longjmp返回则为非0. 2返回跳转点void longjmp(jmp_buf env, int val);
env为设置的跳转点val为带会的东西。
注意:longjmp 会涉及到变量的回滚,在不指定优化条件的前提下,自动变量、静态变量、易失性变量、寄存器变量、全局变量都不会回滚(因为这些变量都放到内存中),
但是在指定了-O3 优化的情况下,自动变量和寄存器变量会被放到寄存器中,因此会回滚。
注意:如果多次用同一个 env 调用 setjmp 函数,则最后一次有效。 (跳转实例)
#include stdio.h
#include stdlib.h
#include setjmp.h static jmp_buf save; void d(void)
{ printf([%s]Begin\n,__FUNCTION__); printf([%s]Jump now!\n,__FUNCTION__); longjmp(save,6); printf([%s]End\n,__FUNCTION__);
} void c(void)
{ printf([%s]Begin\n,__FUNCTION__); printf([%s]call d()\n,__FUNCTION__); d(); printf([%s]d() returned.\n,__FUNCTION__); printf([%s]End\n,__FUNCTION__);
} void b(void)
{ printf([%s]Begin\n,__FUNCTION__); printf([%s]call c()\n,__FUNCTION__); c(); printf([%s]c() returned.\n,__FUNCTION__); printf([%s]End\n,__FUNCTION__);
} void a(void)
{
int ret;
printf([%s]Begin\n,__FUNCTION__); ret setjmp(save);
if(ret 0)//set
{
printf([%s]call b()\n,__FUNCTION__);
b();
printf([%s]b() returned.\n,__FUNCTION__);
}
else
{
printf([%s]Jumped back here with code %d.\n,__FUNCTION__,ret);
} printf([%s]End\n,__FUNCTION__);
} int main()
{
printf([%s]Begin\n,__FUNCTION__);
printf([%s]call a()\n,__FUNCTION__);
a();
printf([%s]a() returned.\n,__FUNCTION__);
printf([%s]End\n,__FUNCTION__); exit(0);
} /*
[main]Begin
[main]call a()
[a]Begin
[a]call b()
[b]Begin
[b]call c()
[c]Begin
[c]call d()
[d]Begin
[d]Jump now!
[a]Jumped back here with code 6.
[a]End
[main]a() returned.
[main]End
*/ 8、资源的获取及控制 获得进程资源限制int getrlimit(int resource, struct rlimit *rlim);
修改进程资源限制int setrlimit(int resource, const struct rlimit *rlim);
返回值成功返回0; 出错返回-1,并设置error。 struct rlimit { rlim_t rlim_cur; /* Soft limit */ rlim_t rlim_max; /* Hard limit (ceiling for rlim_cur) */ }; 注意: rlim.rlim_cur 是当前的限制,任何进程都可以修改(但必须小于等于 rlim.rlim_max),
rlim.rlim_max 是一个限制的最大值,只有超级用户进程才可以提高这个值