linux系统编程(3)--系统调用

1.系统调用概念

系统调用,顾名思义,说的是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获得系统时间或设置定时器等。

从逻辑上来说,系统调用可被看成是一个内核与用户空间程序交互的接口——它好比一个中间人,把用户进程的请求传达给内核,待内核把请求处理完毕后再将处理结果送回给用户空间。

请添加图片描述

系统服务之所以需要通过系统调用来提供给用户空间的根本原因是为了对系统进行“保护”,因为我们知道 Linux 的运行空间分为内核空间与用户空间,它们各自运行在不同的级别中,逻辑上相互隔离。

所以用户进程在通常情况下不允许访问内核数据,也无法使用内核函数,它们只能在用户空间操作用户数据,调用用户空间函数。

比如我们熟悉的“hello world”程序(执行时)就是标准的用户空间进程,它使用的打印函数 printf 就属于用户空间函数,打印的字符“hello word”字符串也属于用户空间数据。

但是很多情况下,用户进程需要获得系统服务(调用系统程序),这时就必须利用系统提供给用户的“特殊接口”——系统调用了,它的特殊性主要在于规定了用户进程进入内核的具体位置。

换句话说,用户访问内核的路径是事先规定好的,只能从规定位置进入内核,而不准许肆意跳入内核。有了这样的陷入内核的统一访问路径限制才能保证内核安全无误。

系统调用的实现

系统调用是属于操作系统内核的一部分的,必须以某种方式提供给进程让它们去调用。CPU 可以在不同的特权级别下运行,而相应的操作系统也有不同的运行级别,用户态和内核态。运行在内核态的进程可以毫无限制的访问各种资源,而在用户态下的用户进程的各种操作都有着限制,比如不能随意的访问内存、不能开闭中断以及切换运行的特权级别。显然,属于内核的系统调用一定是运行在内核态下,但是如何切换到内核态呢?

答案是软件中断。软件中断和我们常说的中断(硬件中断)不同之处在于,它是通过软件指令触发而并非外设引发的中断,也就是说,又是编程人员开发出的一种异常(该异常为正常的异常)。操作系统一般是通过软件中断从用户态切换到内核态。

2.系统调用和库函数的区别

Linux 下对文件操作有两种方式:系统调用(system call)库函数调用(Library functions)

库函数由两类函数组成:

  1. 不需要调用系统调用

    不需要切换到内核空间即可完成函数全部功能,并且将结果反馈给应用程序,如strcpy、bzero 等字符串操作函数。

  2. 需要调用系统调用

    需要切换到内核空间,这类函数通过封装系统调用去实现相应功能,如 printf、fread等。

系统调用是需要时间的,程序中频繁的使用系统调用会降低程序的运行效率。当运行内核代码时,CPU工作在内核态,在系统调用发生前需要保存用户态的栈和内存环境,然后转入内核态工作。系统调用结束后,又要切换回用户态。这种环境的切换会消耗掉许多时间 。

3.错误处理函数

errno 是记录系统的最后一次错误代码。代码是一个int型的值,在errno.h中定义。

当Linux C api函数发生异常时,一般会将errno全局变量赋一个整数值,不同的值表示不同的含义,可以通过查看该值推测出错的原因。

#include <stdio.h>  	//fopen
#include <errno.h>  	//errno
#include <string.h> 	//strerror(errno)

int main()
{
    FILE *fp = fopen("xxxx", "r");
    if (NULL == fp)
    {
        printf("%d\n", errno);  			//打印错误码
        printf("%s\n", strerror(errno)); 	//把errno的数字转换成相应的文字
        
        perror("fopen");    				//打印错误原因的字符串
    }

    return 0;
}

查看错误号:

#错误号一般在以下两个文件中定义
/usr/include/asm-generic/errno-base.h
/usr/include/asm-generic/errno.h

4.文件描述符

打开现存文件或新建文件时,系统(内核)会返回一个文件描述符,文件描述符用来指定已打开的文件。这个文件描述符相当于这个已打开文件的标号,文件描述符是非负整数,是文件的标识,操作这个文件描述符相当于操作这个描述符所指定的文件。

