目录
- 介绍
- 一、目录和文件
- [1. 获取文件的属性 : stat](#1. 获取文件的属性 : stat)
- 2.文件属性
- 3.umask
- 4.文件权限管理
- 5.粘住位
- 6.文件系统:FAT、UFS
- 7.硬链接,符号链接
- [9. 文件目录解析 : glob](#9. 文件目录解析 : glob)
- [例子:实现指令 du](#例子:实现指令 du)
- 二、系统数据文件和信息
- [1. 用户信息:/etc/passwd](#1. 用户信息:/etc/passwd)
- [2. 组信息: /etc/group](#2. 组信息: /etc/group)
- [3. 加密: /etc/shadow](#3. 加密: /etc/shadow)
- [4. 时间函数](#4. 时间函数)
- [三、 进程环境](#三、 进程环境)
- [1. main函数](#1. main函数)
- [2. 进程的终止](#2. 进程的终止)
- [3. 命令行参数分析](#3. 命令行参数分析)
- [4. 环境变量](#4. 环境变量)
- [5. C程序的存储空间和库](#5. C程序的存储空间和库)
- [6. setjmp\longjmp](#6. setjmp\longjmp)
- [7. 资源获取及控制](#7. 资源获取及控制)
介绍
类ls的实现,如myls, -l -a -i -n
一、目录和文件
二、系统数据文件和信息
三、进程环境
参考书籍:apue\csapp
一、目录和文件

1. 获取文件的属性 : stat
cpp
##include <sys/types.h>
##include <sys/stat.h>
##include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
- 参数: 路径,结构体地址,用来填充文件信息
- 返回值:成功0,失败-1
- 区分:
stat
用路径获取文件属性,面对符号链接时获取的是目标文件的属性fstat
用fd
获取文件属性,同上lstat
用fd
获取文件属性,面对符号链接时,获取的符号链接的属性
struct stat
的内容: man 2 stat
查看

主要包括 inode\st_size\uid\gid。 命令 ls
和 stat
的内容都是从中拿取的。
例子:用 stat
函数统计文件大小
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<unistd.h>
static off_t flen(const char *FNAME)
{
struct stat statres;
if(stat(FNAME, &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",(long long)flen(argv[1]));
exit(0);
}
运行结果:

注意: st_size
的属性是 off_t
,大小取决于机器的架构,有的是longlong,有的是int。所以 1. 可以强转为longlong 2.在 makefile
里加 CFLAGS += -D_FILE_OFFSET_BITS=64
文件的 st_size
只是一个文件属性, 而 blocks
才是真正占用的磁盘大小
示例代码
cpp
##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<unistd.h>
##include<fcntl.h>
int main(int argc, char ** argv)
{
if(argc < 2)
{
fprintf(stderr, "Usagne:...\n");
exit(1);
}
int fd;
fd = open(argv[1], O_WRONLY|O_CREAT|O_TRUNC, 0600);
if(fd < 0)
{
perror("open()");
exit(1);
}
if(lseek(fd, 5*1024*1024*1024LL-1LL, SEEK_SET) < 0)
{
perror("lseek()");
exit(1);
}
}
运行结果:

程序解释:
- 所有的常量都有单位 , 没写单位则默认,
lseek(fd, 5*1024*1024*1024LL-1LL, SEEK_SET)
此处如果不加上LL
则默认是int
,那么就会爆 - 运行结果说明:s
- size为5G大小的文件实际占用的磁盘大小为4KB。
cp
发现是空洞内容就不会进行实际的write
所以虽然两个文件的size
一样,但是第二个文件的blocks
为0,实际上不占用磁盘空间。因此出现 siz=5G但不占用磁盘空间。- 二者都表明unix中
size
只是一个文件属性,而不是表示文件大小,这一点要和 windows区分开。
- 当前内核运行结果:

名词 | 解析 | 一般大小 |
---|---|---|
size | 文件的大小,是文件的属性 | / |
扇区 | 磁盘划分的单位,存储的最小单元 | 512B(0.5KB) |
块block | 文件io的最小单元 | 4096B(4KB = 8*扇区) |
即使存储1KB文件,也要占用一个块(4KB),读取时也是一个块一个块地读(cache)
https://blog.csdn.net/daiyudong2020/article/details/53897775
因此,对于 flen.c
,虽然只有496B,但是也要占用4KB的磁盘空间。( stat
的blocks的单位是扇区大小,可以推测 blocks ≥ 8
)
2.文件属性
stat函数中 st_mode
:一个16位的位图,用于表示文件类型,文件访问权限,以及特殊权限位。
16 ⇒ 7种文件类型(3bit) + 文件访问权限(rwx*3=9bit) + 特殊权限位(uid+gid+sticky=3) = 15
七种文件类型: dcb-lsp
目录、字符设备、块设备、常规文件、符号链接、网络socket、管道
下面函数是使用宏实现的, 判断文件类型, 函数返回真假以表明是否属于这种文件类型


例子:写程序判断文件类型
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<unistd.h>
##include<fcntl.h>
static char* ftype(const char *FNAME)
{
struct stat statres;
if(stat(FNAME, &statres) < 0)
{
perror("stat()");
exit(1);
}
if(S_ISREG(statres.st_mode))
return "regular file";
else
return "not regular file";
}
int main(int argc, char **argv)
{
if(argc < 2)
{
fprintf(stderr, "Usage:...\n");
exit(1);
}
printf("%s\n",ftype(argv[1]));
exit(0);
}
运行结果:

3.umask
作用:避免产生权限过松的文件
文件权限 = 0664 & ~umask
命令 umask
是由函数 umask
封装而成,程序中可以使用函数 umask
来改变umask
4.文件权限管理
命令 chmod
chmod 666 file_name
:直接改变文件权限chomod u\g\o+r\w\x
:加减文件权限
函数 chomod
用法类似:
cpp
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
5.粘住位
现在用的越来越少,仅做了解
作用:一开始是为了让可执行文件的装载内容在内存中保留,下次调用的时候装载得更快,但现在已经有page cache机制,所以不那么常用
现在一般是给目录设置t位,如 /tmp

6.文件系统:FAT、UFS
FAT文件系统:

静态单链表可以写为:【算法基础课里的写法】
cpp
struct node{
int next; //下一个的下标
char data[SIZE]; //数据
}nodes[N];
也可以写为上述的形式。
可见:FAT文件系统的承载能力取决于N,惧怕大文件;而且由于是单链表,单向索引,所以取数据不灵活。
UFS文件系统:


inode
是一个结构体,存储文件相关信息(stat命令就是从这里取的),和一个指针数组(12+3),分别指向11个直接数据块,1个一、二、三级间接数据,存的是数据块的物理地址。所以一个文件对应一个inode
,inode
的指针数组整合了所有数据块,没有链式结构了,并且不怕大文件了。inode位图
和块位图
用来表示某一个inode
或者某一个块有没有被使用

- 目录文件就是一个文件,没有什么特殊
- 只不过它的文件块中存储的数据是目录项
inode - filename
的对应关系 - 路径解析:
- 前提:知道
/
目录的数据块,这是周知的,操作系统初始化时就做好,在inode位图
里的第二位。 - 解析
/tmp/out
:根据/
的inode得到/
目录文件的数据块,里面的内容是数据项, 包含 out文件名和它对应的inode,在根据 out文件的inode,就可以得到out文件的数据块。
- 前提:知道
7.硬链接,符号链接
ln
内部用 link
函数实现, 对应的是 unlink
函数 【 man 2 link/unlink
】
unlink实现匿名函数: open 后再 unlink
unlink 封装出命令 rm
和库函数( man 3
)函数 remove
【unlink是系统调用,移植性不够好】
asd
其他:
文件移动和重命名: rename
→ 封装出命令 mv
时间: utime
用以更改 Acess time 和 modify time
9. 文件目录解析 : glob
- 创建删除:
mkdir
rmdir
- 更改路径:
chdir
\fchdir
→ 封装出命令mv
- 获取当前路径:
getcwd
→ 封装出命令pwd
关于main的命令行参数:
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
int main(int argc, char **argv)
{
int i;
for(i=0; argv[i] != NULL; i++)
puts(argv[i]);
exit(0);
}
运行结果:

几点说明:
argv
数组是以NULL
结尾的- 终端执行
./main *c
是可以匹配通配符,是因为main
函数的参数支持通配符匹配吗,并不是。是因为*.c
是经过 终端shell 再到程序的,是shell把通配符匹配掉了
读取和分析目录:
cpp
##include <glob.h>
glob();
//把目录操作视为流操作
opendir();
closedir();
readdir();
seekdir();
telldir();
glob
函数:
cpp
int glob(const char *pattern, int flags,
int (*errfunc) (const char *epath, int eerrno),
glob_t *pglob);
-
参数:
-
pattern
模式串,支持通配符 -
flags
标志选项:GLOB_NOCHECK
GLOB_NOSORT
GLOB_APPEND
等 -
errfunc
函数指针:用来处理错误信息 -
pglob
结构体,反填结果gl_pathc
和gl_pathv
的结构和用法和argc
和argv
一样
-
-
返回值:成功返回0,失败返回1
例子:列出 /etc
目录下a开头的.conf文件
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<glob.h>
##define PAT "/etc/a*.conf"
int main()
{
int i,err;
glob_t globres;
err = glob(PAT, 0, NULL, &globres);
if(err)
{
printf("Error code = %d\n", err);
exit(1);
}
for(i=0; i < globres.gl_pathc; i++)
puts(globres.gl_pathv[i]);
exit(0);
}
运行结果:

说明
-
为什么
glob
出错时不用perror
,因为glob
不会置errno
-
错误处理函数
目录流函数:
cpp
##include <sys/types.h>
##include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
int closedir(DIR *dirp);
struct dirent *readdir(DIR *dirp);
可见和流操作很像,只不过捏的不是 FILE
而是 DIR

例子:列出 /etc
目录下所有文件
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<dirent.h>
##define PAT "/etc"
int main()
{
DIR * dp;
struct dirent *cur;
dp = opendir(PAT);
if(dp == NULL)
{
//会置errno
perror("opendir()");
exit(1);
}
while((cur = readdir(dp)) != NULL)
puts(cur->d_name);
closedir(dp);
exit(0);
}
运行结果:

例子:实现指令 du
指令 du
以KB的形式返回 文件或目录 所占用的大小
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<sys/types.h>
##include<sys/stat.h>
##include<unistd.h>
##include<glob.h>
##include<string.h>
##define PATHSIZE 1024
static int path_noloop(const char *path)
{
char *pos;
pos = strrchr(path, '/');
if(pos == NULL)
exit(1);
if( strcmp(pos+1, ".") == 0 || strcmp(pos+1, "..") == 0)
return 0;
return 1;
}
static int64_t mydu(const char *path)
{
struct stat statres;
glob_t globres;
int err,i;
int64_t sum;
char nextpath[PATHSIZE];
//使用lstat
if(lstat(path, &statres) < 0)
{
perror("stat()");
exit(1);
}
//非目录
if(!S_ISDIR(statres.st_mode))
return statres.st_blocks;
//目录文件:递归处理
//拼接路径
strncpy(nextpath, path, PATHSIZE);
strncat(nextpath, "/*", PATHSIZE-1);
glob(nextpath, 0, NULL, &globres);
/*if(err)
{
fprintf(stderr, "glob():errcode = %d\n", err);
exit(1);
}*/
strncpy(nextpath, path, PATHSIZE);
strncat(nextpath, "/.*", PATHSIZE-1);
glob(nextpath, GLOB_APPEND, NULL, &globres); //追加方式
/*if(err)
{
fprintf(stderr, "glob():errcode = %d\n", err);
exit(1);
}
*/
sum = statres.st_blocks; //算上目录文件本身的大小
//目录下所有文件的大小
for(i=0; i<globres.gl_pathc; i++)
{
if(path_noloop(globres.gl_pathv[i]))
sum += mydu(globres.gl_pathv[i]);
}
globfree(&globres); //释放空间
return sum;
}
int main(int argc, char **argv)
{
if(argc < 2)
{
fprintf(stderr, "Usage:...\n");
exit(1);
}
printf("%ld\t%s\n",mydu(argv[1])/2, argv[1]);
exit(0);
}
运行结果:

注意点:
- 不知道为什么
glob
一校验就出错,所以放弃了校验;/*
表示路径下所有文件,不包括隐藏文件,所以分两步 strncat
的为 PATHSIZE会报警告,所以-1- 常用字符串函数:
strncpy
strcmp
strncat
(追加)strrchr
找到某字符最右边的出现位置
二、系统数据文件和信息

1. 用户信息:/etc/passwd
root
用户才有查看权限
用户信息不能从文件中直接拿,因为不同的系统记录用户数据的方式不同
标准出来和稀泥,规定两个函数:
cpp
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid)

例子:输入uid,得到unam
示例代码:
cpp
##include<stdlib.h>
##include<stdio.h>
##include<sys/types.h>
##include<pwd.h>
int main(int argc, char **argv)
{
struct passwd *pwdline;
if(argc < 2)
{
fprintf(stderr, "Usage:...\n");
exit(1);
}
pwdline = getpwuid(atoi(argv[1]));
puts(pwdline->pw_name);
exit(1);
}
运行结果:

2. 组信息: /etc/group
两个函数,用法同上
cpp
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
3. 加密: /etc/shadow
cpp
##include <shadow.h>
struct spwd *getspnam(const char *name);
//通过用户名得到 shadow文件中的一行
##include <crypt.h>
**链接的时候加这句:-lcrypt**
char *crypt(const char *phrase, const char *setting);
//将phrase和杂质串setting加密,返回加密后的串
##include <unistd.h>
char *getpass(const char *prompt);
//关闭终端回显功能,输入内容,再打开回显,返回输入的内容
//prompt是提示信息


makefile:crypt链接
例子:写一个 check-pass, 用户输入用户名和密码,判断是否正确
思路:
- 用户输入用户名,用户名通过
getnam
拿到shadowline
的一行 - 用户输入密码,密码通过
crypt
拿到用户输入密码加密后的密文 - 将
shadowline
中的密文和用户输入密码加密后的密文比较,相等则成功,否则失败
示例代码:
cpp
##include<crypt.h>
##include<unistd.h>
##include<string.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:");
shadowline = getspnam(argv[1]);
crypted_pass = crypt(input_pass, shadowline->sp_pwdp);
if(strcmp(crypted_pass, shadowline->sp_pwdp) == 0)
puts("ok");
else puts("fail");
exit(0);
}
运行结果:

注意点:不知为何修改 makefile 没有作用,所以手动加上了;要用root执行,否则没有权限访问 /etc/shadow,会报 段错误
4. 时间函数
围绕三种数据类型:
time_t
:大整数,计算机喜欢struct tm
:结构体,程序员喜欢char *
:字符串,用户喜欢

涉及到的函数:
cpp
##include <time.h>
//拿到大整数
time_t time(time_t *tloc)
//大整数转结构体
struct tm *gmtime(const time_t *timep); //格林威治时间
struct tm *localtime(const time_t *timep); //本地时间
//结构体转字符串
size_t strftime(char *s, size_t max, const char *format,
const struct tm *tm);
//结构体转大整数:参数没有const修饰,会调整tm为合法
time_t mktime(struct tm *tm);
-
gmtime
|localtime
:结果指针在静态区,所以结果要马上用,否则会被冲掉 -
结构体成员
例子:编写 timelog.c
输出行号,时间戳,追加输入
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<time.h>
##include<unistd.h>
##define FNAME "/tmp/out"
int main()
{
FILE *fp;
int cnt=0;
size_t linesize;
char *timestr;
time_t stamp;
struct tm *tm;
fp = fopen(FNAME, "a+");
if(fp == NULL)
{
perror("fopen()");
exit(1);
}
timestr = NULL;
linesize = 0;
while(getline(×tr, &linesize, fp) > 0)
cnt++;
while(1)
{
time(&stamp);
tm = localtime(&stamp);
fprintf(fp, "%-4d: %d-%d-%d %d:%d:%d\n", ++cnt,
tm->tm_year+1900, tm->tm_mon+1, tm->tm_mday,
tm->tm_hour, tm->tm_min, tm->tm_sec);
fflush(fp);
sleep(1);
}
fclose(fp);
exit(0);
}
运行结果:

注意点:
- 进程被杀死后没有关闭文件:可控的内存泄漏,之后用钩子函数
- 处理中断,都是全缓冲,要
fflush
否则看不到输出
例子: 100days.c
:查看100天后的日期
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<time.h>
##define TIMESTRSIZE 1024
int main()
{
char timestr[TIMESTRSIZE];
time_t stamp;
struct tm *tm;
time(&stamp);
tm = localtime(&stamp);
//各种格式参数查手册
strftime(timestr, TIMESTRSIZE, "Now: %Y-%m-%d", tm);
puts(timestr);
tm->tm_mday += 100;
mktime(tm); //利用mktime副作用修正tm
strftime(timestr, TIMESTRSIZE, "After 100 days: %Y-%m-%d", tm);
puts(timestr);
exit(0);
}
运行结果:

三、 进程环境

1. main函数
程序的入口与出口,一个程序有且仅有一个
2. 进程的终止
背过: Very Important!

exit、_exit、_Exit
-
三者的返回值都是给父进程看。shell运行程序后可用
echo $?
查看实际返回的状态数不是
int
:保留低八位,有符号char
-128 ~ +127 -
exit
是库函数,其他是系统调用。
exit
:先清理现场,调用钩子函数等,后利用_exit
退出
_exit
:直接退出exit手册说明
-
何时用
_exit
:遇到重大故障,防止故障扩散。func只会返回012,但是现在返回了其他值,有理由认为发生了覆盖写,为了防止错误数据扩散,要立即退出,查看错误现场
钩子函数
执行 exit
之前被逆序调用,像被钩上来一样。作用类似于 析构函数。
cpp
int atexit(void (*function)(void));
例子:演示钩子函数调用时机和顺序
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
void f1(void)
{
puts("f1 is working");
}
void f2(void)
{
puts("f2 is working");
}
void f3(void)
{
puts("f3 is working");
}
int main()
{
puts("Begin");
atexit(f1);
atexit(f2);
atexit(f3);
puts("End");
exit(0);
}
运行结果:

具体的例子 : 释放打开的文件或其他需要逆操作的资源
不写钩子函数,代码体量会越来越大


钩子函数即可解决此问题,打开某一个文件后,立刻调用钩子函数。这样在进程正常结束前,就会调用钩子函数释放资源:

3. 命令行参数分析
cpp
##include <unistd.h>
int getopt(int argc, char * const argv[],
const char *optstring);
//关联的全局变量
extern char *optarg;
extern int optind, opterr, optopt;
- 参数
argc
argv
就是命令行参数optstring
识别的选项- 前面加
-
:表示处理非选项,如ls -a /tmp/out
中的 /tmp/out - 选项后加
:
:表示该选项有参数
- 前面加
- 返回值
- 读取完: -1
- 成功:为选项→选项字符;非选项→1
- 关联的全局变量
optarg
:如果选项有参数,指向该参数optind
:index,当前在解析第几个参数,用的时候减1表示当前参数
getopt_long()
:分析长格式 如 ls --all
例子:mydate.c
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<time.h>
##include<unistd.h>
##include<string.h>
##define TIMESTRSIZE 1024
##define STROPTSIZE 1024
int main(int argc, char **argv)
{
char timestr[TIMESTRSIZE];
char stropt[STROPTSIZE];
time_t stamp;
struct tm *tm;
int c;
FILE *fp = stdout;
time(&stamp);
tm = localtime(&stamp);
//先初始化为空串,再往里追加
stropt[0] = '\0';
while(1)
{
c = getopt(argc, argv, "-H:MSy:md");
if(c < 0) break;
switch(c)
{
/*case 1:
fp = fopen(argv[optind-1], "w");
if(fp == NULL)
{
perror("fopen()");
//文件打开失败则写往终端
fp = stdout;
}
break;*/
case 'H':
if(strcmp("12", optarg) == 0)
strncat(stropt, "%I(%P) ", STROPTSIZE-1);
else if(strcmp("24", optarg) == 0)
strncat(stropt,"%H ", STROPTSIZE-1);
else
fprintf(stderr, "Invalid argument of -H\n");
break;
case 'M':
strncat(stropt,"%M ", STROPTSIZE-1);
break;
case 'S':
strncat(stropt,"%S ", STROPTSIZE-1);
break;
case 'y':
if(strcmp("2", optarg) == 0)
strncat(stropt,"%y ", STROPTSIZE-1);
else if(strcmp("4", optarg) == 0)
strncat(stropt,"%Y ", STROPTSIZE-1);
else
fprintf(stderr, "Invalid argument of -y\n");
break;
case 'm':
strncat(stropt,"%m ", STROPTSIZE-1);
break;
case 'd':
strncat(stropt,"%d ", STROPTSIZE-1);
break;
default:
break;
}
}
strncat(stropt, "\n", TIMESTRSIZE-1);
strftime(timestr, TIMESTRSIZE, stropt, tm);
fputs(timestr, fp);
//非标准输出才关闭,否则会关闭标准输出
if(fp != stdout)
fclose(fp);
exit(0);
}
运行结果:

未实现的功能:解析非选项参数:指定日期的输出位置。 如 ./mydate -y 4 /tmp/out
表示把年份输入到 /tmp/out
文件中
【视频42】
4. 环境变量
本质:Key = Value
cpp
char *getenv(const char *name);
int setenv(const char *name, const char *value, int overwrite);
int putenv(char *string);
getenv()
:提供键,返回值。 例子:puts(getenv("PATH"))
setenv()
:添加或修改键值对overwrite==1
:键已存在,覆盖写overwrite==0
:键已存在,不覆盖写
putenv()
:添加或修改键值对,不推荐使用,因为参数没有const修饰,可能会被改变
注意: 覆盖写的时候是情况原来的空间,然后转移到堆存储新的键值对。如果不这么做,如果修改后的值所占空间比原来的大,就无法存储。
其他:
export
:命令,查看环境变量
environ
:全局变量数组,类似于argv

5. C程序的存储空间和库
存储空间分布:从下到上: 代码段、为初始化的数据段、初始化的数据段、堆、其他、栈、argc\argv。堆和栈的空间可以变动。
3G以上是内核信息

命令 pmap
:查看一个程序的空间:
编一个程序,getchar等待输入,然后另开一个终端 ps axf
查看进程号,然后执行 pmap

库:

手册example。注意使用上述函数的时候要 Link with -ldl.

6. setjmp\longjmp
-
类
goto
函数的必要性查找有1000层的数的某个结点,没有改变函数空间,那么可以goto快速返回;C++异常抛出机制
经过复杂步骤进来,且没有改变空间,就可以goto快速返回
goto不能实现跨函数跳转,因为现场没有切换。
setjmp 和 longjmp 解决了这个问题。安全地跨函数跳转,被称为长返回,长跳转。
cpp
##include <setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);
setjmp
返回值类似fork,后面加分支:如果顺次执行,返回0,跳转回来则返回携带的值。longjmp
第二个参数带回0,会被替换为1,防止死循环
例子:跨函数跳转
示例代码:
cpp
##include<stdio.h>
##include<stdlib.h>
##include<setjmp.h>
jmp_buf save;
void c(void)
{
printf("%s():Begin.\n", __FUNCTION__);
longjmp(save, 6);
printf("%s():End.\n", __FUNCTION__);
}
void b(void)
{
printf("%s():Begin.\n", __FUNCTION__);
printf("%s():Call c()\n", __FUNCTION__);
c();
printf("%s():Return from c()\n",__FUNCTION__);
printf("%s():End.\n", __FUNCTION__);
}
void a(void)
{
printf("%s():Begin.\n", __FUNCTION__);
printf("%s():Call b()\n", __FUNCTION__);
b();
printf("%s():Return from b()\n",__FUNCTION__);
printf("%s():End.\n", __FUNCTION__);
}
int main()
{
int ret;
printf("%s():Begin.\n", __FUNCTION__);
ret = setjmp(save);
if(ret == 0)
{
printf("%s():Call a()\n", __FUNCTION__);
a();
printf("%s():Return from a()\n",__FUNCTION__);
}
else
{
printf("%s(): Jmp back here with code: %d\n", __FUNCTION__, ret);
}
printf("%s():End.\n", __FUNCTION__);
exit(0);
}
运行结果:

没跳转

跳转【运行结果】
7. 资源获取及控制
命令 ulimit
用下面两个函数实现:


- 软限制小于等于硬限制
- 普通用户不能提高硬限制, root可以