死锁.png

调度算法.png

1、操作系统的四个特性

并发:同一段时间内多个程序执行;
共享:系统中的资源可以被内存中多个并发执行的进线程共同使用;
虚拟:通过分时复用(如分时系统)以及空分复用(如虚拟内存)技术把一个物理实体虚拟为多个;
异步:系统进程用一种走走停停的方式执行,(并不是一下子走完),进程什么时候以怎样的速度向前推进是不可预知的。

2、多线程相较单线程的好处

  1. 并发提升程序执行效率;
  2. 提升CPU利用率,访存的时候可以切换线程来执行;
  3. 更快的响应速度,可以有专门的线程来监听用户请求和专门的线程来处理请求。比如监听线程和工作线程是两个线程,这样监听就负责监听,工作的就负责工作,监听到用户请求马上把请求转到工作线程去处理,监听线程继续监听。

    3、用户态和系统态(内核态)

  4. 用户态(user mode):用户态运行的进程可以直接读取用户程序的数据。

  5. 系统态(kernel mode):可以简单的理解系统态运行的进程或程序几乎可以访问计算机的任何资源,不受限制。

    4、什么是协程?

    协程:协程是微线程,在子程序内部执行,可在子程序内部中断,转而执行别的子程序,在适当的时候再返回来接着执行。

  6. 线程与协程的区别:

    1. 协程执行效率极高。协程直接操作栈基本没有内核切换的开销,所以上下文的切换非常快,切换开销比线程更小。
    2. 协程不需要多线程的锁机制,因为多个协程从属于一个线程,不存在同时写变量冲突,效率比线程高。
    3. 一个线程可以有多个协程。
  7. 协程的优势:
    1. 协程调用跟切换比线程效率高:协程执行效率极高。协程不需要多线程的锁机制,可以不加锁的访问全局变量,所以上下文的切换非常快。
    2. 协程占用内存少:执行协程只需要极少的栈内存(大概是4~5KB),而默认情况下,线程栈的大小为1MB。
    3. 切换开销更少:协程直接操作栈基本没有内核切换的开销,所以切换开销比线程少。

      5、为什么协程比线程切换的开销小?

  • 协程执行效率极高。协程直接操作栈基本没有内核切换的开销,所以上下文的切换非常快,切换开销比线程更小。
  • 协程不需要多线程的锁机制,因为多个协程从属于一个线程,不存在同时写变量冲突,效率比线程高。避免了加锁解锁的开销。

    4、进程和线程的区别

    线程是进程划分成的更小的运行单位,一个进程在其执行的过程中可以产生多个线程。
  1. 进程是资源分配的最小单位,而线程是 CPU 调度的最小单位;
  2. 创建进程或撤销进程,系统都要为之分配或回收资源,操作系统开销远大于创建或撤销线程时的开销;
  3. 不同进程地址空间相互独立,同一进程内的线程共享同一地址空间。一个进程的线程在另一个进程内是不可见的;
  4. 进程间不会相互影响,而一个线程挂掉将可能导致整个进程挂掉;

    5、进程有哪几种状态?

  • 创建状态(new) :进程正在被创建,尚未到就绪状态。
  • 就绪状态(ready) :进程已处于准备运行状态,即进程获得了除了处理器之外的一切所需资源,一旦得到处理器资源(处理器分配的时间片)即可运行。
  • 运行状态(running) :进程正在处理器上上运行(单核 CPU 下任意时刻只有一个进程处于运行状态)。
  • 阻塞状态(waiting) :又称为等待状态,进程正在等待某一事件而暂停运行如等待某资源为可用或等待 IO 操作完成。即使处理器空闲,该进程也不能运行。
  • 结束状态(terminated) :进程正在从系统中消失。可能是进程正常结束或其他原因中断退出运行。

    6、进程间的通信方式

    大概有 7 种 常见的进程间的通信方式。
    下面这部分总结参考了:《进程间通信 IPC (InterProcess Communication)》(opens new window) 这篇文章,推荐阅读,总结的非常不错。
  1. 管道/匿名管道(Pipes) :管道是一种半双工的通信方式,数据只能单向流动,而且只能在具有亲缘关系的进程间使用。进程的亲缘关系通常是指父子进程关系
  2. 有名管道(Names Pipes) : 匿名管道由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道。有名管道严格遵循先进先出(first in first out)有名管道以磁盘文件的方式存在,可以实现本机任意两个进程通信。匿名管道,由于没有名字,只能用于亲缘关系的进程间通信。为了克服这个缺点,提出了有名管道(FIFO)。
  3. 信号(Signal) :信号是一种比较复杂的通信方式,用于通知接收进程某个事件已经发生;

    1. 信号是Linux系统中用于进程间互相通信或者操作的一种机制,信号可以在任何时候发给某一进程,而无需知道该进程的状态。
    2. 如果该进程当前并未处于执行状态,则该信号就有内核保存起来,知道该进程回复执行并传递给它为止。
    3. 如果一个信号被进程设置为阻塞,则该信号的传递被延迟,直到其阻塞被取消是才被传递给进程。 | SIGHUP | 当用户退出终端时,由该终端开启的所有进程都退接收到这个信号,默认动作为终止进程。 | | —- | —- | | SIGINT | 程序终止(interrupt)信号,在用户键入INTR字符(通常是Ctrl+C)时发出,用于通知前台进程组终止进程。 | | SIGQUIT | 和SIGINT类似,但由QUIT字符(通常是Ctrl+\)来控制。进程在因收到SIGQUIT退出时会产生core文件,在这个意义上类似于一个程序错误信号。 | | SIGKILL | 用来立即结束程序的运行. 本信号不能被阻塞、处理和忽略。 | | SIGTERM | 程序结束(terminate)信号, 与SIGKILL不同的是该信号可以被阻塞和处理。通常用来要求程序自己正常退出。 | | SIGSTOP | 停止(stopped)进程的执行。注意它和terminate以及interrupt的区别:该进程还未结束, 只是暂停执行。 本信号不能被阻塞,处理或忽略。 |
  4. 消息队列(Message Queuing) 消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。管道和消息队列的通信数据都是先进先出的原则。与管道(无名管道:只存在于内存中的文件;命名管道:存在于实际的磁盘介质或者文件系统)不同的是消息队列存放在内核中,只有在内核重启(即,操作系统重启)或者显式地删除一个消息队列时,该消息队列才会被真正的删除。消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比 FIFO 更有优势。消息队列克服了信号承载信息量少,管道只能承载无格式字 节流以及缓冲区大小受限等缺点。

    1. 消息队列特点总结:
      1. 消息队列是消息的链表,具有特定的格式,存放在内存中并由消息队列标识符标识。
      2. 消息队列允许一个或多个进程向它写入与读取消息。
      3. 管道和消息队列的通信数据都是先进先出的原则。
      4. 消息队列可以实现消息的随机查询,消息不一定要以先进先出的次序读取,也可以按消息的类型读取.比FIFO更有优势。
      5. 消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺。
      6. 目前主要有两种类型的消息队列POSIX 消息队列以及 System V消息队列,系统V消息队列目前被大量使用。系统V消息队列是随内核持续的,只有在内核重起或者人工删除时,该消息队列才会被删除。
  5. 信号量(Semaphores) :信号量是一个计数器,用于多进程对共享数据的访问,信号量的意图在于进程间同步。这种通信方式主要用于解决与同步相关的问题并避免竞争条件。
    1. 对信号量的操作分为 P 操作和 V 操作,P 操作是将信号量的值减一,V操作是将信号量的值加一。当信号量的值小于等于0之后,再进行P操作时,当前进程或线程会被阻塞,直到另一个进程或线程执行了V操作将信号量的值增加到大于0之时。锁也是用的这种原理实现的。
    2. 信号量我们需要定义信号量的数量,设定初始值,以及决定何时进行PV操作。
  6. 共享内存(Shared memory) :使得多个进程可以访问同一块内存空间,不同进程可以及时看到对方进程中对共享内存中数据的更新。这种方式需要依靠某种同步操作,如互斥锁和信号量等。可以说这是最有用的进程间通信方式。
    1. 进程间本身的内存是相互隔离的,而共享内存机制相当于给两个进程开辟了一块二者均可访问的内存空间,这时,两个进程便可以共享一些数据了。但是,多进程同时占用资源会带来一些意料之外的情况,这时,我们往往会采用上述的信号量来控制多个进程对共享内存空间的访问。
  7. 套接字(Sockets) : 此方法主要用于在客户端和服务器之间通过网络进行通信。套接字是支持 TCP/IP 的网络通信的基本操作单元,可以看做是不同主机之间的进程进行双向通信的端点,简单的说就是通信的两方的一种约定,用套接字中的相关函数来完成通信过程。
    1. 套接字可以看做是:不同主机之间的进程进行双向通信的端点。(套接字 = IP地址 + 端口号)

