Linux系统编程
2025-03-23 20:37:05

文件操作

open close

打开、关闭文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#include <fcntl.h>   // 用于 open 函数的定义
#include <unistd.h> // 用于 close 函数的定义
int open(const char *pathname, int flags);
/*
- pathname:要打开的文件的路径。可以是绝对路径或相对路径。
- flags:指定打开文件的方式和标志。常见的标志包括:
- O_RDONLY:只读模式
- O_WRONLY:只写模式
- O_RDWR:读写模式
- O_CREAT:如果文件不存在,则创建文件
- O_EXCL:与 O_CREAT 一起使用,确保文件是新创建的
- O_TRUNC:如果文件存在,清空文件内容
- O_APPEND:以追加模式打开文件
- O_NONBLOCK:以非阻塞模式打开文件
- mode:在使用 O_CREAT 标志时,mode 用于指定新创建文件的权限。它通常是一个三位八进制数,表示文件的读写权限。例如,S_IRUSR 表示所有者可读权限,S_IWUSR 表示所有者可写权限。

返回值:
- 成功时,返回文件描述符(非负整数)。
- 失败时,返回 -1,errno 会设置为相应的错误代码。
*/
int fd = open("example.txt", O_RDONLY);
int close(int fd);

read write

读写文件

1
2
3
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
ssize_t write(int fd, const void *buf, size_t count);

lseek

移动文件内指针

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
标准C库的函数
#include <stdio.h>
int fseek(FILE *stream, long offset, int whence);

Linux系统函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
// off_t lseek(int fd, off_t offset, int whence);
// 功能:改变文件描述符 fd 所指文件的偏移量,通常用于随机访问文件。
// 参数:
// - fd:文件描述符,表示要操作的文件。
// - offset:相对于 whence 的偏移量(可以为正、负或 0)。
// - whence:指定偏移量的参考位置,可能的值包括:
// - SEEK_SET:从文件开头开始。
// - SEEK_CUR:从当前文件指针位置开始。
// - SEEK_END:从文件末尾开始。
// 返回值:
// - 成功时,返回文件的新偏移量(以字节为单位)。
// - 失败时,返回 -1 并设置 errno。

stat

获取文件的元数据

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
#include <sys/stat.h>
#include <unistd.h>

int stat(const char *pathname, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);// 直接处理符号链接
// 功能:获取文件的元数据(如大小、权限、时间戳等)。
// 参数:
// - pathname:要查询的文件路径。
// - statbuf:指向 `struct stat` 的指针,用于存储文件的状态信息。
// 返回值:
// - 成功时返回 0。
// - 失败时返回 -1,并设置 errno。

struct stat {
dev_t st_dev; // 文件所在的设备 ID
ino_t st_ino; // 文件的 inode 编号
mode_t st_mode; // 文件的类型和权限
nlink_t st_nlink; // 硬链接的数量
uid_t st_uid; // 文件所有者的用户 ID
gid_t st_gid; // 文件所属组的组 ID
dev_t st_rdev; // 如果是设备文件,则为设备 ID
off_t st_size; // 文件的大小(字节数)
time_t st_atime; // 上次访问时间
time_t st_mtime; // 上次修改时间
time_t st_ctime; // 上次状态更改时间
blksize_t st_blksize; // 文件系统 I/O 块大小
blkcnt_t st_blocks; // 文件占用的块数量
};

st_mode 中的文件类型可以通过以下宏检查:

  • 文件类型:
    • S_ISREG(st_mode):常规文件。
    • S_ISDIR(st_mode):目录。
    • S_ISLNK(st_mode):符号链接。
    • S_ISCHR(st_mode):字符设备。
    • S_ISBLK(st_mode):块设备。
    • S_ISFIFO(st_mode):FIFO/管道。
    • S_ISSOCK(st_mode):套接字。
  • 文件权限:
    • st_mode & S_IRUSR:用户可读。
    • st_mode & S_IWUSR:用户可写。
    • st_mode & S_IXUSR:用户可执行。

