千家信息网

Linux 系统函数open、close、read、writ

发表于:2025-01-24 作者:千家信息网编辑
千家信息网最后更新 2025年01月24日,系统调用系统调用是操作系统提供给用户的一组"特殊接口"。系统调用并非直接和程序员或系统管理员直接打交道,而是通过软中断的方式向内核提交请求,从而获取内核函数的服务入口(系统调用表)。系统调用让系统从用
千家信息网最后更新 2025年01月24日Linux 系统函数open、close、read、writ

系统调用

系统调用是操作系统提供给用户的一组"特殊接口"。系统调用并非直接和程序员或系统管理员直接打交道,而是通过软中断的方式向内核提交请求,从而获取内核函数的服务入口(系统调用表)。系统调用让系统从用户空间进入内核空间内运行,运行后将结果返回给应用程序(内核态->用户空间)。


系统调用和系统API等区别



系统API
主要是通过C库libc来实现,程序员多采用这种方式与内核交互,这些API通过系统调用来实现


系统命令
系统管理员采用系统命令与内核交互,是一个可执行文件,通过系统API及系统调用来实现


外壳程序
一系列系统命令和SHELL脚本共同组合成的程序。

函数库调用 与 系统调用



文件描述符



-每个进程PCB结构中有文件描述符指针,指向files_struct的文件描述符表,记录每个进程打开的文件列表
-系统内核不允许应用程序访问进程的文件描述符表,只返回这些结构的索引即文件描述符ID(File Description)给应用程序
-Linux系统中,应用程序通过这些文件描述符来实现让内核对文件的访问
-每个进程能够访问的文件描述符是有限制的,通过#ulimit -n可以查看

特殊文件描述符


标准输入STDIN_FILENO
标准输出STDOUT_FILENO
标准错误STDERR_FILENO


每个进程被加载后,默认打开0,1,2这三个文件描述符


open



-函数原型
int open(const char *path, int flags,mode_t mode);
-参数
path :文件的名称,可以包含(绝对和相对)路径
flags:文件打开模式
mode: 用来规定对该文件的所有者,文件的用户组及系统中其他用户的访问权限,则文件权限为:mode&(~umask)
-返回值
打开成功,返回文件描述符;
打开失败,返回-1


打开方式

访问权限




O_CREATE会产生特殊权限,需要设置访问权限:
S_IRWXU等价于 S_IRUSR|S_IWUSR|S_IXUSR (文件所有者)
S_IRWXG 等价于 S_IRGRP|S_IWGRP|S_IXGRP (文件用户组)
S_IRWXO 等价于 S_IROTH|S_IWOTH|S_IXOTH (文件其他用户)

close

关闭文件close(将进程中fd对应的文件描述表结构释放):
函数原型:int close(int fd); //如果出现错误,返回-1;调用成功返回0

read

-函数原型:
int read(int fd, void *buf, size_t nbytes);
-参数
fd :想要读的文件的文件描述符
buf: 指向内存块的指针,从文件中读取来的字节放到这个内存块中
nbytes: 从该文件复制到buf中的字节个数
-返回值
如果出现错误,返回-1;
返回从该文件复制到规定的缓冲区中的字节数!

write

-函数原型:int write(int fd,void *buf,size_t nbytes);
-函数参数:
fd :要写入的文件的文件描述符
buf: 指向内存块的指针,从这个内存块中读取数据写入 到文件中
nbytes: 要写入文件的字节个数
-返回值
如果出现错误,返回-1
如果写入成功,则返回写入到文件中的字节个数


练习:采用文件系统调用的方式,完成串口通讯配置文件的读写


配置文件serial.cfg如下:



要求:1、程序运行命令格式:
serialchat [-options];
[options]:
p-输出打印serial.cfg各个配置项信息,格式:参数-----参数值;
s-进行菜单(有保存和退出)让用户设置;
f-指定配配置项设置,输出各选项置文件名,并输出各配置项信息
其他选项提示该程序用法帮助。
2、不支持serialchat运行两次
3、如果无法找到配置文件,则提示运行失败,原因:配置文件无法找到


代码如下:

