本文仅作为学习记录,非商业用途,侵删,如需转载需作者同意。
Linux中创建一个进程需要一个老的进程调用fork这个系统调用来实现,老的进程叫父进程(parent process),新的进程叫子进程(child process)。
先拷贝,再修改。
父进程调用fork创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝一份,对于fork系统调用的返回值,如果当前进程是子进程就返回0;如果当前进程是父进程,就返回子进程的进程号。
根据返回值的不同,做不同的事情,父进程继续做,子进程请求另一个系统调用execve来执行另一个程序。
系统调用waitpid 父进程可以通过它来获取子进程的状态。
内存空间中,存放程序代码的称为代码段。
存放运行中产生的数据被称为 数据段:
局部变量的部分,在当前函数执行的起作用,进入另一个函数时这个变量就释放了;
也有动态分配的,存较长时间,指明才销毁的,叫堆。(heap)
进程要去使用部分部分内存时,才会使用内存管理系统用来登记,说需要部分内存使用。
只有真正写入数据时,发现没有对应的物理内存,才会触发一个中断,先分配物理内存。
内存分配
brk 和 mmap :2个堆里面分配内存的系统调用
分配的内存数量比较小的时候,使用brk,会和之前的堆的数据在一起,就像多分配了两三个工位,多搬几把椅子就可以了。
分配内存比较大的时候,使用mmap,会重新划分一块区域。当办公空间需要太多的时候,索性来个一整块。
文件管理:
- 已有的文件,使用open打开这个文件,close关闭这个文件
- 没有的文件,creat创建这个文件
- 打开文件以后,lseek跳到文件的某个位置
- 对文件的内容进行读写,read和write
Linux 中一切皆文件:
- 启动一个进程,需要一个程序文件,是个二进制文件;
- 启动的时候加载一些配置文件,是文本文件;
- 把日志打印到控制台上,在命令行上唰唰唰的打印出来,这其实也是一个文件,标准输出stdout文件;
- 这个进程的输出可以作为另一个进程的输入,这种方式称为管道,管道也是一个文件;
- 进程可以通过网络和其他进程通信,建立的socket也是一个文件;
- 进程需要访问外部设备,设备也是一个文件;
- 文件都被存储在文件夹里,文件夹也是一个文件;
- 想看到运行中的进程的情况,/proc 下面有对应的进程号,还是一系列文件。
每个文件,Linux都会分配一个文件描述符(File Descriptor)这是一个整数,有了文件描述符,就可以使用系统调用,查看或者干预进程运行的方方面面。
项目异常处理与信号处理
当项目遇到异常情况,例如项目中断,需要发送一个信号(signal)给项目组,经常遇到的信号有以下几种:
- “CTRL + C” 是中断信号,正在执行的命令就会中止退出;
- 用户进程通过kill 函数,将一个用户信号发送给另一个进程;
重要的信号不能忽略,不重要的信号可以忽略。
SIGKILL (用户终止一个进程的信号)和SIGSTOP(用户中止一个进程的信号)。
每种信号都定义了默认的动作,例如硬件故障,默认终止。也可以提供信号处理函数,可以通过sigaction系统调用,注册一个信号处理函数。
提供了信号处理服务,项目执行过程中一旦有变动,就可以及时处理了。
项目组间沟通与进程间通信
当项目比较大的时候,会分成多个项目组,项目组间需要沟通,沟通的方式有很多种:
1、就是发个消息不需要一段很长的数据,这种方式称为消息队列(Message Queue)。 一个公司的多个项目组沟通时,这个消息在内核里,可以通过msgget创建一个新的队列,msgsend将消息发送到消息队列,接收方使用msgrcv从队列中取消息。
2、当2个项目组需要交互的信息比较大的时候,可以使用共享内存的方式,即2个项目组共享一个会议室(这样就不需要数据拷贝来拷贝去),大家都到这个会议室就可以完成沟通了。
这个时候可以通过shmget 创建一个共享内存块,通过shmat将共享内存映射到z自己的内存空间,然后就可以读写了。
但是如果同时修改一块数据咋办,这就需要一种方式能够排他的访问,这就是信号量机制 Semaphore.
对于只允许一个人访问的场景,设置信号量为1。
当A访问的时候先调用 sem_wait ,如果没人访问,则占用这个信号量,他就是可以开发访问了,如果这个时候B 也访问,也会调用 sem_wait 由于前一个人已经占用了1个信号量,必须等信号量释放了才能访问。
A 使用结束后调用sem_post 将信号量释放,这个时候B 就可以访问了。
公司间沟通与网络通信
跨机器之间的沟通,就需要网络通信了。都需要遵循相同的网络协议,TCP/IP 网络协议栈。Linux 有对网络协议栈的实现,如果暴露出服务给项目组使用呢。
网络服务是通过套接字socket(插口,插槽)来提供服务的。
想象成一根网线,一头插在客户端,一头插在服务端然后进行通信。因此在通信之前,双方要建立一个socket。
我们可以通过Socket 系统调用建立一个 socket,Socket也是一个文件,也有一个文件描述符,也可以通过读写函数进行通信。
中介与Glibc
上面说的和平时的开发都不太一样,因为工作中没有直接调用系统调用。
为了对用户友好,可以使用中介 Glibc ,它会帮你转成系统调用。
Glibc 是linux 下使用的开源的标准C库,它是GNU 发布的libc 库。它为程序员提供了丰富的API,例如字符串处理,数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。
Glibc和系统调用的对应关系是灵活的。
- 系统调用sys_open 对应Glibc 中的open函数
- Glibc提供的printf 函数就会调用sys_open、sys_mmap、sys_write、sys_close等等系统调用
- Glibc 下实现的malloc,calloc,free 函数用来分配和释放内存,都利用了内核的sys_brk 的系统调用。
strace 常用来跟踪进程执行时系统调用和所接收的信号。