文件属性操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
int access(const char *pathname, int mode);
/*
功能:
- 检查指定文件的访问权限。

参数:
- pathname:文件的路径,可以是绝对路径或相对路径。
- mode:指定要检查的权限,可以是以下标志之一或组合:
- R_OK:检查文件是否可读。
- W_OK:检查文件是否可写。
- X_OK:检查文件是否可执行。
- F_OK:检查文件是否存在。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

int chmod(const char *filename, int mode);
/*
功能:
- 修改文件的权限。

参数:
- filename:文件路径,可以是绝对路径或相对路径。
- mode:新的权限值,通常是一个三位八进制数,用于指定文件的读、写、执行权限(例如 `0644`)。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

int chown(const char *path, uid_t owner, gid_t group);
/*
功能:
- 修改文件的所有者和所属组。

参数:
- path:文件路径,可以是绝对路径或相对路径。
- owner:新的文件所有者的用户 ID(`uid_t`)。
- group:新的文件所属组的组 ID(`gid_t`)。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

int truncate(const char *path, off_t length);
/*
功能:
- 截断文件到指定长度。如果文件长度大于给定长度,则文件会被剪切。如果文件长度小于给定长度,则文件会被扩展,并且中间的内容会被填充为零字节。

参数:
- path:文件路径,可以是绝对路径或相对路径。
- length:文件的新长度,单位为字节。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

目录操作函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
int rename(const char *oldpath, const char *newpath);
/*
功能:
- 重命名文件或目录。

参数:
- oldpath:原文件或目录的路径。
- newpath:新文件或目录的路径。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

int chdir(const char *path);
/*
功能:
- 改变当前工作目录。

参数:
- path:新的工作目录路径,可以是绝对路径或相对路径。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

char *getcwd(char *buf, size_t size);
/*
功能:
- 获取当前工作目录。

参数:
- buf:一个指向字符数组的指针,保存当前工作目录的路径。
- size:buf 缓冲区的大小。

返回值:
- 成功时返回 buf。
- 失败时返回 NULL,errno 会设置为相应的错误代码。
*/

int mkdir(const char *pathname, mode_t mode);
/*
功能:
- 创建一个新的目录。

参数:
- pathname:目录的路径。
- mode:目录的权限,通常是一个三位八进制数,用于指定目录的读、写、执行权限(例如 `0755`)。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

int rmdir(const char *pathname);
/*
功能:
- 删除一个空目录。

参数:
- pathname:要删除的目录路径。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

目录遍历函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
DIR *opendir(const char *name);
/*
功能:
- 打开一个目录流,以便遍历目录中的文件。

参数:
- name:要打开的目录路径,可以是绝对路径或相对路径。

返回值:
- 成功时,返回指向目录流的指针(`DIR*`)。
- 失败时,返回 NULL,errno 会设置为相应的错误代码。
*/

struct dirent *readdir(DIR *dirp);
/*
功能:
- 从已打开的目录流中读取下一个目录项。

参数:
- dirp:指向已打开目录流的指针。

返回值:
- 成功时,返回指向 `struct dirent` 结构体的指针,其中包含当前目录项的信息。
- 失败时,返回 NULL,且不会改变 errno。如果目录流已到达末尾,返回 NULL 也不是错误。
*/

int closedir(DIR *dirp);
/*
功能:
- 关闭已打开的目录流。

参数:
- dirp:指向已打开目录流的指针。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

struct dirent
{
// 此目录进入点的inode
ino_t d_ino;
// 目录文件开头至此目录进入点的位移
off_t d_off;
// d_name 的长度, 不包含NULL字符
unsigned short int d_reclen;
// d_name 所指的文件类型
unsigned char d_type;
// 文件名
char d_name[256];
};

/* d_type
DT_BLK - 块设备
DT_CHR - 字符设备
DT_DIR - 目录
DT_LNK - 软连接
DT_FIFO - 管道
DT_REG - 普通文件
DT_SOCK - 套接字
DT_UNKNOWN - 未知 */

dup dup2

1
2
3
4
// 复制文件描述符
int dup(int oldfd);
// 重定向文件描述符
int dup2(int oldfd, int newfd);

Linux多进程

管道

mkfifo 是一个用于创建命名管道(FIFO,First In First Out)的系统调用。命名管道用于在不同进程之间进行通信。

1
2
3
4
int mkfifo(const char *pathname, mode_t mode);
/* pathname:指定要创建的命名管道的路径名。该路径必须是有效的文件路径,且在调用时必须没有现存的文件。
mode:指定管道的权限模式,通常使用文件访问模式,如 0644。该参数可以用来指定文件的读写权限。 */
/* 完成有名管道的创建后,就可以像读写文件一样对管道进行读/写 */

内存映射

这两个函数用于在文件映射/匿名映射区开辟内存

