《APUE》的零碎知识点总结

Unix为每个系统调用在标准C库中设置一个具有相同名字的函数,用户进程用标准C调用序列来调用这些函数,然后,函数又调用对应的系统调用来获取相应的内核服务,库函数可以被替换,但系统调用不行。

对于内核而言,所有打开的文件都通过文件描述符引用,标准输入与文件描述符0关联,标准输出与文件描述符1关联,标准错误与文件描述符2关联

文件IO常用函数:open, read, write, lseek, close。

打开文件的内核数据结构:每个进程在进程表中都有一个记录项,包含一张打开文件描述符表,每个表项为一个文件描述符标志和一个指向文件表项的指针,文件表项包含文件状态指针(读、写、同步、非阻塞等),当前文件偏移量,指向文件v节点的指针,v节点包含了文件类型和对此文件进行各种操作函数的的指针,对于大多数文件,v节点还包括了该文件的i节点,包含了文件的信息(文件长度,所有者等)图3-7,使用dup复制打开的文件描述符时,新的文件描述符和和旧的指向同一个文件表项也就是说会共享当前文件的偏移量。

sync系函数控制延迟写(内核先将数据复制到缓冲区,然后排入队列,晚些时候再写入磁盘)数据写入磁盘

fcntl函数改变已经打开的文件的属性

stat系的函数用于获得文件的相关信息

文件类型有:普通文件,目录文件,块特殊文件, 字符特殊文件, FIFO, 套接字, 符号连接

实际用户ID,实际组ID用于表示当前用户的身份(登录时根据口令文件中的登录项),与用户绑定
有效用户ID,有效组ID,有效附属组:当使用文件时检查使用文件的用户是否具有相应的权限,与文件绑定
保存的设置用户ID,保存的设置组ID:当exec函数以特殊权限执行某程序时,用于保存它本身的身份

accesss系的函数用于进行访问权限测试

umask创建屏蔽字

chmod系函数用于更改现有文件的访问权限(进程的有效用户ID必须和文件所有者ID相同,或者具有超级用户权限)

chown修改文件的用户ID和组ID(修改所有者)

文件中可能会存在空洞,其原因是设置的偏移量超过了文件尾端,并写入了数据

可以在打开文件时截断文件(O_TRUNC),也可以调用truncate系函数

对标准IO库的操作是围绕 进行的,fwide函数设置流的定向,该函数没有出错返回,对于没有出错返回的函数,通常的做法是在调用前清空errno标志,然后再在调用后查看该标志

标准IO库

缓冲分为全缓冲(缓冲区满才进行实际IO), 行缓冲(遇到换行符)(终端设备的流),无缓冲(标准错误)

系统调用相比普通函数花费更多的时间

对文件解除连接并不删除其内容,关闭该文件时才删除

进程环境

进程终止方式有8中,5种正常:
从main返回
调用exit
调用_exit和_Exit(与exit的区别就是exit通过普通函数先对文件进行清理工作,再调用系统调用,而而_exit和_Exit直接调用系统调用)
最后一个线程从其启动例程返回
从最后一个线程调用pthread_exit
3种异常终止:
abort
接收到一个信号
最后一个线程对取消请求做出响应

main返回值不为整型,调用exit不带终止状态(参数),执行了一个无返回值的return,则该进程的终止状态是未定义的
终止处理程序,调用atexit注册,最多注册32个,程序结束时,先调用终止处理程序,然后关闭所有打开流(fclose)
内核使程序执行的唯一方式是调用exec系的函数,进程自愿终止的唯一方式是调用exit系函数

每个程序都会接收到一个环境表,表项是name=value形式的字符串

realloc如果在当前空间后面有大于等于指定长度的空间,则在后面分配,否则重新分配一块内存空间,将现有数据复制到新空间,释放旧空间

setjmp和longjmp可以实现跨函数跳转

每个进程都有资源限制,使用getrlimit和setrlimit操作

