UNIX环境高级编程笔记

2023-05-16

UNIX环境编程

  • 一、UNIX基础知识
    • 1.1 Linux 主要特性
    • 1.2 Linux 内核
    • 1.3 Linux 目录结构
    • 1.4 登录
      • 1 登录名
      • 2.shell
    • 1.5 输入和输出
      • 1. 文件描述符
      • 2. 标准输入、标准输出、标准错误
      • 3. 不带缓冲的IO
      • 4. 标准I/O
    • 1.6 程序和进程
      • 1. 程序
      • 2. 进程和进程ID
      • 3. 进程控制
      • 4. 线程和线程ID
    • 1.7 出错处理
    • 1.8 用户标识
    • 1.9 信号
    • 1.10 时间值
    • 1.11 系统调用和库函数
  • 二、UNIX标准及实现
    • 2.1 ISO C
    • 2.2 函数sysconf、pathconf、和fpathconf
    • 2.3 ISO C和IEEE POSIX
  • 三、文件I/O
    • 3.1 文件描述符
    • 3.2 函数open和openat
    • 3.3 函数creat
    • 3.4 函数close
    • 3.5 函数lseek
    • 3.6 函数read/write
    • 3.7 I/O的效率
    • 3.8 文件共享
    • 3.9 原子操作
    • 3.10 函数dup和dup2
    • 3.11 函数sync、fsync和fdatasync
    • 3.12 函数fcntl
    • 3.13 函数ioctl
    • 3.14 /dev/fd
  • 四、文件和目录
    • 4.1 函数stat、fstat、fstatat和lstat
    • 4.2 文件类型
    • 4.3 设置用户ID和设置组ID
    • 4.4 新文件和目录的所有权
    • 4.7 函数access和faccessat
    • 4.8 函数umask
    • 4.9 函数chmod、fchmod和fchmodat
    • 4.10 函数chown、fchown、fchownat和lchown
    • 4.11 文件长度
    • 4.12 文件截断
    • 4.13 文件系统
    • 4.14 函数link、linkat、unlink、unlinkat和remove
    • 4.15 函数rename和renameat
    • 4.16 符号链接
    • 4.17 创建和读取符号链接
    • 4.18 文件时间
    • 4.19 函数futimens、utimensat和utimes
    • 4.20 函数mkdir、mkdirat和rmdir
    • 4.21 读目录
    • 4.22 函数chdir、fchdir、getcwd
    • 4.23 设备特殊文件
  • 五、标准I/O库
    • 5.1 流和FILE对象
    • 5.2 标准输入、标准输出和标准错误
    • 5.3 缓冲
    • 5.4 打开流
    • 5.5 读和写流
    • 5.6 每次一行I/O
    • 5.7 二进制I/O
    • 5.8 定位流
    • 5.9 格式化I/O
    • 5.10 实现细节
    • 5.11 临时文件
    • 5.12 内存流
    • 5.13 标准I/O的替代软件
  • 六、系统数据文件和信息
    • 6.1 口令文件
    • 6.2 阴影文件
    • 6.3 组文件
    • 6.4 附属组ID
    • 6.5 实现区别
    • 6.6 其他数据文件
    • 6.7 登陆账户记录
    • 6.8 系统标识
    • 6.9 时间和日期例程
  • 七、进程环境
    • 7.1 main函数
    • 7.2 进程终止
    • 7.3 命令行参数
    • 7.4 环境表
    • 7.5 C程序的存储空间布局
    • 7.6 共享库
    • 7.7 存储空间分配
    • 7.8 环境变量
    • 7.9 函数setjmo和longjmp
    • 7.10 函数getrlimit和setrlimit
  • 八、进程控制
    • 8.2 进程标识

一、UNIX基础知识

1.1 Linux 主要特性

Linux 是一个基于文件的操作系统

操作系统需要和硬件进行交互,对应 Linux 来说这些硬件都是文件,比如:操作系统会将 硬盘 , 鼠标 , 键盘 , 显示屏等抽象成一个设备文件来进行管理。

Linux 操作系统是一种自由软件,是免费的,并且公开源代码。

可以同时登陆多个用户,并且每个用户可以同时运行多个应用程序。

提供了友好的图形用户界面,操作简单, 易于快速上手。

支持多平台(这里指的是基于不同 CPU 架构的平台,比如国产 Linux 使用的龙芯等)

UNIX体系结构
在这里插入图片描述
内核:控制计算机运行资源,提供程序运行环境

系统调用:内核的接口

共用库函数:共用库函数构建在系统调用的接口上

shell:shell是一个特殊的应用程序,为运行其他的应用程序提供接口

1.2 Linux 内核

Linux 系统从应用角度来看,分为内核空间和用户空间两个部分。内核空间是 Linux 操作系统的主要部分,但是仅有内核的操作系统是不能完成用户任务的。丰富并且功能强大的应用程序包是一个操作系统成功的必要件。这个和武林秘籍一样,不仅得有招式还得有内功心法。

Linux 的内核主要由 5 个子系统组成:进程调度、内存管理、虚拟文件系统、网络接口、进程间通信。下面将依次讲解这 5 个子系统。

  1. 进程调度 SCHED
  • SCHED_OTHER:分时调度策略(默认),是用于针对普通进程的时间片轮转调度策略。

  • SCHED_FIFO:实时调度策略,是针对运行的实时性要求比较高、运行时间短的进程调度策略

  • SCHED_RR:实时调度策略,是针对实时性要求比较高、运行时间比较长的进程调度策略。

  1. 内存管理 MMU
  • 内存管理是多个进程间的内存共享策略。在 Linux 中,内存管理主要说的是虚拟内存。

  • 虚拟内存可以让进程拥有比实际物理内存更大的内存,可以是实际内存的很多倍。

  • 每个进程的虚拟内存有不同的地址空间,多个进程的虚拟内存不会冲突。

  1. 虚拟文件系统 VFS
  • 在 Linux 下支持多种文件系统,如 ext、ext2、minix、umsdos、msdos、vfat、ntfs、proc、smb、ncp、iso9660、sysv、hpfs、affs 等。目前 Linux 下最常用的文件格式是 ext2 和 ext3。
  1. 网络接口
  • Linux 是在 Internet 飞速发展的时期成长起来的,所以 Linux 支持多种网络接口和协议。网络接口分为网络协议和驱动程序,网络协议是一种网络传输的通信标准,而网络驱动则是对硬件设备的驱动程序。Linux 支持的网络设备多种多样,几乎目前所有网络设备都有驱动程序。
  1. 进程间通信
  • Linux 操作系统支持多进程,进程之间需要进行数据的交流才能完成控制、协同工作等功能,Linux 的进程间通信是从 UNIX 系统继承过来的。Linux 下的进程间的通信方式主要有管道、信号、消息队列、共享内存和套接字等方法。

1.3 Linux 目录结构

在这里插入图片描述