重要:见内存映射注意事项.txt

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
/*
参数解释:
addr:期望映射的内存地址,通常设置为 NULL,由操作系统自动选择映射地址。
length:映射区域的大小(字节数)。
prot:内存保护标志,表示映射区域的权限。常见的值有:
PROT_READ:可读
PROT_WRITE:可写
PROT_EXEC:可执行
PROT_NONE:不可访问
PROT_READ | PROT_WRITE:可读写
flags:映射的类型和选项,常见的值有:
MAP_SHARED:映射区域对所有进程可见,写入数据会同步到原始文件。
MAP_PRIVATE:映射区域是私有的,对进程修改不会影响原文件。
MAP_ANONYMOUS:不与任何文件关联,通常用于共享内存。
MAP_FIXED:指定特定的映射地址(不推荐,除非非常有必要)。
fd:文件描述符,如果映射的是文件,应该提供一个有效的文件描述符。如果使用 MAP_ANONYMOUS,此参数应为 -1。
offset:映射的起始偏移量,通常设置为 0。

返回值:
成功:返回映射区域的地址。如果成功映射,返回值将是映射的内存地址。
失败:返回 MAP_FAILED(通常为 (void *)-1),并设置 errno。
*/

int munmap(void *addr, size_t length);
/*
参数解释:
addr:映射的内存区域的起始地址,通常是由 mmap 返回的地址。该地址是通过 mmap 映射到进程虚拟内存中的一段区域。
length:要解除映射的内存区域的大小(字节数)。这应与原来通过 mmap 映射时的大小一致。

返回值:
成功:返回 0。
失败:返回 -1,并设置 errno,表示解除映射失败。

作用:
munmap 函数用于解除通过 mmap 映射的内存区域的映射。解除映射后,进程不能再访问该内存区域,并且操作系统可以回收这部分内存。调用 munmap 后,原来通过 mmap 映射的内存就不再有效,任何尝试访问这块内存都会导致错误。
*/

信号

通用信号发送函数

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
#include <signal.h>

int kill(pid_t pid, int sig);
/*
功能:
- 向指定进程发送信号。

参数:
- pid:目标进程的进程 ID。如果 `pid` 为负数,则信号发送给进程组中的所有进程;如果 `pid` 为 0,则信号发送给当前进程组中的所有进程;如果 `pid` 为 -1,则信号发送给所有进程(除了不可终止的进程);如果 `pid` 为特定进程 ID,则仅向该进程发送信号。
- sig:要发送的信号类型(例如 `SIGKILL`, `SIGTERM`)。

返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

int raise(int sig);
/*
功能:
- 向当前进程发送信号。

参数:
- sig:要发送的信号类型(例如 `SIGKILL`, `SIGSEGV`)。

返回值:
- 成功时返回非负值(通常返回 0)。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/

void abort(void);
/*
功能:
- 引发当前进程的异常终止,通常会导致进程退出并生成核心转储(core dump),用于调试。

参数:
- 无。

返回值:
- 该函数不会返回,进程会立即终止。
*/

定时器

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
/*
功能:
- 设置一个定时器,在指定的秒数后发送一个 `SIGALRM` 信号。

参数:
- seconds:定时器的时间(秒),指定定时器触发前的等待时间。

返回值:
- 返回定时器剩余的时间(秒),如果定时器已设置,则返回剩余的时间;如果没有定时器,则返回 0。
- 如果定时器已经触发并且没有设定新的定时器,则返回之前的定时器剩余时间。

- SIGALARM :默认终止当前的进程,每一个进程都有且只有唯一的一个定时器。
alarm(10); -> 返回0
过了1秒
alarm(5); -> 返回9
*/

int setitimer(int which, const struct itimerval *new_val, struct itimerval *old_value);
/*
功能:
- 设置定时器,根据指定的 `which` 参数来决定使用哪种定时器(实时定时器或虚拟定时器)。
- 该函数可以用来设置定时器,并且在定时器触发时可以发送信号。

参数:
- which:定时器的类型,通常有以下几种:
- `ITIMER_REAL`:实时定时器,触发时会发送 `SIGALRM` 信号。(一般使用真实时间)
- `ITIMER_VIRTUAL`:虚拟定时器,进程使用的 CPU 时间到达时触发。
- `ITIMER_PROF`:统计定时器,进程的用户 CPU 时间和系统 CPU 时间之和达到时触发。
- new_val:指向 `struct itimerval` 结构体的指针,指定新的定时器值。`itimerval` 结构体包含两个成员:
- old_value:指向 `struct itimerval` 结构体的指针,返回旧的定时器设置。(少用)
---------------------------------------------------------------------
struct itimerval { // 定时器的结构体
struct timeval it_interval; // 每个阶段的时间,间隔时间
struct timeval it_value; // 延迟多长时间执行定时器
};
struct timeval { // 时间的结构体
time_t tv_sec; // 秒数
suseconds_t tv_usec; // 微秒
};
----------------------------------------------------------------------
返回值:
- 成功时返回 0。
- 失败时返回 -1,errno 会设置为相应的错误代码。
*/