程序运行起来后(每个进程)都有一张文件描述符的表,标准输入、标准输出、标准错误输出设备文件默认被打开,对应的文件描述符 0、1、2 记录在表中。

#define STDIN_FILENO  0 	//标准输入的文件描述符
#define STDOUT_FILENO 1 	//标准输出的文件描述符
#define STDERR_FILENO 2 	//标准错误的文件描述符

在程序运行起来后打开其他文件时,系统会返回文件描述符表中最小可用的文件描述符,并将此文件描述符记录在表中。

最大打开的文件个数

Linux 中一个进程最多只能打开 NR_OPEN_DEFAULT (即1024)个文件,故当文件不再使用时应及时调用 close() 函数关闭文件。

  • 查看当前系统允许打开最大文件个数:

    cat /proc/sys/fs/file-max

  • 当前默认设置最大打开文件个数1024

    ulimit -a

  • 修改默认设置最大打开文件个数为4096

    ulimit -n 4096

5.文件读写常用函数

5.1 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
    mode:这个参数,只有在文件不存在时有效,指新建文件时指定文件的权限
返回值:
    成功:成功返回打开的文件描述符
    失败:-1

flags选项:

必选项:

取值含义
O_RDONLY以只读的方式打开
O_WRONLY以只写的方式打开
O_RDWR以可读、可写的方式打开

可选项(和必选项按位或):

取值含义
O_CREAT文件不存在则创建文件,使用此选项时需使用mode说明文件的权限
O_EXCL如果同时指定了O_CREAT,且文件已经存在,则出错
O_TRUNC如果文件存在,则清空文件内容
O_APPEND写文件时,数据添加到文件末尾
O_NONBLOCK对于设备文件, 以O_NONBLOCK方式打开可以做非阻塞I/O

5.2 close

#include <unistd.h>

int close(int fd);
功能:
    关闭已打开的文件
参数:
    fd : 文件描述符,open()的返回值
返回值:
    成功:0
    失败: -1, 并设置errno

当一个进程终止时,内核对该进程所有尚未关闭的文件描述符调用close关闭,所以即使用户程序不调用close,在终止时内核也会自动关闭它打开的所有文件。但是对于一个长年累月运行的程序(比如网络服务器),打开的文件描述符一定要记得关闭,否则随着打开的文件越来越多,会占用大量文件描述符和系统资源。

示例:

#include <stdio.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd = -1;

    //1.以只读方式打开一个文件  如果文件不存在就报错
    //fd = open("txt", O_RDONLY);
    //2.以只写的方式打开一个文件 如果文件不存在就报错
    //fd = open("txt", O_WRONLY);
    //3.以只写的方式打开一个文件 如果文件不存在就创建, 如果文件存在就直接打开
    //fd = open("txt", O_WRONLY | O_CREAT, 0644);
    //4.以只读的方式打开一个文件 如果文件不存在就创建
    //fd = open("txt", O_RDONLY | O_CREAT, 0644);
    //5.以读写的方式打开文件 如果文件存在就报错, 如果文件不存在就创建
    //fd = open("txt", O_RDWR | O_CREAT | O_EXCL, 0644);
    //6.以读写的方式打开一个文件 如果文件不存在就创建 如果文件存在就清零
    fd = open("txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }
	printf("fd = %d\n", fd);
    printf("文件打开成功\n");
	
    //关闭文件 释放资源
    close(fd);
    
    return 0;
}

5.3 write

#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
功能:
    把指定数目的数据写到文件(fd)
参数:
    fd :  文件描述符
    buf : 数据首地址
    count : 写入数据的长度(字节)
返回值:
    成功:实际写入数据的字节个数
    失败: -1

示例:

#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main()
{
    int fd = -1;
    int ret = -1;

    //1. 打开文件
    //fd = open("txt", O_WRONLY | O_CREAT | O_TRUNC, 0644);
    fd = open("txt", O_WRONLY | O_CREAT , 0644);

    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }
    printf("fd = %d\n", fd);

    //2. 写文件
    char *str = "ABCDEFG";
    ret = write(fd, str, strlen(str)); 
    if (-1 == ret)
    {
        perror("write"); 
        return 1;
    }

    //3. 关闭文件
    close(fd);

    return 0;
}

