TLPI-Chapter 3系统编程概念

tlpi-exercises:我对 TLPI 练习的解决方案 TLPI 练习 一个包含我对练习的解决方案的存储库。 执照 所有代码都在 MIT 许可下。 立即下载

第三章内容概念讲的其实相对好理解,主要有如下几点:
系统调用是可控的内核入口,进程可以请求内核以自己的名义去执行某些动作,这就用到了系统调用,讲处理器从用户态切换到内核态。
在书中作者用到一个例子X86-32为例,按事件发生顺序:
1.应用程序通过外壳(wapper)函数,发起系统调用
2.参数入栈,传入外壳函数。
3.外壳函数将参数置入特定寄存器(包括系统调用编号)
4执行中断机器指令(int 0x80)。
5.内核响应中断指令,调用system_call()里程处理中断。
如何处理中断呢?
在内核栈保存寄存器的值
审核系统调用编号的有效性
通过编号找到相应的系统调用服务例程,调用时会先检查参数的有效性,然后执行任务。结果状态返回给system_call()例程
从内核栈中恢复寄存器的值,将系统调用返回值置于栈中
返回至外壳函数,切换回用户态
6.若系统调用服务例程的返回值表明调用有误,外壳函数会设置errno为对应的错误码.同时返回一个整型值表明系统调用是否成功。
本章的重点是围绕第6步,作者来处理来自函数库的错误,在阅读这些代码前,必须要学习C语言可变参数相关。ANSI C为了提高可移植性,通过头文件stdarg.h提供了一组方便使用可变长参数的宏。

/*
 * stdarg.h
 *
 * Provides facilities for stepping through a list of function arguments of
 * an unknown number and type.
 *
 * NOTE: Gcc should provide stdarg.h, and I believe their version will work
 *       with crtdll. If necessary I think you can replace this with the GCC
 *       stdarg.h.
 *
 * Note that the type used in va_arg is supposed to match the actual type
 * *after default promotions*. Thus, va_arg (..., short) is not valid.
 *
 * This file is part of the Mingw32 package.
 *
 * Contributors:
 *  Created by Colin Peters <colin@bird.fu.is.saga-u.ac.jp>
 *
 *  THIS SOFTWARE IS NOT COPYRIGHTED
 *
 *  This source code is offered for use in the public domain. You may
 *  use, modify or distribute it freely.
 *
 *  This code is distributed in the hope that it will be useful but
 *  WITHOUT ANY WARRANTY. ALL WARRANTIES, EXPRESS OR IMPLIED ARE HEREBY
 *  DISCLAMED. This includes but is not limited to warranties of
 *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 *
 * $Revision: 1.2 $
 * $Author: noer $
 * $Date: 1998/10/10 00:51:16 $
 *
 */

#ifndef _STDARG_H_
#define _STDARG_H_

/*
 * Don't do any of this stuff for the resource compiler.
 */
#ifndef RC_INVOKED

/* 
 * I was told that Win NT likes this.
 */
#ifndef _VA_LIST_DEFINED
#define _VA_LIST_DEFINED
#endif

#ifndef    _VA_LIST
#define _VA_LIST
typedef char* va_list;
#endif


/*
 * Amount of space required in an argument list (ie. the stack) for an
 * argument of type t.
 */
#define __va_argsiz(t)    \
    (((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))


/*
 * Start variable argument list processing by setting AP to point to the
 * argument after pN.
 */
#ifdef    __GNUC__
/*
 * In GNU the stack is not necessarily arranged very neatly in order to
 * pack shorts and such into a smaller argument list. Fortunately a
 * neatly arranged version is available through the use of __builtin_next_arg.
 */
#define va_start(ap, pN)    \
    ((ap) = ((va_list) __builtin_next_arg(pN)))
#else
/*
 * For a simple minded compiler this should work (it works in GNU too for
 * vararg lists that don't follow shorts and such).
 */
#define va_start(ap, pN)    \
    ((ap) = ((va_list) (&pN) + __va_argsiz(pN)))
#endif


/*
 * End processing of variable argument list. In this case we do nothing.
 */
#define va_end(ap)    ((void)0)


/*
 * Increment ap to the next argument in the list while returing a
 * pointer to what ap pointed to first, which is of type t.
 *
 * We cast to void* and then to t* because this avoids a warning about
 * increasing the alignment requirement.
 */

#define va_arg(ap, t)                    \
     (((ap) = (ap) + __va_argsiz(t)),        \
      *((t*) (void*) ((ap) - __va_argsiz(t))))

#endif /* Not RC_INVOKED */

#endif /* not _STDARG_H_ */

下面我们分别解析每个具体的函数:
看到源文件中typedef char* va_list; 我们可以知道va_list就是char *类型,揭开神秘面纱第一步.
va函数的优势表现在使用的方便性和易用性上,可以使代码更简洁。C编译器为了统一在不同的硬件架构、硬件平台上的实现,和增加代码的可移植性,提供了一系列宏来屏蔽硬件环境不同带来的差异。
ANSI C标准下,va的宏定义在stdarg.h中,它们有:
va_list,va_start(),va_arg(),va_end()。
这里,移动指针使其指向下一个参数,那么移动指针时的偏移量是多少呢,没有具体答案,因为这里涉及到内存对齐(alignment)问题,内存对齐跟具体使用的硬件平台有密切关系,比如大家熟知的32位x86平台规定所有的变量地址必须是4的倍数(sizeof(int) = 4)。va机制中用宏__va_argsiz(_INTSIZEOF(n))来解决这个问题,没有这些宏,va的可移植性无从谈起。注:依据不同glibc版本,对应的宏名字不同。

/*
 * Amount of space required in an argument list (ie. the stack) for an
 * argument of type t.
 */
#define __va_argsiz(t)    \
(((sizeof(t) + sizeof(int) - 1) / sizeof(int)) * sizeof(int))
#define va_start(ap, pN)    \
((ap) = ((va_list) (&pN) + __va_argsiz(pN)))
#endif//第一个可选参数地址
/*
 * Increment ap to the next argument in the list while returing a
 * pointer to what ap pointed to first, which is of type t.
 *
 * We cast to void* and then to t* because this avoids a warning about
 * increasing the alignment requirement.
 */

#define va_arg(ap, t)                    \
(((ap) = (ap) + __va_argsiz(t)),        \
*((t*) (void*) ((ap) - __va_argsiz(t))))

同样注释部分也说明了,我们指向list中的下一个参数,返回list开始指向的参数.
参考如下例子:

/*
    Name: 可变参数
    Copyright: 52coder.net
    Author: 52coder
    Date: 04/09/17 23:44
    Description: 可变参数
*/
#include <stdio.h>
#include <stdarg.h>
void print_args(int count, ...);
int main(int argc, char* argv[]) 
{
    print_args(5,1,2,3,4,5);
    return 0;
}
void print_args(int count, ...) 
{
    int i, value;
    va_list arg_ptr;
    va_start(arg_ptr, count);
    for(i=0; i<count; i++) {
        value = va_arg(arg_ptr,int);
        printf("position %d = %d\n", i+1, value);
    }
    va_end(arg_ptr);
}

参考例子:

/*
    Name: 可变参数
    Copyright: 52coder.net
    Author: 52coder
    Date: 04/09/17 23:44
    Description: 可变参数
*/
#include <stdio.h>
#include <stdarg.h>

double average(int num,...)
{

    va_list valist;
    double sum = 0.0;
    int i;

    /* 为 num 个参数初始化 valist */
    va_start(valist, num);

    /* 访问所有赋给 valist 的参数 */
    for (i = 0; i < num; i++)
    {
        sum += va_arg(valist, int);
    }
    /* 清理为 valist 保留的内存 */
    va_end(valist);

    return sum/num;
}

int main()
{
    printf("Average of 2, 3, 4, 5 = %f\n", average(4, 2,3,4,5));
    printf("Average of 5, 10, 15 = %f\n", average(3, 5,10,15));
}

下面这个例子是征服C指针中的一个例子,我个人认为这个例子非常非常的好。代码如下:

#include <stdio.h>
#include <stdarg.h>
#include <assert.h>
void tiny_printf(char * format,...)
{
    int i;
    va_list ap;
    va_start(ap,format);
    for(i = 0;format[i]!='\0';i++)
    {
        switch(format[i])
        {
            case 's':
                printf("%s ",va_arg(ap,char*));
                break;
            case 'd':
                printf("%d ",va_arg(ap,int));
                break;
            default:
                assert(0);
        }
    }
    va_end(ap);
    putchar('\n');
}
int main()
{
    tiny_printf("sdd","result..",3,5);
    return 0;
}

首先书中从printf入手讲解可变长参数,例如 printf(“%d,%s\n”,100,str);
参数压入栈中,不论有多少个参数,第一个参数(指向”%d,%s\n”的指针)一定存在于距离固定的场所,如果参数不存入栈,按照从左往右的顺序的话,就不能找到第一个参数。
在代码中利用了assert(0),只要程序经过这里就hi报错,因为我们设计的tiny_printf只能处理s和d类型,用户输入不能输入其它类型。

FLACS-CFD 气云爆炸模拟全流程工况构建指南:从泄漏到爆炸 摘要: 本文详细介绍了使用FLACS-CFD模拟气云爆炸的全流程构建方法,重点解决泄漏模拟到爆炸模拟的气云文件传递问题。核心步骤包括: 项目规划:明确泄漏源(甲烷管道腐蚀泄漏)和爆炸场景,建立标准目录结构; 网格设计:泄漏与爆炸模拟需采用兼容的网格,爆炸域应为泄漏域的子区域且网格划分一致; 泄漏模拟配置:通过.inp文件定义几何、网格、风场、燃料及泄漏源参数,确保输出爆炸所需的气云文件(*.uni/*.fla); 关键设置:统一燃料名称、时间点匹配、路径正确性,避免版本兼容性问题。 阅读详情

相关推荐

OLLAMA_HOST环境变量设置全攻略:从127.0.0.1到局域网共享的完整配置

本文详细解析了OLLAMA_HOST环境变量的核心作用与配置方法,指导用户如何将Ollama服务从仅限本机访问(127.0.0.1)扩展到局域网共享。文章涵盖了macOS、Linux、Windows三大平台的实战配置步骤,并提供了安全加固、性能优化及常见故障排查方案,是解决多设备协作访问本地AI模型的完整指南。

weixin_28455801的博客 1894

TLPI UNIX linux系统编程手册源代码运行

Title: TLPI-源代码运行 date: 2017-09-20 23:55 url: TLPI-begin 上周末开始看《UNIX/Linux系统编程手册》这本书,周末的时间看到第四章,觉得这本书比直接看Linux内核要好接受的多,因为从代码入手,可以更好的理解,比如第三章系统调用的讲解,如果只是理论方面可能比较枯燥,那么这本书中的代码,如何运行呢?下载代码这本书的主页.代码版本有两个

尘世中迷途小书童 4917

VINS技术路线与代码详解

VINS技术路线 写在前面:本文整和自己的思路,希望对学习VINS或者VIO的同学有所帮助,如果你觉得文章写的对你的理解有一点帮助,可以推荐给周围的小伙伴们,当然,如果你有任何问题想要交流,欢迎随时探讨。话不多说,下面上正文。 VINS代码地址:https://github.com/HKUST-Aerial-Robotics/VINS-Mono 参考文档:1.VINS-Mono A R...

wangshuailpp的博客 7万+

TLPI(liunx/unix系统编程手册)笔记(二)

第三章 系统编程概念   系统调用       在调用之前有几点必须要掌握,处理器从用户态转变到核心态,以便cpu访问内核内存;每个系统调用都有唯一的数字来标识;可以用一套参数,对用户空间与内核空间传递信息加以规范 。       X86-32为例子。1.app用c语言的wrapper外壳函数发起调用。2.外壳函数必须保证所有的参数可用,使参数复制到特定的寄存器3.(%eax)。4...

283

FA 新增资产API Demo

DECLARE l_trans_rec fa_api_types.trans_rec_type; l_dist_trans_rec fa_api_types.trans_rec_type; l_asset_hdr_rec fa_api_types.asset_hdr_rec_type; l_asset_desc_rec fa_a

chosen1brain的专栏 2142

PostgreSQL+PostGIS实战:5分钟搞定Shapefile数据导入(附避坑指南)

本文详细介绍了如何使用PostgreSQL和PostGIS快速导入Shapefile空间数据,包括5分钟标准流程和常见问题解决方案。通过实战案例和避坑指南,帮助GIS工程师高效处理矢量数据导入中的编码、几何类型、坐标系等典型问题,提升空间数据库管理效率。

weixin_42565652的博客 399

C语言中实现参数个数不确定的函数

C语言中有一种长度不确定的参数,形如:"…",它主要用在参数个数不确定的函数中,我们最容易想到的例子是printf函数。(注意:在C++中有函数重载(overload)可以用来区别不同函数参数的调用,但它还是不能表示任意数量的函数参数。) C语言用va_start等宏来处理这些可变参数。这些宏看起来很复杂,其实原理挺简单,就是根据参数入栈的特点从最靠近第一个可变参数的固定参数开始,依次获取每个可

joshua_cyh的专栏 3715

Unity最强动作战斗框架?Combat System Framework 深度解析

Combat System Framework 并不是一个简单的角色控制器,而是一套完整的动作游戏战斗开发框架。它借鉴了现代 AAA 游戏常见的架构思想,以 Gameplay Tag 为核心,结合 ScriptableObject 数据驱动设计,将技能、属性、Buff、伤害、武器、动画通知等系统有机整合在一起。同时,配套的可视化动画时间轴、Root Motion 工具和实时预览窗口,大幅优化了动作开发流程。当然,这套框架也有一定的学习门槛。

2403_88403568的博客 679

matlab类似于printf的语句,一个移植十分方便的类似printf函数,升级版

/*-----------------------------------------------------------------------------warning:(floatnosupported,thistextbaseonkeil's_print_out)FUNCTION:Printf,Sprintf:intPrintf(constchar*format,....

weixin_42537499的博客 408

基于深度学习的航拍图像中地物变化检测

随着无人机技术的飞速发展,航拍图像成为获取地表信息的重要手段之一。然而,由于地球表面的不断变化,监测和识别航拍图像中的地物变化成为一项具有挑战性的任务。在人工智能领域,深度学习技术的兴起为地物变化检测提供了全新的解决方案。本文将介绍基于深度学习的航拍图像中地物变化检测的方法,并附上相应的代码实例。

一键难忘的博客 4174

AI Agent开发第49课-DIFY让AI自动预约排班后发企业微信通知-MongoDb-Redis-企业微信调用

我们的Dify会讲得很透彻,坚持喂饭级教程,从一个基本的AI自动预约客户到自动记录客户的信息再到自动判断下一步如何走全部是AI化的,这是一个真正的AI原生应用,它来自于我的一个大型物业公司用AI自动预约系统中的一部分。我们会使用这个预约流做完整的Dify例子来讲完,因为它太有代表性了。工作流中的控制反转、AI决策下一步怎么走、人为干涉、用户行为判断、外部中间件如Redis,今天我们就要讲到在Dify中调用MongoDB的数据以及企业微信通知来作为预约派单的最后一步了。

打造全国最全的AI Agent开发知识领域的博客 1752

TLPI-Chapter 11系统限制和选项

关于系统限制C语言标准和SUSv3提供了两种方法: 1.在编译程序时能够获得一些限制和选项。 2.一些限制在程序运行时可能会发生变化。对此SUSv3定义了3个函数sysconf() pathconf()和fpathconf(),供应用程序调用以检查系统实现的限制和选项。 SUSv3将其规定的限制归为三类: 运行时恒定值 路径名变量值 运行时可增加值 在shell中,可以使用getco

尘世中迷途小书童 849

【x265编码器】章节6——x265的码率控制

对于CRF模式而言,右边这部分是是固定值m_rateFactorConstant,在初始化的时候确定,跟分辨率,qCompress这个参数是外部可设参数,默认值0.6,因此QP的选择只跟编码复杂度有关,跟码率无关,编码复杂度越高,QP值越高,因此可以一定程度上说CRF模式是恒定质量的编码模式。其中Complex表示当前帧的编码复杂度,x265中用lookahead模块计算的SATD作为衡量当前帧的复杂度,c为模型参数,qscale是率失真优化中的拉格朗日乘子,R为编码当前帧需要的比特数。

vcvdv123的博客 1万+

TLPI-Chapter 5深入探究文件

原子操作 UNIX/Linux系统编程手册第5章深入探究文件I/O,所有的系统调用都是以原子操作方式执行。 内核保证系统调用中的所有步骤会作为一个独立操作而一次性加以执行,期间不会为其它进程或线程中断. 下面的代码中open并未使用O_EXCL标志,在程序中为了对执行该程序的进程加以区分,打印了进程号。 /****************************************

尘世中迷途小书童 356

OV9734 DATASHEET,完整规格书

OV9734完整规格书,包括电气参数,尺寸规格,寄存器配置表等

老版QQ原生表情gif动态

这是老版的QQ表情,不过全是动态的的,Windows照片查看器是不会动的,可以右击用浏览器打开查看,再说一遍是老版的,只是分享给宁愿用老版也要动态的人。

TLPI-Chapter 20 信号

基本概念信号是事件发生时对进程的通知机制。有时也称为软件中断,信号与硬件中断相似之处在于打断了程序执行的正常流程,大多数情况下,无法预测信号到达的精确时间。 一个具有合适权限的进程能够向另一进程发送信号,信号的这一用法可作为一种同步技术,甚至是进程通信IPC的原始形式。进程也可以向自身发送信号。然而发往进程的诸多信号,通常都是源于内核。 引发内核为进程产生信号的各类事件如下: 硬件发生异常:硬

尘世中迷途小书童 477

jdk-8u144-linux-x64.tar.gz.

该资源为搭建Linux系统Java环境用,资源的后缀为tar.gz格式的压缩包

TLPI-Chapter 6 进程

每个进程都有一个唯一进程标识号(process ID),并保存有对其父进程号的记录。 进程的虚拟内存逻辑上被划分为许多段:文本段、数据段、栈和堆。 栈由一系列帧组成,随函数调用而增长,随函数返回而减,每个帧都包含有函数局部变量、函数实参以及单个函数调用的调用链接信息。 程序调用时命令行参数通过argc argv参数提供给main()函数。通常argv[0]包含调用程序的名称。 每个进程都会获

尘世中迷途小书童 364

LinePhone3.2.7(android端最新版本) sip客户端

SIP客户端For android:LinePhone3.2.7(最新版本),官网地址:http://www.linphone.org/

TLPI-Chapter 10 时间

日历时间Calendat TimeUNIX系统内部对时间的表示均是以自1970年1月1日的零点以来的秒数来度量。日历时间存储与类型time_t的变量中,此类型是由SUSv3定义的整数类型。 系统调用gettimeofday(),可于tv指向的缓冲区中返回日历时间。#include <sys/time.h> int gettimeofday(struct timeval *tv, struct ti

尘世中迷途小书童 360

TLPI-Chapter 4文件IO

所有执行I/O操作的系统调用都以文件描述符(一个非负整数)来指代打开的文件。文件描述符用以表示所有类型的已打开文件,包括管道、FIFO、socket、终端、设备、普通文件。它是一个索引值,指向内核为每一个进程所维护的该进程打开文件的记录表。当程序打开一个现有文件或者创建一个新文件时,内核向进程返回一个文件描述符。每个进程,文件描述符都自成一套。 三种标准的文件描述符: 0 标准输入 1 标准

尘世中迷途小书童 506

TLPI-Chapter 9 进程凭证

这一章开始看的有点懵,在网上搜索到一篇文章有助于理解链接地址Set-User-ID 1.每个进程都有一套用数字表示的用户ID和组ID。具体有实际用户ID(real user ID), 实际组ID(real group ID),有效用户ID(effective user ID),有效组ID(effective group ID),保存的set-user-ID和set-group-ID,文件系统用户I

尘世中迷途小书童 763

TLPI-Chapter 13文件I/O缓冲

函数:void *memalign(size_t alignment, size_t size); The obsolete function memalign() allocates size bytes and returns a pointer to the allocated memory. The memory address will be a multiple of alignment

尘世中迷途小书童 374

对C可变参数宏的一种实现的个人剖析

头文件 : #

郭立东的个人博客 1296

AArch64中va_list/va_start/va_arg/...的实现

一、背景[3] 1. 可变参函数 可变参函数指的是一个可以接受可变个参数的函数, 调用此函数时只有caller知道为此函数传入了多少个参数, 可变参函数callee只知道caller最少传入了多少个参数: callee中可以确定的参数称为命名参数(Named arguments) callee中不确定的参数称为匿名参数(Anonymous arguments) 对于可变参函数(callee), 其需要: 在其函数栈中预留空间存储所有可能的匿名寄存器参数(否则由于不确定传入了多少个寄存器参数,不.

ashimida 4171

关于不定参数

最近在编写不定参数函数的时候发现:  在turbo c中编译可以通过但是在c-free中却不能编译,究其因,才知:  在turbo c中的stdarg.h中是这样的:  typedef void *va_list;  #define va_start(ap, parmN) (ap = ...)/*在这里...是一个参数栈地址*/  #define va_arg(ap, type) (

三叶草的博客 810

shell脚本判断文件是否存在

有一个需求是判断某一个目录下文件是否存在,如果该文件不存在,则从当前目录拷贝文件到该目录。 由于没有学习写过shell脚本,晚上下班回来搜索后写出了如下的脚本,这里shell的if语法格式与C语言大致相同,语法格式: if [ 条件 ]; then 条件成立 else 条件不成立 fi fi是结束if语句。version1.0#!/bin/sh myFile="/

尘世中迷途小书童 4万+

Sublime Text 3 for Mac 使用心得

本文是使用和学习Sublime Text 3 for Mac过程中的体会与心得,欢迎补充。 安装Package Control 安装过程: 使用快捷键 control + ` 或者菜单栏选择View > Show Console 安装Package Control参考官方页面. Sublime Text3在控制台输入 import urllib.request,os,hashlib; h

尘世中迷途小书童 2万+

Linux命令行大全总结(一)

阅读Linux/UNIX系统编程闲暇之余阅读Linux命令行大全这本书,想起当初毕业同学送给我时的场景,书很新,新到什么程度呢,我说这书时昨天刚买的都没毛病。 本篇博客只记录一些印象不太深刻的内容,相对较简单或对我来说比较熟练的,不再记录. shell是一个接受由键盘输入的命令,并将其传递给操作系统来执行的程序。bash是Bourne Again Shell的首字母缩写,Bourne Again

尘世中迷途小书童 1万+
上一篇: TLPI UNIX linux系统编程手册源代码运行
下一篇: GCC编译链接多文件
52coder
博客等级 码龄17年 87粉丝 128原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

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

抵扣说明:

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

余额充值