image.png

请介绍线程之间的通信方式

  1. 锁机制:包括互斥锁、条件变量、读写锁互斥锁提供了以排他方式防止数据结构被并发修改的方法。读写锁允许多个线程同时读共享数据,而对写操作是互斥的。条件变量可以以原子的方式阻塞进程,直到某个特定条件为真为止。对条件的测试是在互斥锁的保护下进行的。条件变量始终与互斥锁一起使用。
  2. 信号量机制(Semaphore):包括无名线程信号量和命名线程信号量
  3. 信号机制(Signal):类似进程间的信号处理线程间的通信目的主要是用于线程同步,所以线程没有像进程通信中的用于数据交换的通信机制。

    说说僵尸进程和孤儿进程

    我们知道在unix/linux中,正常情况下,子进程是通过父进程创建的,子进程在创建新的进程,子进程的结束和父进程的运行是一个异步过程,即父进程永远无法预测子进程到底什么时候结束。 当一个进程完成它的工作终止之后,它的父进程需要调用 wait() 或者 waitpid() 系统调用取得子进程的终止状态。

  4. 孤儿进程一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。

  5. 僵尸进程一个进程使用 fork 创建子进程,如果子进程退出,而父进程并没有调用 wait 或 waitpid获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵尸进程。

    说一说进程的状态

  6. 创建状态:进程在创建时需要申请一个空白PCB,向其中填写控制和管理进程的信息,完成资源分配。如果创建工作无法完成,比如资源无法满足,就无法被调度运行。

  7. 就绪状态:进程已经准备好,已分配到所需资源,只要分配到CPU就能够立即运行。
  8. 执行状态:进程处于就绪状态被调度后,进程进入执行状态。
  9. 阻塞状态:正在执行的进程由于某些事件(I/O请求,申请缓存区失败)而暂时无法运行,进程受到阻塞。在满足请求时进入就绪状态等待系统调用。
  10. 终止状态:进程结束,或出现错误,或被系统终止,进入终止状态。