进程控制

进程为了防止将新进程误认为是刚刚终止的进程,所以使用延迟复用算法使得新建进程的ID不同于最近终止进程的ID

ID为0的进程是调度进程,也被称为交换进程,不执行任何磁盘上的程序,因此也被称为系统进程。ID为1的进程是init进程,自举过程结束时由内核调用,该进程负责在自举内核后启动一个UNIX系统,它通常读取与系统有关的初始化文件,并将系统引导到一个状态。init进程绝不会终止,它是一个普通的用户进程但是它以特权模式运行,init进程是所有孤儿进程的父进程。

由于在fork之后经常伴随着exec,所以很多实现并不会完全复制父进程的数据段,堆栈,而采用写时复制技术

fork之后父进程先执行还是子进程先执行是不确定的,取决于内核的调度算法。

sizeof是在编译时确定缓冲区的长度,所以它并不关心缓冲区里面有什么内容

父进程和子进程共享文件表项 ,子进程还会继承父进程的:
实际ID,实际组ID,有效组ID,有效用户ID ,附属组ID, 进程组ID,会话ID, 控制终端,设置用户ID标志和设置组ID标志,当前工作目录, 根目录, 文件模式创建屏蔽字, 信号屏蔽和安排, 对任意打开文件描述符的执行时关闭标志, 环境, 连接的共享存储段, 存储映像, 资源限制
子进程和父进程的区别:
fork返回值不同(子进程返回0, 父进程返回子进程ID), 进程ID不同, 两个进程的父进程ID不同, 子进程的进程时间设置为0, 子进程不继承父进程的文件锁,子进程的未处理时钟被清除(alarm),子进程未处理的信号集设置为空

vfork相比于fork的优势在于它是为了启动新程序而创造的,它不会完全复制父进程的地址空间,因为它会执行新程序

僵死进程:已终止,但其父进程未对其进行善后处理(获取终止进程的有关信息、释放它仍占用的资源)

内核调用exec函数的进程实际执行的并不是该解释器文件,而是解释器文件(第一行为!pathname)第一行中的pathname所指定的文件

system函数用于在程序中执行一个命令,system的实现其实就是fork,exec,wait,且system进行了所需的各种出错处理及各种信号处理,以特殊权限运行并且需要设置用户ID或者组ID的程序绝对不能使用system,因为你需要自己将子进程改为普通权限

进程会计选项启动后,每当进程结束后内核就会写一个会计记录,记录这进程相关信息

进程的调度策略和调度优先级是内核确定的,普通用户可以通过降低自己的nice值来降低调度优先级,只有特权用户可以提高调度优先级

进程关系

系统启动的过程:系统自举后创建init进程,然后以空环境启动getty程序,该程序打开终端设备,要求用户键入用户名读初始环境集,然后启动login程序获取用户名相关的口令文件登录项,再将用户键入的passwd加密与该用户阴影口令文件登录项中的pw_passwd比较,如果多次无效,则调用exit

pid进程ID, pgid进程组ID,sid会话ID

进程组是一个或多个进程的集合,同一进程组中的各进程接收来自同一终端的各种信号,每个进程组有一个唯一的进程组ID,每个进程组有一个组长进程,组长进程的进程组ID等于其进程ID,组长进程可以创建一个进程组,创建改组中的进程,只要进程组中有一个进程存在,进程组就存在,与组长进程是否存在无关

于Linux是多用户多任务的分时系统,所以必须要支持多个用户同时使用一个操作系统。当一个用户登录一次系统就形成一次会话。一个会话可包含多个进程组,但只能有一个前台进程组。只有当前进程不是进程组的组长时,才能创建一个新的会话。一个会话可以有一个控制终端。这通常是登陆到其上的终端设备(在终端登陆情况下)或伪终端设备(在网络登陆情况下)。建立与控制终端连接的会话首进程被称为控制进程。一个会话中,应该包括控制进程(会话首进程),一个前台进程组和任意后台进程组。