在 linux 中根目录的子目录结构相对是固定的 (名字固定), 不同的目录功能是也是固定的
bin: binary, 二进制文件目录,存储了可执行程序,今天要将的命令对应的可执行程序都在这个目录中
sbin: super binary, root 用户使用的一些二进制可执行程序
etc: 配置文件目录,系统的或者用户自己安装的应用程序的配置文件都存储在这个目录中
lib: library, 存储了一些动态库和静态库,给系统或者安装的软件使用
media: 挂载目录,挂载外部设备,比如:光驱,扫描仪
mnt: 临时挂载目录,比如我们可以将 U 盘临时挂载到这个目录下
proc: 内存使用的一个映射目录,给操作系统使用的
tmp: 临时目录,存放临时数据,重启电脑数据就被自动删除了
boot: 存储了开机相关的设置
home: 存储了普通用户的家目录,家目录名和用户名相同
root: root 用户的家目录
dev: device , 设备目录,Linux 中一切皆文件,所有的硬件会抽象成文件存储起来,比如:键盘, 鼠标
lost+found: 一般时候是空的,电脑异常关闭 / 崩溃时用来存储这些无家可归的文件,用于用户系统恢复
opt: 第三方软件的安装目录
var: 存储了系统使用的一些经常会发生变化的文件, 比如:日志文件
usr: unix system resource, 系统的资源目录
/usr/bin: 可执行的二进制应用程序
/usr/games: 游戏目录
/usr/include: 包含的标准头文件目录
/usr/local: 和 opt 目录作用相同,安装第三方软件
环境变量表:PATH=/bin:/usr/bin:/usr/local/bin:.    PATH 变量包含了一张目录表(称为路径前缀),目录之间用冒号(:)分隔。

1.4 登录

1 登录名

登录名、加密口令、数字用户ID、数字组、注释字段、起始目录、shell程序
dyc:x:205:105:trdycdyc:/home/dyc:/bin/dsh

2.shell

分为两种: 1、和用户交互的交互式shell。 2、 文件类型的shell脚本

1.5 输入和输出

1. 文件描述符

文件描述符(fledescriptor)通常是一个小的非负整数,内核用以标识一个特定进程正在访问的文件。当内核打开一个现有文件或创建一个新文件时,它都返回一个文件描述符。在读、写文件时,可以使用这个文件描述符。

2. 标准输入、标准输出、标准错误

每当运行一个新程序时,所有的shell都为其打开3个文件描述符,标准输入、输出、错误,默认情况下这三个文件描述符都会链接向终端,shell也提供了一种方法(重定向符”>“),是这三个文件描述符重新定向到某个文件,当重定向的文件不存在时,shell会创建它。

编写代码可实现文件的复制

./a.out < infile > outfile

3. 不带缓冲的IO

函数open、read、write、lseek以及close提供了不带缓冲的IO

4. 标准I/O

标准IO为那些不带缓冲的IO提供了一个带缓冲的接口,这样就无需选择最佳的缓冲区大小

1.6 程序和进程

1. 程序

程序是一个存储在磁盘上某个目录中的可执行文件,内核使用exec函数将程序读入内存,并执行

2. 进程和进程ID

程序的执行实例叫进程,进程ID唯一标识了每个进程

3. 进程控制

主要用到三个函数:fork、exec、waitpid

4. 线程和线程ID

一个进程内的所有线程共享同一地址空间、文件描述符、栈、以及与进程相关的属性

1.7 出错处理

在<errno.h>中定义了errno以及可以赋予它的各种常量,这些常量都是以字符E开头的

errno并不是简单的一个数据结构,也不是一个int类型的变量,而是一个宏,下面是其实现

extern int *__errno_location(void);
#define errno (*__errno_location())

c标准提供了两个函数,用于打印出错消息

  • strerror函数将errnum(即errno)的值映射为一个出错的消息字符串,并返回该字符串的指针
#include<string.h>

char *strerror(int errnum);

perror函数基于errno当前值,在标准错误上产生一条出错消息字符串,并返回此字符串的指针

#include<stdio.h>

void perror(const char *msg)

**注意:**只有当一个库函数失败时,errno才会被设置。当函数成功运行时,errno的值不会被修改。这意味着我们不能通过测试errno的值来判断是否有错误存在。反之,只有当被调用的函数提示有错误发生时检查errno的值才有意义。

1.8 用户标识

用户ID、组ID、附属组ID

1.9 信号

信号用于通知进程发生了某种情况,常见的信号处理方式有以下三种

  1. 忽略信号
  2. 按系统默认方式处理
  3. 提供一个函数。即信号发生时,调用该函数,这个过程也叫做捕捉该信号

终端键盘提供了产生两种信号的方法:中断键(ctrl + c)和退出键(ctrl + \)

另一种时调用kill产生信号,例如在一个进程中调用此函数就可以向另一个进程发送信号

1.10 时间值

历史上,共有两种时间:

  1. 日历时间 :保存在time_t中
  2. 进程时间 :保存在clock_t中

度量一个进程的执行时间,用三个时间值:

  • 时钟时间 进程运行的时间总量
  • 用户CPU时间 执行用户指令的时间
  • 系统CPU时间 执行系统调用的时间

1.11 系统调用和库函数

二、UNIX标准及实现

2.1 ISO C

#include<assert.h>                         //验证程序断言
#include<errno.h>						   //出错码
#include<stdio.h>                          //标准I/O库
#include<stdlib.h>                         //使用函数
#include<string.h>						   //字符串操作
#include<time.h>                           //时间和日期
#include<wchar.h>                          //宽字符类型
#include<dirent.h>                         //目录项
#include<fcntl.h>                          //文件控制
#include<netdb.h>						   //网络数据库操作
#include<pthread.h> 					   //线程
#include<unistd.h>						   //符号常量
#include<arpa/inet.h>	                   //因特网定义
#include<net/if.h> 						   //套接字本地接口
#include<sys/select.h>					   //select函数
#include<sys/socket.h>					   //套接字接口
#include<sys/stat.h>					   //文件状态
#include<sys/types.h>					   //基本系统数据类型
#include<sys/un.h> 						   //UNIX域套接字定义

2.2 函数sysconf、pathconf、和fpathconf

#include<unistd.h>
long sysconf(int name);
long pathconf(const char *pathname, int name);
long fpathconf(int fd, int name);

这些函数用来修改一些系统配置,例如进程最大打开文件数、进程最大信号量等

2.3 ISO C和IEEE POSIX

POSIX是一个最初由IEEE制定的标准族,指的是可以指操作系统接口(Portable Operating System Interface),该标准以UNIX为基础,但是并不限于UNIX类系统。

三、文件I/O

3.1 文件描述符

对于内核而言,所有打开的文件都用文件描述符引用。文件描述符是一个非负整数。

按照习惯,UNIX系统shell把文件描述符0与进程的标准输入关联,文件描述符1与进程的标输出关联,文件描述符2与进程的标准错误关联,在哟应用程序中,我们应该将他们替换为STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO以提高程序可读性。这些常量定义在<unistd.h>中,文件描述符,最多打开63个。

