系统调用相关函数.docx
《系统调用相关函数.docx》由会员分享,可在线阅读,更多相关《系统调用相关函数.docx(19页珍藏版)》请在冰豆网上搜索。
系统调用相关函数
窗体顶端
索引:
1.fork、exec和exit对IPC对象的影响
2.fcntl记录锁
3.管道和FIFO的属性
4.管道和FIFO的限制
5.pipe函数
6.popen和pclose
7.mkfifo函数
8.SystemVIPC共性描述
9.msqid_ds结构
10.msgget函数
11.msgsnd函数
12.msgrcv函数
13.msgctl函数
14.在SystemV消息队列上使用select和poll
15.semid_ds结构
16.semget函数
17.semop函数
18.semctl函数
19.shmid_ds结构
20.shmget函数
21.shmat函数
22.shmdt函数
23.shmctl函数
24.mmap函数
25.munmap函数
26.msync函数
27.匿名内存映射
1.fork、exec和exit对IPC对象的影响
IPC类型
fork
exec
_exit
管道和FIFO
子进程取得父进程的所有打开着的描述字的拷贝
所有打开的描述字继续打开着,除非已设置描述字的FD_CLOEXEC位
关闭所有打开着的描述字,最后一个关闭时删除管道或FIFO中残留的所有数据
Posix消息队列
子进程取得父进程的所有打开着的消息队列描述字的拷贝
关闭所有打开着的消息队列描述字
关闭所有打开着的消息队列描述字
SystemV消息队列
没有效果
没有效果
没有效果
Posix互斥锁、条件变量、读写锁、基于内存的信号灯
若驻留在共享内存中而且具有进程间共享属性,则共享
除非在继续打开着的共享内存中而且具有进程间共享属性,否则消失
除非在继续打开着的共享内存中而且具有进程间共享属性,否则消失
Posix有名信号灯
父进程中所有打开着的有名信号灯在子进程中继续打开着
关闭所有打开着的有名信号灯
关闭所有打开着的有名信号灯
SystemV信号灯
子进程中所有semadj值都置为0
所有semadj值都携入新程序中
所有semadj值都加到相应的信号灯上
fcntl记录上锁
子进程不继承父进程持有的锁
只要描述字继续打开着,锁就不变
解开由进程持有的所有未处理的锁
mmap内存映射和Posix共享内存区
父进程中的内存映射存留到子进程中
去除内存映射
去除内存映射
SystemV共享内存区
附接着的共享内存区在子进程中继续附接着
断开所有附接着的共享内存区
断开所有附接着的共享内存区
门
子进程取得父进程的所有打开着的描述字,但是客户在门描述字上激活其过程时,只有父进程是服务器
所有门描述字都应关闭,因为它们创建时设置了FD_CLOEXEC位
关闭所有打开着的描述字
2.fcntl记录锁
Unix内核没有文件内记录的概念,这里的记录是指字节范围(byterange)。
Posix记录上锁定义了一个特殊的字节范围以指定整个文件,它的其始偏移为0(文件的开头),长度为0。
文件上锁是记录上锁的一个特例。
粒度(granularity)用于标记能被锁住的对象的大小。
对于Posix记录上锁来说,粒度就是单个字节。
记录上锁的Posix接口是fcntl函数:
#include
intfcntl(intfd,intcmd,…/*structflock*arg*/);
返回:
成功时取决于cmd,出错时为-1。
对应记录上锁的第三个参数arg是指向某个flock结构的指针:
structflock{
shortl_type;/*F_RDLCK,F_WRLCK,F_UNLCK*/
shortl_whence;/*SEEK_SET,SEEK_CUR,SEEK_END*/
off_tl_start;/*relativestartingoffsetinbytes*/
off_tl_len;/*#bytes;0meansuntilend-of-file*/
pid_tpid;/*PIDreturnedbyF_GETLK*/
};
cmd命令有三个:
∙ F_SETLK:
获取(l_type为F_RDLCK或F_WRLCK)或释放(l_type为F_UNLCK)由arg指向的flock结构所描述的锁。
如果该锁无法授予调用进程,该函数就立即返回一个EACCES或EAGAIN错误而不阻塞。
∙ F_SETLKW:
该命令与上一命令相似,不同在于,若所请求的锁无法授予,则调用进程将阻塞到该锁能够授予为止。
(W的意思是“等待”)
∙ F_GETLK:
检查由arg指向的锁以确定是否有某个已存在的锁会妨碍新锁授予调用进程。
如果当前没有这样的锁存在,由arg指向的flock结构的l_type被置为F_UNLCK。
否则,关于这个已存在锁的信息将在由arg指向的flock结构中返回(该结构的内容由fcntl函数覆写),其中包含持有该锁的进程ID。
l_whence成员有三个值:
∙ SEEK_SET:
l_start相对于文件的开头解释;
∙ SEEK_CUR:
l_start相对与文件的当前字节偏移解释;
∙ SEEK_END:
l_start相对于文件的末尾解释。
l_len成员指定从该偏移开始的连续字节数。
长度为0表示锁住整个文件,一般锁整个文件如下使用:
指定l_whence成员为SEEK_SET,l_start为0,l_len为0。
fcntl记录上锁既可用于读也可用于写,对于一个文件的任意字节,最多只能存在一种类型的锁(读出锁或写入锁)。
而且,一个给定字节可以有多个读出锁,但只能有一个写入锁。
当一个描述字不是打开来用于读时,如果我们对它请求一个读出锁,错误就会发生;同样,当一个描述字不是打开来用于写时,请求一个写锁错误也会发生。
对于一个打开着某个文件的给定进程来说,当它关闭该文件的任何一个描述字或终止时,与该文件关联的所有锁都被删除。
锁不能通过fork由子进程继承。
删除锁的关键是进程ID,而不是引用同一文件的描述字数目及打开目的。
记录上锁不应该同标准I/O函数库一块使用,因为该函数库会执行内部缓冲。
当某个文件需要上锁时,为避免问题,应对它使用read和write。
使用fcntl上锁和解锁的例子见unpv22e:
lock/lockfcntl.c。
劝告性锁和强制性锁
Posix记录上锁是劝告性锁(advisorylocking)。
劝告性锁对协作进程(cooperatingprocesses)是足够了。
有些系统提供了强制性锁(mandatorylocking)。
使用强制性锁后,内核将检查每个read和write请求,以验证其操作不会干扰由某个进程持有的某个锁。
对于通常的阻塞式描述字,与某个强制性锁冲突的read或write将把调用进程投入睡眠,直到该锁释放为止。
对于非阻塞式描述字,与某个强制性锁冲突的read或write将导致它们返回一个EAGAIN错误。
对某个特定文件施行强制性锁,应满足:
∙ 组成员执行位必须关闭;
∙ SGID位必须打开。
强制性锁不需要新的系统调用。
虽然强制性上锁有一定作用,但多个进程在更新同一个文件时,仍然会导致混乱。
进程之间还是需要某种上锁形式的协作。
当一个文件区被锁住时,待处理的读出者和写入者的优先级是不可知的。
3.管道和FIFO的属性
非阻塞方式对管道和FIFO的影响(设置方式:
open时指定O_NONBLOCK;或使用fcntl使能O_NONBLOCK标志):
当前操作
管道或FIFO的现有打开操作
阻塞(缺省)时返回
O_NONBLOCK时返回
openFIFO只读
FIFO打开来写
成功返回
成功返回
FIFO不是打开来写
阻塞到FIFO打开来写为止
成功返回
openFIFO只写
FIFO打开来读
成功返回
成功返回
FIFO不是打开来读
阻塞到FIFO打开来读为止
返回ENXIO错误
从空管道或空FIFOread
管道或FIFO打开来写
阻塞到管道或FIFO中有数据或管道或FIFO不再为写打开为止
返回EAGAIN错误
管道或FIFO不是打开来写
read返回0(文件结束符)
read返回0(文件结束符)
往管道或FIFOwrite
管道或FIFO打开来读
(见如下说明)
(见如下说明)
管道或FIFO不是打开来读
给线程产生SIGPIPE
给线程产生SIGPIPE
其他规则:
如果请求读出的数据量多于管道或FIFO中当前可用数据量,那么只返回这些可用的数据。
如果请求写入的数据的字节数小于或等于PIPE_BUF(一个Posix限制值),那么write操作保证是原子的。
O_NONBLOCK标志的设置对于write操作的原子性没有影响。
然而当一个管道或FIFO设置成非阻塞时,来自write的返回值取决于待写的字节数以及该管道或FIFO中当前可用空间的大小。
如果待写的字节数小于等于PIPE_BUF:
(1)如果该管道或FIFO中有足以存放所请求字节数的空间,那么所有数据字节都写入;
(2)如果该管道或FIFO中没有足以存放所请求字节数的空间,那么立即返回一个EAGAIN错误。
如果待写的字节数大于PIPE_BUF:
(1)如果该管道或FIFO中至少有1字节空间,那么内核写入该管道或FIFO能容纳数目的数据字节,该数目同时作为来自write的返回值;
(2)如果该管道或FIFO已满,那么立即返回一个EAGAIN错误。
如果写入一个没有打开着用于读的管道或FIFO,那么内核将产生一个SIGPIPE信号。
该信号的缺省动作是终止进程。
如果调用进程忽略了该信号,或捕获了该信号并从其信号处理程序中返回,那么write返回一个EPIPE错误。
处理SIGPIPE信号的最容易方法是忽略它,让write返回EPIPE错误,应用应该检查write的返回值。
注意:
使用管道的程序,一定要为SIGPIPE信号做好准备。
4.管道和FIFO的限制
系统加于管道和FIFO的唯一限制是:
∙ OPEN_MAX:
一个进程在任意时刻打开的最大描述字数。
∙ PIPE_BUF:
可原子的写往一个管道或FIFO的最大数据量。
OPEN_MAX的值可通过sysconf函数查询。
PIPE_BUF的值通常定义在中,但也可在运行时通过调用pathconf或fpathconf取得。
尽管针对管道的PIPE_BUF能够修改,但具体依赖于路径名所存放的底层文件系统,实际应该很少这么做。
5.pipe函数
#include
intpipe(intfd[2]);
返回:
成功时为0,出错时为-1。
创建一个管道,函数返回两个描述字:
fd[0]和fd[1],前者打开来读,后者打开来写。
宏S_ISFIFO可用于确定一个描述字或文件是否或是管道,或是FIFO。
它的唯一参数是stat结构的st_mode成员,计算结果或为真(非零),或者为假(0)。
管道是通过内核运作的,使用管道传输的每个字节的数据都穿越了用户-内核接口两次:
一次是在写入管道时,一次是在从管道读出时。
注意:
对管道的read只要该管道中存在一些数据就会马上返回;它不必等待达到所请求的字节数。
6.popen和pclose
#include
FILE*popen(constchar*command,constchar*type);
返回:
成功时为文件指针,出错时为NULL。
intpclose(FILE*stream);
返回:
成功时为shell的终止状态,出错时为-1。
popen函数创建一个管道并启动另一个进程,该进程或者从该管道读出标准输入,或者往该管道写入标准输出。
其中command是一个shell命令行,它由sh程序处理。
popen在调用进程和所指定的命令之间创建一个管道,由popen返回的值是一个标准I/OFILE指针,该指针或者用于输入,或者用于输出,具体取决于字符串type:
∙ 如果type为r,那么调用进程读进command的标准输出。
∙ 如果type为w,那么调用进程写到command的标准输入。
pclose函数关闭由popen创建的标准I/O流stream,等待其中的命令终止,然后返回shell的终止状态。
7.mkfifo函数
#include>
#include
intmkfifo(constchar*pathname,mode_tmode);
返回:
成功是为0,出错时为-1。
FIFO类似于管道,它是一个单向(半双工)数据流,每个FIFO有一个路径名与之关联,从而允许无亲缘关系的进程访问同一个FIFO,也称为有名管道(namedpipe)。
FIFO由mkfifo创建。
其中pahtname是一个普通的UNIX路径名,它是该FIFO的名字,mode参数指定文件权限位,类似于open的第三个参数。
mkfifo已经隐含指定O_CREAT|O_EXCL,即要么创建一个新的FIFO,要么返回一个EEXIST错误。
一个FIFO创建完毕后,它必须或者打开来读,或者打开来写,它不能打开来既读又写,因为它是半双工的。
对管道或FIFO的write总是往末尾添加数据,对它们的read总是从开头返回数据。
如果对管道或FIFO调用lseek,将返回ESPIPE错误。
打开FIFO进行处理有时序上的问题。
如果当前没有任何进程打开某个FIFO来写,那么打开该FIFO来读的进程将阻塞。
所以在多进程操作FIFO时要防止死琐的产生。
8.SystemVIPC共性描述
SystemVIPC指以下三种类型的IPC:
∙ SystemV消息队列
∙ SystemV信号灯
∙ SystemV共享内存区
所有SystemVIPC函数列表:
消息队列
信号灯
共享内存区
头文件
sys/msg.h
sys/sem.h
sys/shm.h
创建或打开函数
msgget
semget
shmget
控制操作函数
msgctl
semctl
shmctl
操作函数
msgsnd
msgrcv
semop
shmat
shmdt
key_t键和ftok函数
SystemVIPC使用key_t值作为它们的名字。
头文件把key_t定义为一个整数,它通常是一个至少32位的整数。
这些整数通常是由ftok函数赋予的。
ftok函数把一个已存在的路径名和一个整数标识符转换成一个key_t值,称为IPC键(IPCkey):
#include
key_tftok(constchar*pahtname,intid);
返回:
成功时为IPC键,出错时为-1。
如果pathname不存在,或者对调用进程不可访问,ftok返回-1。
注意:
∙ 不能保证两个不同的路径名与同一个id值的组合产生不同的键。
∙ 用于产生键的pahtname不能是服务器存活期间由它反复创建并删除的文件,否则会导致ftok多次调用返回不同的值。
ipc_perm结构
内核为每个IPC对象维护一个信息结构:
structipc_perm{
uid_tuid;/*owner'suserid*/
gid_tgid;/*owner'sgroupid*/
uid_tcuid;/*creator'suserid*/
gid_tcgid;/*creator'sgroupid*/
mode_tmode;/*accessmodes*/
ulong_tseq;/*slotusagesequencenumber*/
key_tkey;/*key*/
};
创建与打开IPC对象
创建或打开一个IPC对象需要一个类型为key_t的IPC键,对此键,应用有两种选择:
1. 调用ftok,给它传递pathname和id;
2. 指定IPC_PRIVATE,它保证创建一个新的、唯一的IPC对象。
创建或打开一个IPC对象函数共同的另一个参数是oflag,它指定IPC对象的读写权限位(ipc_perm结构中的mode成员),并选择是创建一个新的IPC对象还是访问一个存在的IPC对象。
选择的规则如下:
oflag标志
不存在
已存在
无特殊标志
出错,errno=ENOENT
成功,引用已存在对象
IPC_CREAT
成功,创建新对象
成功,引用已存在对象
IPC_CREAT|IPC_EXCL
成功,创建新对象
出错,errno=EEXIST
注意:
设置IPC_EXCL但不设置IPC_CREAT没有意义。
权限位的设置如下(八进制):
∙ 0400:
由用户(属主)读;
∙ 0200:
由用户(属主)写;
∙ 0040:
由(属)组成员读;
∙ 0020:
由(属)组成员写;
∙ 0004:
由其他用户读;
∙ 0002:
由其他用户写;
oflag由选择参数和权限参数组合而成。
ipc_perm结构的cuid和cgid成员分别设置为调用进程的有效用户ID和有效组ID,这两个成员合称为创建者ID。
ipc_perm结构的uid和gid成员也分别设置为调用进程的有效用户ID和有效组ID,这两个成员合称为属主ID。
ipc_perm结构中的seq成员是一个槽位使用情况序列号。
该变量是一个由内核为在系统中的每个潜在的IPC对象维护的计数器。
每当删除一个IPC对象时,内核就递增相应的槽位号,若溢出则循环回0。
这避免在短时间内重用IPC标识符。
9.msqid_ds结构
对于系统中的每个SystemV消息队列,内核维护一个如下的结构:
structmsqid_ds{
structipc_permmsg_perm;/*operationpermissionstruct*/
structmsg*msg_first;/*ptrtofirstmessageonq*/
structmsg*msg_last;/*ptrtolastmessageonq*/
unsignedshortmsg_cbytes;/*current#bytesonq*/
msgqnum_tmsg_qnum;/*#ofmessagesonq*/
msglen_tmsg_qbytes;/*max#ofbytesonq*/
pid_tmsg_lspid;/*pidoflastmsgsnd*/
pid_tmsg_lrpid;/*pidoflastmsgrcv*/
time_tmsg_stime;/*lastmsgsndtime*/
time_tmsg_rtime;/*lastmsgrcvtime*/
time_tmsg_ctime;/*lastchangetime*/
};
10.msgget函数
#include
intmsgget(key_tkey,intoflag);
返回:
成功时为非负标识符,出错时为-1。
用于创建一个新的SystemV消息队列或访问一个已经存在的消息队列。
参数key和oflag的说明见前。
返回值是一个整数标识符,其他三个msg函数用它来指代该队列。
当创建一个消息队列时,msqid_ds结构的如下成员被初始化:
∙ msg_perm结构的uid和cuid被设置为当前进程的有效用户ID,gid和cgid被设置为当前用户的有效组ID;
∙ oflag中的读写权限位存放在msg_perm.mode中;
∙ msg_qnum、msg_lspid、msg_lrpid、msg_stime和msg_rtime被置为0;
∙ msg_ctime被设置成当前时间;
∙ msg_qbytes被设置为系统限制值。
11.msgsnd函数
#include
intmsgsnd(intmsgid,constvoid*ptr,size_tlength,intflag);
返回:
成功时为0,出错时为-1。
该函数用于往消息队列上放置一个消息。
msgid是msgget返回的标识符,ptr是一个结构指针,该结构有如下的模板:
structmsgbuf{
longmtype;/*messagetype,mustbe>0*/
charmtext[1];/*messagedata*/
};
消息类型mtype必须大于0,因为非正消息类型有特殊的指示作用。
length参数以字节为单位指定待发送消息的长度。
这是位于长整数消息类型之后的用户自定义数据的长度,该长度可以是0。
flag参数可以是0,也可以是IPC_NOWAIT。
IPC_NOWAIT标志使得msgsnd调用非阻塞。
当有如下情形之一时:
1. 在指定的队列中已经有太多的字节(对应msqid_ds结构中的msg_qbytes值);
2. 在系统范围存在太多的消息。
若设置了IPC_NOWAIT,则msgsnd立即返回,返回一个EAGAIN错误。
若未指定该标志,则msgsnd阻塞,直到:
1. 具备存放新消息的空间;
2. 有msgid标识的消息队列被删除,此时返回EIDRM错误;
3. 被信号中断,此时返回EINTR错误。
12.msgrcv函数
#include
ssize_tmsgrcv(intmsqid,void*ptr,size_tlength,longtype,intflag);
返回:
成功时为读入缓冲区中数据的字节数,出错时为-1。
该函数从某个消息队列中读出一个消息。
ptr参数指定所接收消息的存放位置。
跟msgsnd一样,该指针指向紧挨在真正的消息数据之前返回的长整数类型字段。
length指定由ptr指向的缓冲区中数据部分的大小。
这是该函数能返回的最大数据量。
该长度不包含长整数类型字段。
type指定希望从所给定的队列中读出什么样的消息:
1