Linux复习

Happiness is any appearance, should on the road.

linux操作系统知识点

笔记本: 知识点复习

创建时间: 2017/5/21 15:18 更新时间: 2017/8/6 21:49 作者:

​ 2401503224@qq.com

1、 什么是进程、进程间的通信方式

2、什么是线程、线程的同步以及通信方式 3、进程和线程的区别与联系 4、死锁产生的必要条件、怎样避免。

5、 I/O的方式、I/O多路复用

6、 什么情况下进程会被挂起

7、 什么是僵尸进程、孤儿进程、守护/精灵进程

8、 System V版本和POSIX版本的进程间通信的区别

9、 进行I/O操作的时候,读端和写端的4中特殊情况。

10、 软连接和硬连接的区别

11、 信号处理函数

12、 终端、会话、作业、守护进程

13、 linux登录过程

14、 linux内核都有什么

15、 linux虚拟内存、地址空间

16、 文件描述符等概念,每个进程默认分配多少文件描述符

17、 linux堆多大,栈多大,每个进程分配文件描述符有多少

18、 shell的执行过程

19、 gdb多进程、多线程调试、core dump

多进程:http://blog.csdn.net/lf_2016/article/details/60134523 多线

程: http://blog.csdn.net/lf_2016/article/details/59741705 GDB常用指令: http://blog.csdn.net/lf_2016/article/details/54172864

gcc 常用选项: http://blog.csdn.net/lf_2016/article/details/55532364

20、 shell语法、正则及工具的使用

shell: http://blog.csdn.net/lf_2016/article/details/70477782 正则: http://blog.csdn.net/lf_2016/article/details/70242002 grep:http://blog.csdn.net/lf_2016/article/details/70243475 cut:http://blog.csdn.net/lf_2016/article/details/70477660 sed:

http://blog.csdn.net/lf_2016/article/details/70455439

awk: http://blog.csdn.net/lf_2016/article/details/70477286

21、 linux处理定时任务,crontab

http://blog.csdn.net/lf_2016/article/details/57390570

22、 生产者消费者模型的实现

http://blog.csdn.net/lf_2016/article/details/56064377

23、 静态库、动态库的使用及创建

http://blog.csdn.net/lf_2016/article/details/55563442

24、 文件系统

25、 top、ps、iostat、pidof、df、lsof、stat、等系统工具的使用

1、什么是进程、进程间的通信方式

进程

​ 进程是程序的一次执行实例,是系统分配资源的最小单位。每个进程在内核中都有一个进程控制块PCB来维护进程相关信息,linux内核中的进程控制块是task_struct结构体。

task_struct中包含进程的pid、进程状态、优先级、程序计数器、内存指针、上下文数据、I/O状态信息、记账信息等。

  • 进程的状态:就绪状态、执行状态、暂停状态、深度睡眠、浅度睡眠、僵死状态。僵尸状态是一个比较特殊的状态,当进程退出并且父进程没有读取子进程退出的返回代码,则进程这时候就变成了僵死状态。僵尸进程会一直占用着系统资源不放,直到父进程对它进行回收。
  • PRI表示进程可被执行的优先级,其值越小越早被执行。用户可以微调内核修改进程的优先级,通过nice值进行修改,但是nice值也是有限制的,nice值:-20-19。
  • 进程的创建:创建进程有两种方式,fork和execve系统调用,但是他们的运行方式有点不同。

​ 通过fork系统调用创建的子进程,子进程会得到父进程中数据段、栈段和堆区域的一份拷贝,子进程可以修改这些内存段,但是文本段是子进程和父进程共享的内存段。由于 fork后经常exec,所以子进程使用的是写时拷贝技术。

​ 通过 execve创建一个新进程,这个系统调用会销毁所有内存段重新创建一个新的内存段,并且可以执行和父进程不同的代码。

​ vfoek和fork的区别:vfork保证子进程先运行,在子进程exit之后父进程才可能被调用。vfork创建子进程后,不会立即将父进程的地址空间复制到子进程中。

  • 进程间的通信方式(IPC): linux中的进程间通信有System V版本、POSIX版本、 UNINX进程间通信等。通信方式有:管道(pipe)、命名管道(mkfifo)、消息队列(msg)、信号量(sem)、共享内存(shm)、信号、socket、 socketpair。

    匿名管道(pipe)