3.2 函数open和openat

#include<fcntl.h>
int open(const char *path, int oflag, ...)
int openat(int fd, const char *path, int oflag,...)
													两个函数的返回值:若成功,返回文件描述符;若出错,返回-1
  • path参数是要打开或创建文件的名字。
  • oflag参数可用来说明此函数的多个选项,该参数包括:
    • O_RDONLY 只读打开
    • O_WRONLY 只写打开
    • O_RDWR 读、写打开
    • O_EXEC 只执行打开
    • O_SEARCH 只搜索打开(应用于目录)
    • O_APPEND 写时追加到文件的尾端
    • O_CREAT 文件不存在时创建它,使用此选项还需要指定第三个参数mode,来指定其权限。
    • O_TRUNC 如果此文件存在,而且为只写或读-写成功打开,则其长度截断为0。
    • O_NOFOLLOW 若path引用的是一个符号链接,则出错
  • 如果path指定绝对路径,则两个函数完全相同。

由open和openat函数返回的文件描述符一定是最小的未用的描述符,

3.3 函数creat

#include<fcntl.h>
int creat(const car *path, mode_t mode)

此函数可以被open函数完全替代,这里不作介绍。

3.4 函数close

#include<ubnnistd.h>
int close(int fd);

当一个进程终止时,内核自动关闭它所有打开的文件,关闭文件时还会释放该进程加上去的所有记录锁。

3.5 函数lseek

每打开文件,内核都会维护一个与其相关联的“当前文件偏移量”,通常读、写操作都是从当前文件偏移量处开始,并使偏移量增加所读写的字节数。可以调用lseek显示地为一个文件设置偏移量。

#include<unistd.h>
off_t lseek(int fd, off_t offset, int whence);
			//返回值:若成功,返回新的文件偏移量,若出错,返回-1		

参数whence:

  • 若whence是SEEK_SET,则将该文件的偏移量设置为据文件开始处offset个字节。
  • 若whence是SEEK_CUR,则将该文件的偏移量设置为当前值加offset个字节,offset可正可负。
  • 若whence是SEEK_END,则将该文件的偏移量设置为文件长度加offset个字节。

通常文件的当前偏移量应该是一个非负整数,但是某些设备也允许福德偏移量,因此,在比较lseek的返回值时应该谨慎,不要测试它是否小于0,而要测试是否等于-1。

文件偏移量可以大于文件的当前长度,在这种情况下,对该文件的下一次写将加长该文件,并在文件中构成一个空洞,这一点是允许的,在文件中但没有被写过的字节都被读为0. 空洞并不要求在磁盘上占用存储区。

3.6 函数read/write

#include<unistd.h>
/*不带缓冲的IO*/
ssoze_t read(int fd, void *buf, size_t nbytes);
ssize_t write(int fd, const void *buf, size_t nbytes);	
			//返回值:读到的字节数,若以到文件尾,返回0;若出错,返回-1

3.7 I/O的效率

系统CPU时间的几个最小值差不多出现在BUFFSIZE为4096以后,继续增加缓冲区长度对长度几乎没有效应影响。

3.8 文件共享

内核使用三种数据结构表示打开的文件,

  1. 每个进程在进程表中都有一个记录项,记录项中包含一张打开的文件描述符表,可将其视为一个矢量,每个描述符占用一项。与每个文件描述符相关联的是:

​ a. 文件描述符标志(close_on_exec)

​ b.指向一个文件表项的指针。

  1. 内核为所有打开的文件维持一张文件表,每个文件表包含:

​ a. 文件状态标志(读、写、添加、同步和非阻塞等)

​ b.当前文件偏移量;

​ c.指向该文件v结点表项的指针

  1. 每个打开文件都有一个v节点结构。v节点包含了文件类型和对文件进行各种操作函数的指针。对于大多数文件,v节点还包含了该文件的i节点。
    在这里插入图片描述

  2. 对于多个打开的文件

    • 在每次完成write操作后,当前表项的中当前文件的文件偏移量即增加的字节数,如果超出了当前文件长度,则i节点中的当前文件长度也会同步更新。

    • lseek函数只修改文件表项中的当前文件偏移量,不进行任何I/O操作

3.9 原子操作

当多个进程同时写一个文件时,逻辑操作“先定位到文件尾端,然后写”的逻辑操作会使后一个写的覆盖掉前一个写的内容,解决问题的方法就是将定位到文件尾端+写操作 对于其他进程来说为一个原子操作。在打开文件时设置O_APPEND标志,在每次写时,内核都会先将当前文件偏移量设置到该文件的尾端,这样就不用调用lseek函数了。

  • 函数pread和pwrite
#include <unistd.h>
ssize_t pread(int fd, void *buf, size_t nbytes, off_t offset);
											返回值:读到的字节数,若已到文件尾,返回0;若出错,返回-1
ssize_t pwrite(int fd, const void *buf, size_t nbytes, off_t offset);
返回值:若成功,返回已写的字节数;若出错,返回-1

针对这两个函数,需要注意的是:

  • 调用pread时,无法中断其定位和读操作
  • 不更新当前文件偏移量。(read/write函数都会更改当前文件偏移量)
  • pwrite也有类似的区别

3.10 函数dup和dup2

#include<unistd.h>
int dup(int fd);
int dup2(int fd, int fd2);   
			//两个函数的返回值:若成功,返回新的文件描述符;若出错,返回-1

​ dup和dup2函数都用来复制一个指定的文件描述符,dup2函数可以指定返回的文件描述符fd2,如果fd2已经打开,则要想将其关闭。复制文件描述符的另一种方法是使用fcntl函数,这个将在后面介绍。

3.11 函数sync、fsync和fdatasync

传统的UNIX系统实现在内核中设有缓冲区高速缓存或页高速缓存,大多数磁盘I/O都通过缓冲区进行。当我们像文件写入数据时,通常都先写入高速缓存(为了效率),然后再排队,晚些再写入磁盘。这种方式称为延迟写。

内核需要重用缓存区时,他会把所有延迟写 数据块写入磁盘。为了保证磁盘实际数据和缓存区的内容一致性,UNIX系统提供了sync、fsync和fdatasync三个函数

#include <unistd.h>
int fsync(int fd);
int fdatasync(int fd);
																返回值:若成功,返回0;若失败,返回-1
void sync(void)

sync只是将所有修改过的块缓冲区排入写队列,然后就返回,并不等待实际写磁盘结束。

通常,称为update的守护进程周期性的调用sync函数,这就保证了可以定时冲洗内核的块缓冲区。

fsync函数只对文件描述符fd指定的一个文件起作用,并等待写磁盘结束才返回,比如数据库操作需要调用此函数。

3.12 函数fcntl

fcntl函数可以改变已经打开文件的属性

#include<fcntl.h>

