Linux高级系统编程中的系统调用

概念

是操作系统提供给用户使其可以操作内核提供服务的一组函数接口。

用户态和内核态:

引入
        整个 计算机系统 的。好比你写 一个程序,但是因为你对 硬件操作 不熟悉,出现 问题,那么影响范围是多大?是整个计算机系统,操作系统内核、及其其他所有正在运 行的程序,都会因为你操作失误而受到不可挽回的错误,那么你只有重启整个计算机才 行 而对于 硬件的操作 是非常复杂的,参数众多,出问题的几率相当大,必须及其谨 慎的进行操作,这对于个人开发者来说是个艰巨的任务,同时个人开发者在这方面也是 不被信任的。所以 操作系统内核 直接屏蔽了个人开发者对于硬件操作的可能.
        这方面 系统内核 对 硬件操作 进行了封装处理,对外提供标准函数库,操作更简 单、更安全。比如 我们要打开一个文件,C 标准函数库中对应的是 fopen() ,其内部封 装的是内核中的系统函数open() 因为这个需求,硬件设备商直接提供了硬件级别的支持,做法就是对 CPU 指令设置了 权限,不同级别的权限可以使用的 CPU 指令是有限制的。以 Inter CPU 为例, Inter 把 CPU 指令操作的权限划为 4 级:
        ring 0
        ring 1
        ring 2
        ring 3
        其中 ring 0 权限最高,可以使用所有 CPU 指令, ring 3 权限最低,仅能使用 常规 CPU 指令,这个级别的权限不能使用访问硬件资源的指令,比如 IO 读写、网卡 访问、申请内存都不行,都没有权限
        Linux 系统内核采用了: ring 0 ring 3 2 个权限
ring 0:
        内核态 , 完全在 操作系统内核 中运行,由专门的 内核线程 在 CPU 中 执行其任务
ring 3:
        用户态 , 在 应用程序 中运行,由 用户线程 在 CPU 中执行其任务 Linux 系统中所有对硬件资源的操作都必须在 内核态 状态下执行,比如 IO 的 读写,网络的操作
区别
        1,用户态的代码必须由 用户线程 去执行、内核态的代码必须由 内核线程 去执行
        2,用户态、内核态 或者说 用户线程、内核线程 可以使用的资源是不同的,尤体现在 内存资源上。Linux 内核对每一个进程都会分配 4G 虚拟内存空间地址
        用户态: --> 只能操作 0-3G 的内存地址
        内核态: --> 0-4G 的内存地址都可以操作,尤其是对 3-4G 的高位地址必须由 内核态去操作,因为所有进程的 3-4G 的高位地址使用的都是同一块、专门留给 系统 内核 使用的 1G 物理内存
        3.所有对 硬件资源、系统内核数据 的访问都必须由内核态去执行
如何切换内核态
        通过软件中断
软件中断
        软件中断是由软件程序触发的中断,如系统调用、软中断、异常等。软件中断不是 由硬件设备触发的,而是由软件程序主动发起的,一般用于系统调用、进程切换、异常 处理等任务。软件中断需要在程序中进行调用,其响应速度和实时性相对较差,但是具 有灵活性和可控性高的特点。
        如程序中出现的内存溢出, 数组下标越界等
硬件中断
        硬件中断是由硬件设备触发的中断,如时钟中断、串口接收中断、外部中断等。当 硬件设备有数据或事件需要处理时,会向CPU 发送一个中断请求, CPU 在收到中断请求 后,会立即暂停当前正在执行的任务,进入中断处理程序中处理中断请求。硬件中断具 有实时性强、可靠性高、处理速度快等特点。
        如当点击按钮扫描系统高低电频时等

系统调用与库函数的关系

库函数可以调用系统调用提供的接口 , 也可以不调用系统调用提供的接口
        不调用系统调用的库函数:strcpy,bzero
        调用系统调用的库函数:fread,printf
