5、C/C++——Linux系统编程

一、系统调用:

二、C标准库文件IO函数:

1、错误处理函数:

2、open函数:

2、close函数

3、read、write函数:

4、缓冲区

5、文件描述符:

6、阻塞、非阻塞

7、fcntl函数:

8、Lseek函数:

9、文件系统

9.1、文件存储:

9.2、文件操作

state函数:

lstate函数:

Link/unlink:

隐式回收:

9.3、目录操作

文件、目录权限

重定向dup和dup2:

cat stu.json >> stu2.json 这就是重定向

使用fcntl()函数实现dup2():

一、系统调用:

什么是系统调用:

操作系统实现并提供给外部应用程序的编程接口(Application Programming Interface,API), 是应用程序同系统之间数据交互的桥梁。

C标准函数和系统函数调用关系如一个helloworld 如何打印到屏幕:

二、C标准库文件IO函数:

include unix标准库

1、错误处理函数:

char *strerror(int errnum) ;将errno错误 翻译为文字 void perror(const char *s); 打印错误

2、open函数:

int open(char *pathname, int flag) ##include

参数:

Pathname:欲打开的文件路径名 Flag:文件打开方式 O_RDONLY|O_WRONLY|O_RDWR|O_NONBLOCK|O_CREAT: 若文件不存在则创建。 mode: 使用前提是flags指明了O_CREAT,取值8进制数,

(头文件是#include)

返回值:

成功:打开文件所得的 文件描述符(整数)

失败:-1,设置errno(相当于内核的一个全局变量)

int open(char *pathname, int flag, mode_t mode)

参数:

        Pathname:欲打开的文件路径名

        Flag:文件打开方式 O_RDONLY|O_WRONLY|O_RDWR ……

        mode:使用前提是参数2指定为O_CREAT。取八进制数,用来描述文件的访问权限。rwx 创建文件的最终权限=mode&~unmask

返回值:所得的 文件

成功:打开文件描述符(整数)

失败:-1,设置errno(相当于内核的一个全局变量)

2、close函数

int close(int fd);

fd表示open打开文件流的状态码(open的返回值);

close 返回 0 表示 成功 , 或者 -1 表示 有 错误 发生

借助 man 2 func查找对应函数的使用方法

3、read、write函数:


ssize_t read(int fd, void *buf, size_t count)


参数:

fd:文件描述符

        buf:存数据的缓冲区

        count:缓冲区大小

返回值:

成功:读到的字节数

失败:-1,设置errno

-1:如果errno=EAGIN或EWOULDBLOCK,说明不是read失败,而是read在非阻塞方式读一个设备文件(网络文件),并且文件无数据,

write函数:

ssize_t write(int fd, const void *buf, size_t count) #const 防止在写入到fd的时候被修改

参数:

fd:文件描述符

        buf:待写出数据的缓冲区

        count:数据大小

返回值:

成功:写入的字节数

失败:-1,设置errno

4、缓冲区

read、write 函数常常被称为 Unbuffered I/O。指的是无用户级缓冲区。但不保证不使用内核缓冲区。

由于磁盘的读写需要大量时间,为优化读写,最底层的内核会采用预读入缓输出的机制进行IO操作

系统调用和库函数的区别:

5、文件描述符:

下图表示一个进程./a.out的存储空间

PCB:process control block进程控制块 PCB实质上是一个结构体,struct task_struct{}。

  • 本质: 结构体
  • 成员:文件描述符表

文件描述符:0/1/2/3……1023 表中可用的最小的。

0 – STDIN_FILENO 标准输入

1 - STDOUT_FILENO 标准输出

2- STDERR_FILENO 标准错误

6、阻塞、非阻塞

阻塞是设备文件、网络文件的属性。

产生阻塞的场景,读设备文件or读网络文件。(读常规文件无阻塞概念)

   /dev/tty ----终端文件

open("/dev/tty",O_RDWR|O_NONBLOCK) --以非阻塞状态打开/dev/tty终端设备文件

阻塞:

非阻塞:

超时非阻塞:

7、fcntl函数:
#include<unistd.h>
#include<fcntl.h>
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);
//fcntl用于管理文件记录锁
int fcntl(int fd, int cmd ,struct flock* lock);


