系统调用
进程管理
在Linux中,创建进程的系统调用叫fork,其中老的进程叫做父进程,新的进程叫做子进程。当父进程调用fork创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝了一份,甚至连程序代码也是拷贝过来的。按理说,如果不进行特殊的处理,父进程和子进程都按照相同的程序代码进行下去,这样也就没有意义了。
所以,我们往往会这样处理:对于fork系统调用的返回值,如果当前进程是子进程,就返回0;如果当前进程是父进程,就返回子进程的进程号。这样首先在返回值这里就有了一个区分,然后通过if-else语句判断,如果是父进程,还接着做原来应该做的使其;如果是子进程,需要请求另一个系统调用execve来执行另一个程序,这个时候,子进程和父进程就彻底分道扬镳,也即产生了一个分支(fork)了。
对于操作系统来说,启动的时候会先创建一个所有用户进程的“祖宗进程”。
有时候,我们需要通过父进程查看子进程的运行情况,我们可以通过父进程调用waitpid,将子进程的进程号作为参数传递给它,这样父进程就知道子进程有没有运行完毕。
内存管理
在操作系统中,每个进程都有自己的内存,互相之间不干扰,有独立的进程内存空间。
在内存空间中,我们会将程序(代码段),数据(数据段),堆放入进程的内存空间中。
但是一个进程的内存空间很大,我们不可能有那么多物理内存,所以一定是按需分配的,只有进程要去使用部分内存的时候,才会使用内存管理的系统调用来登记,希望分配一部分内存给它,但这还不代表真的就对应到了物理内存。只有真的写入数据但时候,发现没有对应对物理内存,才会出发一个终端,实现分配物理内存。
在这里,我们介绍两个在堆里面分配内存对系统调用,brk和mmap。
当分配的内存数量比较小的时候,使用brk,会和原来的堆的数据连在一起,这就像多分配两三个工位,在原来的区域旁边搬两把椅子就行了。当分配的内存数量比较大的时候,使用mmap,会重新划分一块区域,也就是说,当办公空间需要太多的时候,索性来个一整块。
文件管理
程序,文档,照片等,需要放在文件系统中进行保存,之所以能不会丢失,一方面是因为介质,另一方面也是因为格式。
对于文件的操作,下面六个系统调用是最重要的:
- 对于已经有的文件,可以使用open打开这个文件,close关闭这个文件;
- 对于没有的文件,可以使用creat创建文件;
- 打开文件以后,可以使用lseek跳到文件的某个位置;
- 可以对文件的内容进行读写,读的系统调用是read,写是write。
在Linux中,一切皆文件。
- 启动一个进程,需要一个程序文件,这是一个二进制文件。
- 启动的时候,要加载一些配置文件,例如yml,properties等,这是文本文件;启动之后会打印一些日志,如果写到硬盘上,也是文本文件。
- 但是如果我想把日志打印到交互控制台上,在命令行打印出来,这其实也是一个文件,是标准输出stdout文件。
- 这个进程的输出可以作为另一个进程的输入,这种方式称为管道,管道也是一个文件。
- 进程间的网络通信,建立的Socket,也是一个文件。
- 进程需要访问外部设备,设备也是一个文件。
- 文件都被存储在文件夹中,其实文件夹也是一个文件。
- 进程运行起来,要想看到进程运行的情况,会在/proc下面有对应的进程号,还是一系列文件。
每个文件,Linux都会分配一个文件描述符,这是一个整数,有了这个文件描述符,我们就可以使用系统调用,查看或者干预进程运行的方方面面。
所以说,文件操作是贯穿始终的,这也是“一切皆文件”的优势,就是统一了操作的入口,提供了极大的便利。
信号处理
在Linux中,我们经常遇到一些信号:
- 在执行一个程序的时候,在键盘输入“CTRL+C”,这就是中断的信号,正在执行的命令就会中止退出;
- 如果非法访问内存,会出现一些异常;
- 硬件故障
- 用户进程通过kill函数,将一个用户信号发送给另一个进程。
我们可以通过sigaction系统调用,注册一个信号处理函数。
提供了信号处理服务,程序执行过程中一旦有变动,就可以及时处理了。
进程间通信
- 消息队列(Message Queue):适用于小数据
这个消息队列是在内核中的,我们可以通过msgget创建一个新的队列,msgsnd将消息发送到消息队列,而消息接收方可以使用msgrcv从队列中取消息。 - 共享内存
数据大时,可以使用共享内存的方式,我们可以通过shmget创建一个共享内存块,通过shmat将共享内存映射到自己的内存空间,然后就可以读写了。
但这就涉及到了排他访问,这就是信号量但机制Semaphore(sem_wait sem_post)。
网络通信
不同机器的通过网络互相通信,要遵循相同的网络协议,也即TCP/IP网络协议栈。Linux内核中有对于网络协议栈的实现。如何暴露出服务给项目组使用呢?
网络服务是通过套接字Socket来提供服务的。
我们可以通过Socket系统调用建立一个Socket。Socket也是一个文件,也有一个文件描述符,也可以通过读写函数进行通信。
中介与Glibc
Glibc是Linux下使用的开源的标准C库,它是GUN发布的libc库。Glibc为程序员提供了丰富的api,除了例如字符串处理,数学运算等用户服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。
每个特定的系统调用对应了依照一个Glibc封装的库函数,比如说,系统提供的打开文件系统调用sys_open对应的是Glibc中的open函数。
总结: