linux c 基本函数
open
函数原型
1 |
|
open 函数可以打开 pathname 指定的文件,如果这个指定的文件不存在,可以选择创建它(前提是 O_CREAT 在 flags 中指定了),而指定了 O_CREAT 后需要指定第三个参数 mode 。
其中 flags 定义的宏在 fcntl.h 头文件中。
举例:
1 | int fd = open("yuhan.txt", O_RDWR | O_CREAT, 0644); |
该语句打开当前目录下的 yuhan.txt 文件,指定打开的权限是可读可写,如果该文件不存在则创建它,创建该文件的权限为 rw-r–r–。
前面加0,0644代表八进制数。
如果不创建:
1 | int fd = open("yuhan.txt", O_RDWR); |
返回值
该函数返回一个 int 类型的文件描述符(非负整数),搭配 read、write 等函数使用。
成功调用的返回值是当前未被进程打开的最小的可用的文件描述符。
如果失败:
返回 -1 并且设置 errno , errno 是被系统创建的一个用来标识错误的变量,用 perror 输出错误。
其它描述
默认的该函数返回的文件描述符所打开的文件处于始终打开的状态,可以用 close 函数关闭。
参数 flags 必须包含 O_RDONLY、O_WRONLY、O_RDWR 中的一个。
O_TRUNC 覆盖写
比如:
1 | int fd = open("yuhan.txt", O_RDWR | O_CREAT | O_TRUNC , 0644); |
- O_APPEND 追加写
- 创建文件 creat 可以用 open 实现,使用 flags : O_CREAT|O_WRONLY|O_TRUNC
比较重要的大概就这一些。
read
函数原型
1 |
|
read 函数通过文件描述符 fd 读取 count 字节大小的内容到 buf 缓冲区中
举例:
1 | char buf[1024]; |
返回值
调用成功时,返回读取的字节数(为0时,则表示读到了文件结尾)。并且,下次读取文件的位置被偏移了返回值的大小个字节。也就是第二次 read 会从第一次 read 的结尾处继续读取。
如果返回的值小于请求的参数 count , 这不是一个错误,因为剩余可读取的字节数小于 count 的大小是完全有可能的。*或者我们可能在一个管道上读取。其它原因不展开。
调用失败时,返回 -1 ,并且系统会设置 errno 来指示错误。
比如:
1 | n = read(1000, buf, 1024); |
其它描述
关于 count 的大小选取,如果比 SSIZE_MAX 大,结果未定义。
write
函数原型
1 |
|
write 函数从缓冲区 buf 中读取 count 字节的内容写入文件描述符 fd 所指向的文件中。
举例:
1 | int fd = open("yuhan.txt", O_RDWR | O_TRUNC); |
返回值
调用成功时,返回写入的字节数。
调用失败时,返回 -1,并设置 errno 。
注意:
返回的字节数可能小于 count 的大小,可能的原因之一是写入的硬件磁盘的容量不够。
其它描述
暂无
fcntl
函数原型
1 |
|
fcntl 函数用来控制文件描述符
cmd 可以是各种被设置好的宏,当 cmd 表示为不同的宏时 fcntl 具有不同的功能。
复制描述符
F_DUPFD (int类型)
举例:
1 | int fd = open("yuhan.txt", O_RDWR | O_TRUNC); |
此时 fd2 和 fd 所指向的文件相同。
注意第三个参数 0 ,代表返回一个大于等于 0 的可用的文件描述符。
这个函数还不太熟悉。
lseek - 定位读/写文件偏移量
参考: https://man7.org/linux/man-pages/man2/lseek.2.html
函数原型
1 |
|
定位读或者写文件的偏移量。
描述
lseek 函数根据偏移量 offset 和偏移模式 whence 来定位 fd 所关联文件的偏移位置。
whence 参数
SEEK_SET: 从文件开始的位置偏移 offset 个偏移量
SEEK_CUR: 从文件指针当前挪到的位置偏移 offset 个偏移量
SEEK_END: 从文件结尾的位置偏移 offset 个偏移量
offset 可以是负数, 表示向左偏移。
lseek 函数允许偏移量设置在文件的结尾之后,但是这将不会改变文件的大小,只有在偏移之后再写入数据到文件之后,那些偏移了但是还没有填入数据的位置为一个空洞(’\0’)。
返回值
成功时,返回从文件开头到所偏移到的位置一共的偏移量(以字节为单位)。
失败时,返回 -1, 设置 errno。
一些用处
拓展文件大小:
1 | int fd = open("txt", O_RDWR | O_CREAT, 0644); |
此文件大小为 101个字节。
其它方式还有:
ftruncate()
1 |
|
ftruncate(fd, 100);
dup, dup2, dup3 - 复制一个文件描述符
参考: https://man7.org/linux/man-pages/man2/dup.2.html
函数原型
1 |
|
举例:
dup() :
1 | int fd = open("txt", O_RDWR); |
dup2() :
1 | int fd = open("txt", O_RDWR); |
描述
暂无
返回值
成功时,这些系统调用将返回新的文件描述符。
失败时,返回 -1, 设置 errno。
fork - 创建一个子进程
参考:https://man7.org/linux/man-pages/man2/fork.2.html
函数原型
1 |
|
fork函数用来创建一个子进程。
举例:
1 | printf("start fork()\n"); |
描述
fork() 函数通过重复调用进程创建一个新的进程,这个新的进程被称为子进程,调用 fork() 的进程被称为父进程。子进程和父进程在独立的内存空间中运行。在 fork() 时,两块内存空间具有相同的内容。
子进程与父进程完全相同,除了一下几点:
- 子进程有它独特的进程 ID
- 子进程的父进程ID与父进程ID相同。
等等
返回值
成功时,在父进程中,返回子进程的 PID;在子进程中,返回0。
失败时,父进程中返回 -1,并且没有子进程被创建。并且,errno 被设置。
getpid, getppid - 获取进程标识
参考:https://man7.org/linux/man-pages/man2/getpid.2.html
函数原型
1 |
|
描述
getpid() 返回该进程的 ID(PID)。
getppid() 返回该进程的父进程的 ID。
exec系列 - 执行一个文件
参考:https://man7.org/linux/man-pages/man3/exec.3.html
函数原型
1 |
|
可以按照函数名的后缀对其进行分类:
l - execl(), execlp(), execle()
这三个函数有一个共同的参数 const char *arg,这个const char *arg和后面的省略号可以看成 arg0,arg1,arg2 … … argn。这些参数构成了执行程序的参数列表。第一个参数 const char *file/pathname,约定俗成,应该指向正在执行的文件的文件名。
后面的参数列表必须以空指针 NULL 结尾,用来告诉这个可变参数的函数这已经是最后一个参数了。
除此之外,参数列表的第一个参数 arg0 也必须是执行的文件名。拿execlp举例来说,execlp根据环境变量中的路径找到pathname所指向的文件名,然后执行该文件,执行该文件的 argv[0],argv[1],… ,argv[n] 与参数列表中 arg0,arg1,arg2 … … argn 相对应,而argv[0] 正是当前执行的文件名,也就是说,除了第一个参数 const char *file/pathname 要指定文件名,第二个参数 arg0 也需要指定文件名。
举例:
1 | execlp("ls", "ls", "-l", NULL); |
v - execv(), execvp(), execvpe()
e - execle(), execvpe()
p - execlp(), execvp(), execvpe()
描述
exec() 系列函数用一个新的进程映像替换当前进程映像。也就是说当前进程的 PID 和其父进程都没有改变,而该进程所执行的东西已经变为了 exec 所执行的文件。
该系列的函数是在 execve 函数的基础上是实现的。execve 函数参考:https://man7.org/linux/man-pages/man2/execve.2.html
返回值
exec() 系列函数仅在发生错误时返回 -1,并设置 errno。
wait, waitpid, waitid - 等待进程改变状态
参考:https://man7.org/linux/man-pages/man2/waitpid.2.html
函数原型
1 |
|
描述
所有这些 系统调用 都是用来等待正在进行的子进程的状态发生变化的,并且获取到子进程状态发生变化的具体信息。
状态改变 可以被认为是:
- 子进程终止
- 子进程被一个信号中断了
- 子进程被一个信号恢复了
在子进程结束的情况下,父进程执行等待可以有效释放子进程的系统资源,如果没有执行等待,那么结束的子进程将以一个“僵尸”状态继续被保留。
如果一个子进程改变了状态,这些调用将立即返回,否则,它们会阻塞,直到子进程改变状态或信号处理程序中断调用。
wait 和 waitpid
wait() 系统调用 将暂停调用线程的执行,直到它的一个子线程终止。
函数调用 wait(int *wstatus) 等同于:
1 | waitpid(-1, &wstatus, 0); |
waitpid 系统调用 挂起当前调用线程,直到由 pid 参数指定的子进程发生变化。默认情况下,waitpid 只等待终止的子进程,但此行为可以通过修改参数 options 改变。
pid 参数
pid 的值可以是:
- < -1 意思是等待任何*进程组 ID 等于 pid 绝对值的子进程
- -1 意思是任何子进程
- 0 意思是等待任何进程组 ID 等于该调用 waitpid 的进程的进程组 ID 的子进程
- > 0 意思是等待进程 ID 等于 pid 的子进程
options 参数
options 是一个组合值,包含 0 个或多个一下值:
WNOHANG: 如果没有孩子死亡则立即返回
WUNTRACED:
。。。
返回值
pipe, pipe2 - 创建管道(进程间通信)
参考: https://man7.org/linux/man-pages/man2/pipe.2.html
函数原型
1 |
|
举例:
1 | int fd[2]; |
描述
pipe() 创建一个 单向 的可以用于进程间通信的数据通道。 pipefd[] 用来返回两个分别指向管道入端和出端的文件描述符。 pipefd[0] 指的是管道的读端, pipefd[1] 为写端。写入写端的数据会被内核缓冲,直到从读端读取数据,写入的数据只能被读取一次,不能重复读取。
返回值
成功时,返回 0
失败时,返回 -1,设置 errno,并且 pipefd 不会被改变。
mmap, munmap - 映射或取消映射文件或设备到内存中(进程间通信)
参考: https://man7.org/linux/man-pages/man2/mmap.2.html
函数原型
1 |
|
举例:
描述
mmap() 在调用进程的虚拟地址空间中创建一个新的映射。新映射的起始地址子在 addr 中指定。 length 指定映射的大小(必须大于0)。如果 addr 为NULL,那么内核选择(页面对齐的)地址来创建映射。这种方式创建的 mmap() 具有最好的可移植性。如果 addr 不为NULL,那么内核将其作为放置映射的位置的提示。 offset 必须是内存页(page size = 4096)的整数倍,可以通过 sysconf(_SC_PAGE_SIZE) 的返回值查看。
文件映射的内容,初始化时使用从文件描述符 fd 引用的文件(或其他对象)的偏移量 offset 处开始的 length 字节大小的映射空间。
prot 参数
prot 指定映射的内存保护权限。有下面几个宏:
- PROT_EXEC:映射的内存页可以执行
- PROT_READ:映射的内存页可以读
- PROT_WRITE:映射的内存页可以写
- PROT_NONE:映射的内存页不可访问
可以组合,比如 PROT_READ | PROT_WRITE 。
flag 参数
flags 参数确定对映射的更新是否对映射同一区域的其他进程可见,以及是否对底层文件进行更新。
此行为由包含以下值之一确定:
- MAP_SHARED: 分享这个映射。映射的更新对映射同一区域的其它进程是可见的。*并且(在文件支持映射的情况下)将被传递到底层文件。
- MAP_PRIVATE: 创建私有的 写时拷贝 映射。
还能由以下值的零个或多个确定:
- MAP_32BIT
- MAP_ANON
。。。
返回值
mmap() :
成功时,返回一个指向映射区域的指针。
失败时,值 MAP_FAILED (类型为 (void *)-1 ),并设置 errno。
munmap():