信号捕捉

注意:SIGKILL SIGSTOP不能被捕捉,不能被忽略

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
sighandler_t signal(int signum, sighandler_t handler);
/*
功能:
- 设置信号的处理程序。此函数用于指定当接收到指定信号时,应该执行的处理函数。

参数:
- signum:要捕获的信号编号(例如 `SIGINT`, `SIGTERM`)。
- handler:信号处理函数,通常是一个函数指针,指向处理该信号时调用的函数。可以是以下几种类型之一:
- 指向用户自定义的信号处理函数。
- `SIG_DFL`:恢复默认处理方式。
- `SIG_IGN`:忽略该信号。

返回值:
- 成功时返回先前信号的处理函数(即上一个处理函数)。
- 失败时返回 `SIG_ERR`,并且 `errno` 会设置为相应的错误代码。
*/

int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
/*
功能:
- 更强大、更灵活地控制信号的处理方式,替代 `signal` 函数。

参数:
- signum:要捕获的信号编号(例如 `SIGINT`, `SIGTERM`)。
- act:指向 `struct sigaction` 的指针,用于指定信号的处理方式。`sigaction` 结构体包含了有关信号的详细信息。
- oldact:指向 `struct sigaction` 的指针,用于保存先前信号的处理方式。如果不关心旧的信号处理方式,可以将其设置为 `NULL`。

返回值:
- 成功时返回 0。
- 失败时返回 -1,`errno` 会设置为相应的错误代码。
*/

信号集

这些函数用于操作和管理信号集(sigset_t),常用于信号处理的信号屏蔽和管理。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
int sigemptyset(sigset_t *set);
/* 初始化信号集,将其清空。 */
int sigfillset(sigset_t *set);
/* 初始化信号集,将其填充为所有信号。 */
int sigaddset(sigset_t *set, int signum);
/* 将指定信号 signum 添加到信号集 set 中。 */
int sigdelset(sigset_t *set, int signum);
/* 从信号集 set 中移除指定的信号 signum。 */
int sigismember(const sigset_t *set, int signum);
/* 检查指定信号 signum 是否在信号集 set 中。 此处的set为传入参数。 */
// ---------------------------------------------------------------------
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
/* 修改信号屏蔽字 set,并将修改前的信号集保存到 oldset。
这个函数可以用于设置、获取内核的信号集;内核信号集与set进行位操作
how: SIG_BLOCK(mask | set), SIG_UNBLOCK(mask & ~set), SIG_SETMASK(mask = set) */
int sigpending(sigset_t *set);
/* 获取内核中的未决信号集并存储到信号集 set 中。 */

SIGCHLD 信号产生条件

SIGCHLD信号产生的3个条件:
1.子进程结束
2.子进程暂停了
3.子进程继续运行
都会给父进程发送该信号,父进程默认忽略该信号。

共享内存

头文件

#include <sys/ipc.h>

此头文件定义了与 System V IPC(进程间通信) 相关的常量和数据结构。主要功能包括:

  • 定义键值类型和标志
    • key_t:共享内存、消息队列或信号量的键值类型。
    • IPC_CREAT:如果指定键的 IPC 对象不存在,则创建一个新的对象。
    • IPC_EXCL:与 IPC_CREAT 一起使用,确保指定键的 IPC 对象不存在,否则操作失败。
    • IPC_PRIVATE:创建一个仅供调用进程使用的新 IPC 对象。
  • 通用命令(用于控制 IPC 对象)
    • IPC_RMID:删除 IPC 对象。
    • IPC_SET:设置 IPC 对象的权限。
    • IPC_STAT:获取 IPC 对象的状态信息。

#include <sys/shm.h>

此头文件定义了与 System V 共享内存 相关的常量、数据结构和函数。主要内容包括:

  • 共享内存标识符和标志
    • shmget():创建或访问一个共享内存段。
    • shmat():将共享内存附加到调用进程的地址空间。
    • shmdt():将共享内存从调用进程的地址空间分离。
    • shmctl():控制共享内存段(例如删除或查询状态)。
  • 常量定义
    • SHM_RDONLY:共享内存以只读方式附加。
    • SHM_RSHM_W:读取和写入共享内存的权限。
  • 数据结构
    • struct shmid_ds:存储共享内存段的元数据,例如大小、权限和使用情况。