int flag = fcntl(fd, F_GETFL); 
flag |= O_NONBLOCK;
fcntl(fd, F_SETFL, flag);

获取文件状态:F_GETFL

设置文件状态:F_SETFL

8、Lseek函数:

Off_t lseek(int fd ,off_t offset, int whence)

参数:

          fd:文件描述符

          offset:偏移量

          whence:启始偏移位置(SEEK_SET/SEEK_CUR/SEEK_END)

返回值:

成功:较启始位置偏移量

失败:-1,设置errno

1、文件读写是使用的同一个偏移量。

2、使用lseek获取文件大小 int lseek(fd , 0 , SEEK_END)

3、扩展文件大小的方式,要使文件大小真正扩展,必须引起IO操作

传入参数:

  1. 指针作为函数参数
  2. 通常有const关键字修饰
  3. 指针指向有效区域,在函数内部做读操作

传出参数:

  1. 指针作为函数参数
  2. 在函数调用之前,指针指向的空间可以无意义,但必须有效
  3. 在函数内部做写操作
  4. 在函数调用结束后,充当函数返回值

传入传出参数:

  1. 指针作为函数参数
  2. 在函数调用之前要有实际意义
  3. 在函数内部,先做读操作,在做写操作
  4. 函数调用结束后,充当函数返回值

9、文件系统

文件系统是一组规则,规定对文件的存储及读取的一般方法。文件系统在磁盘格式化过程中指定。常见的文件系统有:fat32 ntfs exfat ext2 、ext3 、ext4

9.1、文件存储:

inode:

其本质为结构体,存储文件的属性信息。如:权限、类型、大小、时间、用户、盘块位置……也叫作文件属性管理结构,大多数的 inode 都存储在磁盘上。少量常用、近期使用的 inode 会被缓存到内存中。

dentry:

dictionary entry目录项,其本质依然是结构体,重要成员变量有两个 {文件名,inode,...},而文件内容(data)保存在磁盘盘块中。

9.2、文件操作

state函数:

lstate函数:

Link/unlink:

隐式回收:

state函数:
#include<sys/ stat.h>
int state(const char *pathname, struct stat *statbuf);  

参数:

   Pathname:文件路径

Statbuf:(传出参数)存放文件属性

返回值:

成功:0

失败:-1,设置errno。
struct stat {
    dev_t     st_dev;     /* ID of device containing file */
    ino_t     st_ino;     /* inode number */
    mode_t    st_mode;    /* protection */
    nlink_t   st_nlink;   /* number of hard links */
    uid_t     st_uid;     /* user ID of owner */
    gid_t     st_gid;     /* group ID of owner */
    dev_t     st_rdev;    /* device ID (if special file) */
    off_t     st_size;    /* total size, in bytes */
    blksize_t st_blksize; /* blocksize for file system I/O */
    blkcnt_t  st_blocks;  /* number of 512B blocks allocated */
    time_t    st_atime;   /* time of last access */
    time_t    st_mtime;   /* time of last modification */
    time_t    st_ctime;   /* time of last status change */
};
代码:
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int main(int argc, char *argv[])
{
    struct stat sb;

    if (argc != 2) {
        fprintf(stderr, "Usage: %s <pathname>\n", argv[0]);
        exit(EXIT_FAILURE);
    }

    if (stat(argv[1], &sb) == -1) {
        perror("stat");
        exit(EXIT_SUCCESS);
    }

    printf("File type:                ");

    switch (sb.st_mode & S_IFMT) {
        case S_IFBLK:  printf("block device\n");            break;
        case S_IFCHR:  printf("character device\n");        break;
        case S_IFDIR:  printf("directory\n");               break;
        case S_IFIFO:  printf("FIFO/pipe\n");               break;
        case S_IFLNK:  printf("symlink\n");                 break;
        case S_IFREG:  printf("regular file\n");            break;
        case S_IFSOCK: printf("socket\n");                  break;
        default:       printf("unknown?\n");                break;
    }

    printf("I-node number:            %ld\n", (long) sb.st_ino);

    printf("Mode:                     %lo (octal)\n",
           (unsigned long) sb.st_mode);

    printf("Link count:               %ld\n", (long) sb.st_nlink);
    printf("Ownership:                UID=%ld   GID=%ld\n",
           (long) sb.st_uid, (long) sb.st_gid);

    printf("Preferred I/O block size: %ld bytes\n",
           (long) sb.st_blksize);
    printf("File size:                %lld bytes\n",
           (long long) sb.st_size);
    printf("Blocks allocated:         %lld\n",
           (long long) sb.st_blocks);

    printf("Last status change:       %s", ctime(&sb.st_ctime));
    printf("Last file access:         %s", ctime(&sb.st_atime));
    printf("Last file modification:   %s", ctime(&sb.st_mtime));

    exit(EXIT_SUCCESS);
}
lstate函数:
int lstat(const char *path, struct stat *buf); 