int fcntl(int fd, int cmd, .../*int arg*/);
		      //返回值:若成功,则依赖于cmd;若出错,返回-1

fcntl的返回值与命令有关。如果出错,所有命令有返回-1,成功则返回值其他值。

3.13 函数ioctl

ioctl函数是I/O操作的杂物箱。

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

int ioctl(int fd, int request,...);
						//返回值:若出错,返回-1;若成功,返回其他值

3.14 /dev/fd

系统都会提供/dev/fd的目录,打开文件dev/fd/n等效于复制文件描述符n

例如fd = open("dev/fd/0",等效于fd = dup(0);值得注意的是/dev/fd在linux中是指向底层物理文件的,操作不当可能会造成底层截断。

例如,命令filter file2 | cat file1 - file3 | lprfilter file2 | cat file1 /deb/fd/0 file3 | lpr相比,缺少了文件名参数的一致性。

该命令的解释:cat读file1,接着读其标准输入(也就是filter file2命令的输出),然后读file3文件

四、文件和目录

4.1 函数stat、fstat、fstatat和lstat

#include<sys/stat.h>

int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int fd, const char *restrict pathname, struct sat *restrict buf, int flag);

4.2 文件类型

文件类型包括以下几种:

  1. 普通文件
  2. 目录文件
  3. 块特殊文件
  4. 字符特殊文件
  5. FIFO
  6. 套接字
  7. 符号链接

4.3 设置用户ID和设置组ID

4.4 新文件和目录的所有权

新文件的用户ID设置为进程的有效用户ID。关于组ID,用户可以选择:1. 新文件的组ID可以是进程的有效组ID.2.新文件的组ID可以是他所在的目录的组ID。

4.7 函数access和faccessat

#include<unistd.h>

int access(const char *pathname, int mode);
int faccessat(int fd, const char *pathname, int mode, int flag);
				//两个函数返回值:若成功,返回0;若出错,返回-1

4.8 函数umask

#include<sys/stat.h> 

mode_t umask(mode_t cmask);
							//返回值:之前的文件模式创建屏蔽字

在这里插入图片描述

参数cmask是由图4-6中列出的9个常量中的若干位按照“或”构成的

在进程创建一个新文件或新目录时,就一定会使用文件模式创建屏蔽字,

下面程序创建两个文件,创建第一个时,umask为0.创建第二个时,umask值禁止所有组合和其他用户的访问权限。

#include<unistd.h>
#include<stdio.h>
#include<fcntl.h>

#define RWRWRW (S_IRUSR|S_IWUSR|S_IWGRP|S_IRGRP|S_IROTH|S_IWOTH)

int main(void)
{
    umask(0);
    if(creat("foo",RWRWRW) < 0)
        err_sys("creat error for foo");
    uamsk(S_IWGRP|S_IRGRP|S_IROTH|S_IWOTH);
    if(creat("bar",RWRWRW) < 0)
        err_sys("creat error for bar");
    exit(0);
}

4.9 函数chmod、fchmod和fchmodat

这三个函数是我们可以更改现有文件的访问权限

#include<sys/stat.h>

int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);
int fchmodat(int fd, const char *pathname, mode_t mode, int flag);
							//	成功返回0;出错,返回-1

chmod函数在指定文件上进行操作,而fchmod函数则是对已经打开的文件进行操作。

4.10 函数chown、fchown、fchownat和lchown

下面这几个chown函数可用于更改文件的用户ID和组ID。

#include <unistd.h>
int chown (const char *pathname, uid_t owner, gid_t group) ;
int fchown(int fd, uid_t owner, gid_t group) ;
int fchownat(int fd, const char *pathname, uid_t owner, gid_t group, int flag) ;
int lchown (const char *pathname, uid_t owner, gid_t group) ;
			//4个函数的返回值:成功,返回0;失败,返回-1

4.11 文件长度

stat结构成员st_size表示亿字节为单位的文件长度。此字段只对普通文件、目录文件和符号链接有意义。

  • 对于普通文件,得到的是文件的实际长度
  • 对于目录文件,得到的是一个数(16或者512)的整数倍.
  • 对于符号链接,文件长度是文件名中的实际字节数

当文件中存在空洞时,实际长度可能和ls -l所得到的不相同,这是正常现象,当复制一个文件时,所有的空洞都会按实际字节被填充为0

4.12 文件截断

有时我们需要在文件尾端出截取一些数据以缩短数据。将一个文件的长度截断为0是一个特例,再打开文件使用O_TRUNC标志可以做到这一点,为了截断文件可以调用函数truncate和ftruncate。

#include<unistd.h>

int truncate(const char *pathname, off_t length);
int ftruncate(int fd, off_t length);
				//两个函数返回值:若成功,返回0;若出错,返回-1

4.13 文件系统

磁盘、分区和文件系统

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-uNmKp1dS-1668346492367)(D:\file\课程\md\image-20221106172800189.png)]

如果更仔细的观察一个柱面组的i节点和数据块部分,则可以看出如下图所示情况。

在这里插入图片描述

  • 在图中有两个目录项指向同一个i节点。每个i节点都有一个链接计数,其值时指向该i节点的目录项数。只有当链接计数减至0时,才可以删除文件。这也是为什么删除一个目录项的函数称之为unlink的而不是delete的原因,该链接称为硬链接。
  • 另外一种链接类型称为符号链接。任何一个叶目录的链接计数总是2,数值2来源于命名该目录的目录项..以及在该目录中的.项。如果文件有一个子目录,则数值应该为3,另一个为其子目录中的..。注意,每个子目录又会使父目录的引用计数增加1。

4.14 函数link、linkat、unlink、unlinkat和remove

#include<unistd.h>
int link(const char *existingpath, const char *newpath);
int linkat(int efd, const char *existingpath, int nfd, const char *newpath, int flag);
				//两个函数的返回值:若成功,返回0;若出错,返回-1

这两个函数都用来创建一个新目录想newpath,他引用现有文件existingpath。

为了删除一个现有的目录项,可以调用unlink函数

#include<unistd.h>
int link(const char *pathname);
int linkat(int efd, const char *pathname, int flag);
			//两个函数的返回值:若成功,返回0;若出错,返回-1

unlink的这种特性经常被程序用来确保即使在程序崩溃时,她所创建的临时文件也不会遗留下来。进程用open或creat创建一个文件,然后立即调用unlink,引文该文件仍然是打开的,所以不会将其删除,只有当进程终止时(内核会关闭打开的所有文件描述符),该文件才会删除。

#include<stdio.h>

int remove(const char *pathname);
							//返回值:若成功,返回0;出错,返回-1

4.15 函数rename和renameat

文件或者目录可以用rename函数或者renameat函数重命名

#include<stdio.h>
int rename(const char *pathname);
int renameat(int oldfd, const char *oldname, int newflag,const char *newname);
					//两个函数的返回值:若成功,返回0;若出错,返回-1

4.16 符号链接

4.17 创建和读取符号链接

4.18 文件时间