5.4 read

#include <unistd.h>

ssize_t read(int fd, void *buf, size_t count);
功能:
    把指定数目的数据读到内存(缓冲区)
参数:
    fd : 文件描述符
    buf : 内存首地址
    count : 读取的字节个数
返回值:
    成功:实际读取到的字节个数
    失败: -1

阻塞和非阻塞的概念

读常规文件是不会阻塞的,不管读多少字节,read一定会在有限的时间内返回。从终端设备或网络读则不一定,如果从终端输入的数据没有换行符,调用read读终端设备就会阻塞,如果网络上没有接收到数据包,调用read从网络读就会阻塞,至于会阻塞多长时间也是不确定的,如果一直没有数据到达就一直阻塞在那里。

同样,写常规文件是不会阻塞的,而向终端设备或网络写则不一定。

示例:读文件

#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SIZE 128

int main(void)
{
    int fd = -1;
    int ret = -1;
    char buf[SIZE];

    //1. 打开文件  只读的方式
    fd = open("txt", O_RDONLY);
    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }
    printf("fd = %d\n", fd);

    //2. 读文件
    memset(buf, 0, SIZE);
    ret = read(fd, buf, SIZE);
    if (-1 == ret)
    {
        perror("read"); 
        close(fd);
        return 1;
    }
    printf("buf: %s\n", buf);

    //3. 关闭文件
    close(fd);
    return 0;
}

示例:向终端文件read

#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SIZE 128

int main()
{
    int fd = -1;
    int ret = -1;
    char buf[SIZE];

    //1.打开一个终端设备  该设备默认是阻塞的
    //fd = open("/dev/tty", O_RDWR);
    fd = open("/dev/tty", O_RDWR | O_NONBLOCK);
    if (-1 == fd)
    {
        perror("open"); 
        goto err;
    }

    //2. 读设备
    memset(buf, 0, SIZE);
    ret = read(fd, buf, SIZE);
    if (ret <= 0)
    {
        perror("read"); 
    }
    printf("buf:%s\n", buf);

    //3. 关闭文件
    close(fd);

    return 0;
err:
    return 1;
}

5.5 lseek

#include <sys/types.h>
#include <unistd.h>

off_t lseek(int fd, off_t offset, int whence);
功能:
    改变文件的偏移量
参数:
    fd:文件描述符
    offset:根据whence来移动的位移数(偏移量),可以是正数,也可以负数,如果正数,则相对于whence往右移动,如果是负数,则相对于whence往左移动。如果向前移动的字节数超过了文件开头则出错返回,如果向后移动的字节数超过了文件末尾,再次写入时将增大文件尺寸。

    whence:其取值如下:
        SEEK_SET:从文件开头移动offset个字节
        SEEK_CUR:从当前位置移动offset个字节
        SEEK_END:从文件末尾移动offset个字节
返回值:
    若lseek成功执行, 则返回新的偏移量
    如果失败, 返回-1

所有打开的文件都有一个当前文件偏移量(current file offset),以下简称为 cfo。

cfo 通常是一个非负整数,用于表明文件开始处到文件当前位置的字节数。读写操作通常开始于 cfo,并且使 cfo 增大,增量为读写的字节数。文件被打开时,cfo 会被初始化为 0,除非使用了 O_APPEND 。

示例:

#include <stdio.h>
#include <string.h>

#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

#define SIZE 128

int main()
{
    int fd = -1;
    int ret = -1;
    char buf[SIZE];

    //打开文件
    fd = open("txt", O_RDWR | O_CREAT, 0644);
    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }

    write(fd, "ABCDEFG",  7);
    ret = lseek(fd, 32, SEEK_SET);
    if (-1 == ret)
    {
        perror("lseek"); 
        return 1;
    }
    write(fd, "123456789", 9);

    //获取文件的大小
    printf("file: %ld\n", lseek(fd, 0, SEEK_END));

    lseek(fd, 0, SEEK_SET);
    memset(buf, 0, SIZE);
    ret = read(fd, buf, SIZE);
    printf("read ret:%d   buf:%s\n", ret, buf);

    close(fd);

    return 0;
}