一个前台作业可以由多个进程组成,一个后台也可以由多个进程组成,Shell可以运行一个前台作业和任意多个后台作业,这称为作业控制,只有前台作业接收终端输入。

子进程从fork返回后马上调用exec函数的话,旧的地址空间就被丢弃

信号

信号是一个典型的异步事件。产生信号的事件对于进程而言是随机出现的。在某个信号出现时,必须告诉内核如何处理。有三种方式:
1.忽略。但SIGKILL和但SIGSTOP不能忽略。因为它们向内核提供了使进程终止和停止的可靠方法。
2.捕获。调用某个之前已经注册了,和该信号对应的信号处理函数。
3.执行系统默认动作。

exec函数将原先设置为要捕捉的信号都设置为默认行为,因为执行一个新程序后,信号捕捉函数的地址在新执行的程序文件中已无意义。

进程捕捉到信号并对其进行处理时,进程正在执行的正常指令序列就被信号处理函数临时中断,转而先执行信号处理函数,加入进程正在执行某个函数,而此时发生中断,信号处理函数中调用的某些函数可能会导致当前正在执行的函数出现某些错误,该信号处理函数中执行的这个函数就叫做不可重入函数,相应的可重入函数就不会存在这种情况,可重入函数也被称为异步信号安全的,在信号处理操作执行期间,它会阻塞任何会引起不一致信号的发送。
不可重入函数的三个特征:1.使用静态数据结构。2.调用malloc或free。3.标准IO函数

即使信号处理函数调用的是可重入函数,但还是有一点必须注意,errno变量每个线程只有一个,信号处理函数可能会修改它原先的值,在信号处理函数调用可重入函数前应先保存errno,调用后再恢复

阻塞是等待资源到来(内存中),挂起是服从调度,有更高优先级的事需要执行,于是将他换到外存中

线程

进程是操作系统分配资源的最小单元,线程是操作系统调度的最小单元

每个线程都包含有表示执行环境所必须的信息,其中包括进程值中标示线程的线程ID、一组寄存器值,栈,调度优先级和策略,信号屏蔽字,errno变量以及线程私有数据。一个进程的所有信息对该进程的所有线程都是共享的,包括可执行程序的代码,程序的全局内存和堆内存,栈,以及文件描述符,线程ID只有在其所属的进程上下文才有效

由主线程控制作业的分配,主线程会在每个待处理作业的结构中放置该作业的线程ID,每个工作线程只能移出标有自己线程ID的作业

有时候主线程是需要休眠的,因为如果它不休眠,很可能当他退出的时候新线程还没运行完。

如果进程的任意线程调用了exit系函数,则整个进程都会终止,还有种情况是,如果某个发往线程的信号的默认动作是终止,那么整个进程也就会终止了

线程退出避免连带进程退出的方法:简单的从启动例程返回,被同进程的其他线程取消,调用pthread_exit

类似进程的wait,其他线程可以通过pthread_join阻塞等待线程退出(正常返回,调用pthread_exit,调用pthread_cancel取消),并获取退出状态

线程同步的方法:
互斥量:本质上就是一把锁,访问共享资源时加锁,访问完成后解锁,互斥量有时会导致死锁,A拥有1互斥量,想锁住2互斥量,B拥有2互斥量,想锁住1互斥量,应当通过仔细控制互斥量加锁顺序来避免死锁
读写锁:一次只有一个线程可以占有写模式的读写锁,但一次可以有多个线程同时占有读模式的锁
条件变量:条件变量相对于互斥量最大的优点在于允许线程以无竞争的方式等待条件的发生。当一个线程获得互斥锁后,发现自己需要等待某个条件变为真,如果是这样,该线程就可以等待在某个条件上陷入睡眠并且对互斥量解锁,等待被唤醒时,再对互斥量进行加锁,这样就不需要通过轮询的方式来判断添加,大大节省了CPU时间。
自旋锁:无论是互斥锁,还是自旋锁,在任何时刻,最多只能有一个保持者,也就说,在任何时刻最多只能有一个执行单元获得锁。但是两者在调度机制上略有不同。对于互斥锁,如果资源已经被占用,资源申请者只能进入睡眠状态。但是自旋锁不会引起调用者睡眠,如果自旋锁已经被别的执行单元保持,调用者就一直循环在那里看是否该自旋锁的保持者已经释放了锁.
屏障:屏障同步的方式时使每个线程等待,直到所有的合作线程都到达某一点,然后从该点继续执行

