1. sys_brk
函数原型
/*
* sys_brk()是一个操作系统调用,用于更改进程的堆空间大小。
* sys_brk()函数接收一个无符号长整型参数brk,表示要求的新的程序数据段(堆)结束地址。
* 如果brk的值大于当前的程序数据段结束地址,则增加堆的大小;如果brk的值小于当前的程序数据段结束地址,则缩小堆的大小。
* 函数返回值为long类型,表示数据段结束地址。如果分配失败,则返回-1。
*/
asmlinkage long sys_brk(unsigned long brk);
示例代码
// /*================================================================
// * Copyright (C) 2023 baichao All rights reserved.
// *
// * 文件名称:sys_brk_demo.c
// * 创 建 者:baichao
// * 创建日期:2023年08月01日
// * 描 述:
// *
// ================================================================*/
#include <unistd.h>
#include <stdio.h>
#include <sys/syscall.h> // 增加此头文件
int main()
{
void *cur_brk;
// 获取当前程序数据段结束地址
long ret = syscall(__NR_brk, 0);
cur_brk = (void *)ret;
printf("start, curr data sec end pos: %p\n", cur_brk);
// 增加堆的大小
long delta = 1024;
ret = syscall(__NR_brk, cur_brk + delta); // 堆增长delta
cur_brk = (void *)ret;
printf("add, curr data sec end pos: %p\n", cur_brk);
// 减小堆的大小
delta = -512;
ret = syscall(__NR_brk, cur_brk + delta); // 堆缩小delta
cur_brk = (void *)ret;
printf("sub, curr data sec end pos: %p\n", cur_brk);
// 获取当前程序数据段结束地址
ret = syscall(__NR_brk, 0);
cur_brk = (void *)ret;
printf("end, curr data sec end pos: %p\n", cur_brk);
return 0;
}
执行结果如下:

需要注意的是,在使用sys_brk()函数时必须特别小心,因为它可以轻松地破坏程序的内存布局。通常情况下,建议使用malloc()等库函数来进行内存分配,以避免出现问题。
linux c有一个对于sys_brk的系统调用的封装函数sbrk:
// __delta如果为正,当前程序数据段结束地址在现有的地址上增加delta,即堆增长|delta|
// __delta如果为负,当前程序数据段结束地址在现有的地址上减少delta;即堆减小|delta|
// 返回值:返回增加前的堆的尾地址。
void *sbrk (intptr_t __delta) __THROW;
sbrk 函数用于扩展或缩小进程的堆大小,即为程序动态分配内存提供支持。它可以接受一个整数参数 increment,表示需要增加或减少的字节数。如果 increment 为正数,则将堆大小增加相应的字节数;如果为负数,则将堆大小减小相应的字节数。
示例代码如下:
/*================================================================
* Copyright (C) 2023 baichao All rights reserved.
*
* 文件名称:sbrk.c
* 创 建 者:baichao
* 创建日期:2023年08月01日
* 描 述:
*
================================================================*/
#include <unistd.h>
#include <stdio.h>
int main() {
void *cur_brk;
// 获取当前程序数据段结束地址
cur_brk = sbrk(0);
printf("start, curr data sec end pos: %p\n", cur_brk);
// 增加堆的大小
long delta = 100*1024*1024;
cur_brk = sbrk(delta); // 堆增长delta, 此时的返回值cur_brk是增加前的地址
printf("add1, curr data sec end pos: %p\n", cur_brk);
cur_brk = sbrk(0);
printf("add2, curr data sec end pos: %p\n", cur_brk);
// 减小堆的大小
delta = 50*1024*1024;
cur_brk = sbrk(delta); // 堆缩小delta,同理,此时的返回值cur_brk是减小前的地址
printf("sub1, curr data sec end pos: %p\n", cur_brk);
cur_brk = sbrk(0);
printf("sub2, curr data sec end pos: %p\n", cur_brk);
return 0;
}
运行结果如下:

2. dup2
函数原型
int dup2(int oldfd, int newfd);
dup2系统调用创建一个新的文件描述符,这个新的文件描述符与指定的旧文件描述符指向同一个文件。这意味着新的文件描述符可以被用来进行读写操作,而这些操作会影响到与旧文件描述符相同的文件状态和文件偏移量。
其中,oldfd是已经打开的文件描述符,newfd是新文件描述符的起始值。如果newfd已经打开,则它会被关闭并重新打开,以便可以重新使用这个文件描述符号。
当dup2成功执行时,它会返回新的文件描述符,这个文件描述符与oldfd共享相同的文件状态和文件偏移量。如果oldfd不是一个有效的文件描述符,dup2会失败,并且newfd不会被关闭。如果oldfd和newfd的值相同,那么dup2实际上不会执行任何操作,只是简单地返回newfd。
示例程序
/*================================================================
* Copyright (C) 2024 baichao All rights reserved.
*
* 文件名称:pipe_dup2_std.c
* 创 建 者:baichao
* 创建日期:2024年04月06日
* 描 述:模拟shell管道通信。核心原理就是利用dup2系统调用复制文件描述符。
简单来说,就是第一个子进程的标准输出被绑定到了管道的写入端,于是第一个命令的输出,写入了管道。
而第二个子进程管道将其标准输入绑定到管道的读取端,只要管道里面有了内容,这些内容就成了标准输入。
*
================================================================*/
#include <errno.h>
#include <fcntl.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/wait.h>
#include <unistd.h>
#define READ_END 0
#define WRITE_END 1
int main() {
int pipefd[2];
pid_t pid1, pid2;
// 创建管道
if (pipe(pipefd) < 0) {
perror("pipe");
exit(EXIT_FAILURE);
}
// 创建第一个子进程
pid1 = fork();
if (pid1 < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid1 == 0) { // 子进程1
// 关闭管道的读端
close(pipefd[READ_END]);
// 将标准输出重定向到管道的写端
dup2(pipefd[WRITE_END], STDOUT_FILENO);
// 关闭管道的旧的写端
close(pipefd[WRITE_END]);
// 子进程1写入数据到标准输出(现在关联到管道的写端)
printf("子进程1写入的数据\n");
exit(EXIT_SUCCESS);
}
// 创建第二个子进程
pid2 = fork();
if (pid2 < 0) {
perror("fork");
exit(EXIT_FAILURE);
} else if (pid2 == 0) { // 子进程2
// 关闭管道的写端
close(pipefd[WRITE_END]);
// 将标准输入重定向到管道的读端
dup2(pipefd[READ_END], STDIN_FILENO);
// 关闭管道的旧的读端
close(pipefd[READ_END]);
// 子进程2从标准输入(现在关联到管道的读端)读取数据
for (int i = 0; i < 10; i++) {
char buffer[1024];
ssize_t bytes_read = read(STDIN_FILENO, buffer, sizeof(buffer) - 1);
if (bytes_read > 0) {
buffer[bytes_read] = '\0'; // 确保字符串以null字符结尾
printf("子进程2读取到的数据: %s\n", buffer);
break;
}
printf("bytes_read:%d,error:%s\n\n", bytes_read, strerror(errno));
sleep(1);
}
exit(EXIT_SUCCESS);
}
// 父进程关闭管道两端
close(pipefd[READ_END]);
close(pipefd[WRITE_END]);
// 等待子进程结束
wait(NULL);
wait(NULL);
return 0;
}
运行结果:


本文详细介绍了Linux系统调用中的sys_brk函数,用于改变进程堆大小,以及dup2函数,用于复制文件描述符。还提供了示例代码和运行结果,展示了如何使用这些功能进行内存管理和管道通信。
1446

被折叠的 条评论
为什么被折叠?



