Linux编程基础

linux程序的执行过程

linux程序涉及的过程包括预处理、编译、汇编、和链接等步骤;
1、预编译 用于处理头文件,处理一些预处理命令;
如#include。它的作用是将头文件的内容包含到本文件
中。gcc -e
2、编译环节是指对源代码进行语法分析,并优化产生对应的汇编代码的过程。同样,可以使用gcc得到汇编代码,而非最终的二进制文件。gcc -s
3、汇编就是将源代码翻译成可执行的指令,并生成目标文件。gcc -c
4、链接是生成最终可执行程序的最后一个步骤,也是比较复杂的一步。它的工作就是将各个目标文件——包括库文件(库文件也是一种目标文件)链接成一个可执
行程序。
在这里插入图片描述

系统调用

系统调用是操作系统提供的服务,是应用程序与内核通信的接口。
用户空间的程序默认是通过栈来传递参数的。对于系统调用来说,内核态和用户态使用的是不同的栈,这使得系统调用的参数只能通过寄存器的方式进行传递。
在写代码的时候,程序员根本不用关心参数
是如何传递的,编译器做了压栈、出栈、保存返回地址
等操作,但是编译器如何知道调用的函数是普通函数,还是系统调用?

C库函数

Linux环境下,使用的C库一般都是glibc,它封装了几乎所有的系统调用,代码中使用的“系统调用”,实际
上就是调用C库中的函数。C库函数同样位于用户态,所以编译器可以统一处理所有的函数调用,而不用区分该函数到底是不是系统调用。

线程安全

线程安全是指代码可以在多线程环境下“安全”地执行,即符合正确的逻辑结果,是程序员期望的正常执行结果。为了实现线程安全,该代码要么只能使用局部变量或资源,要么就是利用锁等同步机制,来实现全局变量或资源的串行访问。

原子性

原子被认为是物理组成的最小单元,所以在计算机领域,就借其不可分割
的这层含义作为隐喻。对于计算机科学来说,如果变量是原子的,那么对这个变量
的任何访问和更改都是原子的。如果操作是原子的,那么这个操作将是不可分割
的,要么成功,要么失败,不会有任何的中间状态。

可重入函数

可重入就是可重复进入。在编程领域,它不仅仅意味着可以重复进入,还要求在进入后能成功执行。这里的重复进入,是指当前进程已经处于该函数中,这时程序会允许当前进程的某个执行流程再次进入该函数,而不会引发问题。这里的执行流程不仅仅包括多线程,还包括信号处理、longjump等执行流程。所以,可重入函数一定是线程安全的,而线程安全函数则不一定是可重入函数。
从以上定义来看,很难说出哪些函数是可重入函数,但是可以很明显看出哪些函数是不可以重入的函数。当函数使用锁的时候,尤其是互斥锁的时候,该函数是
不可重入的,否则会造成死锁。若函数使用了静态变量,并且其工作依赖于这个静态变量时,该函数也是不可重入的,否则会造成该函数工作不正常。

阻塞与非阻塞

阻塞与非阻塞,都是指I/O操作。在Linux环境下,所有的I/O系统调用默认都是阻塞的。

阻塞的系统调用是指,当进行系统调用时,除非
出错(被信号打断也视为出错),进程将会一直陷入内核态直到调用完成。

非阻塞的系统调用是指无论I/O操作成功与否,调用都会立刻返回。

同步与非同步

这里的同步与非同步,也是指I/O操作。

同步既可以是阻塞的,也可以是非阻塞的,而常用的Linux的I/O调用实际上都是同步的。这里的同步和非同步,是指I/O数据的复制工作是否同步执行。
同步的操作,即I/O的完成是在当前执行流程下同步完成的。

如果是非同步即异步,则I/O操作不是随系统调用同步完成的。调用返回后,I/O操作并没有完成,而是由操作系统或者某个线程负责真正的I/O操作,等完成后通知原来的线程。

程序的构成

linux下二进制可执行程序文件格式为ELF格式,通过readelf可以查看其Elf格式;
在这里插入图片描述

ELF文件的主要内容就是由
各个section及symbol表组成的。在上面的section列表中,
text段为代码段,用于保存可执行指令。data段为数据段,用于保存有非0初始值的全局变量和静态变量。bss段用于保存没有初始值或初值为0的全局变量和静态变量,当程序加载时,bss段中的变量会被初始化为0。

程序执行顺序

通过strace追踪系统调用
在这里插入图片描述
在Linux环境中,执行一个命令时,首先是
由shell调用fork,然后在子进程中来真正执行这个命令。strace是main开始执行后的输出。首先是调用execve来加载main,然后ld会分别检查ld.so.nohwcap和ld.so.preload。其中,如果
ld.so.nohwcap存在,则ld会加载其中未优化版本的库。如果ld.so.preload存在,则ld
会加载其中的库——在一些项目中,我们需要拦截或替换系统调用或C库,此时就
会利用这个机制,使用LD_PRELOAD来实现。之后利用mmap将ld.so.cache映射到
内存中,ld.so.cache中保存了库的路径,这样就完成了所有的准备工作。接着ld加
载c库——libc.so.6,利用mmap及mprotect设置程序的各个内存区域,程序运行的环境已经完成。后面的write会向文件描述符1(即标准输出)输出"Hello
world!\n",返回值为13,它表示write成功的字符个数。最后调用exit_group退出程序,此时参数为0,表示程序退出的状态——此例中程序返回0。

参考资料:linux环境编程

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值