前言
苦逼本科生要考试,考试还奇难内容齐多
这里我整理一下考试提纲
正好这也是对《unix环境高级编程》的前半部分的一个总结
就当看着乐
还是lxd大佬流弊,都是他带我的
正文
什么是操作系统?什么是UNIX操作系统?什么是Linux操作系统?它们之间的关系是怎样的?
操作系统是管理和控制计算机硬件和软件资源的计算机程序,是直接运行在“裸机”上的最基本的系统软件。
UNIX是一个与1969年在AT&T的贝尔实验室开发的多用户多任务的分时的操作系统
Linux是一个基于POSIX和UNIX的多用户、多任务、支持多线程的开源的由Linus开发的操作系统
Linux是基于UNIX而构造的。
UNIX操作系统有哪些典型的分支?
Solaris,Linux,HP-UX,FreeBSD,SunOS,MacOS
程序运行时,堆栈如何变化?什么是数据帧或活动记录?bp和sp指针在函数运行过程中是如何变化的?
新的栈中变量会在地址低的位置,新的堆中变量会在地址高的位置。
栈是向低地址延伸的,堆是向高地址延伸的。
(这里的数据帧是不是栈帧啊……我google只查到计算机网络相关的数据帧)
(具体看编译原理)
过程活动记录,又称栈帧,栈帧包含了局部变量,函数试产,临时值等数据信息以及必要的控制信息。
在函数调用过程中,会讲被调用者的过程活动记录以堆栈的形式存储
函数的调用过程中,bp会保存当前栈帧的起始地址(保持不变),sp指示栈顶的位置。随着函数运行有出栈入栈的操作,sp的值会随之发生变化。
每当函数调用时,会把被调用者栈帧中的bp压栈,然后讲当前的sp赋给bp表示这是被调用者的栈帧起始地址,然后再讲参数通过bp放入(bp+4*n)的地址
函数调用
[调用过程](javascript:void())涉及到计算机CPU内部的一些概念,最主要的有栈(stack)、栈帧(fram stack)、返回值(一般通过ax寄存器)、基址寄存器(bp)、栈寄存器(sp)、指令指针寄存器(ip)等。言简意赅的说,每个函数在运行过程中,在栈上都以栈帧的形式保存临时变量,也就是中间结果。如果它还调用其他函数,需要传递的参数也是通过栈帧来传递的(不同体系结构可能不一样)。每个栈帧有开始和结束,bp寄存器保存当前栈帧的起始位置(保持不变),sp指示栈顶的位置,随着函数运行会有出栈、入栈操作,sp的值会随之发生变化。ip始终指向下一个运行的汇编代码的虚拟地址。调用函数(caller)在调用被调函数(callee)之前,首先会把参数压栈,然后运行call
指令,CPU就会把当前过程的下一条指令的地址压栈(这一步是CPU自动执行的,汇编代码看不出栈的变化),以便调用返回时继续执行;还会保存标志位寄存器flag和段寄存器cs(视不同调用类型而定)。callee运行,首先会把caller栈帧的bp压栈(返回时需要恢复),然后将当前的sp赋值给bp,表明这时的bp是callee的栈帧起始地址;然后取出caller传进来的参数(在caller栈帧中,通过bp寄存器为参考点获取参数,(bp+8),(bp+12)...)。这里可能会有点困惑的是(bp+0),(bp+4)的内容是什么?上面说到进入callee后首先要做的就push %bp,此处的bp寄存器保存的是上一栈帧的起始位置。而进入callee必须保存上一过程的返回地址。这样就清楚了(bp+0)的值是上一个过程的栈帧值,(bp+4)是callee返回后caller执行的下一个指令地址。通过这样的[函数调用规约](javascript:void()),程序运行时的每次调用就能准确无误。如果有返回值,一般以ax寄存器传递返回值,那么在返回caller后,需保存ax寄存器的值。
函数调用与递归
UNIX环境下编程共同遵循的规范有哪些?“Least Surprise”是什么意思?
KISS(Keep It Simple, Stupid)
还有其他的?UNIX哲学?
Least Surprise 是减少不必要的创新,从而减少用户的学习成本
The Art of Unix Programming
什么是Shell?内置变量$、$*、$@、$?、$#的具体含义是什么?
$$
指当前shell进程的pid
$*
指的是传递给脚本的所有参数
$@
也是把所有参数传递给脚本,但是当参数被双引号包含的时候,$*
会把所有的参数作为一个整体,而$@
会将各个参数分开
$?
指的是上个命令的退出状态,或函数返回值
$#
指的是传递给脚本或函数的参数个数
Shell特殊变量:Shell $0, $#, $*, $@, $?, $$和命令行参数
常用的Shell命令,如ls、cd、dd、pwd、ps、cp、chown、chmod、mkdir、size等的具体用法。
ls (list)列出所有文件
cd (change directory)换工作目录
dd (disk dump)指定大小拷贝文件
pwd (print working directory)打印工作目录
ps (processes status) 显示终端下所有进程
cp (copy)复制
chown (change owner)改变文件的所有者
chmod (change mode )改变文件的权限模式
mkdir (make directory)
size 显示目标文件长度
什么是管道?哪些文件参与了管道?在命令行下和通过编程如何实现管道?
管道是进程间的通信的工具
stdin, stdout, stderr三个文件参与了管道
在命令行下,可以通过"|"实现管道。前一个进程产生的输入传输道后一个进程之中。
在linux c下,可以通过pipe去实现管道
/*
无名管道的生成
*/
#include
int main()
{
int pipefd[2]; // pipefd[0]是输入fd, pipefd[1]是输出fd
pipe(pipefd);
}
常规文件和目录文件的差别是什么?常规文件、目录文件、软链接文件的长度如何计算?
目录文件也是一种特殊的文件,里面存放的是父目录节点和孩子文件。
常规文件只要用户拥有读权限就可以读,如果用户拥有写权限就可以写。
目录文件的读只需用户拥有这个目录的读权限,但是写时靠操作系统内核写的。
常规文件的长度根据其内容计算。
目录大小根据目录文件对应的inode的Block的大小。一般文件不多的情况下都是4096即4K的大小。而一般情况下都是4K的n倍,n取决于目录下文件的数量
软链接的文件长度就是目标文件的文件名字符长度,就比如一个指向AAA
的软连接的文件大小是3,因为AAA
有三个字符
怎样移动文件?在命令行方式下和通过编程如何实现?
在命令行的方式下,可以通过mv命令来移动文件
mv oldFile oneDir/newFile
在Linux C下貌似没有直接移动文件的函数,但是可以通过创建硬链接和删除原有链接的方式实现
但是可以使用rename()
函数,取得同样的效果
怎样利用gcc编译源文件?gcc的-o、-e、-static、-Wall等选项的具体含义是什么?怎样使用?
gcc cFile.c
生成一个名为a.out的可执行文件
-o可以指定生成文件的名字
gcc -o test cFile.c
之后生成名为test的可执行文件
-E只激活预处理,并不生成文件
需要将其重定向到一个输出文件里面
gcc -E cFile.c > redirector.c
-static静态编译,生成的文件不依赖动态链接库而使用静态链接库,可移植性高,缺点是生成的可执行文件非常大
gcc -static cFile.c
gcc -e 设置程序的入口函数
//test.c
void fun()
{
printf("helloWorld");
}
gcc -e fun test.c
一般情况下加上-nostartfiles参数,这个就避免了main的链接
-Wall在编译过程中会显示所有的warning信息
什么是动态链接库?什么是静态链接库?怎样用静态链接的方式编译C程序?
动态链接库(dynamic link library, dll)是指可以c等编程语言在变编译时候依赖的库。在编译过程中这些库不会链接到可执行文件中,所以文件大小比较小,缺点是可移植性不强
静态链接库(.lib文件)是在c等编程语言在编译的时候将库链接到可执行文件中,所以文件大小比较大,但是又较好的可移植性
gcc默认使用动态链接地方式编译,如果想使用静态链接就需要用-static选项
什么是文件系统?UNIX的文件系统有什么特点?有哪些具体的文件类型?
文件系统是操作系统在存储设备上组织文件的方法和数据结构。
UNIX的文件系统是依靠inode的vnode的。
在进程表项中都维护一个文件表,文件表中有文件状态标志、当前文件的偏移量和vnode指针。vnode指针指向了一个vnode节点,其中包含了vnode节点信息和对inode的指针。
inode指向了磁盘上的文件。
磁盘上的文件至少对应一个inode,当一个文件的inode达到0时,文件被正式删除。
每个目录的硬链接数至少有2个,自己与父目录。
具体的文件类型:
- 常规文件
- 目录文件
- 字符特殊文件:对设备不带缓冲的访问
- 块特殊文件:对设备带缓冲的访问
- FIFO:用于进程间的通信
- 套接字:用于进程间的网络通信
- 软链接
什么是文件的访问权限?使用chmod命令怎样改变访问权限?chmod 0777是什么意思?
访问权限指的是文件是否可以被不同类型的人访问的标识符
chmod
在shell中是
chmod u+x g+w filename #user现在可以执行,group现在可以写入
chmod u=rwx g=rw o=r filename #对于filename来说,user现在read write execute都被允许,group只被允许read和write,other可以read
chmod 0777代表着对于user/group/others来说,都可以对filename读写执行了
什么是文件操作是的偏移量(offset)?它的数据类型是什么?操作系统中文件的最大长度取决于什么?
偏移量指的是现在文件操作的指针指向的是文件的第几个字节。
数据类型是offset_t
操作系统中文件的最大长度取决于文件自身的字节数和块大小。#有点迷,不回答
什么是管道?什么是文件重定向?dup()、dup2()函数怎样使用?
管道是进程间的通信的工具
文件重定向是指将进程的标准输出或标准输入换成文件流
int dup(int oldd);
dup返回新文件描述符是当前可用文件描述符中的最小值
int dup2(int fd, int fd2);
dup2可以用fd2指定新描述符的值,如果fd2已经打开,则先将其关闭。
什么是文件的静态属性和动态属性(文件描述符属性)?在文件描述符属性中,哪些是由进程维护的?哪些是由内核维护的?
静态属性:由stat()函数显示的文件属性都是静态属性。包括.... ls -l的结果都是
动态属性:在打开文件之后知道的文件的属性,就比如文件的offset
进程维护:FD_CLOEXEC (file descriptor close on exec() )(ps: 还是lxd大佬流弊,都是他带我)
什么是会话(Session)、进程组?它们之间有什么关系?
进程组是一个或多个进程的集合
会话是一个或多个进程组的集合。
关系:会话是一个或多个进程组的集合。
父进程和子进程之间是什么关系?怎样在父子进程之间共享文件描述符?
父进程通过调用fork函数生成子进程,并且通过waitpid的方式回收子进程使其不成为僵尸进程。在子进程未通过调用exec执行新的代码的时候,父进程与子进程的所有信息完全相同,包括文件表。
在子进程创建之初就已经共享文件描述符(因为父进程与子进程的文件表相同。)只要在fork之前打开的文件,都可以被父子进程共享。
在一个进程中,文件描述符的增长规律是怎样的?例如,如果已经有0、1、2、6这样几个文件描述符,那么用open()返回的下一个文件描述符是什么?
是最小的未被使用的的非负整数。3
什么是process id?父进程和子进程的pid之间有什么关系?(通常子进程的pid要大于父进程的pid)
process id 即进程id,是一个进程独有的编号;
父进程总是会比子进程先创建出来,故父亲的pid<子进程pid。
什么是C语言程序的入口函数?在C Startup Routine(start.S)中接受的main函数原型是什么?
C语言程序的入口函数是main函数。C程序总是从main函数开始执行。
在c语言编译过程中,从_start
开始执行。
而_start
会调用libc_start_main函数,libc_start_main这个函数的第一个参数就是指向一个
main函数的指针
main
函数的签名为int main(int argc, char **argv, char** environ)
三个参数分别代表参数的个数,参数和环境变量。
什么是系统调用?什么是C语言库函数?它们之间有什么区别和联系?
系统调用:unix操作系统所提供的良好定义、数量有限、之间进入内核的入口点
C语言库函数:这里指的是程序员使用的通用库函数。虽然折现函数可能会调用一个或多个内核的系统调用,但是它们都不是内核的入口点
联系:它们都通过C函数的形式出现并且都为应用程序提供服务
区别:
- 库函数是可以灵活地被替换的,但是系统调用不是
- 两者的职责和操作的层面不同。库函数常在用户层次管理上而系统调用在内核层次上
什么是inode?里面存放什么信息?文件的文件名存放在哪里?
inode是索引节点,是UNIX操作系统中包含文件系统重要信息的一种数据结构。
里面存放了包括文件所有者,文件长度,文件在磁盘上的位置的指针等等信息。
文件的文件名存放在目录项中
C程序的内存布局是怎样的?从低地址到高地址依次存放哪些段?
从低到高:代码段,初始化过的数据,未初始化过的数据,堆,栈,命令行参数和环境变量
其中程序的静态变量都放在代码段中。
在栈中,后入的变量居于低地址,先入的变量居于高地址
怎样利用fork()、exec()、waitpid()来创建和控制进程?
父进程调用fork(),之后程序中应该有一个条件判断判断是否为子进程,如果是,则子进程调用exec,并且会将if中其他代码全部替换掉。之后,父进程执行waitpid,回收子进程,防止其变为僵尸进程
什么是孤儿进程、什么是僵尸进程?它们有什么特点?怎样避免产生过多僵尸进程?
孤儿进程就是指父进程早于子进程结束,从而没有父进程的子进程
孤儿进程会被1号进程托管,并且在结束的时候被回收
僵尸进程就是指由于种种原因,父进程迟迟不使用wait
和waitpid
回收的子进程
僵尸进程一直在进程表中占有一个进程表象,但并没有占用任何资源(因为进程结束的时候资源早已回收)
可以使用两次fork的方法避免产生过多的僵尸进程。调用两次fork然后直接杀死子进程,留下孙子进程被1号进程托管,待其结束被1号进程回收。
什么是前台进程?什么是后台进程?一个会话有几个前台进程组和几个后台进程组?
前台进程:用户使用的有控制终端的进程
后台进程:是运行在后台的一种特殊进程。但仍联系控制终端并且周期性地执行某种任务或者等待处理某些发生的事件。
attention: 后台进程和守护进程并不是一个概念。守护进程完全独立于控制终端而后台进程并没有
在控制终端终止时,后台进程会随着前台进程一起终止,但是守护进程不会
守护进程会常驻内存,防止关掉控制终端内存消失
后台进程不等于守护进程
一个会话中包含一个前台进程组,一个或多个后台进程组。
C程序如何退出并返回操作系统?exit()函数和_exit()/_Exit()函数的差别在哪里?
进程的终止方式有 8 种,其中 5 种为正常终止
- 从 main 返回。
- 调用 exit。
- 调用_exit 或_Exit。
- 最后一个线程从其启动例程返回。
- 最后一个线程调用pthread_exit。
另外三种为异常终止方式,它们是
- 调用 abort。
- 接到一个信号并终止。
- 最后一个线程对取消请求做出响应。
void exit(int status)
void _Exit(int status)
void _exit(int status)
第一个是C语言的库函数
第二个是系统调用的POSIX标准的函数
第三个系统调用的ISO标准的函数
系统调用的特点是系统调用不会调用退出处理函数,但是C语言的库函数会
//
// Created by kardel on 5/28/18.
//
#include
#include
#include
#include
void print_syscall()
{
printf("HelloWorld");
_Exit(0);
}
void print_clib()
{
printf("HelloWorld");
exit(0);
}
int main()
{
printf("in clib:\n");
print_clib();
// printf("in syscall:\n");
// print_syscall();
}
这样的结果是
in clib:
HelloWorld
如果注释掉上面两行,结果是
in syscall:
因为printf的打印在结束调用函数中,但是_Exit()直接退出,就不存在结束调用
exec函数族包含哪些具体的函数?其中execve是系统调用,其它都是普通函数。
函数签名 | 解释 |
---|---|
int execl(const char * pathname, const char * arg0, .../*, NULL*/); |
根据pathname所代表的文件路径通过给定的参数执行进程。注意arg0应该是程序本身 |
int execle(const char * pathname, const char * arg0, ..., /*, NULL, char * const envp[]*/); |
根据pathname所代表的文件路径通过给定的参数执行进程。最后一个参数是环境变量数组。注意arg0是程序本身 |
int execlp(const char * pathname, const char * arg0, ... /*, NULL*/); |
在环境变量中根据pathname找文件并且通过给定参数执行进程。注意arg0应该是程序本身 |
int execv(const char * pathname, char * const argv[]); |
根据pathname所代表的文件路径通过给定的参数的数组执行进程。注意数组第一个元素应该是程序本身 |
int execvp(const char * pathname, const char * arg0); |
在环境变量中根据pathname找文件并且通过给定参数的数组执行进程。注意数组第一个元素应该是程序本身 |
int fexecve(int fd, char * const argv[], char * const envp[]); |
在环境变量中根据file descriptor找文件并且通过给定参数的数组执行进程。注意数组第一个元素应该是程序本身。最后一个参数是环境变量数组 |
int execve(const char * pathname, char * const argv[], char * const envp[]); |
真正的系统调用。根据pathname所代表的文件路径通过给定的参数的数组执行进程。注意数组第一个元素应该是程序本身。最后一个参数是环境变量数 |
*什么是信号?SIGINT、SIGSTOP、SIGHUP、SIGALARM、SIGQUIT等信号是如何产生的?缺省的处理动作是什么?
信号是异步事件通知进程的一种方式。
信号处理函数靠内核进行
因为这个已经脱离了用户自定义的范围,转而变成了操作系统的范围
信号产生的方法:
用户通过按某些终端键可以引发终端产生信号
硬件异常产生信号
用户可以通过kill函数讲任意信号发给另一个进程或进程组。接收信号的进程和发送信号的进程的所有者必须相同。或者发送信号进程的所有者是超级用户
用户通过kill命令讲信号发送给其他进程
-
当检测到某种软件条件已经发生并应该将其通知有关进程时也产生信号。
这四个缺省的处理动作都是终止。
什么是硬链接和软链接(符号链接)?读取软连接的函数是什么?(readlink)
硬链接:通过 i 节点链接使多个目录项指向同一个文件的这种链接类型。
符号链接:对一个文件的间接指针,一般用于将一个文件或整个目录结构移到文件系统中的另一个位置。
ssize_t readlink(const char * restrict pathname, char * restrict buf, size_t bufsize)
可以打开软连接并且读该软链接中的名字
函数link()和unlink()的作用是什么?什么时候文件占用的磁盘空间才会真正被释放掉?(两个条件)
int link(const char * existingpath, const char * newpath);
根据existingpath创造一个路径为newpath的硬链接。
int unlink(const char * pathname);
删除一个硬链接
当:
没有硬链接指向文件
-
进程关闭该文件或进程终止
时,在磁盘空间内的文件才会被释放掉
什么是可重入函数?怎样判断一个函数是不是可重入函数?
可重入函数是在信号处理程序中保证调用安全的函数。
不是可重入函数的几个特点:
- 使用静态数据结构
- 他们调用
malloc
或free
- 它们是标准I/O函数
什么是带缓冲的输出和不带缓冲的输出?当父进程的输出缓冲区还未清空时,调用fork创建子进程,会出现什么情况?
不带缓冲的输出是在调用时直接将内容输出到标准输出端/文件,而不是经过缓冲区。比如printf
函数
带缓冲区的输出实在调用的时候将内容缓冲在缓冲区,等到程序结束了再一并写入输入端/文件,比如fprintf
函数
会打印两次相同的内容
//
// Created by kardel on 5/28/18.
//
#include
#include
#include
#include
int main()
{
FILE *fd = fopen("test.txt", "a");
char * buffer = "HELLO\n";
fwrite(buffer, 6, 1, fd);
int pid = fork();
//我懒得写waitpid,僵尸就僵尸吧
}
可以看见在test.txt中的内容是
HELLO
HELLO
编程的题目在这次考试中占很大的比重。一共有两道编程的题目。第一道跟文件操作相关,要求详细掌握的用法包括:能用open,read,write,close等函数来操纵文件;能利用lseek来形成文件中的“空洞”;能动态申请和释放空间等。
注意点: lseek
形成文件空洞之后要立即写入起码一个字节,否则空洞无效。
几个文件状态:
文件状态标志 | 说明 |
---|---|
O_RDONLY | 只读打开 |
O_WRONLY | 只写打开 |
O_RDWR | 读写打开 |
O_EXEC | 只执行打开 |
O_SEARCH | 只搜索打开目录 |
O_APPEND | 只添加新内容 |
O_NONBLOCK | 非堵塞 |
O_SYNC | 等待写完成(数据与属性) |
O_DSYNC | 等待写完成(仅数据) |
O_RSYNC | 同时读写 |
O_FSYNC | 等待写完成 |
O_ASYNC | 异步I/O |
O_TRUNC | 截断文件,重头再来 |
O_CREAT | 创造 |
几个签名:
int open(const char * pathname, int flags, mode_t mode);
返回文件描述符
例如
#include
#include
#include
#include
int main()
{
int fd;
fd=open("abc",O_CREAT,0777);
printf("fd=%d\n",fd);
return 0;
}
ssize_t write(int fd, const void * buf, size_t count)
将存在buf中的写入fd指定的文件中,写入count个字节,返回写入的字节数。如果返回-1则出错
ssize_t read(int fd, void * buf, size_t count)
将fd指定的文件中的内容读到buf中,读count个字节,返回读出的字节数。如果返回-1则读出错
第二道编程的题目跟进程和信号相关。要求详细掌握的用法包括:能用pause函数阻塞进程;能用signal设置信号处理函数;能向特定进程发送信号;能用rename移动文件;能遍历目录;能用S_ISREG宏辨别文件类型等等。切记对最后两道编程的大题要提前做好准备
int pause()
使调用进程睡眠,直到接收到信号。pause
返回-1,errno
设置为EINTR
void (*signal(int signo, void (*func)(int)))(int)
第一个参数使调整的信号,第二个参数是一个函数,这个函数的参数是信号
打开目录通过
DIR * opendir(const char* name);
打开目录通过
struct dirent * readdir(DIR *dir);
返回一个struct dirent *类型
struct dirent{
ino_t dino; //inode number
off_t d_off; //offset to the next dirent
unsigned short d_reclen //length of this record
unsigned char d_type; //type of file
char d_name[256]; //name of the file
关闭文件夹
int closedir(DIR * dir)
注意:遍历目录的时候要把上一级和目录以及隐藏文件去掉,避免死循环遍历
用
if(strncmp(file->d_name, ".", 1) == 0)
{
continue;
}
整体大概是
#include
#include
#include
#include
#include
len = 0;
int trave_dir(char *path)
{
DIR *d; //声明一个句柄
struct dirent *file; //readdir函数的返回值就存放在这个结构体中
struct stat sb;
if (!(d = opendir(path)))
{
printf("error opendir %s!!! \n", path);
return -1;
}
while ((file = readdir(d)) != NULL)
{
stat(file->d_name, &sb);
if(S_ISREG(sb.st_mode))
printf("yes, this is a regular file: ");
printf("%s \n", file->d_name);
}
closedir(d);
return 0;
}
int main()
{
trave_dir(".");
}
在我的cmake-build-debug文件夹下它ls的结果是
ls -al
drwxrwxr-x 3 kardel kardel 4096 Jun 15 20:04 .
drwxrwxr-x 4 kardel kardel 4096 Jun 15 20:04 ..
-rw-rw-r-- 1 kardel kardel 44891 May 28 05:58 CMakeCache.txt
drwxrwxr-x 5 kardel kardel 4096 Jun 15 20:04 CMakeFiles
-rw-rw-r-- 1 kardel kardel 1539 May 28 05:58 cmake_install.cmake
-rw-rw-r-- 1 kardel kardel 5149 Jun 15 19:49 Makefile
-rw-rw-r-- 1 kardel kardel 12 Jun 15 19:24 test.txt
-rwxrwxr-x 1 kardel kardel 12344 Jun 15 20:04 untitled
-rw-rw-r-- 1 kardel kardel 5536 Jun 15 19:49 untitled.cbp
我的程序输出是
yes, this is a regular file: Makefile
yes, this is a regular file: untitled.cbp
CMakeFiles
..
yes, this is a regular file: CMakeCache.txt
yes, this is a regular file: untitled
yes, this is a regular file: test.txt
yes, this is a regular file: cmake_install.cmake
.
rename移动文件
int rename(const char * oldpath, const char * newpath);
如果出错,返回-1
宏 | 描述 |
---|---|
S_ISLNK | 是否为一个链接 |
S_ISREG | 是否为一个常规文件 |
S_ISDIR | 是否为一个目录 |
S_ISCHR | 是否为一个字符串特殊文件 |
S_ISBLK | 是否为一个块特殊文件 |
S_ISFIFO | 是否为FIFO文件 |
S_ISSOCK | 是否为一个socket文件 |
这些都可以通过
int stat(const char * restrict pathname, struct stat * restrict buf);
int fstat(int fd, struct stat * buf);
所得到的stat结构体中的st_mode
得到。他的类型是mode_t
#include
#include
#include
#include
#include
int main()
{
struct stat buf;
int result = stat("../test.c", &buf);
if(result == 0)
{
if(S_ISREG(buf.st_mode))
{
printf("yes, it is a regular file");
}
else
{
printf("this is not a reg file!");
}
}
else{
printf("read error");
}
}
结果
yes, it is a regular file