5.6 truncate

#include <unistd.h>
#include <sys/types.h>

int truncate(const char *path, off_t length);
功能:修改文件大小
参数:
    path:文件文件名字
    length:指定的文件大小
        a)比原来小, 删掉后边的部分
        b)比原来大, 向后拓展
返回值:
    成功:0
    失败:-1

6.文件操作常用函数

6.1 stat

#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);
功能:
    获取文件状态信息
    stat和lstat的区别:
        当文件是一个符号链接时,lstat返回的是该符号链接本身的信息;
        而stat返回的是该链接指向的文件的信息。
参数:
    path:文件名
    buf:保存文件信息的结构体
返回值:
    成功: 0
    失败: -1
        
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;   //最后一次改变时间(指属性)
};

示例:判断文件类型

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

//判断文件类型方式1
int show_file_type(struct stat *s)
{
    switch(s->st_mode & S_IFMT)
    {
        case S_IFSOCK:   
            printf("socket\n");
            break;
        case S_IFLNK :   
            printf("symbolic link\n");
            break;
        case S_IFREG :   
            printf("regular file\n");
            break;
        case S_IFBLK :   
            printf("block device\n");
            break;
        case S_IFDIR :   
            printf("directory\n");
            break;
        case S_IFCHR :   
            printf("character device\n");
            break;
        case S_IFIFO :   
            printf("FIFO\n");
            break;
        defalt:
            printf("未知类型....\n");
    }
    return 0;
}
//判断文件类型方式2
//int show_file_type(struct stat *s)
//{
//	  if (S_ISREG(s->st_mode) ) 
//        printf("is it a regular file \n"); 
//    if (S_ISDIR(s->st_mode) ) 
//        printf("directory            \n");   
//    if (S_ISCHR(s->st_mode) ) 
//        printf("character device     \n");   
//    if (S_ISBLK(s->st_mode) ) 
//        printf("block device         \n"); 
//    if (S_ISFIFO(s->st_mode)) 
//        printf("FIFO (named pipe)    \n");   
//    if (S_ISLNK(s->st_mode) ) 
//        printf("symbolic link        \n");    
//    if (S_ISSOCK(s->st_mode)) 
//        printf("socket               \n");   
//    return 0;
//}

int main(int argc, char **argv)
{
    int ret = -1;
    struct stat s;

    if (2 != argc)
    {
        printf("usage: ./a.out filename\n"); 
        return 1;
    }

    //获取文件信息
    ret = stat(argv[1], &s);
    if (-1 == ret)
    {
        perror("stat"); 
        return 1;
    }
    //printf("st_dev: %lu\n", s.st_dev);
    //printf("st_ino: %lu\n", s.st_ino);
    //printf("st_nlink: %lu\n", s.st_nlink);
    //printf("st_uid: %d\n", s.st_uid);
    //printf("st_gid: %d\n", s.st_gid);
    //printf("st_rdev:%lu\n", s.st_rdev);
    //printf("st_size: %ld\n", s.st_size);
    //printf("st_blksize: %ld\n", s.st_blksize);
    //printf("st_blocks: %ld\n", s.st_blocks);
    
    //显示文件类型
    show_file_type(&s);
    
    return 0;
}

示例:判断文件权限

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>

int main(int argc, char **argv)
{
    int ret = -1;
    struct stat s;

    if (2 != argc)
    {
        printf("usage: ./a.out filename\n"); 
        return 1;
    }

    //获取文件信息
    ret = stat(argv[1], &s);
    if (-1 == ret)
    {
        perror("stat"); 
        return 1;
    }

    //判断文件所属者权限
    if (s.st_mode & S_IRUSR)
        printf("r");
    else
        printf("-");

    s.st_mode & S_IWUSR ? printf("w") : printf("-");
    s.st_mode & S_IXUSR ? printf("x") : printf("-");

    //判断文件所属组权限
    s.st_mode & S_IRGRP ? printf("r") : printf("-");
    s.st_mode & S_IWGRP ? printf("w") : printf("-");
    s.st_mode & S_IXGRP ? printf("x") : printf("-");

    //判断文件其它权限
    s.st_mode & S_IROTH ? printf("r") : printf("-");
    s.st_mode & S_IWOTH ? printf("w") : printf("-");
    s.st_mode & S_IXOTH ? printf("x") : printf("-");
    
    printf("\n");
    
    return 0;
}