非抢占式系统:一个进程完成或者阻塞,才会调度另外一个进程
抢占式系统:进程执行时可以被更高优先级的进程抢占,由内核调度

非分离态线程(默认):线程工作完成后需要等待pthread_join()函数获取终止状态后才算终止,才释放资源
分离态线程:执行完后自动释放资源并退出

从上面我们看出线程并没有独立的地址空间,这就意味着隶属同一进程的所有线程栈,都在所属进程的地址空间中,他们的栈地址不同,但是如果操作栈时发生越界,是有可能破坏其他线程的栈空间的。

如果一个函数在相同的时间点可以同时被多个线程同时使用那么它就是线程安全的函数

如果一个函数对于多个线程来说是可重入的,就说这个函数是线程安全的。但这并不能说明对信号处理函数来说也是可重入的,如果函数对于异步信号处理函数的重入是安全的,则可以说函数是异步信号安全的。

有些函数由于其依赖静态的内存缓冲区,导致它并不是线程安全的,我们可以提供自己的缓冲区来让它变为线程安全的

我们希望每个线程可以访问它自己单独的数据副本,而不需要担心与其他线程的同步访问问题,这种数据就叫做线程特定数据。不仅是防止某个线程的数据与其他线程的数据相混淆,线程特定数据的使用还有两个原因:
1.线程ID并不是小而连续的,所以我们不容易每个线程的特定数据,并不能简单的创建以线程ID为索引的结构来维护每个线程的数据
2.它提供了让基于进程的接口适应多线程环境的机制。比如errno

推迟取消:默认情况下,线程在取消请求发出后还是会继续运行,直到线程到达某个取消点图12-14

每个线程都有自己的信号屏蔽字,但是信号处理程序是所有线程共享的

在子进程内部,只存在一个线程,就是父进程中调用fork的线程的副本,如果父进程中的其他线程占有锁,子进程中的锁也会被占有,但子进程中并不包含占有这个锁的线程的副本,所以如果父进程包含一个以上的线程,则其fork以后,子进程应该先清理锁状态

守护进程

守护进程是生存期长的进程,它们常常在系统引导装入时启动,直到系统关闭才终止,它是后台进程,没有控制终端

对于需要在进程上下文执行工作却不被用户层进程上下文调用的每一个内核组件,通常有它自己的内核守护进程:
kswapd内存换页守护进程:回收脏页
flush守护进程:定时或内存达到某个阙值时将脏页冲洗至磁盘

编写守护进程的规则:
1.调用umask将文件模式创建屏蔽字设置为某个已知值,通常是(0),因为继承而来的文件模式创建屏蔽字可能会被设为拒绝某些权限。
2.调用fork,然后父进程退出,这样就保证了子进程不是一个进程的组长进程
3.调用setsid创建一个新会话
4.将当前工作目录更改为根目录
5.关闭不需要的文件描述符
6.某些守护进程打开/dev/null 使其具有文件描述符0, 1, 2.因为守护进程并不与终端设备相关联,所以其输出无处显示,也无法从交互式用户那里接收输入

高级IO

