我们在调用系统程序的时候,系统如何操作的?这就是这节的主要讲解
系统调用是整个操作系统的灵魂,决定了这个操作系统好不好使,功能全不全,就好比一个办事大厅,能够提供的服务数量决定了公司会被打五星还是差评
1.对于项目的启动,需要一个立项的工作,对应到Linux系统中就是创建一个进程
创建进程的调用是fork,中文是分支
在Linux中,如果需要创建一个新的进程,就需要在一个老的进程的基础上调用fork,老的进程叫做父进程,新的进程叫做子进程
在Linux上如何创建的?
常见的方式是我们列一个新进程需要的配置清单,然后每次有新的项目,就重新配置一遍,
或者我们拷贝别人的,根据自己的情况,讲相应的配置更改
Linux采用的第二种模型,就是父进程调用fork创建进程的时候,讲自己的数据结构拷贝了一份给子进程
然后利用fork函数的返回值,如果返回为0,就是子进程,自己调用execve来执行另一个程序,如果是父进程,函数返回子进程的进程号,就继续执行
这正符合fork这个函数名称的含义
那么所有的进程的父进程是哪一个呢?
作为一个外包公司的老板,有了新的项目会分给手下做,但是一开始没有下属的时候,只好自己上了
所以操作系统在启动的时候,创建一个所有进程的祖宗进程
父进程还可以利用其他函数干预子进程的状态,比如系统调用waitpid,父进程可以调用这种函数,获取子进程的状态
2.对于一个项目,系统层面还需要管理对应的内存
每个进程都有着自己的内存,互相不干扰,有着独立的进程内存空间
一个进程需要将自己的程序代码放入内存空间中,存放程序代码的空间,称为代码段
还有着就是常见的数据段,对应到JVM中就是常见的堆空间
对于常见的内存空间
分为32G和64G的内存空间,那么如何进行分配的呢?
进程是预先申请空间,但是并不是立刻分配,而是等待使用的时候才会分配物理内存
在Linux中,可以申请分配内存的调用函数有 brk和mmap
需要的内存小的时候,使用brk,讲新分配的内存和之前的堆连在一起,分配的内存数量大的时候,使用mmap,重新划分一片区域
3.对于程序产生的文件的管理工作,需要放在文件系统
对于文件的操作,我们常见的调用有 open一个文件 close一个文件,创建create一个文件 lseek跳到某个文件的位置
文件的内容进行读写,读的系统调用是read,写的是write
在Linux中,万物皆文件体现到系统中,启动一个进程,需要一个程序文件,即为二进制文件
启动的时候,需要加载配置文件,或者打印一些日志,即为文本文件
管道符也是一个文件
Socket通信也是一个文件
设备本质上也是一个文件
文件夹也是一个文件
进程号的信息也是文件,保存在/proc下
对于每个文件,都会有一个文件描述符,是一个整数,方便系统调用使用
4.项目的异常信号处理
对于一个程序,需要考虑接受信号的处理问题
一个程序执行的时候,如果在键盘输入CTRL+C,就是一个中断信号
同理kill函数也是如此
收到一个信号,如果不太严重,可以直接忽略,该干啥干啥,但是SIGKILL 终止进程 SIGSTOP 中止进程是不能忽略的,如果不自己实现处理函数,会有默认动作工作
5.项目组之间通信
项目比较大的时候,可能需要分为多个项目组,不同项目组需要彼此交流,相互配合
配合的方式常见的有消息队列,利用msgget在内存中创建一个消息队列,并msgsnd发送消息,msgrcv取消息
如果交互的信息频繁且大的时候,可能使用共享内存,对比现实生活就是多个项目组共用一个会议室
利用shmget创建一个共享内存,利用shmat将共享内存映射到自己的内存空间,进行读写
对于可能出现的并发问题,采用传统的信号量机制即可,也就是传统的Semaphore
对于只允许一个访问,就讲信号量设置为1,开始访问,调用sem_wait,没人访问,占用这个信号量
下一个尝试访问的时候,会发现信号量为0了,就无法进行访问资源了
6.公司之间的通信
就是网络通信,利用TCP/IP进行网络通信
网络通信通过Socket套接字提供的,Socket在Linux上是一个文件,具有对应的文件描述符,可以通过读写函数进行通信
7.中介
对于最后一点,我们想讲一下系统调用的中介Glibc,会将用户的操作转为系统调用
Glibc为程序员提供了丰富的API,除了字符串的处理,数学运算等用户态的服务之外,最重要的封装了系统提供的原生服务,
每个系统对应了至少一个的Glibc的库函数
Glibc提供的printf,调用了系统的sys_open sys_mmap sys_write sys_close
本章我们将其总结为如下: