1、UNIX 培训教程(四)多进程编程 版权所有 第 1 页 共 35 页UNIX 培训教程(四)多进程编程V 1.0UNIX 培训教程(四)多进程编程 版权所有 第 2 页 共 35 页目 录第一章 多进程程序的特点 4第二章 常用的多进程编程的系统调用 52.1 FORK() 52.2 SYSTEM() 62.3 EXEC().72.4 POPEN().82.5 PCLOSE().92.6 WAIT().92.7 WAITPID() .102.8 SETPGRP() .112.9 EXIT()112.10 SIGNAL() .122.11 KILL()132.12 ALARM() .142.1
2、3 MSGSND() .152.14 MSGRCV() .162.15 MSGCTL()172.16 MSGGET()182.17 SHMAT()192.18 SHMDT()202.19 SHMGET()202.20 SHMCTL()212.21 SEMCTL() 232.22 SEMGET() 242.23 SEMOP() 252.24 SDENTER() 272.25 SDLEAVE() 282.26 SDGET().282.27 SDFREE().292.28 SDGETV() 292.29 SDWAITV() 292.30 SBRK().302.31 GETENV() 312.32 P
3、UTENV() 31第三章 多进程编程技巧 323.1 主要程序结构 323.1.1 事件主控方式 323.1.2 信息协调方式 323.2 选择主体分叉点 323.2.1 事件初始产生 32UNIX 培训教程(四)多进程编程 版权所有 第 3 页 共 35 页3.2.2 主程序自主产生 323.3 进程间关系处理 333.3.1 父子进程关系 333.3.2 兄弟进程关系 333.4 进程间通讯处理 33UNIX 培训教程(四)多进程编程 版权所有 第 4 页 共 35 页第一章 多进程程序的特点由于UNIX系统是分时多用户系统, CPU按时间片分配给各个用户使用, 而在实质上应该说CPU按
4、时间片分配给各个进程使用, 每个进程都有自己的运行环境以使得在CPU做进程切换时不会“忘记“该进程已计算了一半的“半成品“.以DOS的概念来说, 进程的切换都是一次“DOS中断“处理过程, 包括三个层次:(1)用户数据的保存: 包括正文段(TEXT), 数据段(DATA,BSS), 栈段(STACK),共享内存段(SHARED MEMORY)的保存.(2)寄存器数据的保存: 包括PC(program counter,指向下一条要执行的指令的地址), PSW(processor status word,处理机状态字), SP(stack pointer,栈指针), PCBP(pointer of
5、 process control block,进程控制块指针), FP(frame pointer,指向栈中一个函数的local变量的首地址), AP(augument pointer,指向栈中函数调用的实参位置), ISP(interrupt stack pointer,中断栈指针), 以及其他的通用寄存器等.(3)系统层次的保存: 包括proc,u,虚拟存储空间管理表格,中断处理栈.以便于该进程再一次得到CPU时间片时能正常运行下去.既然系统已经处理好所有这些中断处理的过程, 我们做程序还有什么要担心的呢? 我们尽可以使用系统提供的多进程的特点, 让几个程序精诚合作, 简单而又高效地把结果
6、给它搞出来.另外,UNIX系统本身也是用C语言写的多进程程序,多进程编程是UNIX的特点,当我们熟悉了多进程编程后,将会对UNIX系统机制有一个较深的认识.首先介绍一下多进程程序的一些突出的特点:1.并行化一件复杂的事件是可以分解成若干个简单事件来解决的, 这在程序员的大脑中早就形成了这种概念, 首先将问题分解成一个个小问题, 将小问题再细分, 最后在一个合适的规模上做成一个函数. 在软件工程中也是这么说的. 如果我们以图的方式来思考, 一些小问题的计算是可以互不干扰的, 可以同时处理, 而在关键点则需要统一在一个地方来处理, 这样程序的运行就是并行的, 至少从人的时间观念上来说是这样的. 而
7、每个小问题的计算又是较简单的.2.简单有序这样的程序对程序员来说不亚于管理一班人, 程序员为每个进程设计好相应的功能, 并通过一定的通讯机制将它们有机地结合在一起, 对每个进程的设计是简单的, 只在总控部分小心应付(其实也是蛮简单的), 就可完成整个程序的施工.3.互不干扰这个特点是操作系统的特点, 各个进程是独立的, 不会串位.UNIX 培训教程(四)多进程编程 版权所有 第 5 页 共 35 页4.事务化比如在一个数据电话查询系统中, 将程序设计成一个进程只处理一次查询即可, 即完成一个事务. 当电话查询开始时, 产生这样一个进程对付这次查询; 另一个电话进来时, 主控程序又产生一个这样的
8、进程对付, 每个进程完成查询任务后消失. 这样的编程多简单, 只要做一次查询的程序就可以了.第二章 常用的多进程编程的系统调用2.1 fork() 功能:创建一个新的进程. 语法:#include #include pid_t fork(); 说明:本系统调用产生一个新的进程, 叫子进程, 是调用进程的一个复制品. 调用进程叫父进程, 子进程继承了父进程的几乎所有的属性:. 实际 UID,GID 和有效 UID,GID. 环境变量. 附加 GID. 调用 exec()时的关闭标志. UID 设置模式比特位. GID 设置模式比特位. 进程组号. 会话 ID. 控制终端. 当前工作目录. 根目录
9、. 文件创建掩码 UMASK. 文件长度限制 ULIMIT. 预定值, 如优先级和任何其他的进程预定参数, 根据种类不同决定是否可以继承. 还有一些其它属性.但子进程也有与父进程不同的属性:. 进程号, 子进程号不同与任何一个活动的进程组号. 父进程号. 子进程继承父进程的文件描述符或流时, 具有自己的一个拷贝并且与父进程和其它子进程共享该资源. 子进程的用户时间和系统时间被初始化为 0. 子进程的超时时钟设置为 0. 子进程的信号处理函数指针组置为空.UNIX 培训教程(四)多进程编程 版权所有 第 6 页 共 35 页. 子进程不继承父进程的记录锁. 返回值: 调用成功则对子进程返回 0,
10、 对父进程返回子进程号, 这也是最方便的区分父子进程的方法. 若调用失败则返回-1 给父进程,子进程不生成. 例子:pid_t pid;if (pid=fork()0) /*父进程处理过程 */else if (pid=0) /*子进程处理过程 */exit(0); /*注意子进程必须用 exit()退出运行*/else printf(“fork errorn“);exit(0);2.2 system() 功能:产生一个新的进程, 子进程执行指定的命令. 语法:#include #include int system(string)char *string; 说明:本调用将参数 string
11、传递给一个命令解释器(一般为 sh)执行, 即 string 被解释为一条命令, 由 sh 执行该命令.若参数 string 为一个空指针则为检查命令解释器是否存在.该命令可以同命令行命令相同形式, 但由于命令做为一个参数放在系统调用中, 应注意编译时对特殊意义字符的处理. 命令的查找是按 PATH 环境变量的定义的. 命令所生成的后果一般不会对父进程造成影响. 返回值:当参数为空指针时, 只有当命令解释器有效时返回值为非零.若参数不为空指针, 返回值为该命令的返回状态(同 waitpid()的返回值. 命令无效或语法错误则返回非零值,所执行的命令被终止. 其他情况则返回-1. 例子:char
12、 command81;int i;for (i=1;iint execl(path,arg0,.,argn,(char*)0)char *path,*arg0,.,*argn;int execv(path,argv)char *path,*argv;int execle(path,arg0,.,argn,(char*)0,envp)char *path,*arg0,.,*argn,*envp;int execve(path,argv,envp)char *path,*argv,*envp;int execvp(file,argv)char *file,*argv; 说明:这是一个系统调用族,
13、用于将一个新的程序调入本进程所占的内存, 并覆盖之, 产生新的内存进程映象. 新的程序可以是可执行文件或 SHELL 批命令.当 C 程序被执行时 ,是如下调用的:main(int argc,char *argv,char *envp);argc 是参数个数,是各个参数字符串指针数组 ,envp 是新进程的环境变量字符串的指针数组.argc 至少为 1,argv0为程序文件名,所以,在上面的 exec 系统调用族中,path 为新进程文件的路径名 ,file 为新进程文件名,若 file 不是全路径名,系统调用会按 PATH 环境变量自动找对应的可执行文件运行.若新进程文件不是一个可执行的目标
14、文件(如批处理文件), 则 execlp()和 execvp()会将该文件内容作为一个命令解释器的标准输入形成 system().arg0,.等指针指向0结束的字符串,组成新进程的有效参数,且该参数列表以一个空指针结束.反过来,arg0 至少必须存在并指向新进程文件名或路径名.同样,argv 是字符串指针数组,argv0指向新进程文件名或路径名,并以一空指针结束.envp 是一个字符串指针数组,以空指针结束,这些字符串组成新进程的环境.在调用这些系统调用前打开的文件指针对新进程来说也是打开的,除非它已定义了 close-on-exec 标志.打开的文件指针在新进程中UNIX 培训教程(四)多进
15、程编程 版权所有 第 8 页 共 35 页保持不变,所有相关的文件锁也被保留.调用进程设置并正被捕俘的信号在新进程中被恢复为缺省设置,其它的则保持不变.新进程启动时按文件的 SUID 和 SGID 设置定义文件的 UID 和 GID 为有效 UID 和 GID.新进程还继承了如下属性:. 附加 GID. 进程号. 父进程号. 进程组号. 会话号. 控制终端. alarm 时钟信号剩下的时间 . 当前工作目录. 根目录. 文件创建掩码. 资源限制. 用户时间,系统时间,子进程用户时间,子进程系统时间. 记录锁. 进程信号掩码. 信号屏蔽. 优先级. 预定值.调用成功后,系统调用修改新进程文件的最
16、新访问时间. 返回值:该系统调用一般不会有成功返回值, 因为原来的进程已荡然无存. 例子:printf(“now this process will be ps commandn“);execl(“/bin/ps“,“ps“,“-ef“,NULL);2.4 popen() 功能:初始化从/到一个进程的管道. 语法:#include FILE *popen(command,type)char *command,type; 说明:本系统调用在调用进程和被执行命令间创建一个管道.参数 command 做为被执行的命令行.type 做为 I/O 模式,“r“为从被执行命令读,“w“为向被执行命令写.返
17、回一个标准流指针,做为管道描述符,向被执行命令读或写数据(做为被执行命令的 STDIN 或STDOUT)该系统调用可以用来在程序中调用系统命令,并取得命令的输出信息或者向命令输入信息.UNIX 培训教程(四)多进程编程 版权所有 第 9 页 共 35 页 返回值:不成功则返回 NULL,成功则返回管道的文件指针.2.5 pclose() 功能:关闭到一个进程的管道. 语法:#include int pclose(strm)FILE *strm; 说明:本系统调用用于关闭由 popen()打开的管道,并会等待由 popen() 激活的命令执行结束后,关闭管道后读取命令返回码. 返回值:若关闭的文
18、件描述符不是由 popen()打开的,则返回-1. 例子:printf(“now this process will call popen system calln“);FILE * fd;if (fd=popen(“ps -ef“,“r“)=NULL) printf(“call popen failedn“);return;else char str80;while (fgets(str,80,fd)!=NULL)printf(“%sn“,str);pclose(fd);2.6 wait() 功能:等待一个子进程返回并修改状态 语法:#include #include pid_t wait(
19、stat_loc)int *stat_loc; 说明:允许调用进程取得子进程的状态信息.调用进程将会挂起直到其一个子进程终止. 返回值:等待到一个子进程返回时,返回值为该子进程号,否则返回值为-1.同时 stat_loc 返回子进程的返回值. 例子:/*父进程*/if (fork()0) wait(int *)0);UNIX 培训教程(四)多进程编程 版权所有 第 10 页 共 35 页/*父进程等待子进程的返回 */else /*子进程处理过程 */exit(0);2.7 waitpid() 功能:等待指定进程号的子进程的返回并修改状态 语法:#include #include pid_t
20、waitpid(pid,stat_loc,options)pid_t pid;int *stat_loc,options; 说明:当 pid 等于-1,options 等于 0 时,该系统调用等同于 wait().否则该系统调用的行为由参数 pid 和 options 决定.pid 指定了一组父进程要求知道其状态的子进程:-1:要求知道任何一个子进程的返回状态.0:要求知道进程号为 pid 值的子进程的状态 .0) waitpid(pid,/*父进程等待进程号为 pid 的子进程的返回 */UNIX 培训教程(四)多进程编程 版权所有 第 11 页 共 35 页else /*子进程的处理过程
21、*/exit(1);/*父进程*/printf(“stat_loc is %dn“,stat_loc);/*字符串“stat_loc is 1“ 将被打印出来*/2.8 setpgrp() 功能:设置进程组号和会话号. 语法:#include pid_t setpgrp() 说明:若调用进程不是会话首进程.将进程组号和会话号都设置为与它的进程号相等.并释放调用进程的控制终端. 返回值:调用成功后,返回新的进程组号. 例子:/*父进程处理*/if (fork()0) /*父进程处理 */else setpgrp();/*子进程的进程组号已修改成与它的进程号相同*/exit(0);2.9 exit
22、() 功能:终止进程. 语法:#include void exit(status)int status; 说明:调用进程被该系统调用终止.引起附加的处理在进程被终止前全部结束. 返回值:无UNIX 培训教程(四)多进程编程 版权所有 第 12 页 共 35 页2.10 signal() 功能:信号管理功能 语法:#include void (*signal(sig,disp)(int)int sig;void (*disp)(int);void (*sigset(sig,disp)(int)int sig;void (*disp)(int);int sighold(sig)int sig;in
23、t sigrelse(sig)int sig;int sigignore(sig)int sig;int sigpause(sig)int sig; 说明:这些系统调用提供了应用程序对指定信号的简单的信号处理.signal()和 sigset()用于修改信号定位.参数 sig 指定信号(除了SIGKILL 和 SIGSTOP,这两种信号由系统处理 ,用户程序不能捕捉到).disp 指定新的信号定位 ,即新的信号处理函数指针.可以为SIG_IGN,SIG_DFL 或信号句柄地址.若使用 signal(),disp 是信号句柄地址,sig 不能为 SIGKILL,SIGTRAP或 SIGPWR,收
24、到该信号时,系统首先将重置 sig 的信号句柄为 SIG_DFL,然后执行信号句柄.若使用 sigset(),disp 是信号句柄地址,该信号时,系统首先将该信号加入调用进程的信号掩码中,然后执行信号句柄.当信号句柄运行结束后,系统将恢复调用进程的信号掩码为信号收到前的状态.另外,使用 sigset()时,disp 为 SIG_HOLD,则该信号将会加入调用进程的信号掩码中而信号的定位不变.sighold()将信号加入调用进程的信号掩码中.sigrelse()将信号从调用进程的信号掩码中删除 .sigignore()将信号的定位设置为 SIG_IGN.sigpause()将信号从调用进程的信号
25、掩码中删除,同时挂起调用进程直到收到信号.若信号 SIGCHLD 的信号定位为 SIG_IGN,则调用进程的子进程在终UNIX 培训教程(四)多进程编程 版权所有 第 13 页 共 35 页止时不会变成僵死进程.调用进程也不用等待子进程返回并做相应处理. 返回值:调用成功则 signal()返回最近调用 signal()设置的 disp 的值.否则返回 SIG_ERR. 例子一:设置用户自己的信号中断处理函数,以 SIGINT 信号为例:int flag=0;void myself()flag=1;printf(“get signal SIGINTn“);/*若要重新设置 SIGINT 信号中
26、断处理函数为本函数则执行以*下步骤*/void (*a)();a=myself;signal(SIGINT,a);flag=2;main()while (1) sleep(2000); /*等待中断信号*/if (flag=1) printf(“skip system call sleepn“);exit(0);if (flag=2) printf(“skip system call sleepn“);printf(“waiting for next signaln“);2.11 kill() 功能:向一个或一组进程发送一个信号. 语法:#include #include int kill(p
27、id,sig);pid_t pid;int sig;UNIX 培训教程(四)多进程编程 版权所有 第 14 页 共 35 页 说明:本系统调用向一个或一组进程发送一个信号,该信号由参数 sig指定,为系统给出的信号表中的一个.若为 0(空信号)则检查错误但实际上并没有发送信号,用于检查 pid 的有效性.pid 指定将要被发送信号的进程或进程组 .pid 若大于 0,则信号将被发送到进程号等于 pid 的进程; 若 pid 等于 0 则信号将被发送到所有的与发送信号进程同在一个进程组的进程(系统的特殊进程除外);若 pid 小于-1, 则信号将被发送到所有进程组号与 pid 绝对值相同的进程;
28、若 pid 等于-1,则信号将被发送到所有的进程(特殊系统进程除外).信号要发送到指定的进程,首先调用进程必须有对该进程发送信号的权限.若调用进程有合适的优先级则具备有权限.若调用进程的实际或有效的 UID 等于接收信号的进程的实际 UID 或用 setuid()系统调用设置的 UID,或 sig 等于 SIGCONT 同时收发双方进程的会话号相同,则调用进程也有发送信号的权限.若进程有发送信号到 pid 指定的任何一个进程的权限则调用成功,否则调用失败,没有信号发出. 返回值:调用成功则返回 0,否则返回-1. 例子:假设前一个例子进程号为 324,现向它发一个 SIGINT 信号,让它做信
29、号处理:kill(pid_t)324,SIGINT);2.12 alarm() 功能:设置一个进程的超时时钟. 语法:#include unsigned int alarm(sec)unsigned int sec; 说明:指示调用进程的超时时钟在指定的时间后向调用进程发送一个SIGALRM 信号.设置超时时钟时时间值不会被放入堆栈中,后一次设置会把前一次(还未到超时时间)冲掉.若 sec 为 0,则取消任何以前设置的超时时钟 .fork()会将新进程的超时时钟初始化为 0.而当一个进程用 exec()族系统调用新的执行文件时,调用前设置的超时时钟在调用后仍有效. 返回值:返回上次设置超时时钟
30、后到调用时还剩余的时间秒数. 例子:int flag=0;void myself()flag=1;UNIX 培训教程(四)多进程编程 版权所有 第 15 页 共 35 页printf(“get signal SIGALRMn“);/*若要重新设置 SIGALRM 信号中断处理函数为本函数则执行*以下步骤*/void (*a)();a=myself;signal(SIGALRM,a);flag=2;main()alarm(100); /*100 秒后发超时中断信号*/while (1) sleep(2000); /*等待中断信号*/if (flag=1) printf(“skip system
31、call sleepn“);exit(0);if (flag=2) printf(“skip system call sleepn“);printf(“waiting for next signaln“);2.13 msgsnd() 功能:发送消息到指定的消息队列中. 语法:#include #include #include int msgsnd(msqid,msgp,msgsz,msgflg)int msqid;void *msgp;size_t msgsz;int msgflg; 说明:发送一个消息到由 msqid 指定消息队列标识号的消息队列.参数msgp 指向一个用户定义的缓冲区,并
32、且缓冲区的第一个域应为长整型,指定消息类型,其他数据放在缓冲区的消息中其他正文区内.下面是消息元素定义:long mtype;char mtext;mtype 是一个整数,用于接收进程选择消息类型.UNIX 培训教程(四)多进程编程 版权所有 第 16 页 共 35 页mtext 是一个长度为 msgsz 字节的任何正文,参数 msgsz 可从 0 到系统允许的最大值间变化.msgflg 指定操作行为 :. 若(msgflgvoid *msgp;int msgsz;long msgtyp;int msgflg; 说明:本系统调用从由 msqid 指定的消息队列中读取一个由 msgtyp 指定类
33、型的消息到由 msgp 指向的缓冲区中,同样的,该缓冲区的结构如前所述,包括消息类型和消息正文.msgsz 为可接收的消息正文的字节数.若接收到的消息正文的长度大于 msgsz,则会被截短到 msgsz 字节为止(当消息标志 msgflgstruct msqid_ds *buf; 说明:本系统调用提供一系列消息控制操作,操作动作由 cmd 定义,以下cmd 定义值表明了各操作动作的定义. IPC_STAT:将 msqid 相关的数据结构中各个元素的当前值放入由buf 指向的结构中 . IPC_SET:将 msqid 相关的数据结构中的下列元素设置为由 buf 指向的结构中的对应值.msg_pe
34、rm.uidmsg_perm.gidmsg_perm.modemsg_qbytes本命令只能由有效 UID 等于 msg_perm.cuid 或 msg_perm.uid 的进程或有效 UID 有合适权限的进程操作.只有具有合适权限的用户才能增加 msg_qbytes 的值. IPC_RMID:删除由 msqid 指示的消息队列.将它从系统中删除并破坏相关的数据结构.本命令只能由有效 UID 等于 msg_perm.cuid 或 msg_perm.uid 的进程或有效 UID 有合适权限的进程操作.UNIX 培训教程(四)多进程编程 版权所有 第 18 页 共 35 页 返回值:调用成功则返回
35、值为 0,否则为-1.2.16 msgget() 功能:取得一个消息队列. 语法:#include #include #include int msgget(key,msgflg)key_t key;int msgflg; 说明:本系统调用返回与参数 key 相关的消息队列的标识符.若以下事实成立,则与消息队列相关的标识符和数据结构将被创建出来:. 若参数 key 等于 IPC_PRIVATE. 若参数 key 没有一个已存在的消息队列标识符与之相关,同时值(msgflg否则返回值为-1. 例子:本例将包括上述所有消息队列操作的系统调用:#define RKEY 0x9001L /*读消息队列
36、的 KEY 值*/#define WKEY 0x9002L /*写消息队列的 KEY 值*/#define MSGFLG 0666 /*消息队列访问权限*/#define IPC_WAIT 0 /*等待方式在 include 文件中未定义*/int rmsqid; /*读消息队列标识符 */int wmsqid; /*写消息队列标识符*/struct msgbuf long mtype;char mtext200; buf;/*若读消息队列已存在就取得标识符 ,否则则创建并取得标识符*/if (rmsqid=msgget(RKEY,MSGFLG|IPC_CREAT)0)printf(“get
37、%ld type message from queue:%sn“,buf.mtype,buf.mtext);else printf(“get message failedn“);exit(3);buf.mtype=3Lif (msgsnd(wmsqid,else printf(“send message failedn“);exit(4);msgctl(wmsqid,IPC_RMID,(struct msqid *)NULL);2.17 shmat() 功能:联接共享内存的操作. 语法:#include #include #include void *shmat(shmid,shmaddr,s
38、hmflg)int shmid;void *shmaddr;int shmid; 说明:将由 shmid 指示的共享内存联接到调用进程的数据段中.被联接的段放在地址,该地址由以下准则指定:. 若 shmaddr 等于(void *)0,则段联接到由系统选择的第一个可用的地址上. 若 shmaddr 不等于(void *)0 同时(shmflg否则 ,若值不为真且调用进程有读写权限,则被联接的段为可读写的. 返回值:若调用成功则返回被联接的共享内存段在数据段上的启始地址.否则返回值为-1.2.18 shmdt() 功能:断开共享内存联接的操作. 语法:#include #include #inc
39、lude void *shmdt(shmaddr)void *shmaddr; 说明:本系统调用将由 shmaddr 指定的共享内存段从调用进程的数据段脱离出去. 返回值:若调用成功则返回值为 0,否则返回值为-1.2.19 shmget() 功能:取得共享内存段 语法:#include #include #include int shmget(key,size,shmflg)key_t key;int size,shmflg; 说明:本系统调用返回 key 相关的共享内存标识符.共享内存标识符和相关数据结构及至少 size 字节的共享内存段能正常创建,要求以下事实成立:. 参数 key 等于
40、 IPC_PRIVATE. 参数 key 没有相关的共享内存标识符,同时(shmflgstruct shmid_ds *buf; 说明:本系统调用提供一系列共享内存控制操作.操作行为由 cmd 指定.以下为 cmd 的有效值:. IPC_STAT:将 shmid 相关的数据结构中各个元素的当前值放入由buf 指向的结构中 . IPC_SET:将 shmid 相关的数据结构中的下列元素设置为由 buf 指向的结构中的对应值.shm_perm.uidshm_perm.gidshm_perm.mode本命令只能由有效 UID 等于 shm_perm.cuid 或 shm_perm.uid 的进程或有
41、效 UID 有合适权限的进程操作. IPC_RMID:删除由 shmid 指示的共享内存.将它从系统中删除并破坏相关的数据结构.本命令只能由有效 UID 等于 shm_perm.cuid 或 shm_perm.uid 的进程或有效 UID 有合适权限的进程操作. 返回值:若调用成功则返回 0,否则返回-1. 例子:本例包括上述所有共享内存操作系统调用:#include #include #include #define SHMKEY 74#define K 1024int shmid;cleanup()UNIX 培训教程(四)多进程编程 版权所有 第 22 页 共 35 页shmctl(shm
42、id,IPC_RMID,0);exit(0);main()int *pint;char *addr1,*addr2;extern char *shmat();extern cleanup();for (i=0;i#include #include int semctl(semid,memnum,cmd,arg)int semid,semnum,cmd;union semun int val;struct semid_ds *buf;ushort *array;arg;UNIX 培训教程(四)多进程编程 版权所有 第 23 页 共 35 页 说明:本系统调用提供了一个信号量控制操作,操作行为由
43、cmd 定义,这些命令是对由 semid 和 semnum 指定的信号量做操作的.每个命令都要求有相应的权限级别:. GETVAL:返回 semval 的值,要求有读权限. SETVAL:设置 semval 的值到 arg.val 上.此命令成功执行后,semadj 的值对应的所有进程的信号量全部被清除,要求有修改权限. GETPID:返回 sempid 的值,要求有读权限. GETNCNT:返回 semncnt 的值,要求有读权限. GETZCNT:返回 semzcnt 的值,要求有读权限.以下命令在一组信号量中的各个 semval 上操作:. GETALL:返回每个 semval 的值,同
44、时将各个值放入由 arg.array指向的数组中.当此命令成功执行后,semadj 的值对应的所有进程的信号量全部被清除,要求有修改权限. SETALL:根据由 arg.array 指向的数组设置各个 semval 值.当此命令成功执行后,semadj 的值对应的所有进程的信号量全部被清除,要求有修改权限.以下命令在任何情况下都是有效的:. IPC_STAT:将与 semid 相关的数据结构的各个成员的值放入由arg.buf 指向的结构中 .要求有读权限. IPC_SET:设置 semid 相关数据结构的如下成员,设置数据从arg.buf 指向的结构中读取 :sem_perm.uidsem_p
45、erm.gidsem_perm.mode本命令只能由有效 UID 等于 sem_perm.cuid 或 sem_perm.uid 的进程或有效 UID 有合适权限的进程操作. IPC_RMID:删除由 semid 指定的信号量标识符和相关的一组信号量及数据结构.本命令只能由有效 UID 等于 sem_perm.cuid 或sem_perm.uid 的进程或有效 UID 有合适权限的进程操作 . 返回值:若调用成功,则根据 cmd 返回以下值:GETVAL:semval 的值.GETPID:sempid 的值.GETNCNT:semncnt 的值.GETZCNT:semzcnt 的值.其他:0.
46、若调用失败则返回-1.2.22 semget() 功能:取得一组信号量. 语法:#include UNIX 培训教程(四)多进程编程 版权所有 第 24 页 共 35 页#include #include int semget(key,nsems,semflg)key_t key;int nsems,semflg; 说明:返回和 key 相关的信号量标识符.若以下事实成立,则与信号量标识符,与之相关的 semid_ds 数据结构及一组 nsems 信号量将被创建:. key 等于 IPC_PRIVATE. 系统内还没有与 key 相关的信号量,同时(semflg否则返回-1.2.23 semo
47、p() 功能:信号量操作. 语法:#include #include #include int semop(semid,sops,nsops)int semid;struct sembuf *sops;unsigned nsops; 说明:本系统调用用于执行用户定义的在一组信号量上操作的行为集合.该组信号量与 semid 相关.参数 sops 为一个用户定义的信号量操作结构数组指针.参数 nsops 为该数组的元素个数.数组的每个元素结构包括如下成员:sem_num; /* 信号量数 */sem_op; /* 信号量操作 */sem_flg; /* 操作标志 */由本系统调用定义的每个信号量操
48、作是针对由 semid 和 sem_num 指定的信号量的.变量 sem_op 指定三种信号量操作的一种:UNIX 培训教程(四)多进程编程 版权所有 第 25 页 共 35 页. 若 sem_op 为一负数并且调用进程具有修改权限,则下列情况之一将会发生:* 若 semval 不小于 sem_op 的绝对值,则 sem_op 的绝对值被减去semval 的值.若(semflgunsigned int count;/*在文件 sys/sem.h 中定义的 sembuf 结构* struct sembuf * unsigned short sem_num;UNIX 培训教程(四)多进程编程 版权所有 第 26 页 共 35 页* short sem_op;* short sem_flg;* */struct sembuf psembuf,vsembuf; /*P 和 V 操作*/cleanup()semctl(semid,2,IPC_RMID,0);exit(0);main(argc,argv)int argc;char *argv;int i,first,second;short initarray2,outarray2;extern