UNIX下C语言编程技术交流.ppt
二、二、UNIX下下C编程基础编程基础ObjectivesObjectives学习完本章以后,我们应该了解如下内容:1)UNIX下C编程的常见类型2)常用的系统调用和库函数3)了解UNIX编程的基本概念:文件操作、进程控制、信号、信号灯、共享内存、消息队列、SOCKET通信、4)了解curses编程接口。5)了解Pro*C编程接口 二、二、UNIX下下C编程基础编程基础 文件用户角度文件编码普通文件设备文件ASCII/文本文件二进制文件/流式文件普通文件是指驻留在磁盘或其它外部介质上的一个有序数据集,可以是源文件、目标文件、可执行程序;也可以是一组待输入处理的原始数据,或者是一组输出的结果。设备文件是指与主机相联的各种外部设备,如显示器、打印机、键盘等 文件I/O 二、二、UNIX下下C编程基础编程基础 文件指针:在语言中用一个指针变量指向一个文件,这个指针称为文件指针。通过文件指针就可以对它所指的 文件进行打开、关闭、读、写、定位等各种操作。说明文件指针的一般形式为:FILE *指针变量标识符;其中FILE应为大写,它实际上是由系统定义的一个结构,该结构中含有文件名、文件状态和文件当前位置等信息。常用缓冲文件系统函数 名称功能名称功能名称功能fopen()打开一个文件fputs()写串到文件rewind()把文件定位指示置回文件头fclose()关闭一个文件fseek()在文件中定位特定字节remove()删除一个文件putc()向文件中写入一个字符ftell()返回当前文件位置fflush()请缓冲fputc()与putc()相同fprintf()与文件的关系如同printf和控制台的关系getc()从文件中读取一个字符fscanf()与文件的关系如同scanf和控制台的关系fgetc()与getc()相同feof()到达文件尾时返回truefgets()从文件中读一字串ferror()发生错误是返回true方式意义方式意义r只读打开一个文本文件,只允许读数据 w+读写打开或建立一个文本文件,允许读写 w只写打开或建立一个文本文件,只允许写数据 a+读写打开一个文本文件,允许读,或在文件末追加数据a追加打开一个文本文件,并在文件末尾写数据 r+b读写打开一个二进制文件,允许读和写 rb只读打开一个二进制文件,只允许读数据 w+b读写打开或建立一个二进制文件,允许读和写wb只写打开或建立一个二进制文件,只允许写数据 a+b读写打开一个二进制文件,允许读,或在文件末追加数据ab追加打开一个二进制文件,并在文件末尾写数据 r+读写打开一个文本文件,允许读和写 文件I/O 二、二、UNIX下下C编程基础编程基础 1、使用fopen()、getc()、putc()和fclose()/*this is a test for file I/O use function fopen(),getc(),putc()and fclose()*/#include stdio.h.#include void main(int argc,char*argv)FILE*fp;char ch;if(argc!=2)printf(“please enter filename.n”);exit(1);If(fp=fopen(argv1,”w”)=NULL)printf(“cant open file.n”);exit(1);do ch=getchar();putc(ch,fp);while(ch!=#);fclose(fp);return;文件I/O 二、二、UNIX下下C编程基础编程基础/*feof sample*/#include stdio.h.#include void main(int argc,char*argv)FILE*in,*out;char ch;while(!feof(in)ch=getc(in);putc(ch,out);fclose(in);fclose(out);return;/*ferror sample */#include stdio.h.#include void main(int argc,char*argv)FILE*in,*out;char ch;while(!feof(in)ch=getc(in);if(ferror(in)printf(“read file errorn”);break;putc(ch,out);fclose(in);fclose(out);return;2、文件检测函数feof()、ferror()文件I/O 二、二、UNIX下下C编程基础编程基础信号的概念 信号是送到进程的“软中断”,它通知进程在它们的环境中出现了非正常的事件,进程可以进行相应的处理。信号可由进程发出,也可由终端或内核发出,大多数标准的UNIX信号使得一个进程在它收到信号时终止进程。信号一般是异步发生的。信号的传递方式如下:1)一个进程发给另一个进程;2)内核发给进程。每个信号都有一个名字,定义在中。1)系统调用kill允许一个进程把信号传给另一个进程或本身。为了发送信号,发送进程和接收进程必须有相同的有效用户号或发送进程有超级用户特权,系统调用kill的形式如下:intkill(shortpid,intsig);第一个参数指出接收信号的进程的进程ID;第二个参数是一个信号值。a、参数pid=0,信号发送给发送进程组中的所有进程。b、pid=-1,发送进程非超级用户,信号发送给所有实际用户号等于发送进程有效用户号的进程;c、pid=-1,发送进程是超级用户,则信号发送给除系统进程外的所有其它进程。d、pid为负数且不等于-1,则信号发送给所有进程组号为pid绝对值的进程;e、参数sig=0,则不发送信号只做错误检测,此选项用来测试进程号的有效性。信号产生的5个条件2、kill命令也可用来发送信号。上述各选项也适用于此命令。3、特定的终端字符也会产生信号,如ctl-c或Delete、ctl-y、ctl-z。这些 由终端产生的信号不只是发给正在运行的进程,也发给所有终端控制组 中的进程,一般由内核发给进程。4、一定的硬件条件也产生信号。比如浮点算术错产生信号SIGPE。5、一定的软件条件也产生信号 信号二、二、UNIX下下C编程基础编程基础信号的处理 忽略信号:除信号SIGKILL、SIGSTOP外,所有其它信号皆可忽略;这使得操作系统停止输送信号到进程。捕捉信号:这也就是对应于捕获到一个信号,包含把一个用户提供的过程告诉操作系统在信号到达时进程的执行转到某个函数,而当函数返回时返回到它被调用的那个位置。信号默认动作:系统为每种信号规定了一个默认的动作,这个默认的动作是由UNIX内核来完成。5中可能的默认动作:1)流产 2)终止 3)忽略 4)挂起 5)继续 信号的分类:1)程序错误类信号 2)程序终止类信号 3)时钟信号类信号 4)异步I/O类信号 5)作业控制类信号 6)操作错误类信号 7)其他信号 信号二、二、UNIX下下C编程基础编程基础1)程序错误类信号 SIGABRT:调用abort()函数产生。SIGFPE:算术异常 SIGILL:执行了非法硬件指令或企图执行特权指令。经过编译器生成的可执行文件只包含合法指令,出现这种情 况一般是因为可执行文件造到破坏或函数指针类型的对象赋予了非法值。SIGBUS:总线错误 SIGSYS:非法的系统调用。2)程序终止类信号 SIGHUP:进程挂起 SIGINT:终端的中断键被按下(如Ctrl-c)SIGKILL:致命的中断(不可以被忽略,也不可以被阻塞)。SIGQUIT:当我们按下终端结束键的时候会出现这种情况。SIGTERM:是一个使程序终止的普通信号。3)时钟类信号 SIGALRM:时钟到期(如用alarm()设定的时间到期)SIGPROF:当前进程用去的CPU时间+系统为该进程服务用去的时间到期。SIGVTALRM:当前进程用去的CPU时间到期。信号二、二、UNIX下下C编程基础编程基础4)I/O类信号 SIGIO SIGPOLL SIGURG 这些信号的默认动作是忽略他们。5)作业控制类信号 SIGCHLD:只要进程终止或停止就会向其父进程发送这个信号。SIGCONT:恢复被挂起的进程 SIGSTOP:停止一个进程。6)操作错误类信号 SIGPIPE:该信号指出管道破裂 SIGXFSZ:当进程企图扩大文件以至于超过文件大小限制时出现这个信号。7)其他信号 SIGUSR1,SIGUSR2:用户自己定义的信号 信号二、二、UNIX下下C编程基础编程基础信号的操作 signal函数:通过调用signal来确定怎样处理一个信号,调用形式如下:#include void (*signal(int sig,void(*func)(int)(int);signal函数的功能是为信号sig建立func指定的动作。func 指明信号sig发生时可以采取的3种动作之一:1)SIG_DFL:表示信号以缺省的方式处理;2)SIG_IGN:表示忽略信号的处理。3)信号句柄地址:信号捕获函数。信号二、二、UNIX下下C编程基础编程基础一个简单的例子#include#include static void sig_usr(int);int main()if(signal(SIGUSR1,sig_usr)=SIG_ERR)printf(无法捕获信号一n);if(signal(SIGUSR2,sig_usr)=SIG_ERR)printf(无法捕获信号二n);for(;)pause();static void sig_usr(int signo)if(signo=SIGUSR1)printf(捕获到一了,呵呵n);if(signo=SIGUSR2)printf(捕获到二了,呵呵n);return;信号二、二、UNIX下下C编程基础编程基础信号的操作 sigaction函数:该函数与signal有基本相同的作用,但它比signal可靠,而且还提供了对信号更多的控制能力#include int sigaction(int sig,const struct sigaction*act,struct sigaction*oact);sig用来指定信号 act 和oact 都是指向类型为 sigaction结构的指针。如果act不为NULL,则指向规定信号动作的一个结构,否则,函数调用不改变信号的动作,但可以用来查询当前信号的动作。如果oact不为NULL,系统将返回先前与该信号相联系的动作于oact所指的位置。返回值:成功为0,失败则不执行新的信号动作,并返回-1。数据结构sigaction用来描述信号的动作,它定义在signal.h文件中:struct sigaction void(*sa_handler)();void(*sa_sigaction)(int,siginfo_t*,void*);sigset_t sa_mask;int sa_flag;信号二、二、UNIX下下C编程基础编程基础 说明:sa_handler:与signal函数的第二个参数func相同,它指向与信号相联系的动作。sa_sigaction:指定一个信号句柄地址。仅当sa_flags中设置了sa_siginfo 标志时才起作用。sa_mask:如果sa_handler指定一信号句柄,则sa_mask指明信号句柄执行期间要阻塞的一组信号。sa_flags:它可以被设置成如下几个标志值。SA_NOCLDSTOP,此标志只对SIGCHLD信号起作用。如果设置该标志位且sig参数是SIGCHLD,则子进程暂停时不发信号给调用进程。SA_RESTART,如果设置了该位并且被捕获的话,系统将在信号句柄返回时自动恢复该信号中断的系统调用。否则返回-1。SA_ONSTACK,如果设定,系统将在用sigalstack函数指定的替代信号栈上运行信号句柄。否则将使用用户栈来交付信号。SA_RESETHAND,如果设定,系统信号句柄的入口,系统将重新设置信号动作为SIG_DFL.SA_NODEFER,如果设定,在信号句柄执行期间系统不自动阻塞该信号。SA_SIGINFO,如果为设置该标志,则信号句柄的原形为:void func(int signo);此时sa_handler成员必须指向信号句柄并禁止修改sa_sigaction成员。否则 void func(int signo,siginfo*info,void*context);第二个参数指向siginfo_t的对象,解释信号生成的原因。第三个参数指向当信号被交付时所中断的接收进程的上下文。信号二、二、UNIX下下C编程基础编程基础 注意:掌握如下几点 1)同signal一样,sigaction函数允许进程指定对某种信号采取相应的动作。但与signal不同的是,当我们调用Sigaction来安装信号句柄时,除非明确的采用sa_flags指明SA_RESETHAND标志,否则该句柄将一直保持下去,直到下一次调用sigactioin为这个信号重新安装动作。2)sigaction提供了对信号的更多控制。信号忽略信号例如:下面进程忽略中断信号,按中断键将不起作用(使用退出键可结束运行该进程)#includemain()signal(SIGINT,SIG_IGN);pause();二、二、UNIX下下C编程基础编程基础 信号常用的信号函数二、二、UNIX下下C编程基础编程基础 1)生成信号#include int raise(ing sig);/发送信号sig给调用它的进程。2)kill函数#include int kill(pid_t pid,int sig);/发送信号给一个进程。3)等待信号#include int pause(void);/悬挂进程直到有一个信号到达。4)检查悬挂信号#include int sigpending(sigset_t*set);。信号二、二、UNIX下下C编程基础编程基础进程标识 每个进程都有一个唯一的非负的ID号。进程ID 为 0 0 的进程称为调度进程。该进程并不执行磁盘上的任何程序-它是内核的一部分,因此也叫系统进程。进程ID 为 1 1 的进程通常是init进程。在系统自举结束后由内核调用(/etc/init;/sbin/init),该进程负责在内核自举后启动一个UNIX系统,init通常调用与系统有关的初始化文件/etc/rc.*,并将系统引导到一个状态(如多用户状态、但用户状态)。进程ID 为 2 2 的进程一般称为页精灵进程(pagedaemon)。此进程负责处理虚存系统的请页操作。与进程ID有关的常用系统调用#include#include pid_t getpid(void)返回:调用进程的进程ID pid_t getppid(void)返回:调用进程的父进程ID uid_t getuid(void)返回:调用进程的实际用户 uid_t geteuid(void)返回:调用进程的有效用户ID gid_t getgid(void)返回:调用进程的实际组 gid_t getegid(void)返回:调用进程的有效组 进程控制二、二、UNIX下下C编程基础编程基础系统调用 fork Unix系统中,新进程被创建的唯一方式是已经存在的进程执行如下fork调用:pid_t fork();在执行时,fork将产生一份当前进程的拷贝。同时带有自己的数据空间(例如,如果一个进程修改一个变量,它不会影响在其他进程中的变量),同时这两个进程将分别被执行。执行调用的进程称为父进程,新的进程称为子进程。如果调用失败,则返回-1。fork调用执行一次返回两次(一次在父进程中返回,一次在子进程中返回),返回值的唯一区别是父进程中返回子进程的进程号,而子进程的返回值为零。若子进程想获等于父进程的进程号,只需调用getppid()。fork调用的一个重要特点是父进程打开的文件与子进程共享,这样为父进程把它打开的文件或设备传给子进程提供了方便。调用fork之后父进程关闭为子进程打开的文件以便两者不共享相同的文件。子进程继承父进程的以下进程变量子进程继承父进程的以下进程变量实际用户号;实际用户组号;有效用户号;有效用户组号;终端组号;根目录;当前工作目录;信号处理设置;文件方式创建掩码.子进程不同于父进程的地方如下子进程不同于父进程的地方如下子进程有一新的进程号;子进程有一不同于父进程的进程号;子进程有它自己的父进程文件描述符副本;在子进程中报警时钟产生的时间量为零.fork调用有以下两个用途1进程创建自己的子进程以便两个进程分别处理两个不同的任务;1进程执行另外一个进程。由于创建新进程的唯一方式是调用fork函数,进程为了执行一新进程必须先调用fork函数创建自己的一个副本,然后由其中某一个进程调用exec函数以执行新进程。进程控制二、二、UNIX下下C编程基础编程基础系统调用 exec Unix系统中程序执行的唯一方式是一个已存在的进程执行exec系统调用。exec系统调用以新程序代替当前进程,进程号不变。我们把执行exec的进程称为调用进程,被加载执行的程序称为新程序。当一个exec执行成功时,调用进程被新程序所覆盖,也就是被调用者所使用的内存被释放,同时新和程序装进该区域。这意味着一旦exec执行,对于调用进程是没有返回的,它永远退出了。所有打开的文件依然被打开并由相同的文件描述字所对应,除非一个文件描述字建立了“在执行中关闭”的标志。被调用进程所忽略的信号在新程序中将依然被忽略。然而,被捕捉的信号将恢复到它们的缺省值。调用进程的实际和有效的用户ID和组ID被赋给新的程序,除非新程序建立了“设置用户ID”和“设置组ID”。如果调用失败了,则返回-1。六个不同的exec调用函数1)intexecve(char*pathname,char*argv,char*envp);第一个参数包含将被执行的程序的路径名;第二个参数是一个指向参数列的指针;第三个参数是指向环境变量的指针。调用的这个形式被shell用来提供环境变量给它执行的程序。2)intexecv(char*pathname,char*argv);第一个参数指明被执行的程序的路径;第二个参数是一个以空字符结尾的指向参数表的指针数组;其中第一个参数按照习惯是程序名称。3)intexecl(char*pathname,char*arg0,char*arg1,char*argn,(char*)0);第一个参数与execv相同;这之后是一系列参数,每一个作为程序的一个参数被给出,第二个参数按照习惯是程序名称。该参数列应该以空字符结尾。4)intexecvp(char*filename,char*argv);像execv一样,除头一个参数在程序的查找路径上被查找外,同时由那名称决定的头一个 存在的执行文件被执行。5)5)int execlpint execlp(char*filename,char*arg0,char*arg1,(char*filename,char*arg0,char*arg1,char*,char*argnargn,(char*)0);,(char*)0);像execl一样,除头一个参数在程序的查找路径上被查找外,同时由那名称决定的头一个存在的执行文件被执行。6)intintexecleexecle(char*pathname,char*arg0,char*arg1,(char*pathname,char*arg0,char*arg1,char*,char*argnargn,(char,(char*)0);*)0);在调用的这个形式中,新的环境作为一个空字符结尾的指针数组与以空字符参数结束的参数表一起传递。进程控制二、二、UNIX下下C编程基础编程基础System函数#include int system(const char*cmd);工作原理 实际上system函数调用是通过fork、exec、waitpid来实现的,它首先fork一子进程,然后由该子进程调用SHELL命令解释程序SH来执行所给的命令:execl(/usr/bin/sh,”sh”,”-C”,cmd,(char*)0);返回值:1,如果不能启动sh来运行此命令,返回127。2,如果出现其他错误,返回-1,3、调用成功,返回shell的终止状态。进程控制二、二、UNIX下下C编程基础编程基础系统调用 wait wait调用用于等待某一子进程的终止,形式为:intwait(int*status);它的返回值是终止进程的进程号,如果调用wait的进程没有子进程,wait返回-1,如果调用wait的进程没有子进程终止,它便由内核挂起,子进程终止时,wait便返回。一个调用一个调用 waitwait的例子的例子#incldue void main()char cmd1024;char args128;int pid,status;。/*输入命令和参数*/if(pid=fork()0)/*得到一个子进程*/exit(1);if(pid=0)/*子进程执行命令*/execvp(cmd,args);exit(1);while(wait(&status)!=pid)/*父进程执行等待*/;进程控制二、二、UNIX下下C编程基础编程基础守护进程的概念及启动方式守护进程是一个在后台运行的进程,这个进程在“后台”执行意味着没有一个与之相关的终端和注册shell。它或 者等待某一事件的发生,或者等待着周期性执行的特定任务。守护进程有不同的启动方式:在系统启动时,大多数系统守护进程由初始化文件/etc/rc.*启动,这进程是在系统进入多用户状态时由 /etc/Init来执行的。由/etc/rc.*启动的守护进程有超级用户特权.守护进程也可由系统文件/usr/lib/crontab在一个定时的周期内来启动。这种途径启动的守护进程也具有超级 用户的权限这是因为cron进程自身是作为超级用户在运行。守护进程还可由用户的crontab文件在一个定时的周期内来启动。普通用户建立自己的crontab文件的能力仅在 系统V中被提供。可由at命令来执行,它安排一个任务在某个晚些时刻被执行。可产生于一个用户终端,作为一个前台的任务或者是作为一个后台的任务均可。典型的守护进程有如下特点:当系统初始化时,它们被一次性的启动;它们的“生命期”便是操作系统的“生命期”;它们在大部分时间内等待着某一事件的发生,事件发生后它们便处理事件;它们频繁地调用其它进程来处理各种请求。守护进程二、二、UNIX下下C编程基础编程基础开发守护进程的规则开发守护进程要注意以下几个方面 (1)关闭所有打开的文件描述符所有不必要的文件描述字都应该被关闭。这一点特别是对于标准输入,标准输出和标准错误输出而言,这是因为它们可能会从执行守护进程的进程中继承而来。下面的代码将关闭所有打开的文件:#include for (i=0;iNOFILE;i+)close(i);这里常量NOFILE定义了每个进程中打开文件的最大个数。(2)改变当前工作目录一个进程的当前工作目录是由核心在该进程的活动期内打开的。当一个活动的进程使它的当前工作目录在某个文件系统中时,这目录所属的被安装文件系统是不能被卸下来的。要允许系统管理员卸下一个文件系统,一个守护进程应该执行:chdir(“/”)来改变它的当前工作目录到根目录。守护进程二、二、UNIX下下C编程基础编程基础开发守护进程的规则(3)复位文件存取创建掩码一个进程从它的父进程继承了文件存取建立的掩码,一个守护进程应执行umask(0)再重设这个掩码。这防止了任何由守护进程建立的文件的存取位被修改。(4)在后台运行如果一个守护进程从一个注册会话层中启动而没有被放在后台,则守护进程在它执行时将批发终端挂起;如从一个shell正文中启动,将引起shell正文的进行停止直到守护进程终止。要避免这两种情况,守护进程应该执行一个fork并且使得父进程exit,让实际的守护进程在子进程中继续。(5)与进程组脱离关系每个进程都会从启动该进程的进程那里继承它的进程组ID。由于守护进程属于某个进程组,所以它容易受到送到整个进程组的信号的影响。为了防止它影响守护进程,守护进程应该脱离它继承的进程组并且建立它自己的进程组。守护进程调用setpgrp来建立它的进程组ID,使之等于它自己的进程组ID,使调用进程成为这新的进程组的组长。在系统V下,调用是:setpgrp();而在4.3BSD下我们有setpgrp(0,getpid();守护进程二、二、UNIX下下C编程基础编程基础开发守护进程的规则 (6)忽略终端I/O信号在支持任务控制的系统上,你可以用stty选项控制一个后台任务在控制终端上产生输出。这里stty是个Unix进程,它允许你使一定的终端特性打开或关闭。如果不允许后台写内容到控制终端,则当它试图这样做时进程会发送一个SIGTTOU信号。如果进程在启动时它不想在控制终端上写一些错误信息,它应该忽略上述信号。signal(SIGTTOU,SIG_IGN);(7)信号、进程组以及控制终端信号、进程组以及控制终端在一个多进程环境中的处理是非常重要的。当不是控制终端的一个终端设备在第一时间内被打开时,核心将设定一个控制终端给进程。在系统V和4.3BSD之间核心给一个进程设定一个控制终端的条件是不同的。4.3BSD设定一个控制终端给一个进程仅在进程组ID是零的情况下。系统V设定一个控制终端给一个进程仅当该进程是一个进程组的组长,即进程组ID等于进程ID。(8)与控制终端脱离关系在系统V下,一个进程可以用系统调用setpgrp来使它自己脱离它的控制终端,通过执行一个fork,确保该进程还不是一个进程组组长,然后在子进程中调用setpgrp。if (fork()!=0)exit(0);setpgrp;守护进程二、二、UNIX下下C编程基础编程基础开发守护进程的规则 (9)阻止重新获得控制终端如果守护进程不再是一个进程组组长,它就不再能重新申请一个控制终端。要做到这一点,我们要使得守护进程再一次的执行fork并且使父进程终止,而子进程继续运行。在调用fork之前,调用signal忽略由第一个子进程调用exit时产生的悬挂信号。if (fork()!=0)exit(0);setpgrp();signal(SIGHUP,SIG_IGN);if (fork()!=0)exit(0);(10)守护进程终止利用SIGTERM信号来通知所有运行的进程系统正在从多用户状态转向单用户状态。这信号是由init进程发送的,然后等待进程终止。如果在一定时间之后进程还没有终止则信号SIGKILL被送到进程,该信号不能被忽略。一个守护进程应该去捕捉SIGTERM信号并用它来停止运行。(11)处理SIGCLP信号有时一个守护进程会对它的子进程的exit状态感兴趣,而有时却不。如果它不感兴趣,并且如果子进程产生时它不处理SIGCLD信号,那么子进程将变为无效的并且只会浪费系统资源。守护进程二、二、UNIX下下C编程基础编程基础 消息和消息队列的结构 消息队列实际上是内核地址空间的内部链接,下图是消息队列的示意图,两个或多个进程可以通过访问一个公共的消息队列来交换信息。消息队列的内核结构 struct msgid_ds struct ipc_perm msg_perm;struct msg*msg_first;struct msg*msg_last;ushort msg_cbytes;ushort msg_qnum;ushort msg_qbytes;ushort msg_lspid;ushort msg_lrpid;ushort msg_stime;ushort msg_rtime;ushort msg_ctime;;Msg结构成员Mgs结构成员描述Struct msg*mas_next指向队列中的下一条消息Long msg_type消息类型Caddr_t msg_spot消息正文Sort msg_ts消息正文大小 用于形成一个消息的结构说明struct msgbuf long mtype;char *mtext;消息队列二、二、UNIX下下C编程基础编程基础工作原理 首先调用msgget请求OS创建一个唯一标识的消息队列-用msgsnd/msgrcv发送和接收消息-最后用msgctl来断开或清除一个队列。初始化消息队列 msgget用于创建一个新的队列或打开一个现存的队列。#include int msgget(key_t key,int msgflag);参数说明:1)key是指定消息队列关键字。如果参数key 为IPC_PRIVATE(值为0),指明创建一个只由调度进程使用的消息队列,非0的key表示该队列可被多个进程使用。在key为非0的情况下,msgget的动作有参数msgflag中的标志来定。2)msgflag指明队列的访问权限和创建标志。访问权限位描述0400拥有者读0200拥有者写0040组成员读0020组成员写0004其他人读0002其他人写。创建标志标志描述IPC_CREAT如果不存在与key相联的消息队列,则创建IPC_EXCL如果同时指定IPC_CREATE,当队列存在的时候,调用失败。单独使用IPC_EXCL不起作用。消息队列二、二、UNIX下下C编程基础编程基础 控制消息队列系统调用msgctl#include int msgctl(int msqid,int cmd,struct msgid_ds*buff);参数说明:消息队列的队列ID;一个命令常量;一个指向类型为msgid_ds的结构指针。系统调用msgctl用来消息队列执行如下的控制操作:1)查看消息队列相连的数据结构。2)改变消息队列的许可权限。3)改变消息队列的拥有者。4)改变消息队列的大小。5)删除一个队列。合法的命令常量定义在引用文件sys/ipc.h中,它们是:IPC_STAT 复制msgid指定的消息队列的内核控制数据结构msgid_ds至 buff所知指的用户区域中。IPC_SET 设置msgid所指的消息队列的有效用户ID、组ID、操作权限、队列的字节数。IPC_RMID 删除msgid以及它所指定的消息队列和相连的数据结构。消息队列二、二、UNIX下下C编程基础编程基础 发送和接收消息 系统调用 msgsnd和msgrcv#include int msgsnd(int msgid,const void*msgp,size_t msggz,int msgflg);ssize_t msgrcv(int msgid,const void*msgp,size_t smggz,long int msgtype,intmsgflg);msgsnd参数说明:msgp指向用户消息缓存区,参数msggz给出消息正文的大小,其值必须大于0且小于系统限制值MSGMAX。参数msgflag在遇到如下两种情况起作用:1)消息队列满2)消息总数已达到系统的限制值。如果msgflag中设置了IPC_NOWAIT,当发生阻塞时,消息不发送,置errno为EAGAIN,并返回-1如果msgflag中没有设置IPC_NOWAIT,则1)消息不阻塞时,消息被发送2)消息对列ID已经删除时,置errno为EIDRM,并返回-1。3)调用进程被中断,消息不发送,置errno为EINTR,并返回-1 消息队列二、二、UNIX下下C编程基础编程基础msgrcv参数说明:msgrcv函数是从msgid指定的队列中读消息到msgp所指的用户消息缓存区,参数msggz给出消息缓存区中消息正文的大小,函数调用成功返回接收到的消息字节数,并把该消息从队列中删除。对于第四个参数msgtype,它指明要接收的消息类型:msgtyp=0,接收队列中的第一个消息。msgtyp0,接收类型等于msgtyp的第一个消息。msgtyp0,接收类型小于或等于msgtyp绝对值的第一个最低类型的消息。第五个参数指明所希望的消息不在消息队列时,以及接收的消息大于msggz时采取的动作。如果接收的消息大于msgsz,当msgflg中设置了标志MSG_NOERROR时,所接收的消息自动被截断到msgsz指定的长度,消息中别截断的部分将丢失。否则msgrcv调用失败。如果消息不在队列中时,msgrcv调用将被阻塞。此时,如果msgflag中设置了标志IPC_NOWAIT,调用将不会被阻塞而是立即返回。如果没有设置IPC_NOWAIT的话,调用进程将阻塞直到如下情况出现:1)所接收的消息已放到队列中。此时会接收消息并成功返回。2)消息队列ID已从系统中清除,此时置errno为EIDRM,并返回-1。3)调用进程被中断,消息不发送,置errno为EINTR,并返回-1 消息队列二、二、UNIX下下C编程基础编程基础一个简单的接收消息例子#include#include#include#include#include#incluestructmy_msglongintmy_type;chartext1024;intmain(void)intmsgid;structmy_msgmsgbuf;msgid=msgget(key_t)1234,0666|IPC_CREAT);If(msgid=-1)printf(“msggeterrorn”);exit(1);While(1)if(msgrcv(msgid,(void*)&msgbuf,1024,0,0)=-1)printf(“msgrcverrorn”);exit(1);printf(“yougetmsg:%sn”,msgbuf.text);if(strncmp(msgbuf.text,“exit”),4)=0)break;If(msgctl(msgid,IPC_RMID,0)=-1)printf(“smgctlerrorn”);Exit(0);消息队列二、二、UNIX下下C编程基础编程基础一个简单的发送消息例子#include#include#include#include#include#include#includestructmy_msglongintmy_type;chartext1024;intmain(void)intmsgid;structmy_msgmsgbuf;msgid=msgget(key_t)1234,0666|IPC_CREAT);If(msgid=-1)printf(“msggeterrorn”);exit(1);While(1)printf(“Pleaseentersomemsg:”);fgets(msgbuf.text,1024,stdin);msgbuf.my_type=1;if(msgsnd(msgid,(void*)&msgbuf,1024,0)=-1)printf(“msgsnderrorn”);exit(1);if(strncmp(msgbuf.text,“exit”),4)=0)break;exit(0);消息队列二、二、UNIX下下C编程基础编程基础 信号灯的概念及用途信号灯为一同步原语,是用于在进程之间收发信号的标志的特殊类型。主要用来协调对共享存贮段的存取。信号灯的结构 信号灯被分配在集合中,每个集合被一个唯一的信号灯ID所定义。一个信号灯集合中的信号灯是从0开始的连续的数。这集合自身被一个类型为semid_ds的结构所描述,在引用文件sys/sem.h中说明;sys/type.h也必须被引用:struct semid_ds struct ipc_perm sem_perm;struct sem *sem_base;ushort sem_nsems;time_t sem_otime;time_t sem_ctime;信号灯二、二、UNIX下下C编程基础编程基础 信号灯的初始化系统调用semget#include int semget(key_t key,int nsems,int semflag);可以通过系统调用semget来创建一个信号灯新集合或者获