image.png

什么情况下会产生死锁?

如果在计算机系统中同时具备下面四个必要条件时,那么会发生死锁。换句话说,只要下面四个条件有一个不具备,系统就不会出现死锁。

  1. 互斥条件。即某个资源在一段时间内只能由一个进程占有,不能同时被两个或两个以上的进程占有。这种独占资源如CD-ROM驱动器,打印机等等,必须在占有该资源的进程主动释放它之后,其它进程才能占有该资源。这是由资源本身的属性所决定的。如独木桥就是一种独占资源,两方的人不能同时过桥。
  2. 不剥夺条件。进程所获得的资源在未使用完毕之前,资源申请者不能强行地从资源占有者手中夺取资源,而只能由该资源的占有者进程自行释放。如过独木桥的人不能强迫对方后退,也不能非法地将对方推下桥,必须是桥上的人自己过桥后空出桥面(即主动释放占有资源),对方的人才能过桥。
  3. 请求和保持条件。进程至少已经占有一个资源,但又申请新的资源;由于该资源已被另外进程占有,此时该进程阻塞;但是,它在等待新资源之时,仍继续占用已占有的资源。还以过独木桥为例,甲乙两人在桥上相遇。甲走过一段桥面(即占有了一些资源),还需要走其余的桥面(申请新的资源),但那部分桥面被乙占有(乙走过一段桥面)。甲过不去,前进不能,又不后退;乙也处于同样的状况。
  4. 循环等待条件。存在一个进程等待序列{P1,P2,…,Pn},其中P1等待P2所占有的某一资源,P2等待P3所占有的某一源,……,而Pn等待P1所占有的的某一资源,形成一个进程循环等待环。就像前面的过独木桥问题,甲等待乙占有的桥面,而乙又等待甲占有的桥面,从而彼此循环等待