#include   < stdio.h>#include   < string.h>#include   < sys/types.h>#include   < sys/stat.h>#include   < unistd.h>#include    < fcntl.h>int icount = 0;//去空格 '\0'void rm_space(char *pStr){    char *pos = pStr;    pos = strchr(pStr,' ');    while(pos != NULL)    {        strcpy(pos,pos+1);        pos = strchr(pos,' ');    }}//去注释 '#' '\n#'和中文,留下\n ... \r之间的数据void rm_annotation(char *pStr){    icount++;    char *pos = pStr;    char *end = NULL;    if(icount == 1) //第一行有可能顶格    {        pos = strchr(pStr,'#');    }    else    {        pos = strstr(pStr,"\n#");    }    while(pos != NULL)    {        end = strchr(pos+1,'\n');        strcpy(pos,end);        pos = strstr(pStr,"\n#");    }   }//输出配置项信息void printMsg(char *buf){    char key[20] = " ";    char value[20] = " ";    char line[50] = " ";    char *pos = buf;    char *end = NULL;    int flag = 0;    printf("配置项信息如下:\n");    pos = strchr(buf,'\n');    end = strchr(pos,'=');    while(end != NULL)    {        memset(key,0,sizeof(key));        memset(value,0,sizeof(value));        memcpy(key,pos+1,end - (pos + 1));        pos = end;        end = strchr(pos,'\r');        if(end == NULL) //if the final data        {            flag = 1;            break;        }        memcpy(value,pos+1,end - (pos + 1));        sprintf(line,"%s-----%s",key,value);        printf("%s\n",line);        pos = end + 1;        end = strchr(pos,'=');      }    if(flag)        {        end = strchr(pos,'\0');        memcpy(value,pos+1,end - (pos + 1));        sprintf(line,"%s-----%s",key,value);        printf("%s\n",line);    }}//修改配置文件 (传入的buf是未经处理的)void updateFile(int fd,char *buf){    char *pos = buf;    char *end = NULL;    char key[20] = "";    char newvalue[20] = "";    int lse;    int count;    printf("请输入配置参数: ");    scanf("%s",key);    printf("请输入配置参数的值: ");    scanf("%s",newvalue);    strcat(key,"=");    pos = strstr(buf,key);    if(pos == NULL)    {        printf("输入参数不存在!");        return;    }    pos = pos + strlen(key);    while(*pos == ' ')  //处理'='后面有空格的情况    {        pos = pos + 1;    }    end = strchr(pos,'\r');    if(end == NULL) //if the final data    {        end = strchr(pos,'\0');    }    //修改文件    lse = lseek(fd,pos-buf,SEEK_SET);    write(fd,newvalue,end-pos); //注意新旧值的长度    printf("修改成功!\n");}/*--------------------主函数---------------------*/int main(int argc,char *argv[]){    int fd;    int readByte = 0;    char buf[512] = "";    char option[3] = "";    char filename[20] = "";    int  index;    int s_flag = 0;    int f_flag = 0;    struct flock lock = {0};    //判断参数个数    if(argc !=2 && argc != 3)    {        printf("输入格式有误!\n");        return 1;    }    //打开配置文件    strcpy(option,argv[1]);    if(strcmp(option,"-f") == 0)    //打开指定文件    {        if(argc != 3)        {            printf("输入格式有误!\n");            return 1;        }        f_flag = 1;        strcpy(filename,argv[2]);        fd = open(filename,O_RDWR,S_IRWXU|S_IRGRP);    }    else    {        fd = open("serial.cfg",O_RDWR,S_IRWXU|S_IRGRP);    }    if(fd == -1)    {        printf("运行失败,无法找到配置文件!\n");        return 1;    }    //读取配置文件    readByte = read(fd,buf,512);    if(readByte <= 0)    {        printf("读文件失败!\n");        return 1;    }    //判断是否已经加锁    fcntl(fd,F_GETLK,&lock);    if(lock.l_type != F_UNLCK)    {        printf("文件已经被上锁!执行错误\n");        return 1;    }    //上锁    memset(&lock,0,sizeof(struct flock));    lock.l_whence   = SEEK_SET;    lock.l_start        = 0;    lock.l_len      = sizeof(buf);    lock.l_type     =F_WRLCK;    if((fcntl(fd,F_SETLK,&lock)) == -1)    {        printf("设置写锁失败!\n");        return 1;    }    //操作    if(f_flag || strcmp(option,"-p") == 0)  //打印配置信息    {        rm_space(buf);        rm_annotation(buf);        printMsg(buf);    }    else if(strcmp(option,"-s") == 0)   //进行菜单    {        while(!s_flag)        {            printf("1.修改配置项\n2.退出\n请输入选择: ");            scanf("%d",&index);            switch(index)            {                case 1:                    updateFile(fd,buf);                    break;                case 2:                    s_flag = 1;                    break;            }        }    }    else    {        printf("serialchat用法:\n");        printf("\t命令格式:serialchat [-options]\n ");        printf("\t   [options]: p-输出打印serial.cfg各个配置项信息\n ");        printf("\t   [options]: s-进行菜单\n ");        printf("\t   [options]: f-指定配置文件\n ");        printf("\t   [options]: 其他-查看帮助\n ");    }    //关闭文件    close(fd);    //释放锁    lock.l_type     =F_UNLCK;    fcntl(fd,F_SETLK,&lock);    return 0;}

运行结果:
1、 serialchat -p

2、 serialchat-s

3、 serialchat-f

4、 异常处理

5、 上锁
终端1:

终端2:

0