6.2 access

#include <unistd.h>

int access(const char *pathname, int mode);
功能:测试指定文件是否具有某种属性
参数:
    pathname:文件名
    mode:文件权限,4种权限
        R_OK:   是否有读权限
        W_OK:   是否有写权限
        X_OK:   是否有执行权限
        F_OK:   测试文件是否存在
返回值:
    0: 有某种权限,或者文件存在
    -1:没有,或文件不存在

6.3 chmod

#include <sys/stat.h>

int chmod(const char *pathname, mode_t mode);
功能:修改文件权限
参数:
    filename:文件名
    mode:权限(8进制数)
返回值:
    成功:0
    失败:-1

6.4 chown

#include <unistd.h>

int chown(const char *pathname, uid_t owner, gid_t group);
功能:修改文件所有者和所属组
参数:
    pathname:文件或目录名
    owner:文件所有者id,通过查看 /etc/passwd 得到所有者id
    group:文件所属组id,通过查看 /etc/group 得到用户组id
返回值:
    成功:0
    失败:-1

6.5 link

#include <unistd.h>

int link(const char *oldpath, const char *newpath);
功能:创建一个硬链接
参数:
    oldpath:源文件名字
    newpath:硬链接名字
返回值:
    成功:0
    失败:-1

6.6 symlink

#include <unistd.h>

int symlink(const char *target, const char *linkpath);
功能:创建一个软链接
参数:
    target:源文件名字
    linkpath:软链接名字
返回值:
    成功:0
    失败:-1

6.7 readlink

#include <unistd.h>

ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
功能:读软连接对应的文件名,不是读内容(该函数只能读软链接文件)
参数:
    pathname:软连接名
    buf:存放软件对应的文件名
    bufsiz :缓冲区大小(第二个参数存放的最大字节数)
返回值:
    成功:>0,读到buf中的字符个数
    失败:-1

6.8 unlink

#include <unistd.h>

int unlink(const char *pathname);
功能:删除一个文件(软硬链接文件)
参数:
    pathname:删除的文件名字
返回值:
    成功:0
    失败:-1

6.9 rename

#include <stdio.h>

int rename(const char *oldpath, const char *newpath);
功能:把oldpath的文件名改为newpath
参数:
	oldpath:旧文件名
	newpath:新文件名
返回值:
	成功:0
	失败:-1

6.10 dup

#include <unistd.h>

int dup(int oldfd);
功能:
    通过 oldfd 复制出一个新的文件描述符,新的文件描述符是调用进程文件描述符表中最小可用的文件描述符,最终 oldfd 和新的文件描述符都指向同一个文件。
参数:
    oldfd : 需要复制的文件描述符 oldfd
返回值:
        成功:新文件描述符
        失败: -1

示例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    int fd = -1;
    int newfd = -1;

    //1. 打开文件
    fd = open("txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }

    //2. 复制文件描述符
    newfd = dup(fd);
    if (-1 == newfd)
    {
        perror("dup"); 
        return 1;
    }

    printf("fd: %d  newfd: %d\n", fd, newfd);

    write(fd, "123456789", 9);
    write(newfd, "ABCDEFGHI", 9);
    
    //3. 关闭文件
    close(fd);
    close(newfd);

    return 0;
}

6.11 dup2

#include <unistd.h>

int dup2(int oldfd, int newfd);
功能:
    通过 oldfd 复制出一个新的文件描述符 newfd,如果成功,newfd 和函数返回值是同一个返回值,最终 oldfd 和新的文件描述符 newfd 都指向同一个文件。
参数:
    oldfd : 需要复制的文件描述符
    newfd : 新的文件描述符,这个描述符可以人为指定一个合法数字(0 - 1023),如果指定的数字已经被占用(和某个文件有关联),此函数会自动关闭 close() 断开这个数字和某个文件的关联,再来使用这个合法数字。
