Linux
1、数据在内存中的位置
32位计算机内存寻址大小4G
什么是内存对齐?为什么要内存对齐?
链接: link.
2、NFS服务器配置
NFS服务器是Linux操作系统所提供的一个局域网文件挂载服务器,用户可以在局域网中相互访问Linux系统的文件夹。
3、SSH 远程登陆服务器
SSH是Linux操作系统提供给用户使用的一套远程登陆协议, 我们可以利用SSH协议去登陆到别人的Linux 系统中, 从而控制它的系统!
(类似于window 的远程协助)
链接: 参考链接.
linux 查看cpu,内存运行信息
top命令是Linux下常用的性能分析工具,能够实时显示系统中各个进程的资源占用状况,类似于Windows的任务管理器。
最下部分的进程列表栏:
以 PID 区分的进程列表将根据所设定的画面更新时间定期的更新。通过 top 内部命令可以控制此处的显示方式:
PID:进程的ID
USER:进程所有者
PR:进程的优先级别,越小越优先被执行
NInice:值
VIRT:进程占用的虚拟内存
RES:进程占用的物理内存
SHR:进程使用的共享内存
S:进程的状态。S表示休眠,R表示正在运行,Z表示僵死状态,N表示该进程优先值为负数
%CPU:进程占用CPU的使用率
%MEM:进程使用的物理内存和总内存的百分比
TIME+:该进程启动后占用的总的CPU时间,即占用CPU使用时间的累加值。
COMMAND:进程启动命令名称
linux查看端口占用情况 ,并结束占用该端口进程
Linux 查看端口占用情况可以使用 lsof 和 netstat 命令
lsof -i:8080
或者
netstat -tunlp|grep 8080
得到占用对应端口号8080的进程ID,再使用ps -ef命令看看进程更多信息,如是否有子进程等!
ps -ef|grep 15181
最后用kill命令杀死进程
kill -9 15181
kill -9 15182
linux查看进程
ps命令用于报告当前系统的进程状态。ps命令是最基本同时也是非常强大的进程查看命令,使用该命令可以确定有哪些进程正在运行和运行的状态、进程是否结束、进程有没有僵死、哪些进程占用了过多的资源等等,总之大部分信息都是可以通过执行该命令得到的。
awk命令 (不会)
AWK是一种处理文本文件的语言,是一个强大的文本分析工具。
线程切换是切换那些东西
进程切换:切换虚拟地址空间,切换内核栈和硬件上下文
线程切换:切换内核栈和硬件上下文
.
对于单核CPU来说(对于多核CPU,此处就理解为一个核),CPU在一个时刻只能运行一个线程,当在运行一个线程的过程中转去运行另外一个线程,这个叫做线程上下文切换(对于进程也是类似)。由于可能当前线程的任务并没有执行完毕,所以在切换时需要保存线程的运行状态,以便下次重新切换回来时能够继续切换之前的状态运行。
内核的这种切换过程伴随的最显著的性能损耗是将寄存器中的内容切换出。
进程切换与线程切换的一个最主要区别就在于进程切换涉及到虚拟地址空间的切换而线程切换则不会。因为每个进程都有自己的虚拟地址空间,而线程是共享所在进程的虚拟地址空间的,因此同一个进程中的线程进行线程切换时不涉及虚拟地址空间的转换。
举一个不太恰当的例子,线程切换就好比你从主卧走到次卧,反正主卧和次卧都在同一个房子中(虚拟地址空间),因此你无需换鞋子、换衣服等等。但是进程切换就不一样了,进程切换就好比从你家到别人家,这是两个不同的房子(不同的虚拟地址空间),出发时要换好衣服、鞋子等等,到别人家后还要再换鞋子等等。
因此我们可以形象的认为线程是处在同一个屋檐下的,这里的屋檐就是虚拟地址空间,因此线程间切换无需虚拟地址空间的切换;而进程则不同,两个不同进程位于不同的屋檐下,即进程位于不同的虚拟地址空间,因此进程切换涉及到虚拟地址空间的切换,这也是为什么进程切换要比线程切换慢的原因。
有的同学可能还是不太明白,为什么虚拟地址空间切换会比较耗时呢?这也是面试官紧接会问的第二个问题。
系统编程的一些概念
1、虚拟内存通过CPU的MMU模块映射到实际的物理内存!
2、fork函数:
pid_t fork(void) 创建子进程。父子进程各自返回。父进程返回子进程pid。 子进程返回 0.
getpid();getppid(); 获取进程PID
父子进程相同:刚fork后。 data段、text段、堆、栈、环境变量、全局变量、宿主目录位置、进程工作目录位置、信号处理方式
父子进程不同:进程id、返回值、各自的父进程、进程创建时间、闹钟、未决信号集
父子进程共享:读时共享、写时复制。———— 全局变量。
exec函数族:使进程执行某一程序。成功无返回值,失败返回 -1
3、孤儿进程:父进程先于子进终止,子进程沦为“孤儿进程”,会被 init 进程领养。
僵尸进程:子进程终止,父进程尚未对子进程进行回收,在此期间,子进程为“僵尸进程”。 kill 对其无效。这里要注意,每个进程结束后都必然会经历僵尸态,时间长短的差别而已。
子进程终止时,子进程残留资源PCB存放于内核中,PCB记录了进程结束原因,进程回收就是回收PCB。回收僵尸进程,得kill它的父进程,让孤儿院去回收它。
4、回收子进程
wait函数: 回收子进程退出资源, 阻塞回收任意一个。
waitpid函数: 指定某一个进程进行回收。可以设置非阻塞。
5、创建会话
会话:多个进程组的集合
创建会话的6点注意事项:
- 调用进程不能是进程组组长,该进程变成新会话首进程
- 该进程成为一个新进程组的组长进程
- 需要root权限(ubuntu不需要)
- 新会话丢弃原有的控制终端,该会话没有控制终端
- 该调用进程是组长进程,则出错返回
- 建立新会话时,先调用fork,父进程终止,子进程调用setsid
6、守护进程
①概念:守护进程:daemon进程。通常运行于操作系统后台,脱离控制终端。一般不与用户直接交互。周期性的等待某个事件发生或周期性执行某一动作。
②特性:不受用户登录注销影响。通常采用以d结尾的命名方式。
③创建守护进程,最关键的一步是调用setsid函数创建一个新的Session,并成为Session Leader
守护进程创建步骤:
1. fork子进程,让父进程终止。
2. 子进程调用 setsid() 创建新会话
3. 通常根据需要,改变工作目录位置 chdir(), 防止目录被卸载。
4. 通常根据需要,重设umask文件权限掩码,影响新文件的创建权限。 022 -- 755 0345 --- 432 r---wx-w- 422
5. 通常根据需要,关闭/重定向 文件描述符
6. 守护进程 业务逻辑。while()
7、线程概念:
进程和线程控制原语对比:
线程控制原语---------------进程控制原语
pthread_create() --------------- fork();
pthread_self()-----------------getpid();
pthread_exit() --------------exit(); / return
pthread_join()--------------wait()/waitpid()
pthread_cancel() --------------kill()
pthread_detach()线程分离,线程结束后系统会自动回收资源
8、线程同步
目的:协同步调,对公共区域数据按序访问。防止数据混乱,产生与时间有关的错误。
数据混乱的原因:
① 资源共享(独享资源则不会)
② 调度随机(意味着数据访问会出现竞争)
③ 线程间缺乏必要同步机制
9、锁的使用:
建议锁!对公共数据进行保护。所有线程【应该】在访问公共数据前先拿锁再访问。但,锁本身不具备强制性。
互斥锁、读写锁、try锁(尝试加锁)
互斥锁,本质是结构体。 我们可以看成整数,初始值为1;
① 加锁: --操作, 阻塞线程。
② 解锁: ++操作, 唤醒阻塞在锁上的线程。
10、死锁
死锁是使用锁不恰当导致的现象:
1. 对一个锁反复lock。
2. 两个线程,各自持有一把锁,请求另一把。
11、条件变量的生产者消费者模型分析
条件变量:本身不是锁! 但是通常结合锁来使用。 mutex
pthread_cond_signal(): 唤醒阻塞在条件变量上的 (至少)一个线程。
pthread_cond_broadcast(): 唤醒阻塞在条件变量上的 所有线程。
网络编程的一些概念
1、网络套接字: socket
一个文件描述符指向一个套接字(该套接字内部由内核借助两个缓冲区实现。)
在通信过程中, 套接字一定是成对出现的。
2、socket函数
int socket(int domain, int type, int protocol);创建一个 套接字(成功则返回新套接字对应的文件描述符)
int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 给socket绑定一个 地址结构 (IP+port)
int listen(int sockfd, int backlog); 设置同时与服务器建立连接的上限数。(同时进行3次握手的客户端数量)
int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen); 阻塞等待客户端建立连接,成功的话,返回一个与客户端成功连接的socket文件描述符。
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen); 使用现有的 socket 与服务器建立连接
3、多路IO转接
select也只是起到监听的作用,然后再由服务器调用accept或read进行处理!
这里服务器起一个lfd以后,交给select去监管; 有连接时通知server,server调用accept可直接返回,不会阻塞;
后面建立通信以后,服务器创建的cfd也是交给select监管; 有数据通信时通知server,server调用read可直接返回,不会阻塞;
4、多路IO转接的优缺点
内核返回传出的文件描述符集合,用户需要对文件描述符集合进行遍历查询,遍历到最大文件描述符+1,(包括比最大文件描述符小,但不被监听的文件描述符),后续的poll集合解决的只是以数组形式返回所有监听的文件描述符!但也包含了没有事件响应的文件描述符,epoll进一步完善,只返回有事件响应的文件描述符集合!
① select优缺点:
缺点: 监听上限受文件描述符限制。 最大 1024
优点: 跨平台。win、linux、macOS、Unix、类Unix、mips
② poll的优缺点
优点:自带数组结构。 可以将 监听事件集合 和 返回事件集合 分离。拓展 监听上限。 超出 1024限制。
缺点:不能跨平台。 Linux;无法直接定位满足监听事件的文件描述符, 编码难度较大。
③ epoll优缺点
优点:高效。突破1024文件描述符。
缺点:不能跨平台。 Linux。
三个函数:
int epoll_create(int size);创建红黑树,返回根节点
int epoll_ctl(int epfd, int op, int fd, struct epoll_event *event); 操作监听红黑树
int epoll_wait(int epfd, struct epoll_event *events, int maxevents, int timeout); 阻塞监听。events:传出参数,【数组】, 满足监听条件的 那些 fd 结构体
5、EPOLL事件有两种模型:
Edge Triggered (ET) 边缘触发只有数据到来才触发,不管缓存区中是否还有数据。
Level Triggered (LT) 水平触发只要有数据都会触发
简单理解就是,水平触发就是有数据就触发,边沿触发是有新数据进来才触发。
6、反应堆:不但要监听 cfd 的读事件、还要监听cfd的写事件。
7、链接: Linux- C语言服务器简单模型(epoll+线程池).
利用生产者消费者模型来实现这个服务器模型;
生产者就是epoll模型,消费者就是线程池。