1.什么是gcc
gcc是一个能够编译多种语言的编译器。最开始gcc是作为C语言的编译器(GNU C Compiler),现在除了c语言,还支持C++、java、Pascal等语言。
2.gcc工作流程
- 预处理(--E)
- 宏替换
- 头文件展开
- 去掉注释
- .c文件变成了.i文件(本质上还是.c文件,只不过#include中的程序给链接进去)
- 编译(--S)
- gcc调用不同语言的编译器
- .i文件编程.s(汇编文件)
- 生成汇编文件
- 汇编(-c)
- .s文件转化成.o文件
- 翻译成机器语言指令
- 二进制文件
- 链接
- .o文件变成可执行文件,一般不加后缀
3.gcc常用参数
- -v/--version:查看gcc的版本
- -I:编译的时候指定头文件路径,不然头文件找不到
- -c:将汇编文件转换成二进制文件,得到.o文件
- -g:gdb调试的时候需要加
- -D:编译的时候指定一个宏(调试代码的时候需要使用例如printf函数,但是这种函数太多了对程序性能有影响,因此如果没有宏,则#ifdefine的内容不起作用)
- -wall:添加警告信息
- -On:-O是优化代码,n是优化级别:1,2,3
4.静态库和动态库
1. 什么是库?
- 库是写好的现有的,成熟的,可以复用的代码。
- 现实中每个程序都要依赖很多基础的底层库,不可能每个人的代码都从零开始,因此库的存在意义非同寻常。
- 库是二进制文件,.o文件
- 将源代码变成二进制的源代码
- 主要起到加密的作用,为了防止泄露
2. 静态库的制作
- 原材料:源代码(.c或.cpp文件)
- 将.c文件生成.o文件(ex:g++ a.c -c)
- 将.o文件打包
- ar rcs 静态库名称 原材料(ex: ar rcs libtest.a a.0)
3. 动态库的制作(so代表动态库)
- 命名规则:libxxx.so
- 制作步骤
- 将源文件生产.o文件(gcc a.c -c -fpic)
- 打包(gcc -shared a.o -o libxxx.so)
- 动态库的使用
- 跟静态库一样
- 动态库无法加载的问题
- 使用环境变量(临时设置和全局设置)
4.gdb
5.linux权限相关问题
ls -l显示如下内容
-rwxrw-r-- 1 root root 1213 Feb 2 09:39 adc
0-9位说明:
1.第0位确定文件类型(d,-,,l,c,b):
“-”表示普通文件,如".txt",".c",".h"
"d"表示目录
“l”表示软链接文件
"b"是块设备,比如硬盘
"c"是字符设备文件,如鼠标、键盘
2.第1-9位
第1-3位确定所有者(该文件的所有者)拥有该文件的权限。 --User
第4-6位确定所属组(同用户组的)拥有该文件的权限, --Group
第7-9位确定其他用户拥有该文件的权限 --Other
rwx权限详解:
r代表可读:可以读取
w代表可写:可以修改,但是不代表可以删除该文件,删除一个文件的前提条件是对该文件所在的目录有写权限,才能删除该文件
x代表可执行:可以被执行。
可用数字表示:
r=4;w=2,x=1因此rwx=4+2+1=7,数字可以进行组合
其他说明:
1 文件:硬连接数或 目录:子子目录数
root 用户
root 组
1213 文件大小(字节),如果是文件夹,显示4096字节
Feb 2 09:39 最后修改日期
abc 文件名
改变用户权限:
方式一:+、-、=变更权限
u:所有者 g:所有组 o:其他人 a:所有人(u、g、o的总和)
1)chmod u=rwx,g=rx,o=x 文件/目录名
2)chmod o+x 文件/目录名
3)chmod a-x 文件/目录名
方式二:通过数字变更权限
chmod 751 文件/目录名
改变文件所有者
chown newowner 文件/目录
6.windows和linux的区别
1.开源性:linux是开源的,任何人都可以查看和修改源代码;windows是闭源的
2.用户界面:windows的用户界面相对直观易于上手;linux命令行功能强大,不推崇界面
3.应用程序:windows有大量的软件和游戏,linux更适合开发和运行开源软件
4.安全性:linux比windows更安全,因为不需要频繁的更新和修补漏洞
5.硬件支持:windows运行缓慢,特别是在旧硬件上,linux运行速度显著更快。
7.共享内存段被映射进进程空间之后,存在于进程空间的什么位置?共享内存段最大限制是多少?
将一块内存映射到两个或者多个进程地址空间。通过指针访问该共享内存区。一般通过mmap将文件映射到进程地址共享区。存在于进程数据段,最大限制是32M。
8.动态链接和静态链接的区别?
动态链接是只建立一个引用的接口,而真正的代码和数据存放在另外的可执行模块中,在可执行文件运行时再装入;而静态链接是把所有的代码和数据都复制到本模块中,运行时就不再需要库了
9.说出你所知道的各类linux系统的各类同步机制(重点),什么是死锁?如何避免死锁(每个技术面试官必问)
10.系统如何将一个信号通知到进程?
内核给进程发送信号,是在进程所在的进程表项的信号域设置对应的信号的位。进程处理信号的时机就是从内核态即将返回用户态的时候。
执行用户自定义的信号处理函数的方法很巧妙。把该函数的地址放在用户栈栈顶,进程从内核返回到用户态的时候,先弹出信号处理函数地址,于是就去执行信号处理函数了,然后再弹出,才是返回进入内核时的状态。
11.在磁盘中打开文件往里面写数据操作系统都做了什么事
读文件
进程调用库函数向内核发起读文件请求;
内核通过检查进程的文件描述符定位到虚拟文件系统的已打开文件列表表项;
调用该文件可用的系统调用函数read()
read()函数通过文件表项链接到目录项模块,根据传入的文件路径,在目录项模块中检索,找到该文件的inode;
在inode中,通过文件内容偏移量计算出要读取的页;
通过inode找到文件对应的address_space;
在address_space中访问该文件的页缓存树,查找对应的页缓存结点:
如果页缓存命中,那么直接返回文件内容;
如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页;重新进行第6步查找页缓存;
文件内容读取成功。
写文件
9. 前6步和读文件一致,在address_space中查询对应页的页缓存是否存在:
1. 如果页缓存命中,直接把文件内容修改更新在页缓存的页中。写文件就结束了。这时候文件修改位于页缓存,并没有写回到磁盘文件中去。
2. 如果页缓存缺失,那么产生一个页缺失异常,创建一个页缓存页,同时通过inode找到文件该页的磁盘地址,读取相应的页填充该缓存页。此时缓存页命中,进行第6步。
2. 一个页缓存中的页如果被修改,那么会被标记成脏页。脏页需要写回到磁盘中的文件块。有两种方式可以把脏页写回磁盘:
1. 手动调用sync()或者fsync()系统调用把脏页写回
2. pdflush进程会定时把脏页写回到磁盘
同时注意,脏页不能被置换出内存,如果脏页正在被写回,那么会被设置写回标记,这时候该页就被上锁,其他写请求被阻塞直到锁释放。
12.内存泄漏
1.堆内存泄漏(Heap leak)
堆内存指的是程序执行中依据须要分配通过malloc / calloc / realloc / new等从堆中分配的一块内存,用完后必须通过调用相应的 free或者delete 删掉。假设程序的设计错误导致这部分内存没有被释放,那么以后这部分空间将无法再被使用,就会产生Heap Leak。
2.系统资源泄漏
指程序使用系统分配的资源,比方套接字、文件描述符、管道等没有使用对应的函数释放掉,导致系统资源的浪费,严重可导致系统效能减少,系统执行不稳定。
13.linux打开的最大文件数。
ulimit -SHn 10000
其实ulimit 命令身是分软限制和硬限制,加-H就是硬限制,加-S就是软限制。默认显示的是软限制,如果运行ulimit 命令修改时没有加在这里插入代码片上-H或-S,就是两个参数一起改变。
软限制和硬限制的区别?
硬限制就是实际的限制,而软限制是警告限制,它只会给出警告。
永久生效
要想ulimits 的数值永久生效,必须修改配置文件/etc/security/limits.conf
在该配置文件中添加
14.实现基于TCP/IP的客户端服务端
- **TCP服务端默认函数调用顺序**
1. socket() 创建套接字
2. bind() 分配套接字地址
3. listen() 等待连接请求状态
4. accpet() 允许连接
5. read()/write() 交换数据
6. close() 断开连接
- **TCP客户端默认函数调用顺序**
1. socket() 创建套接字
2. connect() 请求连接
3. read()/write() 交换数据
4. close() 断开连接
实现服务器端重要/必经过程就是给套接字分配IP和端口号(bind),但是客户端实现过程并未出现套接字的分配,而是创建套接字后立即调用了connect函数,为什么?
答:客户端其实是分配了IP和端口号的。在调用connect的时候分配的(何时),在操作系统内核中进行分配(何地),IP用的是主机的IP,端口号随机分配(何种方式)
- **注意事项**
- 服务器端创建套接字后连续调用bind、listen函数进入等待状态,客户端通过connect函数发起连接请求
- 客户端只能等到服务端调用listen后才能调用connect函数
- 客户端调用connect函数前,服务器可能率先调用accept函数,然后服务端进入阻塞状态,直到客户端调用connect为止
- **TCP套接字中的I/O缓冲**
write函数调用后并不是立刻传送数据,read函数调用后也不是马上接收数据。而是将这些数据移到输入和输出缓冲中
I/O缓冲有以下特点:
1. I/O缓冲在每个TCP套接字中单独存在
2. I/O缓冲在创建套接字时自动生成
3. 即使关闭套接字也会继续传递输出缓冲中遗留的数据
4. 关闭套接字将丢失输入缓冲中的数据
15.Linux操作系统中断
中断概念
中断是指在CPU正常运行期间,由于内外部事件或由程序预先安排的事件引起的CPU暂时停止正在运行的程序,转而为该内部或外部事件或预先安排的事件服务的程序中去,服务完毕后再返回去继续运行被暂时中断的程序。这样的中断机制极大的提高了CPU运行效率。
根据CSAPP中所描述,中断是异常的一种类别。(p504)什么是异常?比如说在处理器中,依次执行对应的指令流程,这样的控制转移序列叫做控制流。但是系统会出现一些变化,系统必须对这种变化做出反应,而且这种变化是随机的,也不一定跟执行当前的程序关联,比若说子进程终止时父进程必须得到通知,硬盘定时器定期产生一个信号。这种叫做**控制流发生突变**,这些突变成为**异常控制流**。计算机的各个层次都会发生异常。(p502)
异常可以分为:中断、陷阱、故障、终止
| 类别 | 原因 | 同步/异步 | 返回行为 |
| :--: | :---------------: | :-------: | :------------------: |
| 中断 | 来自I/O设备的信号 | 异步 | 总是返回到下一条指令 |
| 陷阱 | 有意的异常 | 同步 | 总是返回到下一条指令 |
| 故障 | 潜在可恢复的错误 | 同步 | 可能返回到当前指令 |
| 终止 | 不可恢复的错误 | 同步 | 不会返回 |
- **硬中断**
硬中断是我们通常所说的中断的概念。硬中断是由硬件产生的,比如,像磁盘,网卡,键盘,时钟等。每个设备或设备集都有它自己的IRQ(中断请求)。内核中维护了一个IDT(中断描述符表),表中是中断处理函数的地址和一些其他的控制位。0-31号中断号位系统为预定义的中断和异常保留的,用户不得使用,所以硬件中断号从32开始分发。每当CPU接收到一个中断或者异常信号,CPU首先要做的决定是否响应这个中断(具体由中断控制器根据中断优先级决定是否给CPU发送中断信号),如果决定响应,就终止当前运行进程的运行,根据IDTR寄存器获取中断描述符表基地址,然后根据中断号定位具体的中断描述符。
- **软中断**
软中断是由当前正在运行的进程所产生的。 软中断并不会直接中断CPU。这种中断是一种需要内核为正在运行的进程去做一些事情(通常为I/O)的请求。
> 中断在本质上是软件或者硬件发生了某种情形而通知处理器的行为,处理器进而停止正在运行的指令流,去转去执行预定义的中断处理程序。软件中断也就是通知内核的机制的泛化名称,目的是促使系统切换到内核态去执行异常处理程序。这里的异常处理程序其实就是一种系统调用,软件中断可以当做一种异常。总之,软件中断是当前进程切换到系统调用的过程。
16.系统调用知识点
用户态和内核态
Linux内核分析(四):系统调用,用户态及内核态_系统调用是内核态吗-CSDN博客
## 系统调用过程
用户空间的程序无法直接执行内核代码,它们不能直接调用内核空间中的函数,因为内核驻留在受保护的地址空间上。如果进程可以直接在内核的地址空间上读写的话,系统安全就会失去控制。所以,应用程序应该以某种方式通知系统,告诉内核自己需要执行一个系统调用,希望系统切换到内核态,这样内核就可以代表应用程序来执行该系统调用了。
通知内核的机制是靠软件中断实现的。首先,用户程序为系统调用设置参数。其中一个参数是系统调用编号。参数设置完成后,程序执行“系统调用”指令。这个指令会导致一个异常:产生一个事件,这个事件会致使处理器切换到内核态并跳转到一个新的地址,并开始执行那里的异常处理程序。此时的异常处理程序实际上就是系统调用处理程序。它与硬件体系结构紧密相关。
> **系统调用的过程:**首先将API函数参数压到栈上,然后将函数内调用系统调用的代码放入寄存器,通过陷入中断,进入内核将控制权交给操作系统,操作系统获得控制后,将系统调用代码拿出来,跟操作系统一直维护的一张系统调用表做比较,已找到该系统调用程序体的内存地址,接着访问该地址,执行系统调用。执行完毕后,返回用户程序
## 系统调用和函数调用区别
**库函数调用**
函数调用主要通过压栈出栈的操作,面向应用开发。库函数顾名思义是把函数放到库里。是把一些常用到的函数编完放到一个文件里,供别人用。别人用的时候把它所在的文件名用#include<>加到里面就可以了。一般是指编译器提供的可在c源程序中调用的函数。可分为两类,一类是c语言标准规定的库函数,一类是编译器特定的库函数。(由于版权原因,库函数的源代码一般是不可见的,但在头文件中你可以看到它对外的接口)
**系统调用**
系统调用就是用户在程序中调用操作系统所提供的一个子功能,也就是系统API,系统调用可以被看做特殊的公共子程序。通俗的讲是操作系统提供给用户程序调用的一组“特殊”接口。用户程序可以通过这组“特殊”接口来获得操作系统内核提供的服务,比如用户可以通过文件系统相关的调用请求系统打开文件、关闭文件或读写文件,可以通过时钟相关的系统调用获得系统时间或设置定时器等。系统中的各种共享资源都由操作系统统一掌管,因此在用户程序中,凡是与资源有关的操作(如存储分配、进行I/O传输及管理文件等),都必须通过系统调用方式向操作系统提出服务请求,并由操作系统代为完成。
17.文件(不带缓存的)I/O和标准(带缓存的)I/O
**首先要明确一个问题:有无缓存是相对于用户层面来说的,而不是系统内核层面。在系统内核层面,一直都存在有“内核高速缓存”**
- 不带缓存的概念
> 所谓不带缓存,并不是指内核不提供缓存,而是在用户进程层次没有提供缓存。不带缓存的I/O只存在系统调用(write和read函数),不是函数库的调用。**系统内核对磁盘的读写都会提供一个块缓冲(在有些地方也被称为内核高速缓存)**,当用write函数对其写数据时,直接调用系统调用,将数据写入到块缓存进行排队,当块缓存达到一定的量时,才会把数据写入磁盘。因此所谓的不带缓存的I/O是指用户进程层面不提供缓存功能(但内核还是提供缓冲的)。
文件I/O以文件标识符(整型)作为文件唯一性的判断依据。这种操作与系统有关,移植有一定的问题。
- 带缓存的概念
> 与之相对的就是带缓存I/O。而带缓存的是在不带缓存的基础之上封装了一层,在用户进程层次维护了一个输入输出缓冲区,使之能跨OS,成为ASCI标准,称为标准IO库。其实就是在用户层再建立一个缓存区,这个缓存区的分配和优化长度等细节都是标准IO库代你处理好了,不用去操心。第一次调用带缓存的文件操作函数时标准库会自动分配内存并且读出一段固定大小的内容存储在缓存中。所以以后每次的读写操作并不是针对硬盘上的文件直接进行的,而是针对内存中的缓存的。
>
> 不带缓存的文件操作通常都是系统提供的系统调用, 更加低级,直接从硬盘中读取和写入文件,由于IO瓶颈的原因,速度并不如意,而且原子操作需要程序员自己保证,但使用得当的话效率并不差。另外标准库中的带缓存文件IO 是调用系统提供的不带缓存IO实现的。
- 因此,标准I/O函数有两个优点:
1. 具有良好的移植性
2. 利用用户层提供的缓存区(流缓冲)提高性能
- 标准I/O函数缺点
1. 不容易进行双向通信
2. 有时可能频繁调用fflush函数
3. 需要以FILE结构体指针的形式返回文件描述符
- 举例说明
> **带缓冲的I/O在往磁盘写入相同的数据量时,会比不带缓冲的I/O调用系统调用的次数要少。**比如内核缓冲存储器长度为100字节,在进行写操作时每次写入10个字节,则你需要调用10次write函数才能把内核缓冲区写满。但是要注意此时数据还在缓冲区,不在磁盘中,缓冲区满时才进行实际的I/O操作,把数据写入到磁盘,这样调用了太多次系统调用,显得效率很低。但是若调用标准I/O函数,假设用户层缓冲区为50字节(称为流缓存),则用fwrite将数据写入到流缓存,等到流缓存区存满之后再进入内核缓存区,在调用write函数将数据写入到内核缓冲区中,若内核缓冲区满或执行fflush操作,那么内核缓冲区的数据会写入到磁盘中
- 无缓存IO操作数据流向路径:**数据——内核缓存区——磁盘**
- 标准IO操作数据流向路径:**数据——流缓存区——内核缓存区——磁盘**
- apue三种io的总结
在apue中有三种io类型,如下:
1. 文件I/O(不带缓冲的I/O):open、read、write、lseek、close
2. 标准I/O(带缓冲的I/O):标准I/O库由ISO C标准说明
3. 高级I/O:非阻塞I/O、I/O多路转接、异步I/O、readv和writev
19.对于套接字socket的理解
socket* 其实就是操作系统提供给程序员操作「网络协议栈」的接口,说人话就是,你能通过*socket* 的接口,来控制协议找工作,从而实现网络通信**
20.C++网络通信中send和receive的为什么会阻塞
使用tcp协议进行通讯的双方,都各自有一个发送缓冲区和一个接收缓冲区。而缓冲区是有大小的,因此发生阻塞的本质原因是缓冲区满了,别的字节流消息无法进入缓冲区。
send函数只是将内存中的数据拷贝到内核中tcp的发送缓冲区或者说写缓冲区,但是什么时候发送数据是send无法控制的。同时tcp是一个可靠传输协议,发送端必须收到确认报文信息后才会清空发送缓冲区中的内容。对于读缓冲区来,收到数据放到自己的读缓冲区,同时要给发送端发送一个确认消息表明自己收到了信息。这个时候如果读缓冲区的数据被填满,由于滑动窗口协议,导致接收端不在读取数据,进而写缓冲区也会阻止发送数据。这个时候write函数就会阻塞。总结:**接收端接收数据的速度小于发送端发送数据的速度,导致接收端的读缓冲区填满,接收端发送报文给发送端告诉他我已经满了,先别发。这样发送端的写缓冲区被占满了,导致阻塞**
receive阻塞是因为读缓冲区中没数据。
记住,send是等到写缓冲区被填满之后才发送,但是write只要发现读缓冲区有数据,就将数据拷贝。
21.用过哪些linux命令
22.linux惊群效应详解
Linux惊群效应详解
**定义:**惊群效应(thundering herd)是指多进程(多线程)在同时阻塞等待同一个事件的时候(休眠状态),如果等待的这个事件发生,那么他就会唤醒等待的所有进程(或者线程),但是最终却只能有一个进程(线程)获得这个时间的“控制权”,对该事件进行处理,而其他进程(线程)获取“控制权”失败,只能重新进入休眠状态,这种现象和性能浪费就叫做惊群效应。
**危害:**
1. Linux 内核对用户进程(线程)频繁地做无效的调度、上下文切换等使系统性能大打折扣。上下文切换(context switch)过高会导致 cpu 像个搬运工,频繁地在寄存器和运行队列之间奔波,更多的时间花在了进程(线程)切换,而不是在真正工作的进程(线程)上面。直接的消耗包括 cpu 寄存器要保存和加载(例如程序计数器)、系统调度器的代码需要执行。间接的消耗在于多核 cache 之间的共享数据。
2. 为了确保只有一个进程(线程)得到资源,需要对资源操作进行加锁保护,加大了系统的开销。目前一些常见的服务器软件有的是通过锁机制解决的,比如 Nginx(它的锁机制是默认开启的,可以关闭);还有些认为惊群对系统性能影响不大,没有去处理,比如 lighttpd
**Linux 解决方案之 Accept**
Linux 2.6 版本之前,监听同一个 socket 的进程会挂在同一个等待队列上,当请求到来时,会唤醒所有等待的进程。Linux 2.6 版本之后,通过引入一个标记位 WQ_FLAG_EXCLUSIVE,解决掉了 Accept 惊群效应。
23. Linux进程、进程组、会话、僵尸
研究 Linux 之前,首先要对进程、进程组、会话,线程有个整体的了解:一个会话包含多个进程组,一个进程组包含多个进程,一个进程包含多个线程。
**进程:**
进程是 Linux 操作系统环境的基础,它控制着系统上几乎所有的活动。每个进程都有自己唯一的标识:进程 ID,也有自己的生命周期。进程都有父进程,父进程也有父进程,从而形成了一个以 init 进程 (PID = 1)为根的家族树。除此以外,进程还有其他层次关系:进程组和会话。
有几种比较特殊的进程:
1. 孤儿进程:一个父进程退出,而它的一个或多个子进程还在运行,那么那些子进程将成为孤儿进程。孤儿进程将被 init 进程(进程号为1)所收养,并由 init 进程对它们完成状态收集工作。
2. 僵尸进程:一个进程使用 fork 创建子进程,如果子进程先退出,而父进程并没有调用 wait 或 waitpid 获取子进程的状态信息,那么子进程的进程描述符仍然保存在系统中。这种进程称之为僵死进程。
3. 守护进程:(英语:daemon)是一种在后台执行的程序。此类程序会被以**进程**的形式初始化。**守护进程**程序的名称通常以字母“d”结尾:例如,syslogd 就是指管理系统日志的守护进程。
**进程ID:**
Linux每个进程都会有一个非负整数表示的唯一进程 ID,简称 pid。Linux 提供了getpid 函数来获取进程的 pid,同时还提供了 getppid 函数来获取父进程的 pid。
**进程组:**
进程组的概念并不难理解,可以将人与人之间的关系做类比。一起工作的同事,自然比毫不相干的路人更加亲近。shell 中协同工作的进程属于同一个进程组,就如同协同工作的人属于同一个部门一样。引入了进程组的概念,可以更方便地管理这一组进程了。比如这项工作放弃了,不必向每个进程一一发送信号,可以直接将信号发送给进程组,进程组内的所有进程都会收到该信号。
**会话(session):**
一般是指 shell session。Shell session 是终端中当前的状态,在终端中只能有一个 session。当我们打开一个新的终端时,总会创建一个新的 shell session。
就进程间的关系来说,session 由一个或多个进程组组成。我们可以通过下图来理解进程、进程组和 session 之间的关系:
24.守护进程
**概念:**
守护进程(Daemon)是运行在后台的一种特殊进程。它独立于控制终端并且周期性地执行某种任务或等待处理某些发生的事件。守护进程是一种很有用的进程。 Linux的大多数服务器就是用守护进程实现的。比如,Internet服务器inetd,Web服务器httpd等。同时,守护进程完成许多系统任务。 比如,作业规划进程crond,打印进程lpd等。守护进程的编程本身并不复杂,复杂的是各种版本的Unix的实现机制不尽相同,造成不同 Unix环境下守护进程的编程规则并不一致。
当我们在命令行提示符后输入类似./helloworld程序时,在程序运行时终端被占用,此时无法执行其它操作。即使使用./helloworld &方式后台运行,当连接终端的网络出现问题,那么也会导致运行程序中断。这些因素对于长期运行的服务来说很不友好,而「守护进程」可以很好的解决这个问题。
**特性:**
守护进程最重要的特性是后台运行。其次,守护进程必须与其运行前的环境隔离开来。这些环境包括未关闭的文件描述符,控制终端,会话和进程组,工作目录以及文件创建掩模等。这些环境通常是守护进程从执行它的父进程(特别是shell)中继承下来的。最后,守护进程的启动方式有其特殊之处。它可以在Linux系统启动时从启动脚本/etc/rc.d中启动,可以由作业规划进程crond启动,还可以由用户终端(通常是 shell)执行。总之,除开这些特殊性以外,守护进程与普通进程基本上没有什么区别。因此,编写守护进程实际上是把一个普通进程按照上述的守护进程的特性改造成为守护进程。如果对进程有比较深入的认识就更容易理解和编程了。
**编程:**
setsid()函数主要是重新创建一个session,子进程从父进程继承了SessionID、进程组ID和打开的终端,子进程如果要脱离父进程,不受父进程控制,我们可以用这个setsid命令。想脱离父进程,自己自由自在的活着,就要用这个命令执行后面的操作,简单粗暴。
编写守护进程过程如下:
1. 创建子进程,父进程退出
进程 fork 后,父进程退出。这么做的原因有 2 点:
- 如果守护进程是通过 Shell 启动,父进程退出,Shell 就会认为任务执行完毕,这时子进程由 init 收养
- 子进程继承父进程的进程组 ID,保证了子进程不是进程组组长,因为后边调用setsid()要求必须不是进程组长
2. 子进程创建新会话
调用setsid()创建一个新的会话,并成为新会话组长。这个步骤主要是要与继承父进程的会话、进程组、终端脱离关系。fork()的目的是想成功调用setsid()来建 立新会话,子进程有单独的sid,并且成为新进程组的组长,不关联任何终端。
3. 禁止子进程重新打开终端
此刻子进程是会话组长,为了防止子进程重新打开终端,再次 fork 后退出父进程,也就是此子进程。这时子进程 2 不再是会话组长,无法再打开终端。其实这一步骤不是必须的,不过加上这一步骤会显得更加严谨。
4. 设置当前目录为根目录
如果守护进程的当前工作目录是/usr/home目录,那么管理员在卸载/usr分区时会报错的。为了避免这个问题,可以调用chdir()函数将工作目录设置为根目录/。
5. 设置文件权限掩码
文件权限掩码是指屏蔽掉文件权限中的对应位。由于使用 fork()函数新建的子进程继承了父进程的文件权限掩码,这就给该子进程使用文件带来了诸多的麻烦。因此,把文件权限掩码设置为 0,可以大大增强该守护进程的灵活性。通常使用方法是umask(0)。
6. 关闭文件描述符
子进程会继承已经打开的文件,它们占用系统资源,且可能导致所在文件系统无法卸载。此时守护进程与终端脱离,常说的输入、输出、错误描述符也应该关闭。
25.有哪些常用命令
1.pwd:显示当前所在位置
2.su:申请切换root用户,需要输入root用户密码
3.grep:在文件中搜素指定文本
4.kill -9 [pid]:销毁进程(-9表示强制停止),先用ps查找进程
5.mkdir
6.rmdir:删除空目录
7.rm:删除文件或命令
8.cp:复制文件或目录
9.mv
10.touch:创建空文件或更新文件的时间戳
11.cat:显示文件内容
12.more/less:逐页显示文本文件内容
13.ps:显示当前运行的进程
14.ifconfig:查看网络接口信息
15.ping:测试与主机的连通性
16.chmod:修改文件或目录权限
17.chown:修改文件或目录的拥有者
18.top:显示系统资源的实时使用情况
19.ssh:远程登录到其他计算机
20.echo:将文本输出到标准输出
21.zip/unzip:用于压缩和解压ZIP文件
26.如何理解一切皆文件
Linux中所有内容都是以文件的形式保存和管理的,包括一切软硬件。这样做最明显的好处是,开发者仅需要一套API和开发工具即可调取Linux系统中绝大部分资源。同时也带来不利之处,如使用任何硬件设备都必须与根目录下某一目录执行挂载操作,否则无法使用。