返回值:
    成功:返回 newfd
    失败:返回 -1

示例:

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    int fd = -1;
    int newfd = -1;

    //1. 打开文件
    fd = open("txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }

    //2. 复制文件描述符
    newfd = 10;
    //如果newfd文件描述符已经与其它文件关联, 那么先close, 然后在使用该数字
    newfd = dup2(fd, newfd);
    if (-1 == newfd)
    {
        perror("dup2"); 
        return 1;
    }

    printf("fd: %d  newfd: %d\n", fd, newfd);

    write(fd, "123456789", 9);
    write(newfd, "ABCDEFGHI", 9);
    
    //3. 关闭文件
    close(fd);
    close(newfd);

    return 0;
}

6.12 fcntl

#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=F_GETLK, F_SETLK或F_SETLKW)

示例:文件描述符复制

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    int fd = -1;
    int newfd = -1;

    //1. 打开文件
    fd = open("txt", O_RDWR | O_CREAT | O_TRUNC, 0644);
    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }

    //2. 复制文件描述符 寻找一个大于或者等于10的最小的可以用文件描述符返回
    newfd = fcntl(fd, F_DUPFD, 10);
    if (-1 == newfd)
    {
        perror("fcntl"); 
        return 1;
    }
    printf("fd: %d  newfd: %d\n", fd, newfd);

    write(fd, "123456789", 9);
    write(newfd, "ABCDEFGHI", 9);

    //3. 关闭文件
    close(fd);
    close(newfd);

    return 0;
}

示例:设置和获取文件状态标志

#include <stdio.h>
#include <string.h>
#include <stdlib.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>

int main(void)
{
    int fd = -1;
    int ret = -1;

    //打开文件
    fd = open("txt", O_RDWR | O_CREAT, 0644);
    if (-1 == fd)
    {
        perror("open"); 
        return 1;
    }

    //获取文件状态标志
    ret = fcntl(fd, F_GETFL);
    if (-1 == ret)
    {
        perror("fcntl"); 
        return 1;
    }
    if (ret & O_APPEND)
    {
        printf("设置为非追加...\n"); 
    }
    else
    {
        printf("设置为追加...\n"); 
    }
    
    //设置为非追加
    ret |= O_APPEND;
    ret = fcntl(fd, F_SETFL, ret);
    if (-1 == ret)
    {
        perror("fcntl"); 
        return 1;
    }

    //获取文件状态标志
    ret = fcntl(fd, F_GETFL);
    if (-1 == ret)
    {
        perror("fcntl"); 
        return 1;
    }
    if (ret & O_APPEND)
    {
        printf("设置为非追加...\n"); 
    }
    else
    {
        printf("设置为追加...\n"); 
    }
        
    //关闭文件
    close(fd);

    return 0;
}

7.目录操作常用函数

7.1 getcwd

#include <unistd.h>

char *getcwd(char *buf, size_t size);
功能:获取当前进程的工作目录
参数:
    buf : 缓冲区,存储当前的工作目录
    size : 缓冲区大小
返回值:
    成功:buf中保存当前进程工作目录位置
    失败:NULL

7.2 chdir

#include <unistd.h>

int chdir(const char *path);
功能:修改当前进程(应用程序)的路径
参数:
    path:切换的路径
返回值:
    成功:0
    失败:-1

7.3 opendir

#include <sys/types.h>
#include <dirent.h>

DIR *opendir(const char *name);
功能:打开一个目录
参数:
    name:目录名
返回值:
    成功:返回指向该目录结构体指针
    失败:NULL

7.4 closedir

#include <sys/types.h>
#include <dirent.h>

int closedir(DIR *dirp);
功能:关闭目录
参数:
    dirp:opendir返回的指针
返回值:
    成功:0
    失败:-1

7.5 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];               //文件名
};

8.时间相关函数

char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);

char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);

struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);

time_t mktime(struct tm *tm);

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.kler.cn/a/5694.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

