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

上一节我们讲了几个重要的Linux命令行,只有通过这些命令,用户才能把Linux系统用起来,不知道你掌握得如何了?其实Linux命令也是一个程序,只不过代码是别人写好的,你直接用就可以了。你可以自己试着写写代码,通过代码把Linux系统用起来,这样印象会更深刻。

不过,无论是别人写的程序,还是你写的程序,运行起来都是进程。如果你是一家外包公司,一个项目的运行要使用公司的服务,那就应该去办事大厅,也就是说,你写的程序应该使用系统调用。

你看,系统调用决定了这个操作系统好用不好用、功能全不全。对应到咱们这个公司中,作为一个老板,你应该好好规划一下,你的办事大厅能够提供哪些服务,这决定了你这个公司会被打五星还是打差评。

立项服务与进程管理

首先,我们得有个项目,那就要有立项服务。对应到Linux操作系统中就是创建进程

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

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

前面我们说过,一个进程的运行是要有一个程序的,就像一个项目的执行,要有一个项目执行计划书。本来老的项目,按照项目计划书按部就班地来,项目执行到一半,突然接到命令,说是要新启动一个项目,这个时候应该怎么办呢?

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

所以,我们现在有两种方式,一种是列一个清单,清单里面写明每个新项目组都要开哪些账号。但是,这样每次有项目,都要重新配置一遍新的Jira、Wiki,复杂得很。另一种方式就是咱们程序员常用的方式,CTRL/C + CTRL/V。也就是说,如果想为新项目建立一套Jira,但又觉得一个个填Jira里面的选项太麻烦,那就可以拷贝一个别人的,然后根据新项目的实际情况,将相应的配置改改。

Linux就是这样想的。当父进程调用fork创建进程的时候,子进程将各个子系统为父进程创建的数据结构也全部拷贝了一份,甚至连程序代码也是拷贝过来的。按理说,如果不进行特殊的处理,父进程和子进程都按相同的程序代码进行下去,这样就没有意义了。

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

同样是“先拷贝,再修改”的策略,你可能会问,新进程都是父进程fork出来的,那到底谁是第一个呢?

作为一个外包公司老板,有了新项目当然会分给手下做,但是当公司刚起步的时候呢?没有下属,只好自己上了。先建立项目运行体系,等后面再做项目的时候,就都按这个来。

对于操作系统也一样,启动的时候先创建一个所有用户进程的“祖宗进程”。这个在讲系统启动的时候还会详细讲,我这里先不多说。

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

所以说,所有子项目最终都是老板,也就是祖宗进程fork过来的,因而它要对整个公司的项目执行负最终的责任。

会议室管理与内存管理

项目启动之后,每个项目组有独立的会议室,存放自己项目相关的数据。每个项目组都感觉自己有独立的办公空间。

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

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

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

项目执行的过程中,会产生一些架构图、流程图,这些也放在会议室里面。有的画在白板上,讨论完了,进入下个主题就会擦了;有的画在纸和本子上,讨论的时候翻出来,不讨论的时候堆在那里,会保留比较长的一段时间,除非指明的确不需要了才会去销毁。

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

一个进程的内存空间是很大的,32位的是4G,64位的就更大了,我们不可能有这么多物理内存。就像一个公司的会议室是有限的,作为老板,你不可能事先都给项目组分配好。哪有这么多会议室啊,一定是需要的时候再分配。

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

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

当分配的内存数量比较小的时候,使用brk,会和原来的堆的数据连在一起,这就像多分配两三个工位,在原来的区域旁边搬两把椅子就行了。当分配的内存数量比较大的时候,使用mmap,会重新划分一块区域,也就是说,当办公空间需要太多的时候,索性来个一整块。

档案库管理与文件管理

项目执行计划书要保存在档案库里,有一些需要长时间保存,这样哪怕公司暂时停业,再次经营的时候还可以继续使用。同样,程序、文档、照片等,哪怕关机再开机也能不丢的,就需要放在文件系统里面。

文件之所以能做到这一点,一方面是因为介质,另一方面是因为格式。公司之所以强调资料库,也是希望将一些知识固化为标准格式,放在一起进行管理,无论多少人来人走,都不影响公司业务。

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

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

  • 对于已经有的文件,可以使用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将信号量释放,于是下一个人等待结束,可以访问这个资源了。

公司间沟通与网络通信

同一个公司不同项目组之间的合作搞定了,如果是不同公司之间呢?也就是说,这台Linux要和另一台Linux交流,这时候,我们就需要用到网络服务。

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

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

我们可以通过Socket系统调用建立一个Socket。Socket也是一个文件,也有一个文件描述符,也可以通过读写函数进行通信。

好了,我们分门别类地规划了这么多办事大厅的服务,如果这些都有了,足够我们成长为一个大型跨国公司了。

查看源代码中的系统调用

你如果问,这里的系统调用列举全了吗?其实没有,系统调用非常多。我建议你访问https://www.kernel.org下载一份Linux内核源代码。因为在接下来的整个课程里,我讲述的逻辑都是这些内核代码的逻辑。

对于64位操作系统,找到unistd_64.h文件,里面对于系统调用的定义,就是下面这样。

#define __NR_restart_syscall	  0
#define __NR_exit		  1
#define __NR_fork		  2
#define __NR_read		  3
#define __NR_write		  4
#define __NR_open		  5
#define __NR_close		  6
#define __NR_waitpid		  7
#define __NR_creat		  8
......

中介与Glibc

如果你做过开发,你会觉得刚才讲的和平时咱们调用的函数不太一样。这是因为,平时你并没有直接使用系统调用。虽然咱们的办事大厅已经很方便了,但是为了对用户更友好,我们还可以使用中介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的系统调用。

总结时刻

学了这么多系统调用,我们还是用一个图来总结一下。

课堂练习

有个命令strace,常用来跟踪进程执行时系统调用和所接收的信号。你可以试一下咱们学过的命令行,看看都执行了哪些系统调用。

欢迎留言和我分享你的疑惑和见解,也欢迎你收藏本节内容,反复研读。你也可以把今天的内容分享给你的朋友,和他一起学习、进步。