4.19 函数futimens、utimensat和utimes

4.20 函数mkdir、mkdirat和rmdir

用mkdir和mkdirat函数创建目录,用rmdir函数删除目录

#include<sys/stat.h>
int mkdir(const char *pathname, mode_t mode);
int mkdirat(int fd, const char *pathname, mode_t mode);
					//	两个函数的返回值:若成功,返回0;若出错,返回-1

两个函数创建一个新的空目录。其中...自动创建。访问权限由mode指定,对于牡蛎,通常至少要设置一个执行权限位,以允许访问该目录中的文件。

#include<unistd.h>
int rmdir(const char *pathname);
						//函数的返回值:若成功,返回0;若出错,返回-1

4.21 读目录

4.22 函数chdir、fchdir、getcwd

4.23 设备特殊文件

st_dev和st__rdev这两个字段经常引起混淆。

五、标准I/O库

5.1 流和FILE对象

在第三章,所有I/O函数都围绕文件描述符的,而对于标准I/O库,他们操作都是围绕进行的,当标准I/O库打开或创建一个文件时,我们已使一个流与一个文件相关联。

我们称指向FILE对象的指针(FILE *)为文件指针。

这里有必要科普一下流和文件描述符的区别:

  • 任何操作系统,在程序访问(读写)时,都需要建立程序与文件之间的通道,这一过程称之为打开文件。UNIX提供了两种机制,分别为:(1)文件描述符。(2)流。

  • 两者的相同点:

    • 都是作为程序与文件之间的通道。
    • 都包含了一大类的I/O库函数
  • 两者不同点:

    • 文件描述符使用int类型的变量来表示打开的文件,而流使用FILE*文件指针来进行标识
    • 如果需要对特定设备进行控制操作,必须使用文件描述符。
    • 如果需要使用特殊的方式进行I/O操作(例如非阻塞等),必须使用文件描述符的方式。
    • 在执行实际的输入输出时,流提供操作接口更加灵活、强大。
    • 文件描述符只提供简答的穿过送字符块的函数,而流函数提供格式化I/O,字符I/O,面向行的I/O的大量函数。
    • 流函数更利于程序的移植,任何基于ANSI C的系统都支持流。
  • 两者的关系:

    流为用户提供了一些更高一级的I/O接口,它处在文件描述符的上层,也就是说流是通过文件描述符来实现的。

5.2 标准输入、标准输出和标准错误

对一个进程预定义了3个流,并且这3个流可以自动被进程使用。这3个标准I/O流通过预定义文件指针stdin、stdout和stderr加以引用,这三个文件指针定义在头文件<stdio.h>中。

5.3 缓冲

标准I/O库提供缓冲的目的是尽可能减少使用read和write调用的次数。他也对每个I/O流自动地进行缓冲管理,从而避免不必要的麻烦。遗憾的是,标准I/O库最令人迷惑的也是他的缓冲。

标准I/O提供了以下3种类型的缓冲。

  • 全缓冲。在填满标准I/O缓冲区后才进行实际I/O操作。对于驻留在磁盘上的文件通常是由I/O库实施全缓冲的。术语冲洗说明标准I/O缓冲区的写操作,缓冲区也可由标准i/o例程子自动冲洗,或者可以调用函数fflush冲洗一个流。
  • 行缓冲。这种情况下,当输入和输出中遇到换行符时,才会执行I/O操作。这允许我们一次输入一个字符(啊函数fputc),但只有在写了一行之后,才会进行实际的I/O操作。
  • 不带缓冲。标准I/O库不对字符进行缓冲存储。

5.4 打开流

#include<stdio.h>
FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(in//三个函数的返回值:若成功,则返回0;若出错,则返回-1

调用fclose函数关闭流

#include<stdio.h>

int fclose(FILE *fp);
					//返回值:若成功,则返回0;若出错,则返回-1

再关闭文件之前,冲洗缓冲中的输出数据。缓冲区中的任何输入数据则被丢弃。如果标准I/O库以为该流自动分配了一个缓冲区,则释放此缓冲区。当一个程序正常终止时,所有的流都会被正确关闭。

5.5 读和写流

  1. 输入函数
#include<stdio.h>
int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);
								//若成功,返回c;若出错,返回EOF
  1. 输出函数
#include<stdio.h>
int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);
					       	//若成功,返回c;若出错,返回EOF

5.6 每次一行I/O

#include<stdio.h>
char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);
		    //若成功,返回buf;若已到达文件尾端或者出错,返回NULL

gets函数从标准输入读,而fgets函数从指定的流读。gets有些不安全,是不建议使用的

#include<stdio.h>
int *fputs(char *restrict str, FILE *restrict fp);
int *puts(char *str);
				//若成功,返回buf;若已到达文件尾端或者出错,返回NULL

5.7 二进制I/O

5.6和5.7节中的函数,因为各自的特点均不适合读写二进制I/O函数,因此特地提供以下两个函数进行二进制I/O

#include<stdio.h>

size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
							           //返回值:读写的对象数

此函数常见的两种用法。

  1. 读或者写一个二进制数组。下面例程将数组中2~5个元素写至一文件中。
float data[10];
if(fwrite(&data[2], sizeof(float), 4, fp) != 4)
	perror("fwrite error");
  1. 读写一个结构。
struct {
	short count;
	long total;
	char name[NAMESIZE];
}item;
if(fwrite(&item, sizeof(item), 1, fp) != 1)
    perror("fwrite error");

使用二进制I/O的基本问题是,它只能用于都在同一系统上已写的数据。具体表现为,在一个系统上写的数据,在另一个系统上进行处理,此时会出问题,不能正常工作,其原因包括:

  1. 在一个结构中,同一个成员的偏移量可以随编译程序和系统的不同而不同(由于不同的对要求)
  2. 用来存储多字节整数和浮点值的二进制格式在不同的系统结构间也可能不同。

5.8 定位流

5.9 格式化I/O

  1. 格式化输出

格式化输出由5个printf函数来处理的。

#include<stdio.h>

int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fd, const char *restrict format, ...);
int dprintf(int fd, const char *restrict format, ...);
									//3个函数的返回值:成功,返回输出字符数,若输出错误,返回负值
int sprintf(char *restrict buf, const char *restrict format, ...);
								   //返回值:成功,返回存入数组的字符数,若编码错误,返回负值
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);
								   //返回值:若缓冲区足够大,返回将要存入数组的字符数,若编码错误,返回负值

printf函数将格式化数据写到标准输出,fprintf写至指定的流,dprintf写至指定的文件描述符,sprintf将格式化的数据写入数组,同时在该数组的尾端自动加一个null字节,但该字符不包括到返回值中。snprintf函数显式的提供了缓冲区的长度,以防止缓冲区溢出的隐患,但是此函数不会再数组最后加NULL字节。

格式说明控制其余参数如何编写,以后如何显示,转换说明是以%开始的。一个转换说明有4个可选择的部分。具体说明和示例见来链接:https://blog.csdn.net/weixin_44567318/article/details/115441167。

  1. 格式化输入