文章八:YOLOv5车牌识别系统的Web应用与API开发

在本篇文章中&#xff0c;我们将探讨如何使用YOLOv5车牌识别系统开发一个Web应用&#xff0c;以及如何创建一个车牌识别API供其他开发者使用。我们将介绍Flask框架的基本概念&#xff0c;并实现一个简单的车牌识别Web应用和API。 车牌识别视频Flask框架介绍 Flask是一个轻量级的…

Leetcode.2171 拿出最少数目的魔法豆

题目链接 Leetcode.2171 拿出最少数目的魔法豆 Rating &#xff1a; 1748 题目描述 给你一个 正 整数数组 beans&#xff0c;其中每个整数表示一个袋子里装的魔法豆的数目。 请你从每个袋子中 拿出 一些豆子&#xff08;也可以 不拿出&#xff09;&#xff0c;使得剩下的 非空…

element plus 语言切换组件使用

背景:现在的很多网站要求支持多种语言切换需求&#xff0c;element plus 正好有这种组件可以满足这种需求。 使用element plus的效果 1、介绍组件的引用 import ElementPlus from element-plus import zhCn from element-plus/dist/locale/zh-cn.mjsapp.use(ElementPlus, {lo…

python——面向对象(下)

目录 一、继承 二、私有属性私有方法 三、多态 四、属性和方法 五、异常 六、test 一、继承 故事继续&#xff1a;大猫掌握了 师傅的配方 和 学校的配方&#xff0c;通过研究&#xff0c;大猫在两个配方的基础上&#xff0c;创建了一种全新的煎饼果子配方&#xff0c;称之…

C 中的循环

C 循环 有的时候&#xff0c;我们可能需要多次执行同一块代码。一般情况下&#xff0c;语句是按顺序执行的&#xff1a;函数中的第一个语句先执行&#xff0c;接着是第二个语句&#xff0c;依此类推。 编程语言提供了更为复杂执行路径的多种控制结构。 循环语句允许我们多次…

Jenkins使用

Jenkins使用 目录 本节实践 实战名称&#x1f498; 实践&#xff1a;创建自由风格流水线-2023.3.21(测试成功)&#x1f498; 实践&#xff1a;自定义agent通信端口-2023.3.21(测试成功)&#x1f498; 实践&#xff1a;Jenkins配置node方法-2023.3.21(测试成功)&#x1f498; 实…

【计算机视觉 | 目标检测】DETR风格的目标检测框架解读

文章目录一、前言二、理解2.1 DETR的理解2.2 DETR的细致理解2.2.1 Backbone2.2.2 Transformer encoder2.2.3 Transformer decoder2.2.4 Prediction feed-forward networks (FFNs)2.2.5 Auxiliary decoding losses2.3 更具体的结构2.4 编码器的原理和作用2.5 解码器的原理和作用…

Java多态

目录 1.多态是什么&#xff1f; 2.多态的条件 3.重写 3.1重写的概念 3.2重写的作用 3.3重写的规则 4.向上转型与向下转型 4.1向上转型 4.2向下转型 5.多态的优缺点 5.1 优点 5.2 缺点 面向对象程序三大特性&#xff1a;封装、继承、多态。 1.多态是什么&#xff1…

利用 Docker 搭建主从服务器

利用 Docker 搭建主从服务器 1.拉取 docker 镜像 首先拉取 docker 镜像,我们这里使用 5.7 版本的 mysql: docker pull mysql:5.7 然后使用此镜像启动容器,这里需要分别启动主从两个容器 Master (主):docker run -p 3339:3306 --name mysql-master -e MYSQL_ROOT_PASSWORD…

【新2023Q2押题JAVA】华为OD机试 - 服务依赖

最近更新的博客 华为od 2023 | 什么是华为od,od 薪资待遇,od机试题清单华为OD机试真题大全,用 Python 解华为机试题 | 机试宝典【华为OD机试】全流程解析+经验分享,题型分享,防作弊指南华为od机试,独家整理 已参加机试人员的实战技巧本篇题解:服务依赖 题目 在某系统中有…

UDSonLIN(ISO14229-7)诊断协议