实际实现

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
#include <sys/ipc.h>
#include <sys/shm.h>

int shmget(key_t key, size_t size, int shmflg);
/*
* 功能: 创建或获取一个共享内存段。
* 参数:
* key: 标识共享内存的键值,通过 ftok() 生成或 IPC_PRIVATE。
* size: 共享内存段的大小(字节)。
* shmflg: 标志,用于指定共享内存的权限和行为(如 IPC_CREAT)。
* 返回值: 成功返回共享内存段标识符 (shmid),失败返回 -1。
*/
// 创建一个新的共享内存,并且设置所有使用者的权限为可读可写
int shmid = shmget(key, 1024, 0666 | IPC_CREAT | IPC_EXCL);

void *shmat(int shmid, const void *shmaddr, int shmflg);
/*
* 功能: 将共享内存段附加到调用进程的地址空间。
* 参数:
* shmid: 共享内存段标识符,由 shmget() 返回。
* shmaddr: 指定附加地址(通常设为 NULL 让系统自动选择)。
* shmflg: 附加标志(SHM_RDONLY只读模式;0读写模式)。
* 返回值: 成功返回共享内存段的起始地址,失败返回 (void *) -1。
*/

int shmdt(const void *shmaddr);
/*
* 功能: 将共享内存段从调用进程的地址空间中分离。
* 参数:
* shmaddr: 共享内存段的起始地址,由 shmat() 返回。
* 返回值: 成功返回 0,失败返回 -1。
*/

int shmctl(int shmid, int cmd, struct shmid_ds *buf);
/*
* 功能: 对共享内存段执行各种控制操作。(销毁)
* 参数:
* - shmid: 共享内存的ID
* - cmd : 要做的操作
* - IPC_STAT : 获取共享内存的当前的状态
* - IPC_SET : 设置共享内存的状态
* - IPC_RMID: 标记共享内存被销毁
* - buf:需要设置或者获取的共享内存的属性信息
* - IPC_STAT : buf存储数据
* - IPC_SET : buf中需要初始化数据,设置到内核中
* - IPC_RMID : 没有用,NULL
* 返回值: 成功返回 0,失败返回 -1。
*/
// 被标记为销毁的共享内存不会立即释放,只有当所有附加的进程分离后,内核才会真正释放它。
// 如果创建共享内存的进程退出,但未销毁共享内存,内存段仍会占用系统资源。
struct shmid_ds {
struct ipc_perm shm_perm; // 权限信息
size_t shm_segsz; // 共享内存段的大小(字节)
time_t shm_atime; // 最后一次附加时间
time_t shm_dtime; // 最后一次分离时间
time_t shm_ctime; // 最后一次更改时间
pid_t shm_cpid; // 创建该段的进程 ID
pid_t shm_lpid; // 最后操作该段的进程 ID
shmatt_t shm_nattch; // 当前附加到该段的进程数
};
struct ipc_perm {
uid_t uid; // 拥有者的用户 ID
gid_t gid; // 拥有者的组 ID
mode_t mode; // 访问权限(9 位权限位,类似于文件权限)
// 其他字段与系统实现相关,通常不涉及。
};

key_t ftok(const char *pathname, int proj_id);
/*
* 功能: 生成一个用于共享内存、消息队列或信号量的唯一键值。
* 参数:
* pathname: 指向文件路径名的指针,该文件必须存在。
* proj_id: 项目标识符,通常为一个非零整数。
* 返回值: 成功返回生成的键值,失败返回 -1。
*/

共享内存操作命令

  • ipcs
1
2
3
4
5
# 查看进程间通信资源信息
ipcs -a # 打印当前系统中所有的进程间通信方式的信息
ipcs -m # 打印使用共享内存进行进程间通信的信息
ipcs -q # 打印使用消息队列进行进程间通信的信息
ipcs -s # 打印使用信号进行进程间通信的信息
  • ipcrm
1
2
3
4
5
6
7
# 删除进程间通信资源
ipcrm -M shmkey # 移除用 shmkey 创建的共享内存段
ipcrm -m shmid # 移除用 shmid 标识的共享内存段
ipcrm -Q msgkey # 移除用 msgkey 创建的消息队列
ipcrm -q msqid # 移除用 msqid 标识的消息队列
ipcrm -S semkey # 移除用 semkey 创建的信号
ipcrm -s semid # 移除用 semid 标识的信号

操作系统如何知道一块共享内存被多少个进程关联?

  • 操作系统通过 struct shmid_ds 结构体中的 shm_nattch 成员来记录关联的进程数量。

