系统调用
操作系统内核给软件提供接口的方式主要就是系统调用。系统调用提供了许多功能,这里只讲一些开发常用的。
进程创建 fork
当父进程调用 fork 创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝了一份,甚至连程序代码也是拷贝过来的。通过fork函数的返回值判断当前线程是父进程、还是子进程。子进程返回0, 父进程返回创建的子进程id。根据进程号,后续进行不同的处理流程。
系统调用waitpid,父进程可以调用它,将子进程的进程号作为参数传给它,这样父进程就知道子进程运行完了没有,成功与否。
内存分配
分配内存的系统调用,brk和mmap。当分配的内存数量比较小的时候,使用 brk,会和原来的堆的数据连在一起,这就像多分配两三个工位,在原来的区域旁边搬两把椅子就行了。当分配的内存数量比较大的时候,使用 mmap,会重新划分一块区域,也就是说,当办公空间需要太多的时候,索性来个一整块。
文件操作
对于已经有的文件,可以使用open打开这个文件,close关闭这个文件;
对于没有的文件,可以使用creat创建文件;
打开文件以后,可以使用lseek跳到文件的某个位置;
可以对文件的内容进行读写,读的系统调用是read,写是write
Linux一切皆是文件。
异常与信号
当程序运行时,遇到外部或者内部的异常,需要作对应的处理。
经常遇到的信号有以下几种:
- 在执行一个程序的时候,在键盘输入“CTRL+C”,这就是中断的信号,正在执行的命令就会中止退出;
- 如果非法访问内存,例如你跑到别人的会议室,可能会看到不该看的东西;
- 硬件故障,设备出了问题,当然要通知项目组;
- 用户进程通过kill函数,将一个用户信号发送给另一个进程。
当项目组收到信号的时候,项目组需要决定如何处理这些异常情况。对于一些不严重的信号,可以忽略,该干啥干啥,但是像 SIGKILL(用于终止一个进程的信号)和 SIGSTOP(用于中止一个进程的信号)是不能忽略的,可以执行对于该信号的默认动作。每种信号都定义了默认的动作,例如硬件故障,默认终止;也可以提供信号处理函数,可以通过sigaction系统调用,注册一个信号处理函数。提供了信号处理服务,项目执行过程中一旦有变动,就可以及时处理了。
进程间通信
主要有消息队列、共享内存、信号量等机制相关系统调用。
进程间通信,主要用于不同进程间的信息传递。
网络通信
不同机器间进行通信的唯一手段。不同进程间也可以使用网络通信实现进程间的通信。主要就是socket的系统调用。
Glibc
Glibc是linux系统下的一个C标准库,Glibc封装了许多系统调用,屏蔽了一些底层的细节,更便于开发者调用。
有时候,Glibc 一个单独的 API 可能调用多个系统调用,比如说,Glibc 提供的 printf 函数就会调用如 sys_open、sys_mmap、sys_write、sys_close 等等系统调用。也有时候,多个 API 也可能只对应同一个系统调用,如 Glibc 下实现的 malloc、calloc、free 等函数用来分配和释放内存,都利用了内核的 sys_brk 的系统调用。
strace
strace命令可以跟着程序的系统调用情况。显示系统调用的记录,还是比较详细的。
行动,才不会被动!
欢迎关注个人公众号 微信 -> 搜索 -> fishmwei,沟通交流。