文章目录 前言一、UDSonLIN实现的需求二、定义诊断等级1.诊断等级一2.诊断等级二3.诊断等级三三、LIN主节点的设计主节点的故障管理、传感器读取和I/O控制四、LIN从节点的设计前言 在基于CAN的诊断学习之余,本文为大家介绍UDSonLIN的相关内容,与UDS应用部分相对独立,可以单…

tsconfig.json参数详解

tsconfig.json是ts编译器的配置文件&#xff0c;ts编译器可以根据他的信息来对代码进行编译。 想要学习这篇文章小伙伴&#xff0c;可以先去看看下面文章&#xff0c;可以明白tsconfig.json的由来以及如何编译&#xff1a; 在vscode中使用Typescript并运行_typescript vscode…

【Redis】高可用:Redis的主从复制是怎么实现的?

【Redis】高可用&#xff1a;主从复制详解 我们知道要避免单点故障&#xff0c;即保证高可用&#xff0c;便需要冗余&#xff08;副本&#xff09;方式提供集群服务。而Redis 提供了主从库模式&#xff0c;以保证数据副本的一致&#xff0c;主从库之间采用的是读写分离的方式。…

C语言中void的高级应用

C语言中void的高级应用 C语言中的void关键字&#xff0c;void 表示为“无”、“空”、“没有”的意思。所以void是不能用来定义变量的&#xff0c;因为变量是需要固定的空间的 //定时时&#xff0c;编译不通过 void ch a; void a 10;一、void的常用场景 1.1 函数的参数 v…

spring boot 集成 postgresql mybatis-plus swagger pagehelper

spring boot 集成 postgresql mybatis-plus swagger pagehelper一、前言二、环境配置1.pom.xml2.config文件①&#xff1a;DruidConfig②&#xff1a;MybatisPlusConfig③&#xff1a;SwaggerConfig3.yml文件三、接口代码四、swagger接口测试五、本文案例demo源码一、前言 spr…

【从零开始学习 UVM】7.3、Driver Sequencer Handshake —— get() 和 put() 方法详解【了解即可】

文章目录 driver中`get`和`put`的调用?sequence如何启动和停止一个item?示例定义sequence item定义sequence定义driver定义test运行日志在之前的文章中,我们看到了UVM driver如何通过调用get_next_item方法获取下一个item,并告知sequencer当前item已完成。虽然这是driver-s…

【二阶锥规划】考虑气电联合需求响应的气电综合能源配网系统协调优化运行【IEEE33节点】(Matlab代码实现)

&#x1f4a5;&#x1f4a5;&#x1f49e;&#x1f49e;欢迎来到本博客❤️❤️&#x1f4a5;&#x1f4a5; &#x1f3c6;博主优势&#xff1a;&#x1f31e;&#x1f31e;&#x1f31e;博客内容尽量做到思维缜密&#xff0c;逻辑清晰&#xff0c;为了方便读者。 ⛳️座右铭&a…

短视频矩阵发布系统 如何定时自动发布?

如何有效地管理自己在多个短视频平台上的账号&#xff0c;如何制作出吸引人的短视频内容&#xff0c;并自动发布到不同平台上进行推广&#xff0c;如何维护评论和私信&#xff0c;以及如何分析营销报表&#xff0c;这些都是需要解决的问题。这时&#xff0c;一款短视频发布系统…

第14章_视图

第14章_视图 &#x1f3e0;个人主页&#xff1a;shark-Gao &#x1f9d1;个人简介&#xff1a;大家好&#xff0c;我是shark-Gao&#xff0c;一个想要与大家共同进步的男人&#x1f609;&#x1f609; &#x1f389;目前状况&#xff1a;23届毕业生&#xff0c;目前在某公司…

C的实用笔记29——函数指针(通过指针引用函数)

1.函数名就是地址 1、知识点&#xff1a;程序中定义的每一个函数&#xff0c;在编译时&#xff0c;编译系统会为函数在内存的代码区当中分配一段存储空间&#xff0c;这段存储空间的起始地址&#xff08;又称为入口地址&#xff09;称为这个函数的指针。 2.定义一个函数指针指…
最新文章