为什么将malloc()和printf()称为不可重入?

转载自https://mlog.club/article/1807704

在unix系统中,我们知道malloc()是一个不可重入的函数(系统调用)。为什么?
类似地,printf()也被认为是不可重入的;为什么?
我知道重新进入的定义,但我想知道为什么它适用于这些功能。
是什么阻止了它们被保证可重入?

最佳答案:

malloc和printf通常使用全局结构,并在内部使用基于锁的同步。这就是为什么它们不可重入。
malloc函数可以是线程安全的,也可以是线程不安全的。两者都不可重入:
malloc在全局堆上操作,同时发生的两个不同的malloc调用可能返回相同的内存块。(第二个malloc调用应该在获取块的地址之前发生,但块没有标记为不可用)。这违反了malloc的后置条件,因此此实现不会重新进入。
为了防止这种影响,线程安全的malloc实现将使用基于锁的同步。但是,如果从信号处理程序调用malloc,则可能发生以下情况:

malloc();            //initial call
  lock(memory_lock); //acquire lock inside malloc implementation
signal_handler();    //interrupt and process signal
malloc();            //call malloc() inside signal handler
  lock(memory_lock); //try to acquire lock in malloc implementation
  // DEADLOCK!  We wait for release of memory_lock, but 
  // it won't be released because the original malloc call is interrupted

当从不同线程调用malloc时,这种情况不会发生。实际上,可重入性概念超出了线程安全的范畴,还要求函数正常工作,即使其中一个调用从未终止。这就是为什么任何带有锁的函数都不会重新进入的原因。
printf函数也对全局数据进行操作。任何输出流通常都使用一个附加到资源数据的全局缓冲区(用于终端或文件的缓冲区)。打印过程通常是将数据复制到缓冲区,然后刷新缓冲区的序列。这个缓冲区应该像malloc那样被锁保护。因此,printf也是不可重入的。

以下是用链表实现堆栈的代码: ```c #include <stdio.h> #include <stdlib.h> // 定义链表节点结构体 struct Node { int data; struct Node *next; }; // 压栈函数 void push(struct Node **head, int data) { // 创建新节点 struct Node *new_node = (struct Node*) malloc(sizeof(struct Node)); new_node->data = data; new_node->next = NULL; // 如果堆栈为空,则将新节点作为堆栈头节点 if (*head == NULL) { *head = new_node; printf("Push 1st Data:%d\n", data); return; } // 否则将新节点插入到堆栈头部 new_node->next = *head; *head = new_node; printf("Push %dth Data:%d\n", (*head)->data, data); } // 出栈函数 void pop(struct Node **head) { // 如果堆栈为空,则直接返回 if (*head == NULL) { return; } // 否则弹出堆栈顶部节点 struct Node *temp = *head; printf("Pop %dth Data:%d\n", (*head)->data, temp->data); *head = (*head)->next; free(temp); } int main() { struct Node *head = NULL; // 压入5个数据 for (int i = 1; i <= 5; i++) { int data; printf("请输入要压栈的整数:"); scanf("%d", &data); push(&head, data); } // 弹出5个数据 for (int i = 1; i <= 5; i++) { pop(&head); } return 0; } ``` 在上面的代码中,我们使用了链表来实现堆栈数据结构。在 `push` 函数中,我们首先创建一个新节点,并将它插入到链表头部,同时打印出“Push”语句以及数据的编号和值。在 `pop` 函数中,我们首先判断链表是否为空,如果不为空,则弹出链表头部节点,并释放该节点所占的内存,同时打印出“Pop”语句以及数据的编号和值。最后在主函数中,我们先压入5个数据,然后再弹出这5个数据。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值