​ 匿名管道是单工的,pipe(intfd[2]),只能用0 读,用1写,匿名管道必须用于具有亲缘关系的进程间的通信。匿名管道的声明周期随进程。

​ 匿名管道的原理就是在内核中开辟一块缓冲区,进程1把数据拷到内核缓冲区,进程而再拷走。管道缓冲区的大小是4k,内核缓冲区是环形队列,可以用uliimit查看。

命名管道(mkfifo)

​ 命名管道是一个设备文件,存在于磁盘上,命名管道提供一个路径名,只要进程可以访问该路径就可以通过 fifo 进行通信,所以命名管道可以使没有亲缘关系的进程间通信,命名管道的声明周期随内核。通过

mkfifo(path,S_FIFO|mode)创建命名管道之后,接下来的通信方式就和操作文件一样了。

消息队列(msg):

​ 消息队列是消息的链接表,有足够权限的进程可以向队列中添加消息,被赋予读权限的进程可以从队列中读走消

息,消息队列发送的是消息块,而且每一个消息块的大小都是有上限的(MSGMAX)。

​ 用msgget(key_tkey,int msflag)创建一个消息队列,msgrecv用来读取消息,msgsend发送消息。

​ 消息队列的生命周期也是随内核的。可以用 ipcs -q查看已经创建的消息队列,用ipcrm -q删除已经创建的消息队列。

信号量(sem):

​ 信号量本质是一种数据操作锁,它本身不具有数据交换的功能。信号量就像是一个计数器,负责数据操作的互斥和同步功能。信号量一般好共享内存配合使用。信号量的生命周期随内核。

​ semget(key_t key,int nsems,intsemflg) 用来创建一个信号量集合。semop用来对信号量进行PV操作。需要注意的是,信号量可以设置SEM_UNDO,它表示在程序结束时,信号量的值会恢复成调用semop之前的值。这样可以避免死锁的产生。

​ 可以用ipcs-s查看创建的信号量集合的个数。 ipcrm -s删除一个信号量集合。

共享内存:

​ 共享内存就是在内存中开辟一块空间,让两个进程同时关联这个内存(由页表完成虚拟地址和物理地址的映射),这样的话对这块内存进行操作的话,两个进程都可以看到,也就达到了通信的目的。共享内存是前五种通信方式中最高效的,因为共享内存的数据拷贝不涉及到内核,而是直接写到内存中。前四种都需要先拷贝到内核中,然后再拷贝到用户缓冲区。

shmget(key_t key,size_t size,int flag)用来创建一块共享内存,一般都是申请4k的整数倍。shmat 的作用是将申请的共享内存挂接在该进程的页表上,使得虚拟地址和物理地址相对应。shmdt的作用是去挂接,将这块共享内存从页表上剥离下来。可以使用ipcs -m查看系统中创建的共享内存,使用ipcrm -m销毁创建的共享内存。

socket:

​ socket套接字是POSIX版本中的进程间通信方式,一般用与网络中的两个进程间的通信。

​ socketpair: 域间套接字只能用于一台主机上的两个进程间的通信。int socketpair(int domain, int type, int protocol, intsv[2]),创建的管道是双工的,两个文件描述符都可以进行读写,但是必须是一个读一个写。其余的操作和匿名管道一模一样。

2、什么是线程、线程间同步与互斥

线程:

​ 线程在进程的内部运行的执行流,线程是内核执行的最小单位,linux下的线程都是用进程模拟的,所以linux下的线程又叫做轻量级进程。同一个进程的多个线程共享同一地址空间,因此同一个进程的所有线程共享数据段和代码,但每个线程都有自己的栈空间,因此线程间通信就非常简。

​ 线程间共享的资源:文件描述符、信号的处理方式、用户id和组id、堆空间和代码段等。

​ 线程私有的资源:栈空间、上下文、线程id、信号屏蔽字,调度优先级等。