可以对共享内存进行多次删除吗?

  • 可以。shmctl 标记共享内存为删除状态,但不会立即删除。只有当与共享内存关联的进程数为 0 时,内核才会实际删除它。

共享内存与内存映射的区别

特性 共享内存 内存映射
创建方式 直接创建共享内存 需要磁盘文件(除非是匿名映射)
效率 更高 较低
内存访问 所有进程访问同一块共享内存 每个进程在自己的虚拟地址空间中有独立的内存
数据安全 进程退出共享内存仍存在 进程退出,内存映射区数据消失
系统宕机时,数据丢失 系统宕机时,内存映射区数据仍保存在磁盘文件中
生命周期 进程退出共享内存依然存在 进程退出,内存映射区销毁
进程退出自动取消关联,关机时可销毁 通过标记删除或关机删除内存映射区

Linux多线程

线程管理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, 
void *(*start_routine) (void *), void *arg);
// 创建一个新的线程
// thread: 指向 pthread_t 类型变量的指针,用于存储新线程的 ID
// attr: 线程属性,NULL 表示使用默认属性
// start_routine: 新线程的主函数,线程启动后会执行此函数
// arg: 传递给 start_routine 的参数
// 返回值: 成功返回 0,失败返回错误码

pthread_t pthread_self(void);
// 获取调用线程的线程 ID
// 返回值: 当前线程的 ID

int pthread_equal(pthread_t t1, pthread_t t2);
// 比较两个线程 ID 是否相等
// t1: 第一个线程 ID
// t2: 第二个线程 ID
// 返回值: 相等返回非 0,不相等返回 0

void pthread_exit(void *retval);
// 终止当前线程并返回一个值
// retval: 返回给其他线程的退出状态
// 当主线程退出时,不会影响其他正常运行的线程。

int pthread_join(pthread_t thread, void **retval);
// 等待指定线程终止,和一个已经终止的线程进行连接,回收子线程的资源,这个函数是阻塞函数,调用一次只能回收一个子线程,一般在主线程中使用
// thread: 要等待的线程 ID
// retval: 接收被等待线程的退出状态(可以为 NULL)
// 返回值: 成功返回 0,失败返回错误码

int pthread_detach(pthread_t thread);
// 分离线程,使其资源在终止后自动释放
// 1.不能多次分离,会产生不可预料的行为。
// 2.不能去连接一个已经分离的线程,会报错。
// thread: 要分离的线程 ID
// 返回值: 成功返回 0,失败返回错误码

int pthread_cancel(pthread_t thread);
// 向指定线程发送取消请求
// thread: 要取消的线程 ID
// 返回值: 成功返回 0,失败返回错误码
// 取消某个线程,可以终止某个线程的运行,但是并不是立马终止,而是当子线程执行到一个取消点,线程才会终止。取消点:系统规定好的一些系统调用,我们可以粗略的理解为从用户区到内核区的切换,这个位置称之为取消点。(其实是终止)

线程属性

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
pthread_attr_t
// 线程属性类型,用于存储线程属性信息(如分离状态、栈大小等)。
// 创建线程前可通过此类型对象设置属性。
typedef struct pthread_attr {
int detachstate; // 分离状态:PTHREAD_CREATE_JOINABLE 或 PTHREAD_CREATE_DETACHED
size_t stacksize; // 栈大小(字节)
void *stackaddr; // 栈起始地址
int schedpolicy; // 调度策略(如 SCHED_FIFO, SCHED_RR, SCHED_OTHER)
int schedparam; // 调度参数,通常是线程优先级
int inheritsched; // 是否继承调度属性(如 PTHREAD_INHERIT_SCHED)
int guardsize; // 栈保护区大小
} pthread_attr_t;

int pthread_attr_init(pthread_attr_t *attr);
// 初始化线程属性对象
// attr: 指向要初始化的 pthread_attr_t 对象
// 返回值: 成功返回 0,失败返回错误码

int pthread_attr_destroy(pthread_attr_t *attr);
// 销毁线程属性对象
// attr: 指向要销毁的 pthread_attr_t 对象
// 返回值: 成功返回 0,失败返回错误码

int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
// 获取线程的分离状态
// attr: 指向 pthread_attr_t 对象
// detachstate: 用于接收分离状态的指针
// 返回值: 成功返回 0,失败返回错误码
// 分离状态值:
// - PTHREAD_CREATE_JOINABLE: 可被 join(默认)
// - PTHREAD_CREATE_DETACHED: 分离状态