#include<stdio.h>

int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char  *restrict format, ...);

5.10 实现细节

#include<stdio.h>

int fileno(FILE *fp);
										//返回值:与文件相关联的文件描述符

5.11 临时文件

5.12 内存流

5.13 标准I/O的替代软件

六、系统数据文件和信息

6.1 口令文件

6.2 阴影文件

6.3 组文件

6.4 附属组ID

6.5 实现区别

6.6 其他数据文件

6.7 登陆账户记录

6.8 系统标识

6.9 时间和日期例程

有UNIX提供的基本时间服务是自1970年00:00:00这一特定时间以来提供的秒数。

time函数返回当前的时间和日期

#include<time.h>
time_t time(time_t *calptr);
//返回值:成功,返回时间数,若参数列表非空,返回值也会保存到参数列表的指针中;失败,返回-1

两个函数localtime和gmtime将日历时间转换为分解后的时间,并将其存放在一个tm的时间结构中。

struct tm{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year
/**还包括夏令时间**/
...
};
#include<time.h>
struct tm *gmtime(const time_t *calptr);
struct tm *localtime(const time_t *calptr);

七、进程环境

7.1 main函数

C程序总是从main函数开始的。main函数的原型是:

int main(int argc, char *argv[]);

argc 是命令行的参数,argv是指向参数的哥哥指针多构成的数组。

当内核执行一个C程序时(使用exec函数),调用main前先调用一个特殊的启动例程。可执行文件将此启动例程指定为程序的起始地址。启动例程从内核取得命令行参数和环境变量值,然后为按照上述方式调用main函数做好安排。

7.2 进程终止

有8种方式是进程终止,其中5种为正常终止,他们是:

  • 从main返回、调用exit、调用 _exit 或 _Exit、最后一个线程从启动例程中返回、从最后一个线程调用pthread_exit。

异常终止方式包括三种:

  • 调用abort、接到一个信号、最后一个线程对取消请求做出响应。

启动例程是这样编写的:使得从main函数返回后立即调用exit函数。该例程通常使用汇编语言编写的

  1. 退出函数

    3个函数用于正常终止一个程序:_exit和 _Exit立即进入内核,exit则先执行一些清理处理,然后返回内核。

    #include<stdlib.h>
    void exit(int status);
    void _Exit(int status);
    #include<uistd.h>
    void _exit(int status);
    

    exit函数总是会先执行一个标准I/O库的清理关闭操作。

    3个退出函数都会带一个整形参数,称为终止状态。大多数UNIX系统都提供了检查进程终止状态的方法。

  2. 函数atexit

按照ISO C一个程序最多可以登记32个函数,这些函数有exit自动调用,我们称这些函数为终止处理函数。并调用atexit来登记这些函数。

#include<stdlib.h>
int atexit(void (*func)(void));
						//返回值:成功,返回0,若出错,返回-1

其中,atexit函数的参数是一个函数地址,exit函数调用这些函数的顺序预登记时顺序相反,同意函数如果登记多次,那么也会调用多次。

根据ISO C和POSIX.1,exit首先谁调用各种终止处理程序,然后关闭所有打开流,此外若程序调用exec函数族,则将清除所有已安装的终止处理函数。

注意,内核使程序执行的唯一方法是调用exec函数。进程终止的唯一方法是显式或隐式的调用_exit或 _Exi。进程也可以通过信号的方式非自愿终止。

7.3 命令行参数

当执行一个程序时,调用exec的进程可将命令行参数传递给新程序。

ISO C和POSIC.1 都要求argv[argc]是一个空指针。这就使得我们可以将参数处理循环写为:

for(i = 0; argv[i] != NULL; i++)

7.4 环境表

每个程序都接收到一张环境表,环境表也是一个字符指针数组,全局变量environ则包含了该指针数组的地址:extern char **environ;每个指针包含了一个以null结尾的C字符串的地址。
在这里插入图片描述
通常用getenv和putenv来访问指定的环境变量。

7.5 C程序的存储空间布局

C程序一直由以下几部分组成:

  • 正文段。这是由CPU执行的机器指令部分。通常正文段是可共享的,所以即使是频繁执行的程序(如文本编辑器、C编译器和shell等)在存储其中也只需要一个副本,另外正文段常常是只读。
  • 初始化数据段。通常将此段称为数据段(data段),他包含了程序中需明确地赋初值的变量。
  • 未初始化数据段。通常将此段称为bss段,在程序开始 执行前,内核将此段中的数据初始化为0或者空指针。
  • 栈。自动变量以及每次函数调用时所保存的信息都存放在此段中。
  • 堆。通常在堆中进行动态存储分配。
  • 命令行参数和环境变量

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-3oRsQuIS-1668346492369)(D:\file\课程\md\3.png)]

size命令报告了正文段、数据段和bss段的长度(以字节为短)。

这里需要补充几点:

  • 程序是由进程使用exec函数执行的,而进程号是由内核指定的。C程序在编译过后,就会将正文段(txt段)和初始化的数据data段的数据写入到可执行文件中,有进程加载磁盘来读取这些数据。
  • bss和data段的区别:
    • bss段存放全局变量和静态变量(声明但未初始化),具体表现为一个占位符;data段存放:全局变量、静态变量(声明且初始化)、常量。
    • bss段不占用磁盘的存储空间,其内容由操作系统初始化(清零)。而data段则占用存储空间。
    • bss段和data段在c语言程序内存分区模型中对应为全局/静态变量区,事实上常量也存放在data区。
    • 进程中堆栈的大小:linux中栈区默认为10M,而在Windows中默认为1M,默认一个线程的栈大小为1M;linux中堆区默认大小为3G(假设内存共4G),而在windows中为2G,高位空间留给内核。
    • 如果函数中存在未释放的内存,则可能会使得进程分配的堆区空间不断增大,最终导致过度得换页开销,造成性能下降甚至程序崩溃。

7.6 共享库

共享库使得可执行文件不再需要包含公共的库函数,而只需要在所有进程都可引用的存储区中保持这种库例程的一个副本。程序第一次执行或者调用某个库函数时,用动态链接方法将程序与共享 库函数相链接。这减少每个可执行文件的长度,但增加了一些运行时间开销。例如编译如下程序:

$ gcc -static hello1.c 阻止gcc使用共享库

a.out -> 979443

$ gcc hello1.c gcc使用共享库

a.out -> 8378

可以看出,使用共享库编译此程序,可执行文件的正文和数据段的长度都显著减小;

7.7 存储空间分配

ISO C说明了三个用于存储空间动态分配的函数

  1. malloc,分配指定字节数的存储区,此存储区中的初始值不缺定。
  2. calloc,为指定数量指定长度的对象分配存储空间,每一位bit都初始化为0.
  3. realloc,增加或者减少以前分配区的长度
