Unix/Linux-04

回顾

  • 内存管理

    • 1 Unix/Linux内存使用 虚拟内存地址空间 方式进行管理。程序员操作的都是 虚拟内存地址。
    • 2 Unix/Linux系统中,内存管理 最小单位 内存页,一个内存页 4096字节。
    • 3 C++用new/delete,底层调用 C语言的malloc/free,底层调用brk/sbrk,底层mmap/munmap。
    • 4 malloc分配内存时,一次分配33个内存页(虚拟内存地址映射物理内存),malloc 分配内存,底层需要额外的空间存储 分配信息。但不影响对内存使用和管理。

今天

  • 系统调用 (概念)

    • 系统调用(System Call)
    • 是Unix系统内核提供的供外部应用程序调用的一系列函数,系统调用能操作内核空间的内存。因此,系统调用是用户程序和内核空间的接口(桥梁)。
    • 注: 标C函数可以运行在各种平台,系统调用只能运行在Unix/Linux等平台。很多标准C函数 底层都调用了Unix的系统调用。(malloc --> brk/sbrk–>mmap/munmap)
    • time 命令可以查看 程序的运行时间,并显示出 用户空间时间和系统空间 时间。 (time a.out)
      Unix/Linux-04_第1张图片

  • 文件的操作(Unix系统函数)

    在Linux中,几乎一切都 是文件。所有Linux的IO(输入输出)都可以按照文件的方式实现。
    关于文件IO的函数:
    open/read/write/close/ioctl。

    目录是文件,设备也是文件,
    比如:
    /dev/tty 是设备键盘和显示器。
    /dev/null 代表空。

    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls
    ConsoleApplication1.out
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls -a
    .  ..  ConsoleApplication1.out
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls -a > a.txt
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls
    a.txt  ConsoleApplication1.out
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ cat a.txt
    .
    ..
    a.txt
    ConsoleApplication1.out
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ rm a.txt
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls
    ConsoleApplication1.out
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls -a > /dev/tty
    .  ..  ConsoleApplication1.out
    

    cat 什么都不敲,表示键盘

    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ cat
    444
    444
    abcdefg
    abcdefg
    !@#
    !@#
    

    利用/dev/null清空文件

    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls -al a.txt
    -rw-rw-r-- 1 liujing liujing 153 Aug 12 13:55 a.txt
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ cat /dev/null > a.txt
    liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls -al a.txt
    -rw-rw-r-- 1 liujing liujing 0 Aug 12 13:55 a.txt
    

open

#include
#include

int open(const char* filename,int oflag, …)

  • filename 是文件名(带路径)
  • … 代表0到N个任意类型的参数
  • oflag 是 标志,必须带上权限:
    – O_RDONLY O_WRONLY O_RDWR
    – O_CREAT 代表创建文件,如果文件不存在,会新建
    – O_APPEND 在文件尾部添加内容
    – O_TRUNC 在打开已经存在的文件时会清空文件内存,要求必须有 写权限
    – O_EXCL 在创建文件时,如果文件不存在,不会新建文件而是返回错误(相当于打开存在的文件而不会新建它)
    • 返回 文件描述符fd(int)。

文件描述符

Unix/Linux-04_第2张图片

Unix/Linux系统在使用open函数时,建立一个文件描述符的表格,在表格中找到没有被使用的最小 正整数,用这个整数映射文件表。 所有文件相关信息存在文件表,但在使用时,用那个整数(文件描述符)就行。

  • 0 1 2 三个 文件描述 被系统使用,代码 标准输入标准输出和标准错误。
  • 一般来说,Linux/Unix 最多可以打开 256(OPEN_MAX)个文件,描述符 0 - 255。
// 结构体
struct Emp {
    char name[20]; // 姓名
    int age;       // 年龄
    double sal;    // 薪水
};

// 使用open时,如果新建文件,需要提供权限0666(有屏蔽), 0表示8进制,rwx, 权限掩码
int fd = open("a.txt",O_RDWR|O_CREAT|O_TRUNC,0666);