int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
// 设置线程的分离状态
// attr: 指向 pthread_attr_t 对象
// detachstate: 要设置的分离状态
// 返回值: 成功返回 0,失败返回错误码
// 分离状态值:
// - PTHREAD_CREATE_JOINABLE: 可被 join
// - PTHREAD_CREATE_DETACHED: 分离状态

互斥量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
// 互斥量类型
pthread_mutex_t mutex;
// 用于线程间同步访问共享资源

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
// 初始化一个互斥量
// 参数:
// - mutex: 指向要初始化的互斥量
// - attr: 指向互斥量属性对象的指针,NULL 表示使用默认属性
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如内存不足或参数无效

int pthread_mutex_destroy(pthread_mutex_t *mutex);
// 销毁一个互斥量
// 参数:
// - mutex: 指向要销毁的互斥量
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如互斥量正在被锁定或无效
// 注意: 在销毁之前,必须确保没有线程持有该互斥量

int pthread_mutex_lock(pthread_mutex_t *mutex);
// 加锁操作,阻塞式
// 参数:
// - mutex: 指向要加锁的互斥量
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如互斥量无效
// 行为:
// - 如果互斥量已被其他线程锁定,调用线程会阻塞直到互斥量可用

int pthread_mutex_trylock(pthread_mutex_t *mutex);
// 尝试加锁操作,非阻塞式
// 参数:
// - mutex: 指向要尝试加锁的互斥量
// 返回值:
// - 成功返回 0,表示加锁成功
// - 失败返回错误码,例如 EBUSY 表示互斥量已被其他线程锁定

int pthread_mutex_unlock(pthread_mutex_t *mutex);
// 解锁操作
// 参数:
// - mutex: 指向要解锁的互斥量
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如解锁无效的互斥量或当前线程未持有锁
// 行为:
// - 解锁后,其他等待该互斥量的线程会被唤醒


读写锁

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
#include <stdio.h>
#include <pthread.h>
#include <unistd.h>

// 读写锁类型
pthread_rwlock_t rwlock;
// 用于线程间同步,实现共享资源的读写分离
// 多个线程可以同时读,但同一时刻只有一个线程可以写,且写优先于读。

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr);
// 初始化一个读写锁
// 参数:
// - rwlock: 指向要初始化的读写锁
// - attr: 指向读写锁属性的指针,NULL 表示使用默认属性
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如 EINVAL 表示参数无效,ENOMEM 表示内存不足

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
// 销毁一个读写锁
// 参数:
// - rwlock: 指向要销毁的读写锁
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如锁仍在使用中或无效
// 注意: 在销毁之前,必须确保没有线程持有该锁

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
// 加读锁,阻塞式
// 参数:
// - rwlock: 指向要加读锁的读写锁
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如 EDEADLK 表示死锁,EINVAL 表示锁无效
// 行为:
// - 如果没有线程持有写锁,则加锁成功;否则阻塞直到写锁被释放
// - 允许多个线程同时持有读锁

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
// 尝试加读锁,非阻塞式
// 参数:
// - rwlock: 指向要加读锁的读写锁
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如 EBUSY 表示写锁被其他线程持有
// 行为:
// - 如果写锁被其他线程持有,立即返回 EBUSY

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
// 加写锁,阻塞式
// 参数:
// - rwlock: 指向要加写锁的读写锁
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如 EDEADLK 表示死锁
// 行为:
// - 如果有其他线程持有读锁或写锁,则调用线程会阻塞直到锁被释放
// - 只有一个线程可以持有写锁

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock);
// 尝试加写锁,非阻塞式
// 参数:
// - rwlock: 指向要加写锁的读写锁
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如 EBUSY 表示锁被其他线程持有
// 行为:
// - 如果有其他线程持有读锁或写锁,立即返回 EBUSY

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);
// 解锁
// 参数:
// - rwlock: 指向要解锁的读写锁
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如锁无效或当前线程没有持有锁
// 行为:
// - 释放线程持有的读锁或写锁
// - 如果有其他线程在等待锁,会根据优先级唤醒

条件变量

条件变量的条件指的是被cond所阻塞的线程需要等待的一个相应通知,而不是某个逻辑表达式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
// 条件变量类型
pthread_cond_t cond;
// 条件变量用于线程间的同步,通过等待和通知机制协调线程的执行顺序。
// 通常与互斥量 (pthread_mutex_t) 配合使用。

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
// 初始化条件变量
// 参数:
// - cond: 指向要初始化的条件变量
// - attr: 条件变量属性,NULL 表示使用默认属性
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如 ENOMEM 表示内存不足
// 注意: 在初始化之前,不能使用条件变量

