linux编程修炼心法
文章平均质量分 95
系统编程和网络编程读书笔记,主要参考书籍为《APUE》,《UNP》,《TLPI》
song->_->
调试的错误就是编程给你最好的东西,因为在每个错误上面都标志着前进的一步
展开
-
linux编程学习目录
参考资料:《UNIX环境高级编程第三版》《Linux/UNIX系统编程手册》《UNIX网络编程(第1卷:套接口API)》《TCP/IP详解卷一》Linux 学习交流群: 610441700 ,欢迎加群一起讨论系统编程文件I/O1-文件IO—open/close函数2-C标准的I/O缓存和FILE结构体3-文件描述符和标准文件4-文件描述符与打开的文件之间的关系5-文件I/O—read/write函数6-改变文件偏移量原创 2018-07-22 23:13:33 · 2239 阅读 · 2 评论 -
21-非阻塞accept
1. 回忆accept函数之前在10-在accept之前中止连接(连接异常)这一篇中已经讨论过在accept之前中止连接的情况了,不过从最终的结果来看,accept并没有返回错误,而是之后调用read读取已连接套接字时产生了错误。另外,当一个已完成连接正等待被服务端accept时,select会把该连接的套接字作为读描述符并返回。这意味着之后的accept就不应该阻塞,但是会引发一个...原创 2019-01-07 14:58:41 · 6019 阅读 · 0 评论 -
20-unix域套接字地址结构
1. unix域协议看到这个标题,不知道的小伙伴肯定以为这是一个协议族之类的,但实际上unix域协议是在单台主机上客户端与服务端之间的通信方法,简单来说,unix域协议也是一种进程间通信方式,用于同一台主机上的客户端和服务端,为不同的进程间传递描述符。通常在同一台主机上,使用unix域套接字通常比TCP套接字效率更高,同时unix域套接字还可以用于在进程间传递描述符等等。那么unix域...原创 2019-01-05 14:00:25 · 612 阅读 · 0 评论 -
19-高级I/O函数——套接字和标准I/O
之前我们一直使用的read,write函数以及它们的变体recv, send等函数执行I/O,这些函数都是要使用描述符的,通常这些函数都作为unix内核中的系统调用实现。除了以上说的系统调用,我们也可以使用标准I/O函数库(standard I/O libary),这个函数库由 ANSI C 标准进行规范,不过使用标准I/O函数需要创建一个标准 I/O 流,我们可以使用fdopen函数来完成,...原创 2019-01-04 17:44:41 · 473 阅读 · 0 评论 -
18-UDP的connect函数
1. 面向连接的UDP在上一篇中遗留了一个问题:sendto函数产生的异步错误一般是不会返回给udp套接字的(主要是因为udp是无连接的原因),如果这个错误要返回给udp套接字,那么就需要调用connect函数。是的,你没看错,udp也可以调用connect函数达到面向连接,但是这并不意味着udp也会产生三次握手的过程。udp调用connect函数最主要的目的只是记录对端的ip地址和...原创 2018-12-01 12:33:11 · 717 阅读 · 0 评论 -
17-sendto函数和异步错误
对于客户端的sendto函数引发的错误,服务端会以一个port unreachable(端口不可达)的ICMP报文消息响应,遗憾的是这个错误并不会返回给客户端,我们称这个错误为异步错误(asyncchronous error),虽然这个错误是由sendto引起的,但是sendto却返回成功,而不是返回错误。换句话说,udp套接字的sendto并不会返回这个异步错误,因为udp是无连接的。考...原创 2018-11-27 21:26:42 · 2163 阅读 · 0 评论 -
16-基于udp的客户端/服务端通信
1. 使用udp协议通信在此之前,我们基于TCP的客户端/服务端通信示例是有连接方式的,客户端会先调用connect发起连接,当两端tcp连接建立完成,然后服务端调用accept接受客户端的连接,通过套接字开始通信。UDP协议是无连接且不可靠的,没有被动套接字和主动套接字之分,客户端和服务端之间通信不需要提前建立好连接,而是直接调用sendto或recvfrom函数收发数据,如下图所示:...原创 2018-10-22 11:15:11 · 1181 阅读 · 0 评论 -
15-SO_RCVBUF和SO_SNDBUF套接字选项
1. 套接字缓冲区对于tcp协议来说,客户端和服务端在建立tcp连接的时候,双方会通告自己的接收窗口大小(告诉对方自己能接收的数据大小),然后双方就会根据接收窗口来调整自己的发送窗口大小。如果你已经忘的差不多了,请参考: 19-tcp连接建立 , 如果你完全看不明白请参考:tcp/ip协议学习目录,结合《tcp/ip详解卷一》把tcp协议系统的学一遍,正所谓万丈高楼平地起,想要建一座高楼大...原创 2018-10-14 00:12:36 · 1297 阅读 · 0 评论 -
14-改写tcp服务器
1. 使用select改写tcp服务器在此之前,回顾一下多进程并发型服务器通信过程,并发型服务器的做法是针对每一个客户端请求,服务器的父进程就fork创建一个子进程来处理客户端的请求。如果有大量的客户端请求的话,那么服务器也需要创建大量的子进程来处理请求,这将会消耗服务器大量的系统资源。 因此我们可以使用select改写tcp服务器,通过select来处理多个客户端的请求,具体处理过程...原创 2018-10-13 16:05:57 · 396 阅读 · 0 评论 -
13-select重写客户端和tcp优雅关闭
在阅读此篇之前,请先看完第11篇的一小节,传送门:11-服务端进程终止和SIGPIPE信号1. 服务器进程终止的问题在第11篇中的第1小节中,tcp客户端需要同时处理两个输入:标准输入和tcp套接字,tcp客户端代码如下所示://循环读写数据while (1) { //tcp客户端阻塞于fgets上 fgets(buf, siz...原创 2018-10-09 22:19:53 · 1483 阅读 · 2 评论 -
46-内存映射的保护和同步
1.保护权限mmap函数的prot参数可指定内存映射区的保护权限,例如指定PROT_READ和PROT_EXEC,在调用open打开文件就应该指定O_RDONLY或者O_RDWR。如果指定了PROT_WRITE,打开的文件应该使用O_WRONLY或者O_RDWR。因为有些硬件的架构实现有些不同,所以对于保护权限还是有区别的:1. 一般打开文件指定O_RDWR标记就已经满足基本的...原创 2018-10-06 13:39:21 · 1266 阅读 · 0 评论 -
45-内存映射的陷阱
本篇将介绍内存映射过程中的一些系统分页边界陷阱,可助你打通任督二脉,掌握内存映射基本的使用和出错处理。 1. 第一种情况之前有说过在调用mmap函数创建内存映射时,建议指定参数length最好是系统内存分页的整数倍,虽然这并不是强制的,但这么做当然是有原因的,如果参数length恰好是系统内存分页的整数倍,那么内存映射区和文件映射区范围恰好是一样的。我们来看一个mma...原创 2018-10-06 09:34:38 · 557 阅读 · 0 评论 -
44-共享文件映射
1. 文件映射内存映射主要分两种:文件映射和匿名映射。上一篇讲的就是文件映射,一般创建文件映射需要以下几个步骤:1. 调用open函数打开一个文件,获得其文件描述符2. 将文件描述符作为mmap函数的参数传入 执行完以上步骤后,mmap函数会将文件映射到进程空间中,如果不需要用到文件描述符可以调用close关闭。在创建文件...原创 2018-10-05 17:53:53 · 1049 阅读 · 0 评论 -
43-初窥内存映射mmap
1. 文件通信如果你已经完成了进程间通信的学习,相信你对进程间通信已经有了一个初步的了解。由于进程之间是相互独立的,不能相互访问,进程之间想要通信必须借助内核空间,因为内核空间对于进程之间是共享的。像这样的进程间通信的方式有很多种,例如管道、信号、共享内存、消息队列、套接字、命名管道等,在linux早期是使用文件来完成进程间通信的。对于文件通信,本质上也是通过内核空间来完成的。首先系统...原创 2018-10-04 19:51:10 · 509 阅读 · 0 评论 -
42-使用flock文件锁
1. flock函数flock函数是专门用于实现对文件加锁的,与fcntl不同的是,flock系统调用最初是源自BSD的,而fcntl则是源自System V,flock是对整个文件进行加锁,而fcntl不仅可以对整个文件加锁,还可以对文件部分区域加锁,更加具有灵活性。 flock - apply or remove an advisory lock on an open file...原创 2018-10-03 16:04:47 · 1956 阅读 · 4 评论 -
41-fcntl设置文件锁
1. 文件锁当多个进程打开同一文件进行读写时可能会出现数据混乱的问题,原因在于进程间共享同一文件读写指针位置,也就是f_pos(关于f_pos参考:4-文件描述符与打开的文件之间的关系的第三小节file结构体)。解决这个问题的方法有很多,比如可以使用信号量完成进程同步,但通常使用文件锁会更好一些,因为内核会将锁跟文件关联起来,这就需要借助fcntl函数来实现对一个文件进行加锁。只有拿到锁的...原创 2018-10-02 14:28:01 · 1764 阅读 · 0 评论 -
40-System V——信号量的细节问题
1. 多个进程操作信号量假设此时有多个进程在请求信号量发生阻塞时,且每个进程请求信号量减去的值都是一样的,那么到底哪个进程能继续执行是不确定的,这取决于调度算法。如果在阻塞的进程中,每个进程请求信号量减去的值都是不一样的,那么则会按照先满足条件的先执行的顺序进行。例如此时有A和B两个进程阻塞,当前信号量的值为0,A进程请求将信号量值减去2,B进程请求将信号量减去1,当C进程请求将信号量加1...原创 2018-10-01 22:49:47 · 485 阅读 · 0 评论 -
39-System V——信号量
1. 什么是信号量 前面学过的System V IPC两种进程通信方式:共享内存和消息队列都是用于在进程间对共享区域的数据的交换传递来实现进程间通信,如果有多个进程同时向共享内存写数据可能就会出现数据混乱的问题。 信号量也是System V IPC的一种通信方式,和其他IPC方式不同的是,信号量主要是为了防止多个进程间访问共享数据时,产生竞争等一系列问题,信号量就是用来进程同步的。...原创 2018-10-01 20:57:29 · 532 阅读 · 0 评论 -
38-System V——消息队列
消息队列也是进程间通信的一种方式。消息队列的本质上是内核中一个消息链表,存储在内核空间。当进程间使用消息队列进行通信时,可以一个进程向内核空间中写入消息(向消息队列中添加一个消息),另一个进程从内核空间中读取消息(从消息队列中删除一个消息)。原创 2019-06-19 13:03:12 · 487 阅读 · 0 评论 -
35-System V 进程间通信
1. System V IPC的由来 Unix 系统早期的IPC形式主要包括无名管道,有名管道,信号,而System V IPC是AT&T实验室对Unix早期的进程间通信手段进行了改进和扩充形成的。 System V IPC主要包括以下三种进程间通信机制:System V 共享内存、System V 消息队列、System V 信号量。虽然以上这三种IPC在功能上存在着很原创 2018-09-27 01:03:16 · 1170 阅读 · 0 评论 -
36-System V——创建共享内存
1. System V共享存储 所谓共享存储就是允许两个或多个进程共享物理内存的同一存储区,进程在访问这一内存空间时,因为数据不需要在进程间复制,所以效率比较高。 另外,内核没有对访问共享内存进行同步,必须提供自己的同步措施,比如有进程在对存储区写入数据之前,不允许其他进程对其进行读写操作(这里我们暂时不讨论同步问题)。2. 操作共享内存步骤 共享内存操作函数:shmget,sh...原创 2018-09-27 16:11:22 · 860 阅读 · 0 评论 -
37-System V——共享内存函数详解
1. shmat函数 前面我们说过,调用shmget函数后,系统会在内核空间中分配一块共享物理内存,进程想要访问该共享内存的话,必须调用shmat函数把进程空间的虚拟地址映射到该共享内存的物理地址。 当然这个地址映射过程是由系统来完成的,也就是说,系统内核会在当前的进程空间选择合适的,且空闲未使用的虚拟地址映射到共享内存的物理页面。#include <sys/ty原创 2018-09-28 09:57:04 · 1077 阅读 · 0 评论 -
32-线程控制——线程特定数据
1. 线程特定数据 线程特定数据(thread-specific-data),也称为线程私有数据,是存储和查询某个特定线程相关数据的一种机制(APUE的说法)。 之所以称为线程私有数据,是因为让每个线程访问属于自己独有的数据,这样就不用关心线程同步的问题。比如线程id就是线程私有数据,每个线程都有一个唯一不重复的线程id,为了防止与其他线程的数据混淆,需要进行一些保护。 但是一个...原创 2018-09-24 16:11:44 · 586 阅读 · 0 评论 -
33-进程间通信——管道
1. 什么是管道 管道是unix系统最古老的IPC通信方式了,适合于有血缘关系的进程之间完成数据传输,比如父子进程,兄弟进程。 管道允许一个数据流向另一个进程,管道中的数据流向是单向的。这样进程可以通过文件描述符1连接到管道写入端,另一个进程通过文件描述符0连接到管道读取端,实际上,这两个进程并不知道管道的存在,它们只是从文件描述符中读写数据,所以管道也称为匿名管道。2. 管道...原创 2018-09-25 17:18:54 · 631 阅读 · 0 评论 -
34-进程间通信——FIFO(命名管道)
1. 何为命名管道 在前面的学习中我们知道管道(pipe)只能用于“有血缘关系”的进程间。 它们之间最大的区别就是FIFO提供一个路径名与之关联,在文件系统中有一个索引块,以文件路径的形式存在(在磁盘中没有数据块,所有数据都存放在内核),而这个文件路径是FIFO被称为命名管道的重要原因。 FIFO与管道类似,其本质上也是内核的一块缓冲区,FIFO也有一个写入端和读取端,FIFO中...原创 2018-09-26 12:07:08 · 606 阅读 · 0 评论 -
24-线程共享资源问题
1. 线程共享资源问题 如果说pthread_create函数跟fork函数是对应的,一个创建线程,一个创建进程。但是进程调用fork创建进程的代价较高,即便是依靠写时复制机制,仍然需要复制诸如内存页表和文件描述符表之类的多种进程属性,fork调用的过程实际上非常复杂,这意味着fork调用在时间上的开销不少。 线程解决了这个问题,线程之所以能够方便,快速的共享数据,是因为进程调用fork创...原创 2018-09-20 18:43:15 · 894 阅读 · 0 评论 -
25-线程终止详解
1. 线程终止线程的终止包括主动终止和被动终止两大类。主动终止: 线程主函数执行return正常返回,返回值是线程的退出码。 线程主函数执行pthread_exit函数退出,其参数是一个传出参数,保存线程退出码被动终止: 在同一进程中其他线程调用pthread_cancel函数 任意线程调用了exit、_Exit、_exit 导致整个进程终止,又或者主线程在main函数中执行...原创 2018-09-20 19:18:50 · 690 阅读 · 0 评论 -
26-设置线程分离属性
1. 线程属性 使用pthread_create函数创建新线程时,可通过类型为pthread_attr_t的attr参数指定新线程的属性,比如可以通过设置线程栈的大小来降低内存的使用,增加最大线程个数,线程调度策略和优先级等。pthread_attr_t主要结构体成员:typedef struct{int detachstate; //线程的分离状态int ...原创 2018-09-20 22:51:05 · 1675 阅读 · 0 评论 -
27-线程同步——互斥量
1. 银行存款问题来看一个银行存款问题,比如在银行里存了1000块钱,模拟同一时刻有2个人都去银行取钱。#include <stdio.h>#include &原创 2018-09-21 21:08:50 · 353 阅读 · 2 评论 -
28-线程同步——死锁现象
1. 死锁现象一 死锁在多线程中是非常经典,常见的现象。来看一个最简单的死锁问题,有时候一个线程对同一个互斥量加锁两次,那么该线程就会阻塞,这种现象就是死锁,另外,其他方式也有可能产生死锁。1 . 线程试图对同一个互斥量加锁两次: 线程1拿到锁后,调用pthread_mutex_lock进行加锁成功,访问数据时,线程1闲着无聊又调用了pthread_mutex_lock加锁。这时线...原创 2018-09-22 01:43:21 · 824 阅读 · 0 评论 -
29-线程同步——读写锁和自旋锁
1. 读写锁 读写锁跟互斥量是类似的,也是一种锁,但是读写锁相当于是互斥锁的加强版。 因为互斥锁缺乏并发性,例如有多个线程要对数据进行读取操作,但是由于互斥锁每次只能支持一个线程访问,那么其他线程想要访问数据只能阻塞等待当前线程操作完,才有机会读取数据。而读写锁则支持多个线程进行读取操作。 还是拿银行取钱的例子来说,假如当前有10个人去银行查看银行账户,如果使用互斥锁的话每次只能允许...原创 2018-09-23 13:43:47 · 703 阅读 · 0 评论 -
30-线程同步——条件变量
在此之前我们先回忆一下所学的互斥锁,读写锁的特点,主要从以下几点进行总结:1.共同点 互斥锁和读写锁的作用都是用于线程同步。2.锁的状态 互斥锁的状态:加锁和非加锁,只有这两种状态 读写锁状态:非加锁,读方式加锁状态,写方式加锁状态3.优缺点 优点:互斥锁保证了线程同步,读写锁保证了线程同步的同时,还提高了线程的并发性,提高了程序的执行效率。 缺点:互斥锁只有加锁和非加锁...原创 2018-09-23 23:37:41 · 597 阅读 · 0 评论 -
31-线程属性——互斥锁类型
1. 创建互斥量的两种方式 就像线程具有属性一样,用于线程互斥的互斥量的也有相应的属性,互斥量属性的数据类型是用pthread_mutexattr_t结构来表示的,在使用互斥量前必须初始化。 创建互斥量有两种方法:静态创建和动态创建。对于静态创建而言,POSIX定义了一个宏PTHREAD_MUTEX_INITIALIZER来静态初始化互斥量,使用方法如下:pthread_mutex_t...原创 2018-09-24 13:29:06 · 1139 阅读 · 0 评论 -
15-getcwd和chdir函数
1. getcwd函数 工作目录:“./”代表当前目录,指的是进程当前的工作目录,默认是进程所执行的程序所在的目录位置。 每一个进程都有一个工作目录,而getcwd函数用于获取进程当前工作目录。 (man手册,标库函数)。函数原型:char *getcwd(char *buf, size_t size); 参数说明: buf:保存当前进程工作目录位置的路径 ...原创 2018-07-03 11:25:26 · 724 阅读 · 0 评论 -
18-用fork函数创建新进程
1. 程序和进程 简单来说,程序是一组存储在磁盘的机器语言指令集合(本质上是一个二进制文件),不占用cpu、内存等系统资源。 而进程是一个可执行程序的运行实例,说白了就是运行着的程序,每一个运行着的进程可以看做是一个独立的程序,会占用系统资源。2. 感受进程 如果同一个程序多次运行,每次都在内存中创建出不同的进程,每个进程都有自己的代码空间和数据空间,且进程间彼此独立,...原创 2018-09-05 14:29:02 · 8859 阅读 · 0 评论 -
16-main函数和进程终止
1. 从main函数开始 C程序总是从main函数开始执行的,main函数原型为:int main(int argc , char *argv[ ]); 当内核执行C程序时,在调用main函数前先调用一个特殊的启动例程,可执行程序文件这个启动例程的起始位置。2. 进程的终止进程的终止分为两种:正常终止和异常终止。正常终止有以下几种情况: 1.从main函数中r...原创 2018-09-05 10:46:13 · 1654 阅读 · 0 评论 -
17-atexit函数——进程终止
1. 终止函数 一般来说,内核中每个启动的进程默认都有一个标准的终止函数,用于在进程终止时执行的函数,该函数主要用来释放进程所占用的资源,也可以自定义终止函数,按照ISO C规定,一个进程可以注册32个终止函数,这些函数将由exit函数自动调用。 登记的终止函数以栈的形式运行,先注册的后执行。如果自定义注册了进程终止函数,那么内核提供的默认的终止函数将会被覆盖。 有同学可能会问...原创 2018-09-05 11:13:51 · 1297 阅读 · 1 评论 -
19-孤儿进程与僵尸进程
1. 孤儿进程 孤儿进程顾名思义就是,父进程先于子进程结束,子进程就成为孤儿进程,通常孤儿进程会被pid为1的init进程领养,并由init进程进行回收孤儿进程。 在前面的学习中我们说过,父进程调用fork成功创建子进程的时候,父进程和子进程谁先执行是不确定的。假设这么一种情况:父进程调用fork后立马执行,而子进程由于某种原因一直在做循环重复做某件事情,当父进程运行结束时,子进程就成...原创 2018-09-07 14:24:00 · 453 阅读 · 0 评论 -
20-wait,waitpid,waitid系列函数
1. wait函数前面说过子进程结束后,需要由父进程回收子进程,所以父进程肯定是不能先于子进程结束的,父进程怎么才能知道子进程结束了呢?答案是子进程结束后会通知父进程(子进程会给父进程发送SIGCHILD信号),然后父进程接收到通知就会处理子进程了,这时就需要用到wait函数了。另外,子进程结束后,在发送SIGCHILD信号通知父进程之前,子进程在这一瞬间实际是处于僵尸状态的,但是这一...原创 2018-09-07 22:47:19 · 1497 阅读 · 0 评论 -
21-关于linux信号的基本使用
1. 什么是信号 简而言之,信号是事件发生时用于给进程发送各种通知的机制,以便通知进程发生何种事件。 站在操作系统的角度来说,信号也称为软件中断,因为信号可以改变程序的执行流程,大多数情况下无法预测信号到达的精确时间。当信号传递给进程时,它会中断进程当前正在进行的任何操作,并强制进程处理或忽略信号,又或者在某些情况下终止进程。2. 向进程发送信号 比如写一个A程序每隔1秒打印一次h...原创 2018-09-18 12:52:04 · 582 阅读 · 0 评论