趣谈Linux操作系统(3)—常用的系统调用
背景
操作系统下各程序/进程的正常运行,需要依靠系统调用来完成。故本篇文章介绍了一些常用的系统调用,可帮助我们进一步了解操作系统。
几个常用系统调用
进程管理
1.创建进程fork[“分支”]
程序运行起来之后称之为进程,故想要运行一个新的程序,需要先创建一个进程,linux系统中创建进程的系统调用叫fork。且创建新的进程需要基于老的进程调用fork实现,老的进程称之为父进程,新的进程称之为子进程。
当父进程调用fork来创建子进程时,子进程会将父进程的所有内容拷贝一份,包含数据结构和程序代码等。通过if-else语句判断fork系统调用的返回值:
a.当返回的值为0,则代表当前进程是子进程,需要请求另一个系统调用execve来执行另外的程序,此时子进程和父进程就分开了,即相当于产生了一个分支fork
b.当返回的值为子进程的进程号,则代表当前进程是父进程,则接着做父进程的事即可。
注:因为子进程都是父进程fork出来的,故对于任意一个操作系统启动的时候会先创建一个所有用户进程的“祖宗进程”
2.等待子进程结束waitpid
父进程将子进程的进程号作为参数传给系统调用waitpid,可知道子进程当前运行的情况,比如运行完了没有/成功与否
内存管理
在操作系统中,每个进程都有自己的内存,互相之间不干扰,有独立的进程内存空间。
内存空间里面包含以下部分内容:
a.代码段,程序代码的部分
b.数据段,进程运行过程中产生了一些数据的部分,包括局部变量的部分[当前函数执行的时候起作用,进入另一个函数,变量释放了];动态分配的,需要保存较长时间,指明的确不需要才会去销毁,这种称为堆。
根据分配内存数量的大小,针对堆这种需要长时间保存的内容,会采用两种不同的系统调用brk和mmap。其中分配的内存数量小的时候,采用brk方式[和已有的堆数据连在一起];分配的内存数量大的时候,采用mmap方式[重新划分一块区域]。
注:考虑到一个进程的内存空间是很大的,32位的是4G,64位的话需要的空间地址会更大,不可能给每一个进程都事先分配一个内存空间,只能等进程需要用到的时候再进行分配。当进程需要使用部分内存的时候,才会调用内存管理的系统调用来登记,但此时并不意味着已经给进程分到了物理内存。只有进程真正写入数据的时候,发现没有物理内存的时候,会触发中断,分配物理内存。
文件管理
包含6个常用到的系统调用:
1.已有的文件,可使用open打开文件;close关闭文件
2.没有的文件,可使用create创建文件
3.打开文件以后,可使用lseek跳到文件的某个位置
4.对文件内容进行读写,读是read,写是write
针对Linux系统来说,一切皆文件,包含各种类型的文件,比如二进制文件/文本文件/stdout文件等
PS.linux会给每个文件分配一个文件描述符[一个整数],通过这个文件描述符,可直接使用系统调用查看或者干预进程运行的方方面面。
进程间通信
一台Linux服务器:
一个项目下可能需要多个进程的相互配合才能完成,各进程间的配合需通过存储在内核里面的消息队列[一个消息&不是一段很长的数据]来实现。
1.消息队列相关系统调用
1)进程A通过msgget创建一个新的队列
2)进程A通过msgsnd将步骤1中的队列发送到消息队列中
3)进程B[消息接收方]通过msgrcv从队列中取消息
2.两个进程间通信较多,可采用内存共享的方式,通过shmget创建一个共享内存块,两个进程将共享内存通过shmat映射到自己的内存空间就可以
Ps.当共享内存块设置成只允许一个人访问的时候,需借助于sem_wait信号量来达到占用共享内存的目标,使用完之后使用sem_post释放信号量来达到释放共享内存的目标
多台Linux服务器:
多台服务器间的通信需借助于TCP/IP网络协议栈,通过套接字Socket来实现
PS.可通过strace来跟踪进程执行时系统调用和所接收的信号