#include<stdlib.h>
void *malloc(size_t size);
void *calloc(size_t nobj, size_t size);
void *realloc(void *ptr, size_t newsize);
										//成功返回非空指针,若出错,返回NULL
void free(void *ptr);

因为这三个alloc函数都返回void*, 所以若果程序中包含了#include<stdlib.h>,那么当我们i将这些返回赋予一些其他类型时,就不需要显式的强制类型转换了。

大多数实现所分配的存储空间比所要求的要稍大一些,额外的空间用来记录管理信息——分配块的长度、指向下一个分配块的指针等,这就意味着,如果超过一个已分配区的尾端或者起始位置首端,则会造成在灾难性问题。

其他可能产生致命性错误的是:释放一个已经释放的块;调用free时使用的指针不是alloc函数返回值等。弱国一个进程调用malloc函数,但是忘了调用free函数,那么该进程占用的存储空间就会连续增大,这被称为泄露

7.8 环境变量

ISO C定义使用getenc函数来获取环境变量得值,注意,此函数返回一个指针,指向name=value中得value,而且获取得是一个副本,而不是直接访问environ的。

#include<stdlib.h>
char *getenv(const char *name);
								//返回值:指向name对应value的指针,若未找到,返回NULL

此外,还可以修改环境变量的值:

#include<stdlib.h>
int putenv(char *str);
int setenv(const char *name, const char *value, int rewrite);
int unsetenv(const char *name);

7.9 函数setjmo和longjmp

在C中,goto语句不能跨越函数的,而执行这种跳转功能的函数时setjmp和longjmp。

#include<setjmp.h>
int setjmp(jmp_buf env);
void longjmp(jmp_buf env, int val);

7.10 函数getrlimit和setrlimit

每个进程都有一组资源限制,其中一些可用以下两个函数执行

#include<sys/resource.h>
int getgetrlimit(int resource, struct rlimit *rlptr);
int setrlimit(int resource, const struct rlimit *rlptr);

八、进程控制

8.2 进程标识

本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

UNIX环境高级编程笔记 的相关文章

  • 报错 java: 程序包org.apache.ibatis.annotations不存在

    今天在使用mybatis运行项目时 xff0c 项目忽然报错了 xff0c 检查了自己的pom xml文件也没有问题 xff0c 报错情况如图 xff1a 在网上找了一大堆方法 xff0c 都没有解决问题 xff0c 这里附上一个网上的常用
  • spring框架

    文章内容 介绍spring框架 为什么要使用spring框架 如何使用spring IOC控制反转 1 介绍spring框架 1 spring是一个轻量级开源的JAVAEE框架 2 Spring提高了IOC和AOP IOC 控制反转 把创建
  • Ubuntu忘记密码怎么办 如何重置Ubuntu登入密码

    1 首先重新启动Ubuntu系统 xff0c 然后快速按下shift键 xff0c 以调出grub启动菜单 2 在这里我们选择第二个 xff08 Ubuntu高级选项 xff09 xff0c 选中后按下Enter键 3 选择第二个recov
  • 快速掌握e语言,以js语言对比,快速了解掌握。

    易语言 xff0c 怎么调试输出 xff0c 查看内容 在js语言里 xff0c 弹窗是 alert 在易语言里 xff0c 弹窗是 信息框 弹出显示内容 0 标题 在js语言里 xff0c 调试输出是 console log 在易语言里
  • java 实现Comparable接口排序,升序、降序、倒叙

    本人由于项目开发中需要对查询结果list进行排序 xff0c 这里根据的是每一个对象中的创建时间降序排序 本人讲解不深 xff0c 只实现目的 xff0c 如需理解原理还需查阅更深的资料 1 实现的效果 2 创建排序的对象 package
  • gitbash不能粘贴

    点击鼠标滚轮或者shift 43 ins
  • Hive安装与配置常见问题解决

    欢 43 迎使用Markdown编辑器 提示 xff1a 文章写完后 xff0c 目录可以自动生成 xff0c 如何生成可参考右边的帮助文档 目录 前言一 Hive是什么 xff1f 二 安装步骤1 引入jar包2 配置步骤1 hive s
  • 【Linux】生产者消费者模型

    文章目录 1 生产者消费者模型1 1生产者消费者模型的特点1 2生产者消费者模型的原则1 3生产者消费者模型的优点 2 基于阻塞队列的生产者消费者模型2 1如何理解生产者消费者模型的并发 xff1f 3 信号量3 1信号量接口3 2基于环形
  • Ubuntu设置允许root用户登录

    Ubuntu激活root用户 sudo passwd root 设置root密码 设置允许root通过ssh默认登录 vim etc ssh sshd config root权限编辑 PermitRootLogin yes 在第一行添加内容
  • python编写程序统计一元人民币换成一分、两分和五分的所有兑换方案个数(用while循环)

    a 61 int input 34 输入钱数 xff08 单位 xff1a 元 xff09 34 e 61 a 100 count 61 0 i 61 1 while i lt e 5 43 1 i 43 61 1 b 61 e 5 i 2
  • springboot简易整合mybatis

    SpringBoot整合Mybatis篇 实现单表mybatis整合 准备sql 创建数据库 create database if not exists 96 mybatis 96 修改数据库默认字节编码 alter database 96
  • Springboot+PageHelper实现分页(前后端简单展示)

    Springboot 43 PageHelper实现分页 前后端简单展示 创建表及赋值 创建表 DROP TABLE IF EXISTS 96 page learn 96 CREATE TABLE 96 page learn 96 96 i
  • SpringBoot缓存注解使用(无数据库操作)

    SpringBoot缓存注解使用 无数据库操作 缓存注解介绍 64 EnableCaching注解 xff1a 开启注解缓存的支持 64 Cacheable注解 xff1a 对方法的查询结果进行缓存 64 CachePut注解 xff1a
  • JavaScript(基于Java开发的学习)

    JavaScript 基于Java基础学习 JavaScript结构图 1 JS简介 JavaScript xff08 行为 xff09 xff1a 是一种弱类型脚本语言 xff0c 其源码不需经过编译 xff0c 而是由浏览器解释运行 x
  • MyBatis框架

    MyBatis学习结构 1 MyBatis框架简介 MyBatis是一款优秀的持久层框架 它支持定制化SQL 存储过程以及高级映射 MyBatis避免了几乎所有的JDBC代码和手动设置参数以及获取结果集 MyBatis可以使用简单的XML或
  • springboot+vue实现增删改查小demo

    学习vue xff0c 就想着自己搭建一个框架学习一下 xff0c 本文属于vue与后台的增删改查入门demo xff0c 不做讲解 xff0c 只为了记录一下代码 后台框架前台框架的搭建自己百度就可以做到了 项目的源码地址 https g
  • JWT(Json web token)

    1 什么是JWT 官网地址 JSON Web Token Introduction jwt io 翻译 jsonwebtoken xff08 JWT xff09 是一个开放标准 xff08 rfc7519 xff09 xff0c 它定义了一
  • MyBatis-plus

    MyBatis plus学习结构图 1 MyBatis plus简介 为什么要学习它呢 MyBatisPlus可以节省我们大量工作时间 xff0c 所有的CRUD代码它都可以自动化完成 JPA tk mapper MyBatisPlus 偷
  • QT制作简易串口

    QT 实现一个简易版串口调试助手 文章目录 1 设计 UI 界面 2 具体代码编写 3 最终实现效果图 一 设计 UI 界面 设计 UI 界面之前 xff0c 让我们先看一下别人设计的串口助手大概长什么样子 xff0c 具体有哪些功能 我们
  • MVC的执行流程

    MVC的执行流程 1 执行服务器2 发送请求到tomcat3 响应客户端总结 1 执行服务器 根据配置创建DispatcherServlet对象 这个对象已创建 xff0c 会根据contextConfigLocation的配置查找spri