// 写
int res = write(fd,"hello c",7);

// 一次读完
char buf[100] = {};
int res = read(fd,buf,sizeof(buf));//考虑是否可能需要循环

// 循环读, 边读边写
char buf[3] = {};
while(1){
  int res = read(fd, buf, sizeof(buf));//需要写循环
  if(res == -1) perror("read"), exit(-1);
  if(res == 0) break;  //效果更好
  int res2 = write(fd2, buf, res);
  if(res2 == -1) perror("write"), exit(-1);
}

// 写结构体数据
struct Emp emp = {"zhangfei",1800,12000.0};
int res = write(fd, &emp, sizeof(emp));

// 读结构体数据
struct Emp emp;
int res = read(fd, &emp, sizeof(emp));

说明, 其存储的数据直接在文本中查看是乱码
----------------------------------------------------------
但如何保存数据为文本不乱码?
方法:
全部转换成字符串写入(sprintf/printf/fprintf)

Emp emp = {"zhangfei", 18000, 12000.0};
char buf[100] = {};
sprintf(buf, "%s %d %lf", emp.name, emp.age, emp.sal);
write(fd, buf, strlen(buf));

UC 和 标C 一般用哪个好?

以后在开发中,关于文件的读和写,最好使用标C的函数,因为标C的IO函数都有缓冲区,可以提高效率,
而 系统调用虽然在内核也有小的缓冲区,但效果还是比较慢。

也可以自定义缓冲区 提升效率。

char buf[1024] = {};
int index = 0;
strcpy/strcat --> strlen == 1024 --> write

作业

用户登录和用户注册,用今天的文件函数。

存储/读取
姓名,年龄,薪水 char*/int/double

结构体

补充:
strlen 与 sizeof

区别一

#include
#include

int main()
{
    char buf[] = "abcd";
    printf("sizeof(buf) = %d\n",sizeof(buf));
    printf("strlen(buf) = %d\n",strlen(buf));

    return 1;
}

// 结果
// sizeof(buf) = 5 算上了\0(这里其实和区别二有点契合)
// strlen(buf) = 4 只算了到\0之前的所有数据长度(不包括\0)

区别二(其实也算是区别一)

#include
#include

int main()
{
    char str[100] = {0};
    strcpy(str, "abcd");

    int str_len = strlen(str);
    int str_size = sizeof(str);

    printf("strlen(str) = %d\n", (str_len));
    printf("sizeof(str) = %d\n", (str_size));

    return 0;
}


// 结果
// strlen(str) = 4  只算了到\0之前的所有数据长度(不包括\0)
// sizeof(str) = 100 算上了char[]所占用的总长度(并不关注其中实际用了多少数据)

文件表

Unix/Linux-04_第3张图片

文件表 – 对应内存

  • 文件状态标识
  • 当前文件偏移量
  • V节点指针
    • V节点表 – 对应 硬盘
      • V节点信息
      • i节点信息 – inode 硬盘文件
      • 文件长度
liujing@ubuntu:~/projects/ConsoleApplication1/bin/x64/Debug$ ls -i
132574 a.txt  132575 b.txt  131612 ConsoleApplication1.out  132577 emp.txt

命令ls -i
显示出来的数字为
数据在硬盘上的硬盘地址

lseek

文件偏移量

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

whence
  - SEEK_SET
      -- The offset is set to offset bytes.
  - SEEK_CUR
      -- The offset is set to its current location plus offset bytes.
  - SEEK_END
      -- The offset is set to the size of the file plus offset bytes.

lseek(fd, 3, SEEK_SET);//从头开始+3


lseek(fd,2,SEEK_CUR); // 当前位置往后+2

lseek(fd, -1, SEEK_END); // -1 修改的是尾部文件结束符
lseek(fd, -2, SEEK_END); // -2 才能修改到文件最后实际的内容


另注意:
read/write 函数使用完之后,文件指针都会往后+1

你可能感兴趣的:(Linux,C,linux,C)