非阻塞IO可以使我们发出read,open,write,这样的IO操作,并使这些操作立即返回,如果操作不能完成,则调用立即出错返回,表示该操作如果立即执行将会阻塞,常用的操作是在open时指定为非阻塞,或者使用fcntl将文件描述符更改为非阻塞

记录锁:我们更应该称它为字节范围锁,当第一个进程正在读或修改文件的某个部分时,使用记录锁可以阻止其他进程修改同一文件区(文件的某个区域),记录锁的兼容性类似于读写锁

记录锁的自动继承和释放有3条规则:
当一个进程终止时,它所建立的锁全部释放,无论一个描述符何时关闭,该进程通过这一文件描述符引用的文件上的所有锁都会释放
由fork产生的子进程不继承父进程所设置的记录锁,因为锁的作用就是阻止多个进程同时写同一个文件
在执行exec后,新程序可以继承原执行程序的锁

建议性锁和强制性锁并不是真正存在的锁,而是一种能对诸如记录锁、文件锁效果产生影响的两种机制。
建议性锁机制是这样规定的:每个使用文件的进程都要主动检查该文件是否有锁存在,当然都是通过具体锁的API,比如fcntl记录锁F_GETTLK来主动检查是否有锁存在。如果有锁存在并被排斥,那么就主动保证不再进行接下来的IO操作

强制性锁机制是这样规定的: 所有记录或文件锁功能内核执行的。上述提到的破坏性IO操作会被内核禁止。当文件被上锁来进行读写操作时,在锁定该文件的进程释放该锁之前,内核会强制阻止任何对该文件的读或写违规访问,每次读或写访问都得检查锁是否存在。

读写锁是线程同步,记录锁是它的扩展,可以用来对有亲缘关系和无亲缘关系的进程进行读与写的同步

从一个描述符读数据再写到另一个描述符上很简单,但是如果同时等待读取两个描述符上的数据如何处理呢?
1.开两个进程?但是这有个问题,子进程结束可以通知父进程,父进程结束要通知子进程就要占用一个自定义信号,程序会变得复杂。
2.开两个线程?但这又需要处理线程同步问题
3.轮询?效率太差
4.异步IO?但这需要内核用信号通知进程,可移植性成了问题,而且信号对于进程而言只有一个,怎么区分是哪个可读呢?
5.IO多路转接。select poll epoll

select:select维护三个描述符集,读,写,异常,当有描述符准备号时,返回已经准备好的描述符个数,于是缺点就体现出来了,返回已准备好的描述符个数,但并不会告知哪个准备好了,于是需要一个一个的去检查。效率会比较低,且默认只支持1024个描述符
poll:poll维护一个pollfd数组,每个数组对应一个感兴趣的描述符,还有对他感兴趣的事件,revent由内核设置说明该描述符发生了哪些事件
epoll:epoll相比于select和poll的优势是,select和poll在有描述符就绪时,需要遍历整个集合来判断哪些描述符准备就绪,而epoll在有描述符就绪时就会将他加入一个就绪链表,然后当他处理时只需要判断该链表是否为空就行

epoll的两种工作模式:边缘触发(ET)和条件触发(LT):
LT模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序可以不立即处理该事件。下次调用epoll_wait时,会再次响应应用程序并通知此事件。
ET模式:当epoll_wait检测到描述符事件发生并将此事件通知应用程序,应用程序必须立即处理该事件。如果不处理,下次调用epoll_wait时,不会再次响应应用程序并通知此事件。

异步IO,当某个所关心的描述符发生我们所关心的事件时,内核使用某一信号通知进程

readv(散布读,将从文件描述符读取的数据按照规定形式散布到缓冲区中)和writev(将缓冲区里的数据聚集起来写到描述符中)

在读,写一个网络设备,管道或终端时,一次读的数据可能少于所要求的数据,即使没达到文件尾,一次写的数据也可能小于所要求的数据即使缓冲区没满,于是便需要用到readn和writen,两函数按需多次的调用read和write直到达到规定的N字节为止。