注意 :
        
        系统调用是需要时间的,程序中频繁的使用系统调用会降低程序的运行效率。当运行内核 代码时,CPU 工作在内核态,在系统调用发生前需要保存用户态的栈和内存环境,然后转 入内核态工作。系统调用结束后,又要切换回用户态。这种环境的切换会消耗掉许多时 间。

文件操作

文件描述符概念

        文件描述符是一个非负整数, 代表已打开的文件。
        每一个进程都会创建一张文件描述符表 记录的是当前进程打开的所有文件描述符。
        每一个进程默认打开三个文件描述符:
        0(标准输入设备 scanf)
        1(标准输出设备 printf)
        2(标准错误输入设备 perror)
        新打开的文件描述符 为最小可用文件描述符。
扩展
        ulimit是一个计算机命令,用于 shell 启动进程所占用的资源,可用于修改系统资源限 制。使用ulimit 命令用于临时修改资源限制,如果需要永久修改需要将设置写入配置文 件/etc/security/limits.conf
        ulimit -a 查看 open files 打开的文件最大数。
        ulimit -n 最大数 设置 open files 打开的文件最大数。

文件读写

第一位说是文件还是文件夹
        2~4位说明所有者权限
        5~7位说明同组用户权限
        8~10位说明其他用户权限
        r 4
        w 2
        x 1
注意
        man 2 系统调用
        查看系统调用函数对应的头文件与函数信息
语法 :
open: 打开文件
所需头文件
        #include <sys/types.h>
        #include <sys/stat.h>
        #include <fcntl.h>
函数
        int open(const char *pathname, int flags);
        int open(const char *pathname, int flags, mode_t mode);
参数
        pathname:打开的文件地址
        flags:代码操作文件的权限
必选项
        O_RDONLY 以只读的方式打开
        O_WRONLY 以只写的方式打开
        O_RDWR 以可读、可写的方式打开
可选项
        O_CREAT 文件不存在则创建文件,使用此选项时需使用 mode 说明文件的权限
        O_EXCL 如果同时指定了 O_CREAT ,且文件已经存在 , 则打开 , 如果文件 不存在则新建
        O_TRUNC 如果文件存在,则清空文件内容
        O_APPEND 写文件时,数据添加到文件末尾
        O_NONBLOCK 对于设备文件 , O_NONBLOCK 方式打开可以做非阻塞 I/O
mode: 文件在磁盘中的权限
格式 :
        0ddd
        d的取值 :4( 可读 ),2( 可写 ),1( 可执行 )
第一个 d: 所有者权限
第二个 d: 同组用户权限
第三个 d: 其他用户权限
如果需要可读可写就是 6, 可读可执行 5
:
        0666:所有者可读可写 , 同组用户可读可写 , 其他用户可读可写
        0765:所有者可读可写可执行 , 同组用户可读可写 , 其他用户可读可执行
返回值:
        成功:得到最小可用的文件描述符
        失败:-1
经验 :
        操作已有文件使用两参
        新建文件使用三参
close 关闭文件
所需头文件
        #include <unistd.h>
函数
        int close(int fd);
参数
        关闭的文件描述符
返回值
        成功:0
        失败: -1, 并设置 errno
示例 : 以只读的方式打开关闭文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //1打开文件
    // man 2 系统调用函数名
    // int open(const char *pathname, int flags);
    // int open(const char *pathname, int flags, mode_t mode);
    int fileTag = open("a.txt",O_RDONLY);
    if (fileTag < 0)
    {
        printf("读取文件不存在,文件标识符为:%d\n",fileTag);
        return 0;
    }
    printf("文件打开成功,文件标识符为:%d\n",fileTag);
    int tag = close(fileTag);
    if (tag < 0)
    {
        printf("关闭文件失败\n");
    }
    else
    {
        printf("关闭文件成功\n");
    }
    return 0;
}
示例 : 以写的方式打开关闭文件
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //1打开文件
    // man 2 系统调用函数名
    // int open(const char *pathname, int flags);
    // int open(const char *pathname, int flags, mode_t mode);
    int fileTag = open("a.txt",O_WRONLY| O_CREAT, 0644);
    if (fileTag < 0)
    {
        printf("文件不存在,文件标识符为:%d\n",fileTag);
        return 0;
    }
    printf("文件打开成功,文件标识符为:%d\n",fileTag);
    int tag = close(fileTag);
    if (tag < 0)
    {
        printf("关闭文件失败\n");
    }
    else
    {
        printf("关闭文件成功\n");
    }
    return 0;
}
write 写入
所需头文件
        #include <unistd.h>
