• 学会几个系统调用:咱们公司能接哪些类型的项目?


    学会几个系统调用:咱们公司能接哪些类型的项目?

    无论是别人写的程序,还是你写的程序,运行起来都是进程。你写的程序应该使用系统调用。

    系统调用决定了这个操作系统好用不好用、功能全不全。

    立项服务与进程管理

    立项服务,对应到 Linux 操作系统中就是创建进程

    创建进程的系统调用叫fork。这个名字很奇怪,中文叫“分支”。为啥启动一个新进程叫“分支”呢?

    在 Linux 里,要创建一个新的进程,需要一个老的进程调用 fork 来实现,其中老的进程叫作父进程(Parent Process),新的进程叫作子进程(Child Process)

    一个项目的执行是很复杂的,需要涉及公司各个部门的工作,比如说,项目管理部门需要给这个项目组开好 Jira 和 Wiki,会议室管理部要为这个项目分配会议室等等。

    现在有两种方式,一种是列一个清单,清单里面写明每个新项目组都要开哪些账号。但是,这样每次有项目,都要重新配置一遍新的 Jira、Wiki,复杂得很。

    另一种方式就是咱们程序员常用的方式,CTRL/C + CTRL/V。也就是说,如果想为新项目建立一套 Jira,但又觉得一个个填 Jira 里面的选项太麻烦,那就可以拷贝一个别人的,然后根据新项目的实际情况,将相应的配置改改。

    当父进程调用 fork 创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝了一份,甚至连程序代码也是拷贝过来的。

    对于 fork 系统调用的返回值,如果当前进程是子进程,就返回 0;如果当前进程是父进程,就返回子进程的进程号。这样首先在返回值这里就有了一个区分,然后通过 if-else 语句判断,如果是父进程,还接着做原来应该做的事情;如果是子进程,需要请求另一个系统调用execve来执行另一个程序,这个时候,子进程和父进程就彻底分道扬镳了,也就产生了一个分支(fork)了。

    父进程要关心子进程的运行情况,这毕竟是自己身上掉下来的肉。有个系统调用waitpid,父进程可以调用它,将子进程的进程号作为参数传给它,这样父进程就知道子进程运行完了没有,成功与否。

    会议室管理与内存管理

    在操作系统中,每个进程都有自己的内存,互相之间不干扰,有独立的进程内存空间。

    那独立的办公空间里面,都放些什么呢?

    项目执行计划书肯定是要放进去的,因为执行过程中肯定要不断地看。对于进程的内存空间来讲,放程序代码的这部分,我们称为代码段(Code Segment)。

    对于进程的内存空间来讲,放进程运行中产生数据的这部分,我们称为数据段(Data Segment)。其中局部变量的部分,在当前函数执行的时候起作用,当进入另一个函数时,这个变量就释放了;也有动态分配的,会较长时间保存,指明才销毁的,这部分称为(Heap)。

    一个进程的内存空间是很大的,32 位的是 4G,64 位的就更大了,我们不可能有这么多物理内存。

    进程自己不用的部分就不用管,只有进程要去使用部分内存的时候,才会使用内存管理的系统调用来登记,说自己马上就要用了,希望分配一部分内存给它,但是这还不代表真的就对应到了物理内存。只有真的写入数据的时候,发现没有对应物理内存,才会触发一个中断,现分配物理内存。

    这里我们介绍两个在堆里面分配内存的系统调用,brk和mmap。

    当分配的内存数量比较小的时候,使用 brk,会和原来的堆的数据连在一起,这就像多分配两三个工位,在原来的区域旁边搬两把椅子就行了。

    档案库管理与文件管理

    文件管理其实花样不多,拍着脑袋都能想出来,无非是创建、打开、读、写等。

    对于文件的操作,下面这六个系统调用是最重要的:

    • 对于已经有的文件,可以使用open打开这个文件,close关闭这个文件;
    • 对于没有的文件,可以使用creat创建文件;
    • 打开文件以后,可以使用lseek跳到文件的某个位置;
    • 可以对文件的内容进行读写,读的系统调用是read,写是write。

    Linux 里有一个特点,那就是一切皆文件。

    • 启动一个进程,需要一个程序文件,这是一个二进制文件。
    • 启动的时候,要加载一些配置文件,例如 yml、properties 等,这是文本文件;启动之后会打印一些日志,如果写到硬盘上,也是文本文件。
    • 但是如果我想把日志打印到交互控制台上,在命令行上唰唰地打印出来,这其实也是一个文件,是标准输出 stdout 文件。
    • 这个进程的输出可以作为另一个进程的输入,这种方式称为管道,管道也是一个文件。
    • 进程可以通过网络和其他进程进行通信,建立的 Socket,也是一个文件。
    • 进程需要访问外部设备,设备也是一个文件。
    • 文件都被存储在文件夹里面,其实文件夹也是一个文件。
    • 进程运行起来,要想看到进程运行的情况,会在 /proc 下面有对应的进程号,还是一系列文件。

    每个文件,Linux 都会分配一个文件描述符(File Descriptor),这是一个整数。有了这个文件描述符,我们就可以使用系统调用,查看或者干预进程运行的方方面面。

    项目异常处理与信号处理

    当项目遇到异常情况,例如项目中断,做到一半不做了。这时候就需要发送一个信号(Signal)给项目组。经常遇到的信号有以下几种:

    • 在执行一个程序的时候,在键盘输入“CTRL+C”,这就是中断的信号,正在执行的命令就会中止退出;
    • 如果非法访问内存,例如你跑到别人的会议室,可能会看到不该看的东西;
    • 硬件故障,设备出了问题,当然要通知项目组;
    • 用户进程通过kill函数,将一个用户信号发送给另一个进程。

    对于一些不严重的信号,可以忽略,该干啥干啥,但是像 SIGKILL(用于终止一个进程的信号)和 SIGSTOP(用于中止一个进程的信号)是不能忽略的,可以执行对于该信号的默认动作。每种信号都定义了默认的动作,例如硬件故障,默认终止;也可以提供信号处理函数,可以通过sigaction系统调用,注册一个信号处理函数。

    项目组间沟通与进程间通信

    首先就是发个消息,不需要一段很长的数据,这种方式称为消息队列(Message Queue)。由于一个公司内的多个项目组沟通时,这个消息队列是在内核里的,我们可以通过msgget创建一个新的队列,msgsnd将消息发送到消息队列,而消息接收方可以使用msgrcv从队列中取消息。

    当两个项目组需要交互的信息比较大的时候,可以使用共享内存的方式,也即两个项目组共享一个会议室(这样数据就不需要拷贝来拷贝去)。大家都到这个会议室来,就可以完成沟通了。这时候,我们可以通过shmget创建一个共享内存块,通过shmat将共享内存映射到自己的内存空间,然后就可以读写了。

    但是,两个项目组共同访问一个会议室里的数据,就会存在“竞争”的问题。如果大家同时修改同一块数据咋办?这就需要有一种方式,让不同的人能够排他地访问,这就是信号量的机制 Semaphore

    对于只允许一个人访问的需求,我们可以将信号量设为 1。当一个人要访问的时候,先调用sem_wait。如果这时候没有人访问,则占用这个信号量,他就可以开始访问了。如果这个时候另一个人要访问,也会调用 sem_wait。由于前一个人已经在访问了,所以后面这个人就必须等待上一个人访问完之后才能访问。当上一个人访问完毕后,会调用sem_post将信号量释放,于是下一个人等待结束,可以访问这个资源了。

    公司间沟通与网络通信

    不同机器的通过网络相互通信,要遵循相同的网络协议,也即 TCP/IP 网络协议栈。Linux 内核里有对于网络协议栈的实现。如何暴露出服务给项目组使用呢?

    网络服务是通过套接字 Socket 来提供服务的。Socket 这个名字很有意思,可以作“插口”或者“插槽”讲。虽然我们是写软件程序,但是你可以想象成弄一根网线,一头插在客户端,一头插在服务端,然后进行通信。因此,在通信之前,双方都要建立一个 Socket。

    中介与 Glibc

    Glibc 是 Linux 下使用的开源的标准 C 库,它是 GNU 发布的 libc 库。Glibc 为程序员提供丰富的 API,除了例如字符串处理、数学运算等用户态服务之外,最重要的是封装了操作系统提供的系统服务,即系统调用的封装。

    每个特定的系统调用对应了至少一个 Glibc 封装的库函数,比如说,系统提供的打开文件系统调用 sys_open 对应的是 Glibc 中的 open 函数。

    有时候,Glibc 一个单独的 API 可能调用多个系统调用,比如说,Glibc 提供的 printf 函数就会调用如 sys_open、sys_mmap、sys_write、sys_close 等等系统调用。

    也有时候,多个 API 也可能只对应同一个系统调用,如 Glibc 下实现的 malloc、calloc、free 等函数用来分配和释放内存,都利用了内核的 sys_brk 的系统调用。

    总结

  • 相关阅读:
    Junit研究心得——准备研究Junit
    Nginx proxying for Tomcat applications
    将工厂模式升华为插件式框架【文摘】
    JIRA中输入验证时,将验证错误“InvalidInputException”写到对应字段顶部,而不是页面顶部。
    Junit研究心得——Junit中的Annotation
    Junit研究心得——Junit中的约定规则
    Java 编程的动态性【文摘】
    插件式设计开发心得(一)【文摘】
    Java SE 6 新特性: Instrumentation 新功能
    php实现首页自动选择语言跳转
  • 原文地址:https://www.cnblogs.com/liugangjiayou/p/12690091.html
Copyright © 2020-2023  润新知