上面提到的这四个条件在死锁时会同时发生。也就是说,只要有一个必要条件不满足,则死锁就可以排除。

说一说你对自旋锁的理解。

旋锁的定义:当一个线程尝试去获取某一把锁的时候,如果这个锁此时已经被别人获取(占用),那么此线程就无法获取到这把锁,该线程将会等待,间隔一段时间后会再次尝试获取。
这种采用循环加锁 -> 等待的机制被称为自旋锁(spinlock)。
自旋锁有以下特点

  1. 用于临界区互斥
  2. 在任何时刻最多只能有一个执行单元获得锁
  3. 要求持有锁的处理器所占用的时间尽可能短
  4. 等待锁的线程进入忙循环

自旋锁存在的问题

  1. 如果某个线程持有锁的时间过长,就会导致其它等待获取锁的线程进入循环等待,消耗CPU。使用不当会造成CPU使用率极高。
  2. 无法满足等待时间最长的线程优先获取锁。不公平的锁就会存在“线程饥饿”问题。

自旋锁的优点

  1. 自旋锁不会使线程状态发生切换,一直处于用户态,即线程一直都是active的;不会使线程进入阻塞状态,减少了不必要的上下文切换,执行速度快
  2. 非自旋锁在获取不到锁的时候会进入阻塞状态,从而进入内核态,当获取到锁的时候需要从内核态恢复,需要线程上下文切换。(线程被阻塞后便进入内核(Linux)调度状态,这个会导致系统在用户态与内核态之间来回切换,严重影响锁的性能)