int pthread_cond_destroy(pthread_cond_t *cond);
// 销毁条件变量
// 参数:
// - cond: 指向要销毁的条件变量
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如条件变量仍在使用
// 注意: 必须确保没有线程在等待该条件变量时才能销毁

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex);
// 等待条件变量满足
// 参数:
// - cond: 指向条件变量
// - mutex: 指向互斥量,调用前必须已加锁
// 返回值:
// - 成功返回 0
// - 失败返回错误码,例如 EINVAL 表示无效参数
// 行为:
// - 调用线程会释放互斥量(释放互斥锁),并进入等待状态直到被通知(signal 或 broadcast)
// - 当线程被唤醒时,会重新加锁互斥量以确保资源同步。

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);
// 等待条件变量满足,带超时功能
// 参数:
// - cond: 指向条件变量
// - mutex: 指向互斥量,调用前必须已加锁
// - abstime: 指向绝对时间的结构体(struct timespec),表示超时时间
// 返回值:
// - 成功返回 0
// - 超时返回 ETIMEDOUT
// - 失败返回其他错误码
// 行为:
// - 类似于 pthread_cond_wait,但允许线程在超时时间内未被通知时返回。
struct timespec {
time_t tv_sec; // 秒,类型为 time_t
long tv_nsec; // 纳秒,类型为 long
};

int pthread_cond_signal(pthread_cond_t *cond);
// 通知等待中的一个线程
// 参数:
// - cond: 指向条件变量
// 返回值:
// - 成功返回 0
// - 失败返回错误码
// 行为:
// - 如果有线程在等待条件变量cond,则唤醒其中一个。
// - 如果没有线程等待,调用无任何效果。

int pthread_cond_broadcast(pthread_cond_t *cond);
// 通知等待中的所有线程
// 参数:
// - cond: 指向条件变量
// 返回值:
// - 成功返回 0
// - 失败返回错误码
// 行为:
// - 唤醒等待条件变量的所有线程。

信号量

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
// 信号量类型
sem_t sem;
// 信号量用于线程或进程间的同步和资源计数。
// 信号量的值表示可用资源的数量,线程可以通过增加(post)或减少(wait)信号量来控制资源的访问。

int sem_init(sem_t *sem, int pshared, unsigned int value);
// 初始化信号量
// 参数:
// - sem: 指向要初始化的信号量
// - pshared: 指定信号量的共享类型
// - 0: 信号量在线程间共享
// - 非 0: 信号量在进程间共享(需放置在共享内存中)
// - value: 信号量的初始值(可用资源数)
// 返回值:
// - 成功返回 0
// - 失败返回 -1,并设置 errno,如 ENOSYS 表示不支持进程间信号量

int sem_destroy(sem_t *sem);
// 销毁信号量
// 参数:
// - sem: 指向要销毁的信号量
// 返回值:
// - 成功返回 0
// - 失败返回 -1,并设置 errno
// 注意:
// - 信号量销毁前,必须确保没有线程或进程在使用它。

int sem_wait(sem_t *sem);
// 等待信号量
// 参数:
// - sem: 指向信号量
// 返回值:
// - 成功返回 0
// - 失败返回 -1,并设置 errno
// 行为:
// - 如果信号量值大于 0,立即将其减 1,并返回。
// - 如果信号量值为 0,则阻塞,直到信号量值大于 0。

int sem_trywait(sem_t *sem);
// 尝试等待信号量(非阻塞)
// 参数:
// - sem: 指向信号量
// 返回值:
// - 成功返回 0
// - 失败返回 -1,并设置 errno(EAGAIN 表示当前信号量值为 0)
// 行为:
// - 如果信号量值大于 0,将其减 1 并返回。
// - 如果信号量值为 0,立即返回,而不会阻塞。

int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
// 等待信号量,带超时功能
// 参数:
// - sem: 指向信号量
// - abs_timeout: 绝对超时时间(struct timespec 格式)
// 返回值:
// - 成功返回 0
// - 超时返回 -1,并设置 errno 为 ETIMEDOUT
// - 失败返回其他 -1 错误码
// 行为:
// - 如果信号量值大于 0,将其减 1 并返回。
// - 如果信号量值为 0,则等待,直到信号量值大于 0 或超时时间到达。

int sem_post(sem_t *sem);
// 释放信号量(增加资源)
// 参数:
// - sem: 指向信号量
// 返回值:
// - 成功返回 0
// - 失败返回 -1,并设置 errno
// 行为:
// - 增加信号量的值,并唤醒因 `sem_wait` 阻塞的线程(如果有)。