Linux系统调用一、系统调用与C库函数的关系 —— 从进程虚拟地址空间和文件描述符的角度分析

简介: Linux系统调用一、系统调用与C库函数的关系 —— 从进程虚拟地址空间和文件描述符的角度分析

当我们在C语言程序中调用一个库函数的时候,比如调用printf()函数,实际上它是通过文件指针来指向要打印的位置的。并且,printf()函数会调用Linux的系统函数write()函数(它是一个系统接口,也可以人工调用),write()函数再继续调用sys_write()函数(这个函数只能是操作系统去调用),sys_write()继续调用设备驱动,具体调用哪个驱动要看输出的位置,如果是printf()打印到显示器上,那么就调用显示器驱动并打印在屏幕上,如果是写到网络上,就会调用网卡驱动。我们所作的只有在C程序中调用printf()等库函数,其余操作都是操作系统帮我们做的。请看下面这张图。

printf()函数在打印的时候通过一个文件指针来实现打印到某个文件的某个位置。在文件在文件指针中,包含了一个文件描述符,这个文件描述符用于指定目标文件,默认情况下就是STDOUT_FILENO也就是标准输出1号描述符;f_pos指定了读写的位置,比如我们打印的时候他会不停的在上一次打印的末尾位置打印后面的内容,就是通过这个位置去实现的;在最后还有一个缓冲区buffer,那么为什么要有buffer缓冲区呢,其实这是为了提高读写的效率,把读写的内容先放到缓冲区,这样就可以实现一次读写更多的内容。并且,这个缓冲区需要刷新才能得到输入输出,我们可以通过下面程序来测试一下。

测试文件01_print_dm.c

/************************************************************
  >File Name  : 01_print_dm.c
  >Author     : QQ
  >Company    : QQ
  >Create Time: 2022年05月12日 星期四 15时28分15秒************************************************************/
#include <stdio.h>
#include <stdlib.h>
#include <fcntl.h>
#include <sys/types.h>
#include <unistd.h>
int main()
{   
    close(1);
    int fd = open("test.log", O_CREAT | O_TRUNC | O_WRONLY, 0644);
    printf("hello ... \n");
    fflush(stdout);
    close(fd);
    return 0;
}

编译文件makefile

.PHONY:all clean
CC=gcc
CFLAGS=-Wall -g
EXE=01_print_dm
all:$(EXE)
%.o:%.c
  $(CC) $(CFLAGS) -c $< -o $@
clean:
  -@rm -f *.o $(EXE)

在这个程序中close(1);表示关闭标准输出,在前面我们已经说过文件描述符1代表标准输出,这时候通过open()打开一个文件,我们知道,当打开一个文件的时候会使用一个当前空闲的最小文件描述符,因为前面我们把标准输出关闭了,所以当前空闲的最小文件描述符1分配给open()函数打开的文件。虽然1号文件描述符当前已经不是标准输出(终端显示屏)了,但是stdout依然是指向1号文件描述符的,实际上这就相当于把open()打开的文件当作标准输入输出,printf()打印的内容都会打印到test.log文件内。首先我们屏蔽fflush()函数试一下

这个test.log文件内是空的,也就是说,如果不刷新缓冲区的话,无法正常打印内容,我们把刷新函数fflush()加上就可以看到,printf()函数打印内容直接打印到test.log文件内了,而不会打印在终端。

实际上,在Linux下启动一个进程,就会默认打开三个文件描述符:0标准输入、1标准输出、2标准错误。它们分别对应C语言中的stdin、stdout、stderr。当我们每次打开一个文件,就会分配给这个文件一个当前空闲的最小文件描述符,如果此时标准输入0、标准输出1、标准错误2空闲,那么也会把这个文件描述符分配给新打开的文件但是这三个文件描述符0、1、2与stdin、stdout、stderr的对象关系不会变,并且在后续的操作中会把0、1、2指向的新文件当作标准输入输出和标准错误去处理,并将输入输出或错误信息打印到这个文件。

相关文章
|
23天前
|
Linux API 开发工具
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
ijkplayer是由B站研发的移动端播放器,基于FFmpeg 3.4,支持Android和iOS。其源码托管于GitHub,截至2024年9月15日,获得了3.24万星标和0.81万分支,尽管已停止更新6年。本文档介绍了如何在Linux环境下编译ijkplayer的so库,以便在较新的开发环境中使用。首先需安装编译工具并调整/tmp分区大小,接着下载并安装Android SDK和NDK,最后下载ijkplayer源码并编译。详细步骤包括环境准备、工具安装及库编译等。更多FFmpeg开发知识可参考相关书籍。
73 0
FFmpeg开发笔记(五十九)Linux编译ijkplayer的Android平台so库
|
2月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
84 6
|
2月前
|
Linux Shell
Linux系统编程:掌握popen函数的使用
记得在使用完 `popen`打开的流后,总是使用 `pclose`来正确关闭它,并回收资源。这种做法符合良好的编程习惯,有助于保持程序的健壮性和稳定性。
123 3
|
2月前
|
Linux
在Linux内核中根据函数指针输出函数名称
在Linux内核中根据函数指针输出函数名称
|
3月前
|
Linux PHP
Linux CentOS 宝塔 Suhosin禁用php5.6版本eval函数详细图文教程
【8月更文挑战第27天】本文介绍两种禁用PHP执行的方法:使用`PHP_diseval_extension`禁用和通过`suhosin`禁用。由于`suhosin`不支持PHP8,仅适用于PHP7及以下版本,若服务器安装了PHP5.6,则需对应安装`suhosin-0.9.38`版本。文章提供了详细的安装步骤,并强调了宝塔环境下与普通环境下的PHP路径差异。安装完成后,在`php.ini`中添加`suhosin.so`扩展并设置`executor.disable_eval = on`以禁用执行功能。最后通过测试代码验证是否成功禁用,并重启`php-fpm`服务生效。
43 2
|
3月前
|
Shell Linux C语言
Linux0.11 execve函数(六)
Linux0.11 execve函数(六)
55 1
|
2月前
|
Unix Linux
linux中在进程之间传递文件描述符的实现方式
linux中在进程之间传递文件描述符的实现方式
|
3月前
|
Linux
Linux0.11 文件打开open函数(五)
Linux0.11 文件打开open函数(五)
44 0
|
4月前
|
运维 关系型数据库 MySQL
掌握taskset:优化你的Linux进程,提升系统性能
在多核处理器成为现代计算标准的今天,运维人员和性能调优人员面临着如何有效利用这些处理能力的挑战。优化进程运行的位置不仅可以提高性能,还能更好地管理和分配系统资源。 其中,taskset命令是一个强大的工具,它允许管理员将进程绑定到特定的CPU核心,减少上下文切换的开销,从而提升整体效率。
掌握taskset:优化你的Linux进程,提升系统性能
|
4月前
|
弹性计算 Linux 区块链
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
156 4
Linux系统CPU异常占用(minerd 、tplink等挖矿进程)
下一篇
无影云桌面