成返回 0;失败返回-1 设置 errno 为恰当值。

文件类型判断方法:st_mode 取高 4 位。 但应使用宏函数: S_ISREG(m) is it a regular file?

S_ISDIR(m) directory?

S_ISCHR(m) character device?

S_ISBLK(m) block device?

S_ISFIFO(m) FIFO (named pipe)?

S_ISLNK(m) symbolic link? (Not in POSIX.1-1996.)

S_ISSOCK(m) socket? (Not in POSIX.1-1996.)

穿透符号链接:stat:会;lstat:不会

Link/unlink:

结合前面dentry和inode的知识,link可以创建一个文件的dentry也即硬连接,而unlink可以释放一个dentry。

int link(const char *oldpath, const char *newpath); int unlink(const char *pathname);

注意 Linux 下删除文件的机制:不断将 st_nlink -1,直至减到 0 为止。无目录项对应的 文件,将会被操作系统择机释放。(具体时间由系统内部调度算法决定) 因此,我们删除文件,从某种意义上说,只是让文件具备了被释放的条件。

unlink函数的特征:清除文件时,如果文件的硬链接数到0了,没有dentry对应,但该文件仍不会马上被释放。要等到所有打开的文件的进程关闭该文件,系统才会挑时间将文件释放掉。

隐式回收:

  当进程结束运行时,所有该进程打开的文件会被关闭,申请的内存空间会被释放。系统 的这一特性称之为隐式回收系统资源。 在写服务器代码的时候一定要避免隐式回收。

9.3、目录操作

文件、目录权限

重定向dup和dup2:

cat stu.json >> stu2.json 这就是重定向

使用fcntl()函数实现dup2():

文件、目录权限

目录设置黏住位:若有 w 权限,创建不变,删除、修改只能由 root、目录所有者、文件所有者操作。

重定向dup和dup2:

cat stu.json >> stu2.json 这就是重定向

int dup(int oldfd); 复制文件的文件描述符,可以共享绝大部分信息对文件进行操作。

   int dup2(int oldfd, int newfd);

命令行:cat test.c > out 操作文件描述符,将输出到屏幕(或者其他文件)的内容,转输出到其他out 文件。

dup代码示例:复制文件描述符,并向文件写数据.

类似于copy函数,就是复制原有的文件描述符赋值给新的文件描述符

输出:

查看test.txt文件,里面有两段字符串:

:Advanced Programming! write by fd

:Advanced Programming! write by newfd

dup类似与copy

dup2代码示例:

int dup2(int oldfd, int newfd);函数将新文件描述符更改为旧文件描述符。当对新文件描述符进行操作时,其实是在对旧文件描述符进行读写等操作:既是将oldfd赋值给newfd。

使用fcntl()函数实现dup2():

#Linux#
全部评论
老哥,我面试的时候,有一道面试题,一个线程,加锁后释放锁,是否会产生死锁?我感觉是会产生死锁的,既然问了,应该是会产生死锁的,但是不知道怎么回答,百度了一下,我总结了一下:说可能是A,B两个线程,A中包含B,A和B同时访问一块资源,A线程打开,B线程打开,B加锁,释放锁,但是A也在访问这个资源,可能会产生死锁,楼主,您觉得我这个想法对嘛?请楼主有空的时候看看,谢谢啦
点赞 回复 分享
发布于 2022-09-28 09:50 辽宁

相关推荐

01-15 11:05
门头沟学院 Java
华为海思 通软开发 总包大概在30左右
点赞 评论 收藏
分享
评论
2
13
分享

创作者周榜

更多
牛客网
牛客企业服务