​ 通过pthread_t pthread_create(pthread_t,pthread_attr_t,void *(*start_routine)(void *),void *arg)创建一个线程。 return/pthread_exit/pthread_cancel终止一个线程。

pthread_join等待一个线程,一般是由主线程等到回收子线程。

pthread_detach用来分离一个线程,分离之后可以不用回收该线程。通过pthread_self可以获取一个线程的id,通过ps -aL可以查看线程id。

线程间同步与互斥:

  • mutex(互斥锁):多个线程同时访问临界资源时可能会发生冲突,这时我们可以用互斥锁来保证线程间同步访问临界资源。pthread_mutex_t创建一个互斥锁。pthread_mutex_lock用来申请一个个互斥锁,pthread_mutex_unlock用来释放一个互斥锁,申请和释放互斥锁都是原子操作。线程间同步的话有可能会产生死锁的问题。
  • 条件变量(cond):在pthread库中,通过条件变量来阻塞等待一个条件,或者唤醒正在等待这个条件的线程,条件变量一般和互斥锁配合使用。pthread_cond_t用来创建一个条件变量, pthread_cond_wait(pthread_cond_t*cond,pthread_mutex_t *mutex)用来等待一个条件变量, pthread_cond_signal用来唤醒一个条件变量。
  • 信号量(semaphore):信号量可以表示可用资源的数量,像mutex就是一种二元信号量。sem_t sem用来创建一个信号量,使用sem_init(sem_t* sem,int pshared,usigned int value)来初始化一个信号量,sem_wait用来获取资源相当于P操作,信号量的值减1.sem_post用来释放资源,相当于V操作,信号量的值加1,同时他还会唤醒挂起等待的进程。
  • 自旋锁:

​ 读写锁就是一种简单的自旋锁。所谓自旋锁已经被别的执行单元保持,调用者就会一直循环在哪里看是否自旋锁保持者已经释放了锁。因为自旋锁一直占用着CPU,它在未获得锁的情况下会一直运行,所以自旋锁适合在很短时间内就能获得锁的情况。

3、进程和线程的区别与联系

区别:

  • 进程是内核分配资源的单位,线程是执行的单位。
  • 进程间的通信方式有8种,线程间通信方式用全局变量即可。
  • 进程间相互独立。而同一进程中的线程则共享堆空间、数据段和代码段。
  • 线程存在线程安全问题,进程相比线程则耗费的资源多。
  • 线程切换要比进程切换快得多。

4、死锁产生的方式、如何避免 死锁产生的四个必要条件:

  • 互斥:一个资源每次只能被一个线程使用。
  • 请求与保持:一个线程因为请求资源被阻塞时,对于已获得的资源保持不放。
  • 不可剥夺与不可抢占:对于已获得资源,在未使用完之前不能强行被剥夺。
  • 回环等待:多个进程之间形成一种首尾相接的循环等待资源关系。

如何避免死锁:

​ 一种简单的方法就是破坏产生死锁的四个必要条件中的一个。 还有就是在申请资源的时候进行同步。银行家算法进行预分配,避免死锁。

5、I/O操作的方式、I/O多路复用的比较

I/O方式:

​ 五种I/O模型:阻塞、非阻塞、I/O多路复用、信号、异步I/O。

同步I/O和异步I/O的区别:

同步I/O和异步I/O的主要区别就是,当发起一个I/O操作之后,内核向用户返回的是就绪事件还是完成事件。如果是同步I/O,则会返回就绪事件,并由用户进行数据I/O。如果是异步I/O,则会返回完成事件,数据I/O 已经由内核完成。

I/O多路复用:

​ I/O操作一般分为两个过程:等待和数据搬迁,一般来说等待会花费大量的时间。为了提高I/O的效率,就有了I/O多路复用模型,I/O多路复用是一种同步I/O,有三种方式,分别是 select、poll、epoll,其中epoll的效率最高。

  • select:

    select利用三个集合分别记录可读、可写、异常事件,因为这三个集合是输入输出型参数,当事件发生时由内核进行修改,所以每次进行select之前都要对要监测的文件描述符进行重新注册。

    每次调用select的时候,都要把文件描述符集合从用户态拷贝到内核态,当文件描述符多的时候开销大。

    select在返回之后,需要我们去遍历所有检测的文件描述符找到其中的就绪事件,时间复杂度是O(N)。

    select支持的文件描述符数量有限,默认是 1024。

  • poll:

​ poll用一种结构体结构体来管理要监测的事件,这个结构体里面的events记录了要检测的事件,而revents 则记录就绪的事件,由内核设定。所以poll只需要注册一次要监测的事件就可以了,不用每次都注册。同时poll中并没有对监测事件数量的限制,理论上能监测的数量就是文件描述符的数量。

  • epoll:

​ epoll通过epoll_create创建一个事件表,以红黑树的形式记录了要监测的文件描述符,由内核进行维护。我们通过epoll_ctl注册文件描述符后,一旦文件描述符就绪,内核会采取类似于callback的回调机制,将就绪文件描述符从红黑树移到就绪队列中,这里使用了内存映射(mmap)技术,节省了文件描述符在系统调用时复制的开销。因为用户每次都是从就绪队列中拿去就绪的文件描述符进行处理,所以时间复杂度是O(1)。

​ epoll有LT和ET模式,LT模式下内核会多次通知你文件描述符就绪。ET则只通知一次,所以在ET模式下时要一次性将数据读完,必须将文件描述符设置为非阻塞的,否则如果写端一直没关闭的话,读端会一直被阻塞。循环读取时直到读到EAGAIN的时候才退出循环。

​ epoll能检测的文件描述符的数量的大小最大可以是打开文件的数目,linux下约是10万多。

7、什么是僵尸进程、孤儿进程、守护/精灵进程

僵尸进程:

​ 如果子进程先退出、父进程没有回收子进程的话,子进程就一直处于僵尸状态,由于一个进程也是需要一定的系统资源进行维护,所以僵尸进程太多的话,会加重系统的负担。

孤儿进程:

​ 如果父进程比子进程先退出的话,则子进程就变成了孤儿进程,孤儿进程会被1号进程(init进程)领养,1号进程负责孤儿进程的回收。

守护/精灵进程:

​ 守护进程是运行在后台的一种特殊的进程,它独立于控制终端并周期性的执行某种任务或等待处理某些发生的事件。 守护进程的父进程是1号进程。

8、System V版本和POSIX版本的进程间通信的区别

​ 一般来说System V版本的进程间通信用于进程,而POSIX版本的进程间通信用于线程。他们的区别主要在于信号量和共享内存。

信号量的区别:

https://www.sillybee.cn/2017/05/16/semaphore/

​ system v版本的信号量一般是随内核的,无论有无竞争都要执行系统调用,所以性能上比较差。它的接口是 semget,semctl,semop。

​ posix版本的信号量同时支持无命名的信号量和有命名的信号量。它在无竞争的时候是不陷入内核的。所以效率更高。

​ 无命名的信号量一般用于线程同步,它的生命周期是随进程的,它的主要接口有sem_init,sem_destory,sem_wait,sem_post。有命名的信号量一般用于进程同步,一般有一个文件关联他们,有命名的信号量是随内核的,它的主要接口有sem_open,sem_close,sem_unlink。

与共享内存的区别

https://www.sillybee.cn/2017/05/15/sharedmemeroy/

9、进行I/O操作的时候,读端和写端存在的四种情况 写端关闭,读端还未读完:recv读完之后,会返回0。

​ 写端没关,但是也没有在写,读端在读:管道中的数据被 读完之后,读端会阻塞,直到管道中又有数据了。

​ 读端关闭了,写端还在写:这时该进程会受到SIGPIPE信号,程序会异常终止。

​ 读端没有关闭,也没有在读,写端在写数据:这时写端写满管道之后会阻塞。

10、 软连接和硬连接的区别

  • 软连接:软连接也称为符号链接,只是保存了文件的路径,软连接可以指向不存在的文件。软连接的inode则和原文件的inode 是不同的。若原文件删除了,则软连接是不能再访问的。由于符号链接的特殊性质,所以软连接是可以跨磁盘分区的。使用In -s 建立软连接。
  • 硬连接:硬连接是对原文件起了一个别名,修改了原文件的引用计数,删除的时候修改的是引用计数,直到引用计数为0的时候,才真的删除原文件。硬连接是共享一个inode。使用In命令建立硬连接。

