1. 标准IO介绍及缓冲区
Linux IO 进程课程目的:学习编写 linux 应用程序(APP)
Linux 文件的种类 :
常规文件 r
目录文件 d
字符文件 c
块文件 b
链接文件(相当于 windows 快捷方式)l
socket文件 s
管道文件 pIO 的概念
I input: 输入设备 比如键盘鼠标都是 Input 设备
O output 输出设备 比如显示器
优盘,网口,既是输入也是输出系统调用和库函数
系统调用就是操作系统提供的接口函数.
如果我们把系统调用封装成库函数就可以起到隔离的作用,提供程序的可移植性。
Printf 就是库函数然后调用了系统调用才在显示器上显示字符流的概念
就是数据的流,在程序中就是一个结构体。Windows 和 linux 的换行符区别
Windows 是\r\n
Linux 是\n缓冲区的概念
为了减少操作 IO 设备的次数,提高运行效率,在内存里面设置的缓冲区,
全缓冲:缓冲区满才输出
行缓冲:遇到换行符输出-
三种标准 IO :
缓冲区概念演示:
程序正常结束会刷新缓冲区
#include<stdio.h>
int main(int argc, char* argv[]){
printf("hello world");
return 0;
}
- Sleep 函数:是释放 cpu 给其他应用程序使用的库函数。使用的头文件是#include<unistd.h>
查看头文件方法:man 2 函数 ,或者 man 3 函数
#include<stdio.h>
#include<unistd.h>
int main(int argc, char* argv[]){
printf("hello world");
while(1){
sleep(1);
}
return 0;
}
结果无任何输出,因为缓冲区未满也没\n, 程序也没结束。
2. 标准IO: 文件的打开和关闭
文件的打开和关闭概念
打开就是占用资源
关闭就是释放资源
文件的打开
文件的打开函数
FILE *fopen (const char *path, const char *mode);
Path: 普通文件当前路径不需要加目录,其他要使用完整的路径
Mode:
返回值:出错返回 NULL,所以使用 fopen 函数必须判断是否为空-
文件打开的模式(非常重要)
编译错误:
f_open.c:9:38: error: ‘errno’ undeclared (first use in this function)
printf("fopen:%s\n",strerror(errno));
error: ‘errno’ undeclared 表示 errno 变量没有定义
解决方法:如果是系统变量用 include 头文件,如果是你自己的,自己手动定义。
f_open.c:10:29: warning: implicit declaration of function ‘strerror’
[-Wimplicit-function-declaration]
printf("fopen:%s\n",strerror(errno));
warning: implicit declaration of function ‘strerror’ 表示 strerror 函数隐示的声明
解决方法:include 添加对应的头文件。perror 库函数 头文件 stdio.h
strerror 库函数 头文件 errno.h string.h
perror 和 strerror 功能:打印系统的错误描述(注意:是系统错误,不是你自己代码错误)
文件的关闭:
函数原型:int fclose(FILE *stream)
- fclose()调用成功返回 0,失败返回 EOF(-1),并设置 errno
- 流关闭时自动刷新缓冲中的数据并释放缓冲区,比如:常规文件把缓冲区内容写入磁盘
- 当一个程序正常终止时,所有打开的流都会被关闭
- fclose()函数的入参 stream 必须保证为非空,否则出现断错误。
#include<stdio.h>
#include<string.h>
#include<errno.h>
int main(int argc, char * argv[]){
FILE *fp = fopen("1.txt","r");
int fret = EOF;
if(fp == NULL){
perror("fopen");
printf("fopen: %s\n",strerror(errno));
}else{
printf("fopen: open file success\n");
fret = fclose(fp);
if(fret == 1){
printf("file close success\n");
}else{
perror("fclose");
}
}
return 1;
}
3. 标准 IO 的字符输入和输出
字符的输入(读单个字符)
int fgetc(FILE *stream);
int getc(FILE *stream); //宏
int getchar(void);
成功时返回读取的字符;若到文件末尾或出错时返回 EOF(-1),
getchar()等同于 fgetc(stdin)
getc 和 fgetc 区别是一个是宏一个是函数
注意事项:
- 函数返回值是 int 类型不是 char 类型,主要是为了扩展返回值的范围。(char范围是-128 -- 127 unsigned char 0 - 255, int就都包含了)
- stdin 也是 FILE *的指针,是系统定义好的,指向的是标准输入(键盘输入)
- 打开文件后读取,是从文件开头开始读。读完一个后读写指针会后移。读写注意文件位置!
- 调用 getchar 会阻塞,等待你的键盘输入
字符的输出(写单个字符):
int fputc(int c, FILE *stream);
int putc(int c, FILE *stream);
int putchar(int c);
成功时返回写入的字符;出错时返回 EOF
putchar(c)等同于 fputc(c, stdout)
行输入(读取整个行)
char *gets(char *s); 读取标准输入到缓冲区 s
char *fgets(char *s, int size, FILE *stream);
成功时返回 s,到文件末尾或出错时返回 NULL
遇到’\n’或已输入 size-1 个字符时返回,总是包含’\0’
注意事项:
- gets 函数已经被淘汰,因为会导致缓冲区溢出
- fgets 函数第二个参数,输入的数据超出 size,size-1 个字符会保存到缓冲区,最后添加’\0’,如果输入数据少于 size-1 后面会添加换行符。
行输出(写整行)
int puts(const char *s);
int fputs(const char *s, FILE *stream);
- 成功时返回非负整数;出错时返回 EOF
- puts 将缓冲区 s 中的字符串输出到 stdout,并追加’\n’
- fputs 将缓冲区 s 中的字符串输出到 stream,不追加 ‘\n’
4. 标准IO读写:二进制方式
文本文件和二进制的区别:
存储的格式不同:文本文件只能存储文本。
计算机内码概念:文本符号在计算机内部的编码(计算机内部只能存储数字 0101001....,所以所有符号都要编码)
二进制读写函数格式:
size_t fread(void *ptr, size_t size, size_t n, FILE *fp);
void *ptr 读取内容放的位置指针
size_t size 读取的块大小
size_t n 读取的个数
FILE *fp 读取的文件指针
size_t fwrite(const void *ptr, size_t size, size_t n, FILE *fp);
void *ptr 写文件的内容的位置指针
size_t size 写的块大小
size_t n 写的个数
FILE *fp 要写的文件指针
fread: 从1.txt中读出10个字符
#include<stdio.h>
#include<stdlib.h>
int main(){
FILE *fp;
size_t ret;
char* buff;
fp = fopen("1.txt","r");
if(fp == NULL){
perror("fopen");
return -1;
}
buff = (char *)malloc(100);
if(buff == NULL){
perror("buff");
fclose(fp);
return -1;
}
ret = fread(buff,10,1,fp);
if(ret == EOF){
perror("fread");
goto end;
}
printf("ret = %s\n",buff);
end:
free(buff);
fclose(fp);
return 0;
}
fwrite: 往1.bin中写入1个struct Student对象的二进制
#include<stdio.h>
#include<string.h>
struct Student{
char name[16];
int age;
char sex[8];
};
int main(){
FILE *fp;
size_t ret;
fp = fopen("1.bin","w");
if(fp == NULL){
perror("fopen");
return -1;
}
struct Student stu;
strcpy(stu.name,"zhangsan");
stu.age = 18;
strcpy(stu.sex,"male");
ret = fwrite(&stu,sizeof(stu),1,fp);
if(ret == EOF){
perror("fwrite");
goto end;
}
end:
fclose(fp);
return 0;
}
#include<stdio.h>
#include<string.h>
struct Student{
char name[16];
int age;
char sex[8];
};
int main(){
FILE *fp;
size_t ret;
struct Student stu;
struct Student stu2;
fp = fopen("1.bin","w");
if(fp == NULL){
perror("fopen");
return -1;
}
strcpy(stu.name,"zhangsan");
stu.age = 18;
strcpy(stu.sex,"male");
ret = fwrite(&stu,sizeof(stu),1,fp);
if(ret == EOF){
perror("fwrite");
goto end;
}else{
printf("write student success\n");
}
ret = fread(&stu2,sizeof(stu2),1,fp);
if(ret == EOF){
perror("fread");
goto end;
}
printf("stu2.name=%s,stu2.age=%d,stu2.sex=%s\n",stu2.name,stu2.age,stu2.sex);
end:
fclose(fp);
return 0;
}
输出:stu2.name=,stu2.age=0,stu2.sex=
注意事项:
文件写完后,文件指针指向文件末尾,如果这时候读,读不出来内容。
解决办法:移动指针(后面讲解)到文件头;或关闭文件,重新打开
5. 流刷新定位
流的刷新
int fflush(FILE *fp);
- 成功时返回 0;出错时返回 EOF
- 将流缓冲区中的数据写入实际的文件
- Linux 下只能刷新输出缓冲区,输入缓冲区丢弃
- 如果输出到屏幕使用 fflush(stdout)
#include<stdio.h>
#include<unistd.h>
int main(){
printf("abcdefghijklmn");
fflush(stdout); //不加这一行将无法输出 因为缓冲区未满也没\n, 程序也没结束。
while(1){
sleep(1);
}
return 0;
}
#include<stdio.h>
#include<unistd.h>
int main(){
FILE *fp;
size_t ret;
fp = fopen("fflush.txt","w");
if(fp == NULL){
perror("fopen");
return -1;
}
ret = fwrite("abcdefghigklmn",14,1,fp);
if(ret == EOF){
perror("fwrite");
}
fflush(fp); //不加这一行 也无法将内容写入到文件
while(1){
sleep(1);
}
return 0;
}
流的定位
#include <stdio.h>
long ftell(FILE *stream);
long fseek(FILE *stream, long offset, int whence);
void rewind(FILE *stream);
- ftell() 成功时返回流的当前读写位置,出错时返回 EOF
- fseek() 定位一个流,成功时返回 0 ,出错时返回 EOF
- whence 参数: SEEK_SET/SEEK_CUR/SEEK_END
- SEEK_SET 从距文件开头 offset 位移量为新的读写位置
- SEEK_CUR :以目前的读写位置往后增加 offset 个位移量
- SEEK_END :将读写位置指向文件尾后再增加 offset 个位移量
- offset 参数:偏移量,可正可负
- 打开 a 模式 fseek 无效
- rewind() 将流定位到文件开始位置
- 读写流时,当前读写位置自动后移
- 注意事项:
1.文件的打开使用 a 模式 fseek 无效
2.rewind(fp) 相当于 fseek(fp,0,SEEK_SET);
3.这三个函数只适用 2G 以下的文件
#include<stdio.h>
int main(){
FILE *fp;
fp = fopen("1.txt","w");
if(fp == NULL){
perror("fopen");
return 0;
}
fwrite("abcdefg",7,1,fp);
printf("current fp=%d\n",(int)ftell(fp));
rewind(fp);
printf("after fp=%d\n",(int)ftell(fp));
return 0;
}
int ferror(FILE *stream);
int feof(FILE *stream);
- ferror() 返回 1 表示流出错;否则返回 0
- feof() 返回 1 表示文件已到末尾;否则返回 0
6. 格式化输入输出
格式化输出
int printf(const char *fmt, …);
int fprintf(FILE *stream, const char *fmt, …);
int sprintf(char *s, const char *fmt, …);
- 成功时返回输出的字符个数;出错时返回 EOF
#include<stdio.h>
int main(){
FILE *fp;
size_t ret;
int year = 2025;
int month = 8;
int day = 1;
fp = fopen("1.txt","w");
if(fp == NULL){
perror("fopen");
return 0;
}
ret = fprintf(fp,"%d-%d-%d",year,month,day);
if(ret == EOF){
perror("fprintf");
}
fclose(fp);
return 0;
#include<stdio.h>
int main(){
char buff[20]={0};
size_t ret;
int year = 2025;
int month = 8;
int day = 1;
ret = sprintf(buff,"%d-%d-%d",year,month,day);
if(ret == -1){
perror("sprintf");
}
printf("%ld %s",ret,buff);
return 0;
}
格式化输入
int fscanf(FILE *stream, const char *format, ...);
int sscanf(const char *str, const char *format, ...);
- 成功时返回输出的字符个数;出错时返回 EOF
#include<stdio.h>
int main(){
FILE *fp;
int year, month, day;
fp = fopen("1.txt","r");
if(fp == NULL){
perror("fopen");
return 0;
}
fscanf(fp,"%d-%d-%d",&year,&month,&day);
printf("%d-%d-%d\n",year, month, day);
fclose(fp);
return 0;
}
#include<stdio.h>
int main(){
char buff[20]={0};
size_t ret;
int year = 2025;
int month = 8;
int day = 1;
int syear, smonth, sday;
ret = sprintf(buff,"%d-%d-%d",year,month,day);
if(ret == -1){
perror("sprintf");
}
printf("%ld %s\n",ret,buff);
sscanf(buff,"%d-%d-%d",&syear,&smonth,&sday);
printf("%d-%d-%d\n",syear,smonth,sday);
return 0;
}
7. 标准IO练习
- 每隔 1 秒向文件 test.txt 中写入当前系统时间,格式如下:
1, 2014-10-15 15:16:42
2, 2014-10-15 15:16:43
该程序无限循环,直到按 Ctrl-C 中断程序
每次执行程序时,系统时间追加到文件末尾,序号递增
1, 2014-10-15 15:16:42
2, 2014-10-15 15:16:43
3, 2014-10-16 11:35:07
4, 2014-10-16 11:35:08
time() 用来获取系统时间 ( 秒数 )
time_t time(time_t *seconds) 1970.1.1 0:0:0
localtime() 将系统时间转换成本地时间
struct tm *localtime(const time_t timer)
struct tm {
int tm_sec; / 秒,范围从 0 到 59 /
int tm_min; / 分,范围从 0 到 59 /
int tm_hour; / 小时,范围从 0 到 23 /
int tm_mday; / 一月中的第几天,范围从 1 到 31 /
int tm_mon; / 月份,范围从 0 到 11 /
int tm_year; / 自 1900 起的年数 /
int tm_wday; / 一周中的第几天,范围从 0 到 6 /
int tm_yday; / 一年中的第几天,范围从 0 到 365 /
int tm_isdst; / 夏令时 */
};
#include<stdio.h>
#include<unistd.h>
#include<time.h>
#include<string.h>
#include<errno.h>
int main(){
FILE *fp;
time_t ctime;
struct tm *ctimestr;
int line_num = 0;
char buff[32] = {};
fp = fopen("time.txt","a+");
if(fp == NULL){
perror("fopen");
return -1;
}
while(fgets(buff,32,fp) != NULL){
if(buff[strlen(buff)-1] == '\n')
line_num++;
}
while(1){
ctime = time(NULL);
// printf("current time:%ld",ctime);
ctimestr = localtime(&ctime);
printf("%04d-%02d-%02d %02d:%02d:%02d\n",ctimestr->tm_year+1900,ctimestr->tm_mon+1,
ctimestr->tm_mday, ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
fprintf(fp,"%d %04d-%02d-%02d %02d:%02d:%02d\n",line_num,ctimestr->tm_year+1900,ctimestr->tm_mon+1,
ctimestr->tm_mday, ctimestr->tm_hour,ctimestr->tm_min,ctimestr->tm_sec);
fflush(fp);
line_num++;
sleep(1);
}
fclose(fp);
return 0;
}
8. 文件I/O
什么是文件 I/O ?
posix( 可移植操作系统接口 ) 定义的一组函数
不提供缓冲机制,每次读写操作都引起系统调用

文件描述符
- 每个打开的文件都对应一个文件描述符。
- 文件描述符是一个非负整数。 Linux 为程序
中每个打开的文件分配一个文件描述符。 - 文件描述符从 0 开始分配,依次递增。
- 文件 IO 操作通过文件描述符来完成。
- 0, 1, 2 的含义?
open
open 函数用来创建或打开一个文件 :
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t
mode);
成功时返回文件描述符;出错时返回 EOF
打开文件时使用两个参数
创建文件时第三个参数指定新文件的权限,(只有在建立
新文件时有效)此外真正建文件时的权限会受到 umask
值影响,实际权限是 mode-umaks
可以打开设备文件,但是不能创建设备文件



open示例1:
#include<stdio.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){
int fd;
fd = open("1.txt",O WRONLY|O CREATIO TRUNC,0666);
if(fd<0){
perror("open");
return 0;
}
return 0;
}
open示例2:
#include<stdio.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(){
int fd;
fd = open("1.txt",O_WRONLY|O_CREAT|O_EXCL);
if(fd < 0){
if(errno == EEXSIT){
perror("exist error");
}else{
perror("other error");
}
return 0;
}
return 0;
}
close
close 函数用来关闭一个打开的文件 :
#include <unistd.h>
int close(int fd);
- 成功时返回 0 ;出错时返回 EOF
- 程序结束时自动关闭所有打开的文件
- 文件关闭后,文件描述符不代表任何文件
#include<stdio.h>
#include<errno.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<unistd.h>
int main(){
int ret;
int fd;
fd = open("1.txt",O_WRONLY|O_CREAT|O_TRUNC,0666);
if(fd<0){
perror("open file error");
return 0;
}
printf("fd=%d\n",fd);
ret = close(fd);
if(ret < 0){
perror("close");
}
return 0;
}
read
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
- 成功时返回实际读取的字节数;出错时返回 EOF
- 读到文件末尾时返回 0
- buf 是接收数据的缓冲区
- count大小不应超过buf长度
#include<stdio.h>
#include<errno.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
int main(int argc, char *argv[]){
int fd, n, total;
char buf[64] = {};
if(argc < 2){
printf("usage file:%s\n",argv[0]);
return -1;
}
fd = open(argv[1],O_RDONLY);
if(fd < 0){
perror("open");
return 0;
}
while((n = read(fd,buf,64)) > 0){
total += n;
}
printf("file total:%d\n",total);
return 0;
}
write
write 函数用来向文件写入数据 :
#include <unistd.h>
ssize_t write(int fd, void *buf, size_t count);
- 成功时返回实际写入的字节数;出错时返回 EOF
- buf 是发送数据的缓冲区
- count不应超过buf大小
#include<stdio.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<unistd.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>
int main(){
int fd;
char buf[20];
fd = open("1.txt",O_WRONLY|O_CREAT|O_TRUNC,0664);
if(fd < 0)
{
perror("open");
return -1;
}
while(fgets(buf,20,stdin) != NULL){
if(strcmp(buf,"quite\n") == 0)
break;
write(fd,buf,strlen(buf));
}
close(fd);
return 0;
}
lseek
lseek 函数用来定位文件 :
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
- 成功时返回当前的文件读写位置;出错时返回 EOF
- 参数offset和参数whence同fseek
#include<stdio.h>
#include<unistd.h>
#include<sys/types.h>
#include<sys/stat.h>
#include<fcntl.h>
#include<errno.h>
#include<string.h>
int main(){
int fd, ret;
char buf[32] = "hello world";
char buf2[32] = {0};
fd = open("test.h", O_RDWR | O_CREAT | O_APPEND,0666);
if(fd < 0){
perror("open");
return -1;
}
ret = write(fd,buf, strlen(buf));
if(ret < 0){
perror("write");
goto END;
}
ret = lseek(fd,0,SEEK_SET);
if(ret == EOF){
perror("lseek");
goto END;
}
ret = read(fd,buf2,32);
if(ret < 0){
perror("read");
goto END;
}
buf2[31] = '\0';
printf("buf2:%s\n",buf2);
END:
close(fd);
return 0;
}
9. 目录操作、文件属性
访问目录 – opendir
opendir 函数用来打开一个目录文件 :
#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd); 使用文件描述符,要配合 open 函数使用
- DIR 是用来描述一个打开的目录文件的结构体类型
- 成功时返回目录流指针,出错时返回NULL
访问目录 – readdir
readdir 函数用来读取目录流中的内容 :
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
- struct dirent 是用来描述目录流中一个目录项的结构体类型
- 包含成员 char d_name[256] 参考帮助文档
- 成功时返回目录流 dirp 中下一个目录项;
- 出错或到末尾返回NULL
访问目录 – closedir
closedir 函数用来关闭一个目录文件 :
#include <dirent.h>
int closedir(DIR *dirp);
- 成功时返回0 错误返回EOF
访问目录 – 示例代码
#include<stdio.h>
#include<errno.h>
#include<dirent.h>
int main(int argc, char *argv[]){
DIR *dirp;
struct dirent *dp;
int ret;
if(argc < 2){
printf("usage:%s<directory>\n",argv[0]);
}
dirp = opendir(argv[1]);
if(dirp == NULL){
perror("opendir");
return -1;
}
while((dp = readdir(dirp)) != NULL){
printf("%s\n",dp->d_name);
}
ret = closedir(dirp);
if(ret < 0){
perror("closedir");
}
return 0;
}
修改文件访问权限 – chmod/fchmod
chmod/fchmod 函数用来修改文件的访问权限 :
#include <sys/stat.h>
int chmod(const char *path, mode_t mode);
int fchmod(int fd, mode_t mode);
- 成功时返回 0 ;出错时返回 EOF
- root 和文件所有者能修改文件的访问权限
#include<stdio.h>
#include<sys/stat.h>
#include<errno.h>
int main(int argc, char *argV[])
{
int ret;
ret = chmod("readdir_t",0444);
if(ret == EOF){
perror("chmod");
return -1;
}
return 0;
}
获取文件属性 – stat/lstat/fstat
stat/lstat/fstat 函数用来获取文件属性 :
#include <sys/stat.h>
int stat(const char *path, struct stat *buf);
int lstat(const char *path, struct stat *buf);
int fstat(int fd, struct stat *buf);
- 成功时返回 0;出错时返回 EOF
- 如果 path 是符号链接 stat 获取的是目标文件属性;而lstat是获取的链接文件的属性
文件属性 – struct stat
struct stat 是存放文件属性的结构体类型 :
- mode_t st_mode; 类型和访问权限
- uid_t st_uid; 所有者 id
- uid_t st_gid; 用户组 id
- off_t st_size; 文件大小
- time_t st_mtime; 最后修改时间
• struct stat {
• dev_t st_dev; // 文件的设备编号
• ino_t st_ino; // 节点
• mode_t st_mode; // 文件的类型和存取的权限
• nlink_t st_nlink; // 连到该文件的硬连接数目,刚建立的文件值为 1
• uid_t st_uid; // 用户 ID
• gid_t st_gid; // 组 ID
• dev_t st_rdev; //( 设备类型 ) 若此文件为设备文件,则为其设备编号
• off_t st_size; // 文件字节数 ( 文件大小 )
• unsigned long st_blksize; // 块大小 ( 文件系统的 I/O 缓冲区大小 )
• unsigned long st_blocks; // 块数
• time_t st_atime; // 最后一次访问时间
• time_t st_mtime; // 最后一次修改时间
• time_t st_ctime; // 最后一次改变时间 ( 指属性 )
};
文件类型 – st_mode
通过系统提供的宏来判断文件类型 :
S_IFMT 0170000 文件类型的位遮罩
S_ISREG(st_mode) 0100000 是否常规文件
S_ISDIR(st_mode) 0040000 是否目录
S_ISCHR(st_mode) 0020000 是否字符设备
S_ISBLK(st_mode) 0060000 是否块设备
S_ISFIFO(st_mode) 0010000 是否 FIFO 文件
S_ISLNK(st_mode) 0120000 是否链接文件
S_ISSOCK(st_mode)) 0140000 是否socket文件
文件访问权限 – st_mode
通过系统提供的宏来获取文件访问权限 :
S_IRUSR 00400 bit:8 所有者有读权限
S_IWUSR 00200 7 所有者拥有写权限
S_IXUSR 00100 6 所有者拥有执行权限
S_IRGRP 00040 5 群组拥有读权限
S_IWGRP 00020 4 群组拥有写权限
S_IXGRP 00010 3 群组拥有执行权限
S_IROTH 00004 2 其他用户拥有读权限
S_IWOTH 00002 1 其他用户拥有写权限
S_IXOTH 00001 0 其他用户拥有执行权限