精选留言

  • 青石

    2019-04-05 13:25:08

    用strace跟踪"ls -al"的系统调用,大体流程如下:
    1. 先执行execve,调用ls命令;
    2. 调用open、read、fstat、mmap、mprotect、mmap、close读取动态链库和系统配置文件;
    3. 读取passwd、group文件,获取用户信息,lstat、lgetxattr、getxattr获取文件属性,同时判断权限;
    4. 调用write输出内容;
    5. munmap释放内存,close关闭。
    作者回复

    2019-04-05 22:41:15

  • TinnyFlames

    2019-04-05 09:15:55

    关于操作系统,有一本国外的教材叫做OSTEP(Operating System Three Easy Picies) 虽然貌似没有中文版的,但里面的内容讲的相当通俗易懂,强烈推荐给大家作为理论层面的补充
    作者回复

    推荐给大家

    2019-04-05 22:43:29

  • sotey

    2019-04-09 23:54:13

    学习笔记+作业+提问,知识点真多啊,老师这文章货太干了。
    一、 创建进程
    #### 创建进程的总结:
    1、Linux中父进程调用fork创建子进程。
    2、父进程调用fork时,子进程拷贝所有父进程的数据接口和代码过来。
    3、当前进程是子进程,fork返回0;当前进程是父进程,fork返回子进程进程号
    4、如果返回0,说明当前进程是子进程,子进程请求execve系统调用,执行另一个程序。
    5、如果返回子进程号,说明当前进程是父进程,按照原父进程原计划执行。
    6、父进程要对子进程负责,调用waitpid将子进程进程号作为参数,父进程就能知道子进程运行完了没有,成功与否。
    7、操作系统启动的时候先创建了一个所有用户进程的“祖宗进程”,课时1,第3题A选项:0号进程是所有用户态进程的祖先
    ##### 创建进程的系统调用:fork
    ##### 执行另一个程序的系统调用:execve
    ##### 将子进程的进程号作为参数传给它,父进程就能知道子进程运行完了没有,成功与否:waitpid

    二、 内存管理
    ##### 内存管理总结
    1、每个进程都有独立的进程内存空间,互相之间不干扰。(隔离性)
    2、进程内存空间,存放程序代码的部分,称为代码段(Code Segment)。
    3、存放进程运行中产生数据的部分,称为数据段(Data Segment)。
    4、进程写入数据的时候,现用现分物理内存给进程使用。
    5、分配内存数量比较小时,使用brk调用,会和原来的堆数据连在一起。
    6、需要分配的内存数据量比较大的时候,使用mmap,重新划分一块内存区域。
    ##### 分配较小内存数量,和原来堆内数据连在一起:brk
    ##### 分配较大内存数量,重新划分一块内存区域:mmap

    三、 文件管理
    ##### 文件的操作六个最重要系统调用:
    ##### 打开文件:open
    ##### 关闭文件:close
    ##### 创建文件:creat
    ##### 打开文件后跳到文件某个位置:lseek
    ##### 读文件:read
    ##### 写文件:write
    ##### Linux一切皆文件
    ##### 一切皆文件的优势即使统一了操作的入口,提供了极大的便利。

    四、 信号处理(异常处理)
    进程执行过程中一旦有变动,就可以通过信号处理服务及时处理。

    五、 进程间通信
    #### 有两种方式实现进程间通信
    #### 消息队列方式
    ##### 创建一个新的队列:msgget
    ##### 发送消息到消息队列:msgsnd
    ##### 取出队列中的消息:msgrcv

    六、 共享内存方式
    ##### 创建共享内存块:shmget
    ##### 将共享内存映射到自己的内存空间:shmat

    #### 利用信号量实现隔离性
    ##### 占用信号量:sem_wait
    ##### 释放信号量:sem_post
    伪代码:
    假设信号量为1
    signal = 1
    sem_wait伪代码
    while True {
    if sem_wait == 1;
    signal -=1;
    break;
    }
    code.code;
    sem_post伪代码
    signal +=1;

    七、 网络通信
    ##### 网络插口:socket
    ##### 网络通信遵循TCP/IP网络协议栈
    #####

    八、 glibc
    ##### glibc是Linux下开源标准C库
    ##### glibc把系统调用进一步封

    ##### sys_open对应glibc的open函数
    ##### 一个单独的glibcAPI可能调用多个系统调用
    ##### printf函数调用sys_open、sys_mmap、sys_write、sys_close等等系统调用

    ### 课后作业
    strace ls -la
    查看有如下系统调用
    execve
    brk
    mmap
    access
    open
    fstat
    mmap
    close
    read
    stat
    write
    lseek
    lstat
    getxattr
    socket
    connect
    mprotect

    ##### 疑问:局部变量,在当前函数执行的时候起作用,就是说当前函数执行中产生的局部变量是存放在内存中的。为什么不是暂存在CPU缓存或者寄存器,进入另一个函数时,丢掉局部变量,而不写入内存,提升效率。
    作者回复

    哪有这么多寄存器

    2019-09-06 00:18:22

  • rocedu

    2019-04-05 04:33:56

    文件,虚存,进程三大核心概念都谈到了。学习系统调用,专栏可以作为学习地图进行之路,下面还有下极大的功夫。给大家推荐一本非常棒的入门图书Unix/Linux编程实践教程(Understanding UNIX/LINUX Programming),绝版了,但是网上能搜到电子版,看到这本书真是相见恨晚。我的博客https://www.cnblogs.com/rocedu/p/6016880.html就是对这本书的核心方法的总结,供大家参考。
    作者回复

    2019-09-06 23:50:31

  • 刘強

    2019-04-05 11:19:26

    所有把socket翻译成套接字的文章,我一律不看。原因是socket的本意已经很清楚的说明了它的作用,我们翻译成套接字,平白无故的给读者增加了理解的难度。我觉得这个词除了能装逼,别于他用。作者是明白人,是现在读者的角度考虑的。这是真大牛!
    作者回复

    谢谢啦

    2019-04-05 22:42:07

  • 美美

    2019-08-23 16:01:00

    上面推荐的国外的教材叫OSTEP(Operating System Three Easy Picies) 已经有中文版了,顶上去让大家知道:https://book.douban.com/subject/33463930/
    作者回复

    好书

    2019-09-03 15:35:11

  • 不专注的linuxer

    2019-08-14 07:59:14

    老师、各位同学,大家好!
    “fork 系统调用的返回值,如果当前进程是子进程,就返回 0;如果当前进程是父进程,就返回子进程的进程号” 这句话到意思是不是说,父进程调用fork创建子进程之后,得到CPU执行权的可能是父进程叶可能是子进程,所以要从返回值区分一下?
    不是很理解这个具体的过程,有谁可以解释一下吗?
    作者回复

    既然fork是复制,就说明父进程和子进程在用户态的代码段是一模一样的,如果fork这个系统调用后面的语句都是A,则无论是父进程,还是子进程都是接下来要执行A的。好在fork是一个系统调用,刚从内核返回的时候,由于内核是能够区分父进程和子进程的,因而用户态的程序代码里面,往往通过这个返回值来判断自己是父进程还是子进程,因为子进程如果不判断一下,是不知道自己是子进程的。这和CPU执行权也没有关系。就是代码执行完fork以后,由于父子进程的代码是一模一样的,父进程和子进程都不知道自己是哪一个,但是判断返回值就知道了。

    2019-08-20 14:34:59

  • Dsir9527

    2019-05-05 14:42:15

    另外还有一点,文件描述符不是面向操作系统的,而是针对进程的,所以同一个文件在不同的进程里可能会有不同的fd。其实换个理解方式就好懂了:因为操作系统里有太多的文件,一个进程大概率不需要知道所有的文件,所以每当进程用到文件的时候,就像系统要来这个文件,对进程来说,一个类似于数组的东西就可以管理到所有系统分配给他的文件,所以fd就是按照自然数顺序依次排列的,0表示标准输入,1是标准输出,2是标准错误,这三个是固定的,后面的用到就依次往后加。相当于是个下标index的概念。
    作者回复

    是的

    2019-05-06 23:01:31

  • Loner

    2019-04-07 15:28:59

    可以自己搭个opengork服务器,教程百度有,很方便的看linux 源代码
    作者回复

    2019-09-06 22:45:03

  • gwwwwt

    2019-04-05 01:00:53

    刘老师,您好,我想问一下,Linux内核源代码下载后需要编译么? 另外,您能推荐一个Mac上读kernel源码的软件么?感谢。。
    作者回复

    看源码不用编译,但是改代码需要,后面实验环节有这部分

    2019-04-05 22:45:16

  • fy

    2019-04-05 17:19:31

    在留言区能够看到分享好书的人,多谢!
    作者回复

    是的,很感谢他们

    2019-09-06 23:02:33

  • Untitled

    2019-04-14 21:56:59

    用strace跟踪”whoami"命令,执行的系统调用有:
    (1)execve执行/usr/bin/whoami程序
    (2)brk、mmap 内存映射,mprotect内存权限更改
    (3)access、openat、fstat、read、lseek、close 文件权限、属性查看、打开、跳到文件某个位置,关闭等操作
    (4)geteuid 获取用户id
    (5)socket、connect网络通信
    (6)write 文件写入
    (7)arch_pctcl、exit_group 设计架构的进程或线程状态和退出进程中的所有线程
  • 益军

    2020-04-22 12:06:52

    关于进程调用fork操作理解,看着Redis-rdb持久化代码更好理解了。
    if ((childpid = fork()) == 0) {
    int retval;

    /* Child */
    closeListeningSockets(0);
    redisSetProcTitle("redis-rdb-bgsave");
    retval = rdbSave(filename,rsi);
    if (retval == C_OK) {
    size_t private_dirty = zmalloc_get_private_dirty(-1);

    if (private_dirty) {
    serverLog(LL_NOTICE,
    "RDB: %zu MB of memory used by copy-on-write",
    private_dirty/(1024*1024));
    }

    server.child_info_data.cow_size = private_dirty;
    sendChildInfo(CHILD_INFO_TYPE_RDB);
    }
    exitFromChild((retval == C_OK) ? 0 : 1);
    } else {
    /* Parent */
    server.stat_fork_time = ustime()-start;
    server.stat_fork_rate = (double) zmalloc_used_memory() * 1000000 / server.stat_fork_time / (1024*1024*1024); /* GB per second. */
    latencyAddSampleIfNeeded("fork",server.stat_fork_time/1000);
    if (childpid == -1) {
    closeChildInfoPipe();
    server.lastbgsave_status = C_ERR;
    serverLog(LL_WARNING,"Can't save in background: fork: %s",
    strerror(errno));
    return C_ERR;
    }
    serverLog(LL_NOTICE,"Background saving started by pid %d",childpid);
    server.rdb_save_time_start = time(NULL);
    server.rdb_child_pid = childpid;
    server.rdb_child_type = RDB_CHILD_TYPE_DISK;
    updateDictResizePolicy();
    return C_OK;
    }
    作者回复

    厉害,活学活用

    2020-06-17 10:07:43

  • Adam

    2019-07-25 16:20:49

    文中老师提到:程序员常用的方式,CTRL/C + CTRL/V。 老师这是暗示程序员要多复制粘贴网上的代码吗?
    作者回复

    哈哈哈

    2019-08-20 19:09:46

  • 小文同学

    2019-04-05 08:54:31

    我自己比较擅长Java并发编程,对于系统调用其实不是特别熟悉。不过今天老师讲解的内容非常容易能听懂,很多的通用数据结构像消息队列,信号量,共享内存在其他领域的编程里面都有涉及,计算机的设计果然是一通百通的。这样我有信心更好的学习这个课程
    作者回复

    是的,一通百通

    2019-09-06 23:47:11

  • cronusqiu

    2019-04-11 22:08:06

    一直有一个疑问,fork一个进城,会返回两次,这个是怎么做到的,原理是什么?
    作者回复

    不会返回两次呀

    2019-09-05 23:52:00

  • williamcai

    2019-04-05 10:24:39

    项目大了,会被拆分成项目组,项目理解为进程,那么项目组可以理解为进程下的线程么
    作者回复

    子团队

    2019-04-05 22:42:48

  • Jamin

    2019-04-09 08:52:01

    如果当前进程是子进程,就返回 0;如果当前进程是父进程,就返回子进程的进程号?父进程返回了子进程号?
    作者回复

    对啊,如果当前进程是父进程,返回的是子进程的进程号。

    2019-09-06 00:38:22

  • Liam

    2019-04-07 22:13:21

    1 execve 执行二进制文件 ls -al
    2 brk, mmap, mprotect, munmap 内存分配,访问和释放
    3 access,open, openat, close, read, write 文件操作
    4 lstat, fstat, stat 获取文件相关信息
    5 getxattr, lgetxattr 获取文件属性
    6 socket, connect 网络相关
    7 futex 同步机制
  • Gaoy9303

    2021-03-02 09:29:13

    execve:执行/usr/bin/ls ["ls","-la"]
    brk():分配内存
    access:确定/etc/ld.so.preload是否有可读权限

    加载库:
    openat:打开/etc/ld.so.cache
    fstat:获取文件信息
    mmap:将/etc/ld.so.cache的内容换个映射到14545内存地址
    close:关闭/etc/ld.so.cache

    mprotect(0x7fa9a9525000, 4096, PROT_READ):修改内存的保护属性
    munmap:将由mmap所映射的内存,解除映射

    set_tid_address:设置线程属性clear_child_tid
    set_robust_list:请求内核记录此线程对应的rubust futex列表头
    rt_sigaction:设置不同的信号处理
    statfs:查询文件系统相关信息 /sys/fs/selinux /selinux

    /proc/filesystems
    判断/etc/selinux/config是否存在
    /usr/lib/locale/locale-archive:



    ioctl:TIOCGWINSZ: 获取终端窗口的大小
    getdents64:获取目录结构
    write:写入到标准输出