存储映射IO:将一个磁盘文件映射到存储空间的一个缓冲区上,当从缓冲区取数据时就相当于读文件中的相应字节

进程间通信

管道有两个局限性:
历史上管道是半双工的,但现在很多系统提供全双工的管道,所有会存在移植性问题。
管道只能在具有公共祖先的两个进程之间使用

两个进程之间使用管道完成进程间通信:父进程创建一个管道,fork一个子进程,然后两个进程就会共享一个管道,就可以通过该管道通信了,但为了刚刚所说的移植性问题,我们需要关闭一个进程的读和另一个进程的写来保证它是半双工的,通常管道只有一个读进程和一个写进程,尽管可以通过符只管道的描述符给多个进程使用,但这并不推荐,这种情况使用FIFO会更好
复制一个文件描述符可以共享文件表项,为了安全,无论何时将一个文件表项复制到另一个上,都先比较两个文件描述符是否相等

FIFO:命名管道,相比于普通管道的优势是,不相关的进程也能交换数据
当open一个FIFO时,如果没有指定非阻塞标志(O__NONBLOCK),以只读打开时,必须阻塞到某个进程已写打开时,相反已只写打开时也一样。如果指定了O_NONBLOCK,发生以上情况,则只读立即返回,只写返回-1,并将errno设置为并将errno设置为ENXIO

若写一个为进程为其读打开的管道,则产生信号SIGPIPE,如果FIFO的最后一个写进程关闭了,则为读进程产生一个文件结束标志

每个内核中的IPC结构(消息队列,信号量,共享存储段),都用一个非负整数的标示符加以引用,要向一个消息队列发数据或读数据只需要知道它的标示符就行,每个IPC对象都和一个键关联,将这个键作为对象的外部名
这些形式的IPC不使用文件描述符,所以不能对其使用IO多路复用IO,如果要同时管理多个就只有使用一个忙等循环。
所有这些形式的IPC都是被限制在主机上的,所以他们是可靠的,因为我们需要使用键来获取其绑定的标示符,所以我们称其为有连接的,同时该类IPC还拥有流控制,当接收进程不能再接收消息或者队列满,则发送进程就需要休眠。

消息队列:消息的连接表,存储再内核,由消息队列标示符标示,当给一个消息队列发送消息时,消息被存放在队尾,但取消息时则不一定按顺序取,也可根据所需消息类型取

msgctl类似ioctl它可以根据参数执行多种操作,亦称为垃圾桶函数,也可与信号量和共享存储结合使用

信号量:它是一个计数器,用于为多个进程提供对共享数据对象的访问,当一个进程想要获得共享资源时,应做以下操作:
1.应先测试控制该共享资源的信号量
2.为正,则获取信号量,信号量减一
3.为0则进程休眠等待信号量增加。
当一个进程不再使用该共享资源时,该信号量加一,如果由进程休眠等待资源,则唤醒他们

信号量的3个特性:
1.信号量的创建必须是一个集合,即使只有一个信号量
2.信号量的创建和初始化是分开的,不是原子操作
3.即使没有进程正在使用IPC,它们仍是存在的,就需要程序自己在终止前释放所分配的信号量

多个进程中共享一个资源的3种方法:
1.信号量,创建一个包含一个信号量的信号量集合,并指定SEM_UNDO(解决上面第三条)
2.记录锁,创建一个空文件,用该文件的第一个字节作为锁字节,分配资源时对其添加写锁,释放资源时解锁。记录锁的另一个好处是锁的持有者进程终止时自动释放锁
3.互斥量,近臣将该共享的资源映射到自己的地址空间里面,使用PTHREAD_PROCESS_SHARED互斥量属性对文件的相同偏移出初始化互斥量