11、信号处理函数

信号产生的方式:

  • 通过终端按键产生信号。
  • 调用系统函数向进程发信号。
  • 由软件条件产生信号。

信号的状态:

​ 信号有阻塞,未决,递达三种状态。信号的实际处理过程称为信号递达,信号从产生和递达之间的过程称为未决。在 task_struct中维护了两张表,分别是阻塞(block)和未决 (pending)表,还有一个函数指针表示处理动作。信号产生时,内核在进程控制块中设置该信号的未决标志,直到信号递达才清除该标志。

信号的处理方式:

信号的处理方式有忽略,默认处理动作,和信号捕捉 (自定义处理函数)三种。

信号集操作函数:

sigprocmask可以设置信号屏蔽字等等。

信号捕捉:

  • 如果信号的处理动作是用户自定义函数,在信号递达就调用这个函数,称为信号捕捉。当发生中断或异常时,程序从用户态进入内核态,处理完中断或异常之后,在返回用户态之前,先去处理可以递达的信号,如果有信号递达,则切换会用户模式执行自定义函数,处理完之后再切换回内核态,再从内核态返回发生异常或中断的上下文中继续执行。
  • 捕捉信号的函数有sigaction和signal。

竞态条件:

由于时序问题,而导致程序出错。 12、进程组、作业、会话、守护进程

进程组:

每个进程除了有一个进程ID之外,还属于一个进程组。进程组是一个或多个进程的集合,他们与同一作业相关联,可以接受来自同一终端的各种信号。每个进程组有唯一的进程组ID,组长的ID和进程组ID相等。

作业:

shell分前后台控制的是作业,而不是进程或进程组。shell可以运行一个前台作业和任意多个后台作业,称为作业控制。jobs查看后台作业,fg将后台作业调到前台,bg让后台停止的作业运行起来。

会话:

会话是一个或多个进程组的集合,一个会话可以有一个控制终端,建立与控制终端连接的会话首进程被称为控制进程。

守护进程(精灵进程):

守护进程最大的特点就是和终端无关,在后台运行。守护进程用于周期性的执行某种任务或等待处理某些发生的事件。

13、linux终端登录过程

linux能够去区分命令和信号就是通过线路规程:

img

img

终端登录过程:

​ 系统启动时,init进程根据配置文件/etc/inittab确定需要打开哪些终端。

​ getty根据命令行参数打开终端设备作为他的控制终端,把文件描述符0,1,2,都指向控制终端,然后提示用户输入账号。用户输入账号之后,getty任务完成,它在执行login程序(进行程序替换),login程序提示用户输入密码。如果输入成功的话,设置一些环境变量,然后就开始启动bash(通过exec程序替换)。

​ 整个过程中从getty开始exec到login,在exec到 bash,其实都是同一个进程,因此控制终端没变,文件描述符 0,1,2,也任然指向控制终端。

14、 linux内核都有什么

​ linux内核构成:linux内核有进程管理,进程间通信,内存管理单元,网络协议栈,体系结构相关代码,设备驱动,文件系统,系统调用等组成。

img

15、 linux虚拟内存,地址空间