函数
        ssize_t write(int fd, const void *buf, size_t count);
参数
        fd:写入的文件描述符
        buf:写入的内容首地址
        count:写入的长度 , 单位字节
返回值
        成功: 返回写入的内容的长度 , 单位字节
        失败:-1
示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    int fileTag = open("a.txt",O_WRONLY| O_CREAT | O_APPEND, 0744);
    if (fileTag < 0)
    {
        printf("文件不存在,文件标识符为:%d\n",fileTag);
        return 0;
    }
    char str[] = "hello";
    int len = write(fileTag,str,sizeof(str)-1);
    if(len < 0)
    {
        printf("写入失败\n");
    }
    else
    {
        printf("写入成功len = %d,%d\n",len,sizeof(str));
    }
    int tag = close(fileTag);
    if (tag < 0)
    {
        printf("关闭文件失败\n");
    }
    else
    {
        printf("关闭文件成功\n");
    }
    return 0;
}
read 读取
所需头
        #include <unistd.h>
函数
        ssize_t read(int fd, void *buf, size_t count);
参数:
        fd:文件描述符
        buf:内存首地址
        count:读取的字节个数
返回值:
        成功: 实际读取到的字节个数
        失败:-1
示例
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    int id = open("a.txt",O_RDONLY);
    if(id < 0)
    {
        printf("文件打开失败\n");
        return 0;
    }
    char buf[50] = "";
    int len = read(id,buf,sizeof(buf));
    printf("%s",buf);
    if (close(id) < 0)
    {
        printf("文件关闭失败\n");
    }
    return 0;
}
示例 : 文件复制
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    int fd_r = open("a.txt",O_RDONLY);
    if(fd_r < 0)
    {
        printf("读取文件打开失败\n");
        return 0;
    }
    int fd_w = open("b.txt",O_WRONLY|O_CREAT|O_APPEND,0666);
    if(fd_w < 0)
    {
        printf("写入文件打开失败\n");
        return 0;
    }
    while (1)
    {
        char buf[3] = "";
        int len = read(fd_r,buf,sizeof(buf));
        int size = write(fd_w,buf,len);
        if (len < sizeof(buf))
        {
            break;
        }
    }
    if (close(fd_w) < 0)
    {
        printf("写入文件关闭失败\n");
    }
    if (close(fd_r) < 0)
    {
        printf("读取文件关闭失败\n");
    }
    printf("OVER\n");
    return 0;
}

文件的阻塞特性

        read默认为阻塞。如果读不到数据,将阻塞不继续执行 知道有数据可读,才继续往下 执行。
        非阻塞特性:如果没数据,立即返回,继续执行。
        注意: 阻塞与非阻塞是对于文件而言的,而不是指 read write 等的属性。
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>
int main(int argc, char const *argv[])
{
    //dev:liunx设备存储文件夹
    //tty:终端
    ///dev/tty:表示当前终端
    int fd = open("/dev/tty",O_RDONLY);
    //O_NONBLOCK:设置打开的文件为非阻塞
    // int fd = open("/dev/tty",O_RDONLY|O_NONBLOCK);
    char buf[32] = "";
    printf("开始读取\n");
    read(fd,buf,sizeof(buf));
    printf("读取结束\n");
    return 0;
}
通过 open 打开的文件可以设置非阻塞 , 但是如果不是通过 open 打开的文件怎么办 ?
通过 fcntl 函数来解决
funtl
作用 : 针对已经存在的文件描述符设置阻塞状态
所需头文件
#include <unistd.h>
#include <fcntl.h>
函数 :
int fcntl(int fd, int cmd, ... /* arg */);
功能 :
改变已打开的文件性质, fcntl 针对描述符提供控制。
参数:
fd :操作的文件描述符
cmd :操作方式
arg :针对 cmd 的值, fcntl 能够接受第三个参数 int arg
返回值:
成功:返回某个其他值
失败: -1
fcntl 函数有 5 种功能:
        1) 复制一个现有的描述符( cmd=F_DUPFD
        2) 获得/设置文件描述符标记 (cmd=F_GETFD F_SETFD)
        3) 获得/设置文件状态标记 (cmd=F_GETFL F_SETFL)
        4) 获得/设置异步 I/O 所有权 (cmd=F_GETOWN F_SETOWN)
        5) 获得/设置记录锁 (cmd=FGETLK, F_SETLK F_SETLKW)