随机推荐

  • KVM 虚拟化

    kvm虚拟化 Kvm的安装 KVM下的虚拟机安装和相互访问约束 推荐下载TightVNC 也可以使用 VNCSever 一 虚拟化产品介绍 linux类的虚拟化软件 qemu xff0c 软件纯模拟全虚拟化软件 xff0c 特别慢 xen
  • Linux服务器连接失败,针对ping不通和无法上网问题 CentOS 7

    1 防火墙是否关闭 防火墙关闭命令 xff1a systemctl stop firewalld service 关闭 systemctl start firewalld service 重启防火墙 systemctl disable fi
  • 数据挖掘Java——KNN算法的实现

    一 KNN算法的前置知识 k 近邻 xff08 kNN k NearestNeighbor xff09 是在训练集中选取离输入的数据点最近的k个邻居 xff0c 根据这个k个邻居中出现次数最多的类别 xff08 最大表决规则 xff09 x
  • 数据挖掘Java——DBSCAN算法的实现

    一 DBSCAN算法的前置知识 DBSCAN算法 xff1a 如果一个点q的区域内包含多于MinPts个对象 xff0c 则创建一个q作为核心对象的簇 然后 xff0c 反复地寻找从这些核心对象直接密度可达的对象 xff0c 把一些密度可达
  • 数据挖掘Java——Kmeans算法的实现

    一 K means算法的前置知识 k means算法 xff0c 也被称为k 平均或k 均值 xff0c 是一种得到最广泛使用的聚类算法 相似度的计算根据一个簇中对象的平均值来进行 算法首先随机地选择k个对象 xff0c 每个对象初始地代表
  • Git Bash中怎么复制与粘贴

    git里面的复制粘贴 一 第一种键盘复制粘贴 右击 xff0c 把git bash应用的Options 配置项打开 复制 ctrl 43 insert 粘贴 shift 43 insert 二 第二种鼠标复制粘贴 1 选中你要复制的部分 x
  • 最新版 springboot集成kafka

    在上一篇文章中介绍了怎么在mac系统上搭建kafka xff0c 既然搭建成功了 xff0c 肯定要集成到项目中使用啊 xff0c 但是怎么集成呢 xff0c 这里把我本人集成的代码以及怎么一步步集成的放上来 xff0c 源码也会在本文的后
  • Python循环输出1~100,每10个换一行

    for i in range 1 101 print i end 61 34 34 if i 10 61 61 0 print
  • JavaScript中matches和match方法

    matches 主要是用来判断当前DOW节点是否能完全匹配对应的CSS选择器 xff0c 如果匹配成功 xff0c 返回true xff0c 反之则返回false 语法如下 xff1a element mathces seletor 这个方
  • Row size too large (> 8126). Changing some columns to TEXT or BLOB… | Mysql / MariaDB

    Row size too large gt 8126 Changing some columns to TEXT or BLOB Mysql MariaDB 我们最近将客户网站迁移到新服务器 xff0c 并在尝试导入其数据库时遇到以下错误
  • 一文教你完美解决Linux中Unable to locate package xxx问题,解决不了你打我!

    项目场景 xff1a 使用Ubuntu系统进行开发 问题描述 这两天跟着一门网 课学 把html的网页部署到云服务器 xff0c 于是租了个Ubuntu云服务器 xff0c 照着网课的代码去执行 xff0c 然后一直出现这个问题 xff0c
  • Spring相关知识点(全集)

    1Spring概述 1 1Spring概述 1 spring是一个开源框架 2 spring为简化企业级开发而生 xff0c 使用spring xff0c Javabean就可以实现很多以前要考EJB才能实现的功能 同样的功能 xff0c
  • 云服务的三种模式

    1 laaS 基础设施即服务 laaS xff08 Infrastructure as a Service xff09 即基础设施即服务 提供给消费者的服务是对所有计算基础设施的利用 xff0c 包括处理CPU 内存 存储 网络和其他基本的
  • Caused by: java.lang.IllegalStateException: No application config found or it‘s not a valid config!

    复习springboot时遇到的问题 xff0c 找不到application properties 配置文件 xff0c 很奇怪 xff0c 明明放到resource下面了 xff0c 就是编译不进去 xff0c 运行后target根本没
  • Win系统远程桌面连接教程/查询用户名和密码

    要连接的电脑命名为A 被连接的电脑命名为B B电脑 xff1a 右键电脑属性 点击远程设置 点击允许远程连接此电脑 win 43 r打开cmd输入ipconfig查询ip地址 不知道用户名和密码的 输入net user查询用户名 xff0c
  • Tomcat 9.0安装及配置

    目录 一 获取安装包 二 Tomcat9 0 67 环境配置 三 验证 四 补充 一 获取安装包 官网下载https tomcat apache org 解压至英文文件夹下 xff08 路径中需要全英文 xff09 xff0c 记住路径 百
  • 1.Matlab图像的读取和显示

    在开始之前 xff0c 我们需要在脚本里创建个 m文件 xff0c 然后运行 每次运行时要更换至脚本的路径 clc clear closeall 在一个文件的开头经常会看到 那么他们的作用是什么呢 xff1f clc span class
  • 分享一个word转pdf的工具类Aspose[java]

    项目中使用到office文件的格式转换pdf阅读 xff0c 但是没有一款好用的转换工具 由于本人是mac系统 xff0c openoffice也没法使用 xff0c 前期使用itext转换一直中文乱码 xff0c 也没有解决这个问题 xf
  • Window Sever 2012 密码忘记,修改密码的方法

    在VMWare中安装Window Server 2012忘记密码后如何进行破译修复 xff1f 方法如下 xff1a 进入BIOS 设置界面 xff0c 华硕是按f2 xff08 可以查询一下自己相应电脑进入BIOS界面的按键 xff09
  • UNIX环境高级编程笔记

    UNIX环境编程 一 UNIX基础知识1 1 Linux 主要特性1 2 Linux 内核1 3 Linux 目录结构1 4 登录1 登录名2 shell 1 5 输入和输出1 文件描述符2 标准输入 标准输出 标准错误3 不带缓冲的IO4