虚拟内存:

  • 首先,每个进程都会有自己独立的4G内存空间,各个进程的内存空间具有类似的结构。一个新进程建立的时候,会建立起自己的内存空间,此进程的数据,代码等从磁盘拷贝到自己的进程空间,task_struct记录中内存空间的分配情况。每个进程 已经分配的内存空间,都与对应的磁盘空间映射。
  • 每个进程的4G内存空间只是虚拟内存空间,每次访问内存空间的某个地址,都需要把地址翻译为实际物理地址。所有进程共享同一物理内存,每个进程只把自己目前需要的虚拟内存空间映射到物理内存上。虚拟内存到物理内存的映射关系四通过页表(还有MMU内存管理单元)维护的。
  • 页表的每一个表项分为两部分,第一部分记录此页是否在物理内存上,第二部分记录物理内存页的地址。当进程访问某个虚拟地址的时候,去看页表,如果发现对应的数据不在物理内存中的时候,则引起缺页异常。
  • 缺页异常的处理方式就是将数据从磁盘拷贝到物理内存上,如果物理内存已经满了,则会发生换页(将物理内存上的某一页写会磁盘,将磁盘中的数据覆盖到这一页中)。
  • 在每个进程创建加载时,内核只是为了进程“创建”了虚拟内存的布局,也就是mem_struct结构,实际上并不立即把虚拟内存对应位置的程序和代码拷贝到物理内存中,只是建立好虚拟内存和磁盘文件之间的映射(存储器映射),等到运行到对应的程序时,才会通过缺页异常来拷贝数据。
  • 还有进程运行过程中,要动态分配内存时,也只是分配例如虚拟内存,即为这块虚拟内存对应的页表做相应设置,当真正访问到此数据时,才引发缺页异常。

地址空间:

在linux中,前3G是用户空间,后1G是内核空间,地址空间的结构是:环境变量,argv,argc,栈,共享映射区,堆,数据段(data,bss),文本段(只读存储区)。

16、什么是文件描述符,单个进程的文件描述符数量,整个系统的文件描述符数量。

什么是文件描述符:

​ 在linux中“一切皆文件”,文件又可以分为普通文件、目录文件、设备文件、链接文件、管道文件等,而文件描述符是内核为了高效的管理所打开文件所创建的索引,它的值就是一个非负整数,用于指代被打开的文件,所有执行I/O操作的系统调用都是通过文件描述符。默认有0,1,2,三个文件描述符被打开。

​ 每个文件描述符都会与一个打开文件相对应,同时不同的文件描述符也会指向同一个文件。相同的文件可以被不同的进程打开,也可以在一个进程中被打开多次。系统为每个进程都维护了一张文件描述符表,所以在不同进程中会看到相同的文件描述符,这种情况下有可能指向相同的文件,也有可能指向不同的文件。

img 单个进程的文件描述符数量:1024,使用ulimit可以查看。 整个系统的文件描述符数量:10万多个。 在/proc/sys/fs/file-max里面保存着。

17、linux下堆多大,栈多大,windos下堆多大,栈多大

  • linux:堆理论上是2^32,即4G的大小,但是由于有1G的内核空间,所以linux下堆最多可以是3G。linux下栈的大小是10M(ulimit可以查看)。
  • windos:堆和栈都是1G的虚拟内存,实际上只有4K的物理内存。

18、shell的执行过程

对于内置命令和非内置命令,shell的执行方式是不同的。

内置命令:

​ 内置命令会改变shell的参数。比如cd,exit等。内置命令是一些比较简单的命令,这些命令由shell识别并在shell内部运行完成。通常在linux系统加载运行时shell就被加载并驻留在系统内存中。内置命令是直接写在bash的源码里面的,执行速度比外部命令要快,因为他不需要创建子进程。

非内置命令:

非内置命令不会改变shell的参数,比如ls。非内置命令是linux系统中的使用程序部分,因为实用程序的功能通常都比较强大,所以包含的程序量很大,在系统加载的时候并不随系统一起被加载到内存当中,而是在需要的时候才将其调入内存。shell程序管理非内置命令执行的路径查找,加载存放,控制命令执行。

对于非内置命令,父shell先fork一个子shell,然后子进程去执行bash的代码,从文本中一行一行的读取命令,然后再派生一个孙子进程去执行这些命令。

img

19、gdb多进程调试,多线程调试,core dump文件

​ core dump:coredump文件要产生的话,用ulimt -c查看 core文件的大小不能为0。core dump文件是程序程序运行的过程中异常终止或崩溃,操作系统会将当时内存的状态保存下来,保存在一个文件中,这个文件就是core dump。

但实际上core dump 还会同时记下寄存器信息、内存管理信息,其他处理器和操作系统信息。 在gdb调试过程中,使用core-file可以运行core dump 文件。