使用步骤
        1,获取文件状态标记
        2,将得到的文件状态标记设置为非阻塞
        3,将修改后的文件非阻塞状态标记 , 设置到当前文件描述符中
#include <stdio.h>
#include <unistd.h>
#include <fcntl.h>
int main(int argc, char const *argv[])
{
//1,获取文件标记状态
int status = fcntl(0,F_GETFL);
//2修改状态为非阻塞状态
status = status | O_NONBLOCK;
//3,设置文件标记状态为非阻塞状态
fcntl(0,F_SETFL,status);
char buf[32]="";
printf("开始读取\n");
//0(标准输入设备scanf)
int len = read(0,buf,sizeof(buf));
printf("结束读取,读取到的内容为:%s\n",buf);
return 0;
}

文件状态

语法
作用 :
        获取文件状态信息
所需头
        #include <sys/types.h>
        #include <sys/stat.h>
        #include <unistd.h>
函数
        int stat(const char *path, struct stat *buf);
        int lstat(const char *pathname, struct stat *buf);
参数
        1参 : 文件地址
        2参 : 保存文件信息的结构体
返回值
        0:成功
        -1:失败
stat lstat 的区别
        当文件是一个符号链接时
        lstat 返回的是该符号链接本身的信息 ,
        stat 返回的是该链接指向的文件的信息。
stat 结构体解释
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; //文件字节数(文件大小)
blksize_t st_blksize; //块大小(文件系统的 I/O 缓冲区大小)
blkcnt_t st_blocks; //块数
time_t st_atime; //最后一次访问时间
time_t st_mtime; //最后一次修改时间
time_t st_ctime; //最后一次改变时间(指属性)
};
stat 结构体 st_mode 属性
        一个由16 个字节组成 , 简称 16
        0~2其他人权限
        3~5所属组权限
        6~8所有者权限
        12~15文件类型
        具体参考下图
存储权限
S_ISUID 04000 set-user-ID bit
S_ISGID 02000 set-group-ID bit (see below)
S_ISVTX 01000 sticky bit (see below)
S_IRWXU 00700 owner has read, write, and execute permission
S_IRUSR 00400 owner has read permission
S_IWUSR 00200 owner has write permission
S_IXUSR 00100 owner has execute permission
S_IRWXG 00070 group has read, write, and execute permission
S_IRGRP 00040 group has read permission
S_IWGRP 00020 group has write permission
S_IXGRP 00010 group has execute permission
S_IRWXO 00007 others (not in group) have read, write, and execute permission
S_IROTH 00004 others have read permission
S_IWOTH 00002 others have write permission
S_IXOTH 00001 others have execute permission
#include <stdio.h>
#include <sys/types.h>//open
#include <sys/stat.h>//open
#include <fcntl.h>//open
#include <unistd.h>//close
int main(int argc, char *argv[])
{
//获取文件状态
struct stat st;
stat("a.txt", &st);
//分析文件类型
if(S_ISREG(st.st_mode))
{
printf("普通文件\n");
}
else if (S_ISDIR(st.st_mode))
{
printf("目录文件\n");
}
//分析权限
if((st.st_mode & S_IRUSR) ==S_IRUSR )
{
printf("所有者具有读权限\n");
}
if((st.st_mode & S_IWUSR) ==S_IWUSR)
{
printf("所有者具有写权限\n");
}
if((st.st_mode & S_IXUSR) ==S_IXUSR)
{
printf("所有者具有执行权限\n");
}
printf("文件的大小:%ld\n", st.st_size);
return 0;
}

