5.7 符号连接(软链接)与硬链接
- #include <unistd.h>
int link(const char *oldpath, const char *newpath);
创建硬链接函数
oldpath: 用于指定被链接的源文件路径,应避免 oldpath 参数指定为软链接文件,为软链接文件创建硬链接没有意义,虽然并不会报错。
newpath: 用于指定硬链接文件路径,如果 newpath 指定的文件路径已存在,则会产生错误。
返回值: 成功返回 0;失败将返回-1,并且会设置 errno。
int symlink(const char *target, const char *linkpath);
创建软连接函数
target: 用于指定被链接的源文件路径, target 参数指定的也可以是一个软链接文件。
linkpath: 用于指定硬链接文件路径,如果 newpath 指定的文件路径已存在,则会产生错误。
返回值: 成功返回 0;失败将返回-1,并会设置 errno。
创建软链接时,并不要求 target 参数指定的文件路径已经存在,如果文件不存在,那么创建的软链接将成为“悬空链接” 。
ssize_t readlink(const char *pathname, char *buf, size_t bufsiz);
读取软连接函数
pathname: 需要读取的软链接文件路径。 只能是软链接文件路径,不能是其它类型文件,否则调用函数将报错。
buf: 用于存放路径信息的缓冲区。
bufsiz: 读取大小,一般读取的大小需要大于链接文件数据块中存储的文件路径信息字节大小。
返回值: 失败将返回-1,并会设置 errno;成功将返回读取到的字节数。
5.8 目录
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
创建目录
pathname: 需要创建的目录路径。
mode: 新建目录的权限设置,设置方式与 open 函数的 mode 参数一样,最终权限为(mode & ~umask)。
返回值: 成功返回 0;失败将返回-1,并会设置 errno。
pathname 参数指定的新建目录的路径,该路径名可以是相对路径,也可以是绝对路径,若指定的路径名已经存在,则调用 mkdir()将会失败 。
#include <unistd.h>
int rmdir(const char *pathname);
删除目录
pathname: 需要删除的目录对应的路径名,并且该目录必须是一个空目录,也就是该目录下只有.和..这
两个目录项; pathname 指定的路径名不能是软链接文件,即使该链接文件指向了一个空目录。
返回值: 成功返回 0;失败将返回-1,并会设置 errno。
#include <sys/types.h>
#include <dirent.h>
DIR *opendir(const char *name);
打开目录
name: 指定需要打开的目录路径名,可以是绝对路径,也可以是相对路径。
返回值: 成功将返回指向该目录的句柄,一个 DIR 指针(其实质是一个结构体指针),其作用类似于open函数返回的文件描述符fd,后续对该目录的操作需要使用该DIR指针变量;若调用失败,则返回NULL。
struct dirent *readdir(DIR *dirp);
读取目录
dirp: 目录句柄 DIR 指针。
返回值: 返回一个指向 struct dirent 结构体的指针, 该结构体表示 dirp 指向的目录流中的下一个目录条目。在到达目录流的末尾或发生错误时,它返回 NULL。
struct dirent 结构体内容如下所示:
struct dirent {
};ino_t
off_t
unsigned short
unsigned char
chard_ino;
d_off;
d_reclen;
d_type;/* inode 编号 */
/* not an offset; see NOTES */
/* length of this record */
/* type of file; not supported by all filesystem types */d_name[256]; /* 文件名 */
void rewinddir(DIR *dirp);
将目录流重置为目录起点
dirp: 目录句柄。
返回值: 无返回值
int closedir(DIR *dirp);
关闭目录 closedir 函数
dirp: 目录句柄。
返回值: 成功返回 0;失败将返回-1,并设置 errno。
char *getcwd(char *buf, size_t size);
获取进程的当前工作目录
buf: getcwd()将内含当前工作目录绝对路径的字符串存放在 buf 缓冲区中。
size: 缓冲区的大小,分配的缓冲区大小必须要大于字符串长度,否则调用将会失败。
返回值: 如果调用成功将返回指向 buf 的指针,失败将返回 NULL,并设置 errno。
#include <unistd.h>
int chdir(const char *path);
int fchdir(int fd);
改变当前进程工作目录
path: 将进程的当前工作目录更改为 path 参数指定的目录,可以是绝对路径、也可以是相对路径,指定的目录必须要存在,否则会 报错。
fd: 将进程的当前工作目录更改为 fd 文件描述符所指定的目录(譬如使用 open 函数打开一个目录)。
返回值: 成功均返回 0;失败均返回-1,并设置 errno。
5.9 删除文件
int unlink(const char *pathname);
删除文件(准确说是一个硬链接,只有硬链接为0,才会删除十几文件)
pathname: 需要删除的文件路径,可使用相对路径、也可使用绝对路径,如果 pathname 参数指定的文件不存在,则调用 unlink() 失败。
返回值: 成功返回 0;失败将返回-1,并设置 errno。
int remove(const char *pathname);
删除文件或空目录
pathname: 需要删除的文件或目录路径,可以是相对路径、也可是绝对路径。
返回值: 成功返回 0;失败将返回-1,并设置 errno。
int rename(const char *oldpath, const char *newpath);
文件重命名
oldpath: 原文件路径。
newpath: 新文件路径。
返回值: 成功返回 0;失败将返回-1,并设置 errno。
6 字符串处理
6.1 字符串输入/输出
int puts(const char *s);
int putchar(int c);
int fputc(int c, FILE *stream);
int fputs(const char *s, FILE *stream);
char *gets(char *s); 能读取包括空格、 TAB 制表符、换行回车符等
int getchar(void); 能读取包括空格、 TAB 制表符、换行回车符等
char *fgets(char *s, int size, FILE *stream);
int fgetc(FILE *stream);
gets()、 getchar()、 fgetc()、 fgets() 、scanf
gets()与 scanf()的区别
gets()除了在功能上不及 scanf 之外,它们在一些细节上也存在着不同:
⚫ gets()函数不仅比 scanf 简洁,而且,就算输入的字符串中有空格也可以, 因为 gets()函数允许输入的字符串带有空格、制 表 符,输入的空格和制表符也是字符串的一部分,仅以回车换行符作为字符串的分割符; 而对于 scanf 以%s 格式输入的时候, 空格、换行符、 TAB 制表符等都是作为字符串分割符存在, 即分隔符前后是两个字符串, 读取字符串时并不会将分隔符读取出来作为字符串的组成部分, 一个%s 只能读取一个字符串,若要多去多个字符串,则需要使用多个%s、并且需要使用多个字符数组存储。
⚫ gets()会将回车换行符从输入缓冲区中取出来,然后将其丢弃,所以使用 gets()读走缓冲区中的字符串数据之后,缓冲区中将不会遗留下回车换行符;而对于 scanf()来说,使用 scanf()读走缓冲区中的字符串数据时,并不会将分隔符(空格、 TAB 制表符、 回车换行符等) 读走将其丢弃,所以使用 scanf()读走缓冲区中的字符串数据之后,缓冲区中依然还存在用户输入的分隔符。针对上面所提出的两个区别点,下面我们将进行一些列的代码测试。
6.2 字符串处理函数
size_t strlen(const char *s);
char *strcat(char *dest, const char *src);
char *strncat(char *dest, const char *src, size_t n);
char *strcpy(char *dest, const char *src);
char *strncpy(char *dest, const char *src, size_t n);
void *memset(void *s, int c, size_t n);
void bzero(void *s, size_t n); 将一段内存空间中的数据全部设置为 0
void *memcpy(void *dest, const void *src, size_t n);
void *memmove(void *dest, const void *src, size_t n);
void bcopy(const void *src, void *dest, size_t n);
int strcmp(const char *s1, const char *s2);
int strncmp(const char *s1, const char *s2, size_t n);
char *strchr(const char *s, int c);
char *strrchr(const char *s, int c); 从后往前查找字符
char *strstr(const char *haystack, const char *needle) ;
char *index(const char *s, int c); 返回第一次c在str中的指针
char *rindex(const char *s, int c); 返回c最后一次在str中的指针
int atoi(const char *nptr); 将字符串分别转换为 int、 long int 以及 long long 类型的数据
long atol(const char *nptr);
long long atoll(const char *nptr);
两个函数可分别将字符串转为 long int 类型数据和 long long ing 类型数据 ,可以实现将多种不同进制数 表示的字符串转换为整形数 据 。
long int strtol(const char *nptr, char **endptr, int base);
long long int strtoll(const char *nptr, char **endptr, int base);
unsigned long int strtoul(const char *nptr, char **endptr, int base);
unsigned long long int strtoull(const char *nptr, char **endptr, int base);
nptr: 需要进行转换的目标字符串。
endptr: char **类型的指针, 如果 endptr 不为 NULL,则 strtol()或 strtoll()会将字符串中第一个无效字符的地址存储在*endptr 中。 如果根本没有数字, strtol()或 strtoll()会将 nptr 的原始值存储在*endptr 中(并返回 0)。 也可将参数 endptr 设置为 NULL,表 示不
接收相应信息。
base: 数字基数, 参数 base 必须介于 2 和 36(包含)之间,或者是特殊值 0。 参数 base 决定了字符串转换为整数时合法字符的
取值范围,譬如,当 base=2 时,合法字符为' 0 '、 ' 1 '(表示是一个二进制表示的数字字符串) ;当 base=8 时,合法字符为' 0 '、
' 1 '、 ' 2 '、 ' 3 '……' 7 '(表示是一个八进制表示的数字字符串) ;当 base=16 时,合法字符为' 0 ' 、 ' 1 '、 ' 2 '、 ' 3 '……' 9 '、 ' a
'……' f '(表示是一个十六进制表示的数字字符串) ;当 base 大于 10 的时候, ' a '代表 10、 ' b '代表 11、 ' c '代表 12,依次类
推, ' z '代表 35(不区分大小写) 。
返回值: 分别返回转换之后得到的 long int 类型数据以及 long long int 类型数据。
double atof(const char *nptr);
double strtod(const char *nptr, char **endptr);
float strtof(const char *nptr, char **endptr);
long double strtold(const char *nptr, char **endptr);
7. 系统信息与系统资源
7.1 系统信息
#include <sys/utsname.h>
int uname(struct utsname *buf);
获取有关当前操作系统内核的名称和信息
址即可, struct utsname 结构体如下所示:
struct utsname {
char sysname[]; char nodename[]; char release[]; char version[]; char machine[]; #ifdef _GNU_SOURCE | /* 当前操作系统的名称 */ /* 网络上的名称(主机名) */ /* 操作系统内核版本 */ /* 操作系统发行版本 */ /* 硬件架构类型 */ |
char domainname[];/* 当前域名 */
#endif
};
#include <sys/sysinfo.h>
int sysinfo(struct sysinfo *info);
获取系统统计信息
struct sysinfo 结构体如下所示:
struct sysinfo {
long uptime; unsigned long loads[3]; unsigned long totalram; unsigned long freeram; unsigned long sharedram; unsigned long bufferram; unsigned long totalswap; unsigned long freeswap; | /* 自系统启动之后所经过的时间(以秒为单位) */ /* 1, 5, and 15 minute load averages */ /* 总的可用内存大小 */ /* 还未被使用的内存大小 */ /* Amount of shared memory */ /* Memory used by buffers */ /* Total swap space size */ /* swap space still available */ |
unsigned short procs; unsigned long totalhigh; unsigned long freehigh; unsigned int mem_unit; | /* 系统当前进程数量 */ /* Total high memory size */ /* Available high memory size */ |
/* 内存单元大小(以字节为单位) */ |
char _f[20-2*sizeof(long)-sizeof(int)]; /* Padding to 64 bytes */
}
int gethostname(char *name, size_t len);
单独获取 Linux 系统主机名
name: 指向用于存放主机名字符串的缓冲区。
len: 缓冲区长度。
返回值: 成功返回 0,;失败将返回-1,并会设置 errno。
long sysconf(int name);
获取系统的一些配置信息,譬如页大小(page size)、主机名的最大长度、进程可以打开的最大文件数、 每个用户 ID 的最大并发
进程数等
调用 sysconf()函数获取系统的配置信息, 参数 name 指定了要获取哪个配置信息,参数 name 可取以下任何一个值(都是宏定义,
可通过 man 手册查询) :
⚫ _SC_ARG_MAX: exec 族函数的参数的最大长度, exec 族函数后面会介绍,这里先不管!
⚫ _SC_CHILD_MAX: 每个用户的最大并发进程数,也就是同一个用户可以同时运行的最大进程数。
⚫ _SC_HOST_NAME_MAX: 主机名的最大长度。
⚫ _SC_LOGIN_NAME_MAX: 登录名的最大长度。
⚫ _SC_CLK_TCK: 每秒时钟滴答数,也就是系统节拍率。
⚫ _SC_OPEN_MAX: 一个进程可以打开的最大文件数。
⚫ _SC_PAGESIZE: 系统页大小(page size)。
⚫ _SC_TTY_NAME_MAX: 终端设备名称的最大长度。
7.2 日期、时间
GMT(Greenwich Mean Time) 中文全称是格林威治标准时间 。
UTC(Coordinated Universal Time)指的是世界协调时间(又称世界标准时间、世界统一时间)。
CST (China Standard Time)中国标准时间。
GMT 与 UTC 这两者几乎是同一概念,它们都是指格林威治标准时间,也就是国际标准时间,只不过UTC 时间比 GMT 时间更加精
准,所以在我们的编程当中不用刻意去区分它们之间的区别 。
Linux 通过维护jiffies 来维护系统时钟的, 系统时钟在开机的时候读取RTC硬件时钟来初始化的,关机的时候更新RTC时钟。jiffies节
拍率通常为100HZ~1000HZ 。默认为100HZ。
#include <time.h>
time_t time(time_t *tloc);
获取当前时间,以秒为单位,返回得到的值是自 1970-01-01 00:00:00 +0000 (UTC)以来的秒数 。
tloc: 如果 tloc 参数不是 NULL,则返回值也存储在 tloc 指向的内存中。
返回值: 成功则返回自 1970-01-01 00:00:00 +0000 (UTC)以来的时间值(以秒为单位) ;失败则返回-1,并会设置 errno。
#include <sys/time.h>
int gettimeofday(struct timeval *tv, struct timezone *tz);
获取更加精确的时间 ,时间精度可以大道微秒级别,得到1970-01-01...以来的秒数。
tv: 参数 tv 是一个 struct timeval 结构体指针变量。
tz: 参数 tz 是个历史产物,早期实现用其来获取系统的时区信息,目前已遭废弃,在调用 gettimeofday()函数时应将参数 tz 设置为
NULL。
返回值: 成功返回 0;失败将返回-1,并设置 errno。
struct timeval {
time_t tv_sec; /* seconds */
suseconds_t tv_usec; /* microseconds */
};
char *ctime(const time_t *timep); ctime()是一个不可重入函数
char *ctime_r(const time_t *timep, char *buf); 可重入函数
时间转换函数,转换的结果是计算机的本地时间
timep: time_t 时间变量指针。
返回值: 成功将返回一个 char *类型指针,指向转换后得到的字符串;失败将返回 NULL。
#include <time.h>
struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);
struct tm *gmtime(const time_t *timep); 国际标准时间
struct tm *gmtime_r(const time_t *timep, struct tm *result); 国际标准时间
把 time()或 gettimeofday()得到的秒数(time_t 时间或日历时间) 变成一个 struct tm结构体所表示的时间, 该时间对应的是本地
时间。
timep: 需要进行转换的 time_t 时间变量对应的指针,可通过 time()或 gettimeofday()获取得到。
result: 是一个 struct tm 结构体类型指针, 稍后给大家介绍 struct tm 结构体,参数 result 是可重入函数localtime_r()需要额外提供
的参数。
返回值: 对于不可重入版本 localtime()来说,成功则返回一个有效的 struct tm 结构体指针,而对于可重入版本 localtime_r()来说,
成功执行情况下,返回值将会等于参数 result;失败则返回 NULL。
struct tm 结构体如下所示:
struct tm {
int tm_sec; int tm_min; int tm_hour; int tm_mday; int tm_mon; | /* 秒(0-60) */ /* 分(0-59) */ /* 时(0-23) */ /* 日(1-31) */ /* 月(0-11) */ |
int tm_year; /* 年(这个值表示的是自 1900 年到现在经过数) */ int tm_wday; /* 星期(0-6, 星期日 Sunday = 0、星期一=1…) */ int tm_yday; /*一年的第几天*/ int tm_isdst; /*夏令时*/ | |
} |
time_t mktime(struct tm *tm);
将使用 struct tm 结构体表示的分解时间转换为 time_t时间(日历时间)。
tm: 需要进行转换的 struct tm 结构体变量对应的指针。
返回值: 成功返回转换得到 time_t 时间值;失败返回-1。
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);
将 struct tm 格式的时间转换为可打印输出的字符串形式
tm: 需要进行转换的 struct tm 表示的时间。
buf: 可重入版本函数 asctime_r 需要额外提供的参数 buf,指向一个缓冲区,用于存放转换得到的字符串。
返回值: 转换失败将返回 NULL;成功将返回一个 char *类型指针,指向转换后得到的时间字符串,对于 asctime_r 函数来说,返回
值就等于参数 buf。
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
将一个 struct tm 变量表示的分解时间转换为为格式化字符串,并且在功能上比 asctime()和 ctime()更加强大,它可以根据自己的喜好自定义时间的显示格式,而 asctime()和 ctime()转换得到的字符串时间格式的固定的。
s: 指向一个缓存区的指针,该缓冲区用于存放生成的字符串。
max: 字符串的最大字节数。
format: 这是一个用字符串表示的字段, 包含了普通字符和特殊格式说明符,可以是这两种字符的任意组合。 特殊格式说明符将会
被替换为 struct tm 结构体对象所指时间的相应值,这些特殊格式说明符如下:
int settimeofday(const struct timeval *tv, const struct timezone *tz);
使用 settimeofday()函数可以设置时间, 也就是设置系统的本地时间
tv: 参数 tv 是一个 struct timeval 结构体指针变量, struct timeval 结构体在前面章节内容中已经给大家介绍了,需要设置的时间便
通过参数 tv 指向的 struct timeval 结构体变量传递进去。
tz: 参数 tz 是个历史产物,早期实现用其来设置系统的时区信息,目前已遭废弃,在调用 settimeofday()函数时应将参数 tz 设置为
NULL。
返回值: 成功返回 0;失败将返回-1,并设置 errno。
使用 settimeofday 设置系统时间时内核会进行权限检查,只有超级用户(root)才可以设置系统时间,普通用户将无操作权限。
7.3 进程时间
进程时间指的是进程从创建后(也就是程序运行后)到目前为止这段时间内使用 CPU 资源的时间总数,出于记录的目的,内核把 CPU 时间(进程时间) 分为以下两个部分:
⚫ 用户 CPU 时间:进程在用户空间(用户态)下运行所花费的 CPU 时间。有时也成为虚拟时间(virtualtime)。
⚫ 系统 CPU 时间:进程在内核空间(内核态)下运行所花费的 CPU 时间。这是内核执行系统调用或代表进程执行的其它任务(譬如,服务页错误)所花费的时间。
一般来说,进程时间指的是用户 CPU 时间和系统 CPU 时间的总和,也就是总的 CPU 时间。
Tips:进程时间不等于程序的整个生命周期所消耗的时间, 如果进程一直处于休眠状态(进程被挂起、
不会得到系统调度),那么它并不会使用 CPU 资源,所以休眠的这段时间并不计算在进程时间中。
#include <sys/times.h>
clock_t times(struct tms *buf)
获取当前进程时间
buf: times()会将当前进程时间信息存在一个 struct tms 结构体数据中,所以我们需要提供 struct tms 变量,使用参数 buf 指向该变量 。
返回值: 返回值类型为 clock_t(实质是 long 类型), 调用成功情况下,将返回从过去任意的一个时间点(譬如系统启动时间) 所经过的时钟滴答数(其实就是系统节拍数), 将(节拍数 / 节拍率)便可得到秒数,返回值可能会超过 clock_t 所能表示的范围(溢出); 调用失败返回-1,并设置 errno。
示例代码 7.3.1 struct tms 结构体
struct tms {
clock_t tms_utime; /* user time, 进程的用户 CPU 时间, tms_utime 个系统节拍数 */
clock_t tms_stime; /* system time, 进程的系统 CPU 时间, tms_stime 个系统节拍数 */
clock_t tms_cutime; /* user time of children, 已死掉子进程的 tms_utime + tms_cutime 时间总和 */
clock_t tms_cstime; /* system time of children, 已死掉子进程的 tms_stime + tms_cstime 时间总和 */
};
#include <time.h>
clock_t clock(void);
库函数 clock()提供了一个更为简单的方式用于进程时间,它的返回值描述了进程使用的总的 CPU 时间(也就是进程时间,包括用户 CPU 时间和系统 CPU 时间) 。
无参数。
返回值: 返回值是到目前为止程序的进程时间,为 clock_t 类型,注意 clock()的返回值并不是系统节拍数,如果想要获得秒数,请除以 CLOCKS_PER_SEC(这是一个宏)。 如果返回的进程时间不可用或其值无法表示,则该返回值是-1 。
7.4 随机数
#include <stdlib.h>
int rand(void);
void srand(unsigned int seed); 设置随机数种子。
seed: 指定一个随机数中, int 类型的数据,一般尝尝将当前时间作为随机数种子赋值给参数 seed,譬如 time(NULL),因为每次启动应用程序时间上是一样的,所以就能够使得程序中设置的随机数种子在每次启动程序时是不一样的。
返回值: void
使用:
/* 设置随机数种子 */
srand(time(NULL));
/* 生成伪随机数 */
num = rand();
7.5 休眠
#include <unistd.h>
unsigned int sleep(unsigned int seconds);
seconds: 休眠时长,以秒为单位。
返回值: 如果休眠时长为参数 seconds 所指定的秒数,则返回 0;若被信号中断则返回剩余的秒数。
#include <unistd.h>
int usleep(useconds_t usec);
usec: 休眠时长,以微秒为单位。
返回值: 成功返回 0;失败返回-1,并设置 errno。
#include <time.h>
int nanosleep(const struct timespec *req, struct timespec *rem);
req: 一个 struct timespec 结构体指针,指向一个 struct timespec 变量,用于设置休眠时间长度,可精确到纳秒级别。
rem: 也是一个 struct timespec 结构体指针,指向一个 struct timespec 变量,也可设置 NULL。
返回值: 在成功休眠达到请求的时间间隔后, nanosleep()返回 0; 如果中途被信号中断或遇到错误,则返回-1, 并将剩余时间记录在参数 rem 指向的 struct timespec 结构体变量中(参数 rem 不为 NULL 的情况下,如果为 NULL 表示不接收剩余时间),还会设置 errno 标识错误类型。
7.6 申请内存
#include <stdlib.h>
void *malloc(size_t size)
void free(void *ptr);
void *calloc(size_t nmemb, size_t size); 动态地分配内存空间并初始化为 0
nt posix_memalign(void **memptr, size_t alignment, size_t size);
void *aligned_alloc(size_t alignment, size_t size);
void *valloc(size_t size);
#include <malloc.h>
void *memalign(size_t alignment, size_t size);
void *pvalloc(size_t size)