信号量的优势是可以同时对多个资源加锁,记录锁的优势是使用更简单,速度更快,进程终止是系统会管理遗留下来的锁,互斥量是速度最快的方式(因为不需要在两个进程之间复制),但是操作会比较复杂,而且如果一个进程没有释放互斥量而终止,恢复它将会是非常非常困难的,并且,共享的互斥量属性并没有得到普遍的支持

共享存储,多个进程将同一文件映射到它们的地址空间(位于栈和堆中间)

网络IPC:套接字

套接字描述符在UNIX系统中被当作是一种文件描述符

在客户端,套接字绑定的地址通常是系统选择的默认地址,而在服务端,就需要给接收客户端请求的套接字绑定一个众所周知的地址

如果没有连接请求在等待,accept会阻塞直到一个请求到来

send和recv就类似read和write,但是它们可以指定标志来改变处理数据的方式
sendto和recvfrom与send和recv的区别是,前者可以用来指定一个目标地址,因此多用于UDP

有连接的客户端:
1.创建套接字
2.套接字与目标地址(服务名,主机名)建立连接(如果套接字没有与本地地址信息绑定,则由系统默认绑定一个,通常客户端就是这样操作的)
3.开始通信
有连接服务端:
1.创建套接字
2.绑定一个众所周知的地址
3.等待客户端连接请求
4.获得连接并建立连接
5.为客户端开启服务

无链接的客户端:
1.创建套接字
2.直接通过套接字向目标服务器请求服务
无链接的服务器:
1.创建套接字
2.绑定众所周知的地址
3.阻塞于接收消息

无链接的传输(UDP)最关心的两个就是包失序(编号)和包丢失(重传机制)

带外数据:优先级比其他数据高的数据,即使传输列表已经有数据,也会先传输带外数据,TCP支持(叫紧急数据),UDP不支持,但普遍不推荐使用

异步IO:当你用异步方式调用一个function的时候,这个方法会马上返回,事实上多数情况下这种function call只是向某个任务执行体提交一个任务而已。 而你的主thread可以继续执行其他的事情, 不必等待(阻塞), 而当那个任务执行体执行完你提交的这个任务后,它会通过某种方法callback(常用操作是发送一个信号,然后回调函数就是你相对于这个人信号的信号处理函数)给你的thread, 告诉你,你的这个任务已经完成。

高级进程间通信

UNIX域套接字:用于在同一台计算机上的进程间通信,它仅仅复制数据,不需要执行协议处理,不需要添加删除网络报头,无需计算校验和,不需要产生顺序号,无需发送确认报文。它就像是套接字和管道的混合

使用socketpair创建一组无名的,相互连接的UNIX域套接字

无名的套接字意味着无关的进程不可以使用它,所有UNIX套接字也有命名的。UNIX域套接字的地址由socket_un结构标示,其包含一个sun_path路径名.将一个地址绑定到一个UNIX域套接字时,系统会用该路径创建一个S_IFSOCK类型文件,该文件仅仅用于向客户进程告示i套接字名字,无法打开。当我们试图绑定一个地址时,如果该文件已存在,bind就会请求失败

UNIX套接字在两个无关进程之间创建连接就类似域网络中建立TCP连接一样,服务器使用bind,listen,accept为客户安排一个唯一的UNIX域连接,客户端调用connect与服务器联系 图17-6和17-7

两个进程之间传递文件描述符的技术可以使一个进程(通常是服务器进程)能够处理打开一个文件所要做的一切操作,以及向调用进程送回一个文件描述符,该描述符可以用于以后的IO。这里的传送文件描述符是指共享同一个文件表项而不是v节点,从技术上讲,是将一个打开文件表项的指针传递到另一个进程,要注意的是,描述符编号在发送进程和接收进程编号不一定是相同的,接收进程会将其存在第一个空闲的项中

发送进程讲描述符传递给接收进程后,通常回关闭该描述符,但关于该描述符进程的计数器并不会减少,此时会被视为是接收进程打开。传递文件描述符就等于在传递访问权,只能被UNIX套接字传送

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值