目录操作

语法
打开目录
作用 : 打开目录 opendir
所有头文件 :
        #include <sys/types.h>
        #include <dirent.h>
函数 :
        DIR *opendir(const char *name);
参数:
        name:目录名
返回值:
        成功:返回指向该目录结构体指针(DIR *)
        失败:NULL
DIR: 中文名称句柄 , 其实就是目录的结构体指针
读取目录
作用 : 读取目录 readdir
所需头文件
        #include <dirent.h>
函数
        struct dirent *readdir(DIR *dirp);
参数:
        dirp: opendir 的返回值
返回值:
        成功:目录结构体指针
        失败:NULL
注意 : 一次读取一个文件。
相关结构体
相关结构体说明:
struct dirent
{
        ino_t d_ino; // 此目录进入点的 inode
        off_t d_off; // 目录文件开头至此目录进入点的位移
        signed short int d_reclen; // d_name 的长度 , 不包含 NULL 字符
        unsigned char d_type; // d_type 所指的文件类型
        char d_name[256]; // 文件名
};
d_type 说明 :
DT_BLK 这是一个块设备。 ( 块设备如 : 磁盘 )
DT_CHR 这是一个字符设备。 ( 字符设备如 : 键盘 , 打印机 )
DT_DIR 这是一个目录。
DT_FIFO 这是一个命名管道( FIFO )。
DT_LNK 这是一个符号链接。
DT_REG 这是一个常规文件。
DT_SOCK 这是一个 UNIX 域套接字。
DT_UNKNOWN 文件类型未知。
关闭目录
作用 : 关闭目录 closedir
所需头文件
        #include <sys/types.h>
        #include <dirent.h>
函数
        int closedir(DIR *dirp);
参数:
        dirp:opendir 返回的指针
返回值:
        成功:0
        失败:-1
示例:扫描文件目录
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
int main(int argc, char const *argv[])
{
DIR *dir = opendir("./");
if(dir == NULL)
{
printf("打开文件夹失败");
return 0;
}
while(1)
{
struct dirent * d = readdir(dir);
if (d == NULL)
{
break;
}
if (d->d_type == DT_DIR)
{
printf("%s是个文件夹\n",d->d_name);
}
else if(d->d_type == DT_REG)
{
printf("%s是个普通文件\n",d->d_name);
}
}
return 0;
}
#include <stdio.h>
#include <sys/types.h>
#include <dirent.h>
#include <string.h>
void blDIR(char *path)
{
char filedir[128]="";
strcpy(filedir,path);
DIR *dir = opendir(filedir);
if(dir == NULL)
{
printf("打开文件夹失败");
return;
}
while(1)
{
struct dirent * d = readdir(dir);
if (d == NULL)
{
break;
}
/**
* strcmp(字符串1,字符串2):比较字符串是否相同
* strcat(字符串1,字符串2):将字符串2拼接到字符串1后
*/
if (d->d_type == DT_DIR && strcmp(d->d_name,".") != 0 &&
strcmp(d->d_name,"..") !=0)
{
printf("%s是个文件夹\n",d->d_name);
strcat(filedir,"/");
strcat(filedir,d->d_name);
blDIR(filedir);
}
else if(d->d_type == DT_REG)
{
printf("%s是个普通文件\n",d->d_name);
}
}
closedir(dir);
}
int main(int argc, char const *argv[])
{
blDIR("./");
return 0;
}

  • 30
    点赞
  • 13
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值