自旋锁与互斥锁的区别

  1. 自旋锁与互斥锁都是为了实现保护资源共享的机制。
  2. 无论是自旋锁还是互斥锁,在任意时刻,都最多只能有一个保持者。
  3. 获取互斥锁的线程,如果锁已经被占用,则该线程将进入睡眠状态;获取自旋锁的线程则不会睡眠,而是一直循环等待锁释放。

    谈谈IO多路复用。

    IO多路复用是一种同步IO模型,实现一个线程可以监视多个文件句柄;一旦某个文件句柄就绪,就能够通知应用程序进行相应的读写操作;没有文件句柄就绪时会阻塞应用程序,交出cpu。多路是指网络连接,复用指的是同一个线程。
    IO多路复用有三种实现方式:select, poll, epoll

  4. select:时间复杂度O(n),它仅仅知道了,有I/O事件发生了,却并不知道是哪那几个流(可能有一个,多个,甚至全部),只能无差别轮询所有流,找出能读出数据,或者写入数据的流,对他们进行操作。所以select具有O(n)的无差别轮询复杂度,同时处理的流越多,无差别轮询时间就越长。

    1. int select (int n, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
    1. select 函数监视的文件描述符分3类,分别是writefds、readfds、和exceptfds。调用后select函数会阻塞,直到有描述副就绪(有数据 可读、可写、或者有except),或者超时(timeout指定等待时间,如果立即返回设为null即可),函数返回。当select函数返回后,可以 通过遍历fdset,来找到就绪的描述符。
    2. select目前几乎在所有的平台上支持,其良好跨平台支持也是它的一个优点。select的一 个缺点在于单个进程能够监视的文件描述符的数量存在最大限制,在Linux上一般为1024,可以通过修改宏定义甚至重新编译内核的方式提升这一限制,但是这样也会造成效率的降低。

      7、Linux里如何查看一个想知道的进程?

      查看进程运行状态的指令:ps命令。“ps -aux | grep PID”,用来查看某PID进程状态 ```shell // ps使用示例 // 显示当前所有进程
      ps -A

// 与grep联用查找某进程
ps -aux | grep apache

// 查看进程运行状态、查看内存使用情况的指令均可使用top指令。 top

  1. <a name="vJSQT"></a>
  2. ## 8、Linux里如何查看带有关键字的日志文件?
  3. 1. **cat 路径/文件名 | grep 关键词**
  4. ```shell
  5. // 返回test.log中包含http的所有行
  6. cat test.log | grep "http"
  1. grep -i 关键词 路径/文件名 (与方法一效果相同,不同写法而已)
    // 返回test.log中包含http的所有行(-i忽略大小写)
    grep -i "http" ./test.log
    

    9、说说你对grep命令的了解?

    grep 命令。强大的文本搜索命令,grep(Global Regular Expression Print) 全局正则表达式搜索。
    grep 的工作方式是这样的,它在一个或多个文件中搜索字符串模板。如果模板包括空格,则必须被引用,模板后的所有字符串被看作文件名。搜索的结果被送到标准输出,不影响原文件内容。 ```shell
  2. // 参数
  3. -A n —after-context显示匹配字符后n行
  4. -B n —before-context显示匹配字符前n行
  5. -C n —context 显示匹配字符前后n行
  6. -c —count 计算符合样式的列数
  7. -i 忽略大小写
  8. -l 只列出文件内容符合指定的样式的文件名称
  9. -f 从文件中读取关键词
  10. -n 显示匹配内容的所在文件中行数
  11. -R 递归查找文件夹
  12. //grep 的规则表达式:
  13. ^ # 锚定行的开始 如:’^grep’匹配所有以grep开头的行。
  14. $ # 锚定行的结束 如:’grep$’匹配所有以grep结尾的行。
  15. . # 匹配一个非换行符的字符 如:’gr.p’匹配gr后接一个任意字符,然后是p。
    • 匹配零个或多个先前字符 如:’*grep’匹配所有一个或多个空格后紧跟grep的行。

  16. .* # 一起用代表任意字符。
  17. [] # 匹配一个指定范围内的字符,如’[Gg]rep’匹配Grep和grep。
  18. [^] # 匹配一个不在指定范围内的字符,如:’[^A-FH-Z]rep’匹配不包含A-R和T-Z的一个字母开头,紧跟rep的行。
  19. (..) # 标记匹配字符,如’(love)‘,love被标记为1。
  20. \< # 锚定单词的开始,如:’\<grep’匹配包含以grep开头的单词的行。
  21. > # 锚定单词的结束,如’grep>‘匹配包含以grep结尾的单词的行。
  22. x{m} # 重复字符x,m次,如:’0{5}‘匹配包含5个o的行。
  23. x{m,} # 重复字符x,至少m次,如:’o{5,}‘匹配至少有5个o的行。
  24. x{m,n}# 重复字符x,至少m次,不多于n次,如:’o{5,10}‘匹配5—10个o的行。
  25. \w # 匹配文字和数字字符,也就是[A-Za-z0-9],如:’G\w*p’匹配以G后跟零个或多个文字或数字字符,然后是p。
  26. \W # \w的反置形式,匹配一个或多个非单词字符,如点号句号等。
  27. \b # 单词锁定符,如: ‘\bgrep\b’只匹配grep。

// 实例

  1. // 查找指定进程
  2. ps -ef | grep svn
  3. // 查找指定进程个数
  4. ps -ef | grep svn -c
  5. // 从文件中读取关键词
  6. cat test1.txt | grep -f key.log
  7. // 显示包含 ed 或者 at 字符的内容行
  8. grep -E ‘ed|at’ test.txt
    ```

    10、Linux中,如何通过端口查进程,如何通过进程查端口?

  9. linux下通过进程名查看其占用端口: ```shell // 先查看进程pid ps -ef | grep 进程名

// 通过pid查看占用端口 netstat -nap | grep 进程pid


2. **linux通过端口查看进程**:
```shell
netstat -nap | grep 端口号