UNIX环境C语言编程_第1页
UNIX环境C语言编程_第2页
UNIX环境C语言编程_第3页
UNIX环境C语言编程_第4页
UNIX环境C语言编程_第5页
已阅读5页,还剩33页未读 继续免费阅读

下载本文档

版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领

文档简介

1、第一章概述1.1UNIX的版本UNIX操作系统是贝尔实验室于六十年代末用C语言研制开发的。经过几十年的发展,已经成为流行于从大型机、小型机到工作站甚至微机等多种平台的操作系统。UNIX的成功同时也推动了C语言的普及。本教材的目的是讲解UNIX系统下的C程序设计,使C程序员快速掌握UNIX系统下的编程开发。作者在进行UNIX编程开发的实践过程中,深感实例的重要性,一个简短的C语言实例往往胜过长篇累牍的文字说明,当然了,文字说明也是必不可少的。本教材将本着实例优先的原则,使您能够对UNIX编程开发快速入门。UNIX的版本不统一是出了名的,从UNIX的发展历史来看,主要有两大流派:AT&T的UNIX

2、系统V版本和加州大学伯克利分校的BSD版本,在此基础上,各家UNIX厂商均开发了各自的UNIX操作系统。如:工作站厂商中有HP的hpux、SUN的Solaris、SGI的irix、IBM的AIX等,小型机有VAX上的Ultrix,微机上有SCOUNIX、微软的Xenix以及随着Internet而风靡全球的Linux等。由于WindowsNT的异军突起,对UNIX的市场形成巨大的威胁,各大UNIX厂商不得不联合起来,在工作站市场上,统一以系统V版作为标准,加入BSD版本中的一些优点,支持统一的CDE(CommonDesktopEnvironment)窗口环境,以与WindowsNT进行对抗。1.

3、2UNIX编程环境UNIX操作系统通过Shell程序实现系统与用户的交互,在Shell提示符下,用户键入UNIX命令,即可得到操作系统的输出结果。BSD系统的常用Shell是CShell,缺省提示符是,系统V的常用Shell是BourneShell(现在多为KornShell),缺省提示符是$”,有关Shell的编程,我们在后面的章节中进行介绍。UNIX上的标准编译器是cc。在Shell提示符下(以CShell为例)键入下列命令:$cc-ohellohello.c即将C文件hello.c编译为可执行文件hello。在编译多个文件生成一个可执行文件时,UNIX提供命令make。用户需要针对多个C

4、文件,按照一定的格式编写一个叫做Makefile的文本文件。下面是SGI上的一个Makefile的例子:CC=ccCFLAGS=$(DEBUG)-cckr-I$(INC)/X11-DSYSVDEBUG=-gINC=/usr/includeLDFLAGS=-lXext-lXm-lXt-lX11-lPW-lcOBJS=initx.owindowx.oTGTS=showxwinall:$(TGTS)showxwin:$(OBJS)$(CC)-o$(OBJS)$(CFLAGS)$(LDFLAGS)大写字母的字串是一些宏,CC是编译器的名字、CFLAGS定义CC的编译开关、DEBUG是调试宏、INC是头

5、文件所在目录、LDFLAGS定义了编译连接库、OBJ定义了目标文件名、TGTS定义了可执行文件名。在Shell提示符下直接键入:$make即可将Makefile中指定的所有C文件进行编译并生成可执行文件。1.3UNIX编程中的基本概念在讨论UNIX编程开发前,首先需要阐明系统调用和库函数这两个概念。一个系统调用指一个需要操作系统代表用户程序来执行某些任务的请求。例如:read是一个系统调用,它请求操作系统存储在一个磁盘设备(或其他设备)上的数据去填充一个缓冲区。如果任何人在他们想执行任务的时候都能随便访问设备,那么后果将是不可预测的。所以,这种服务必须请求操作系统来做,它(经常是透明地)记录所

6、有处理每个设备的请求。而一个库函数,并不经常需要操作系统来执行其任务。例如数学库函数中的sin(),cos()等,这些计算只需要简单地对一个有限序列求和,所以并不需要操作系统干预。在UNIX操作系统中,有一个常用的命令man,可用来查阅命令、库函数和系统调用等的具体使用方法。传统Unix联机帮助手册的分节法为:用户级命令(User-levelcommands)系统调用(Systemcalls)库函数(Libraryfunctions设备及驱动程序(Devicesanddevicedriver)文件格式(Fileformats)游戏(Games)杂项(Variousmiscellaneousst

7、uff-macropackagesetc.系统维护及操作命令(Systemmaintenanceandoperationcommands第二章标准输入/输出库2.1概述本章介绍UNIX的标准输入/输出库,UNIX提供一些库函数完成高级输入/输出,为程序员提供了三方面的主要功能:自动开辟缓冲区。即使一次读或写的数据只有几个字节,库函数仍然在大到由数千个字节组成的块中执行实际输入或输出(缓冲区大小通常由头文件stdio.h中的常量BUFSIZ定义)。这个缓冲区在内部开辟给库函数使用,对于程序员来说是透明的;自动执行输入和输出转换。输入输出被自动格式化。以上两点在C语言的教程中一般均以讲到。在标准输

8、入/输出库中,一个文件被称为一串字符流,并且被一个指向类型为FILE的目标指针所描述,该指针被称为文件指针。在UNIX中文件指针stdin、stdout、stderr是预先定义好的,分别对应标准输入(键盘)、标准输出(终端屏幕)和标准错误输出。2.2库函数介绍文件创建和关闭fopen()用于打开已存在的文件或创建新文件文件读写1、一次处理一个字符getc(),putc()2、一次处理多个字符fgets(),fputs()3、文件的二进制读写fread(),fwrite()4、文件的格式化输入/输出fscanf(),fprintf()5、字符串的格式化输入/输出sscanf(),sprintf(

9、)文件移动定位用于在文件中移动的标准输入/输出库函数是fseek(),它接收三个参数:一个文件指针指向一个打开的字符流;一个整数指明要移动的字节数,称为offset;个整数指明从文件中什么位置移动。第三章低级输入/输出3.1概述与第二章内容相对应,本章介绍UNIX系统中通过系统调用来实现的输入/输出,通常称之为低级输入/输出。这些系统调用能够直接实现对设备(如磁带驱动器等)的输入和输出,程序员能够决定要使用的缓冲区的大小,而不象标准输入/输出库函数那样透明设定缓冲区大小。在标准输入/输出库中,一个文件是由一个文件指针来对应的。当使用低级界面时,则用一个文件描述字对应一个文件。文件描述字是一个小

10、的整数。有3个事先定义的文件描述字0、1和2,分别对应标准输入、标准输出和标准错误输出。一般说来,文件描述字都是作为系统调用的第一个参数给出的。3.2相关系统调用介绍文件创建和关闭open()用于为读写而打开一个文件,或用它来创建新文件。intopen(constchar*path,intoflag,./*mode_tmode*/);open使用三个参数:一个字符串path包含要打开的文件名;一个整数oflag指明文件将被如何打开;整数mode在创建文件时使用。常用的oflag包括:O_RDONLY打开文件仅用于读。O_WRONLY打开文件仅用于写。O_RDWR打开文件用于读写。O_CREAT

11、如果文件不存在,则创建,此时mode作为第三个参数给出。close。用于关闭一个已经打开的文件。文件读写read()用于读文件,格式为:read(intfildes,void*buf,size_tnbyte);三个参数说明如下:filedes是文件描述字;指针buf指向一个数据将被读入的缓冲区;整数nbytes指明要读的字节个数。成功时返回实际读入的字节数,出错则返回-1。write()用于写文件,与read类似,格式为:write(intfildes,void*buf,size_tnbyte);三个参数说明如下:filedes是文件描述字;指针buf指向一个数据将被写入的缓冲区;整数nbyt

12、es指明要写的字节个数。成功时返回实际写入的字节数,出错则返回-1。文件移动定位用于在文件中移动的低级输入/输出系统调用是lseek(),与fseek()类似,它也接收三个参数:一个文件描述字对应一个打开的文件;一个整数指明要移动的字节数,称为offset;一个整数指明从文件中什么位置移动。复制文件描述字有时候有不只一个文件描述字对应一个文件。当创建子进程时(参加后面关于进程开发的章节),这一点很常用。为了获得一个新的文件描述字,并保证其与fd对应同一个文件,应调用fd2=dup(fd)fd2现在和fd对应同一个文件,并且和fd样在文件中有相同的位置。四章文件与目录编程4.1基本概念文件目录概

13、述文件系统是UNIX对计算机技术的一大贡献!UNIX系统的文件管理十分灵活、功能强大,许多首次在UNIX系统中出现的概念被其他操作系统所采用,如MS-DOS等。UNIX系统提供了一种层次目录方案。目录就象存放一组文件的柜子一样,目录也可以包括在其他目录中,这样就形成了一种庞大的、具有分支的组织方式,这种结构通常被称为树状结构。目录实际上也是一种特殊的文件。命令、数据文件、其他命令甚至设备(特别文件)都可以作为目录中的项(文件)。I标识号、I列表和I节点一个目录是由一系列结构组成的;每个结构包含一个文件名和一个指向文件自身的指针,该指针是一个整数,称为文件的I标识号。当文件被访问时,它的I标识号

14、用来作为索引打开一个系统表(I列表),系统中存放着文件(I节点)的实体。I节点包含了对文件的描述:文件自身的用户和用户组ID文件的保护码文件内容所在的物理磁盘地址文件的大小最后一次I节点改变的时间,最后一次使用和修改的时间连接该文件的次数,即它出现在其他目录中的次数一个指明文件类型的标记(目录、普通文件、特别文件)文件的三级保护UNIX把使用文件的用户分成三个等级:文件所有者、同组用户和其他用户。文件所有者也称文件主,是文件的创建者,对该文件拥有所有权限;同组用户是具有相同组标识号的所有用户,文件主可以决定一个文件属于哪个组以及该组用户对文件的存取权;其他用户是指与文件主无关的用户,他们与文件

15、主不属于同一个用户组,其他用户对一个文件的访问权限也由该文件主决定的。一个文件的访问权限存放在该文件I节点的di_mode域中,di_mode的08位表示文件主、文件组用户和其他用户对该文件的存取权限。举个例子,用Is-l命令可列出文件hello.c的模式和属性:-rwxr-xr-x1ydsuser589月25日10时54分hello.c最左面一栏显示了该文件的模式:文件主对该文件可读(r)、可写(w)、可执行(x),同组用户对该文件可读、可执行,其他用户对该文件可读、可执行。相应的,di_mode的08位为111101101(0755)。4.2文件编程介绍检查访问权限一access系统调用a

16、ccess系统调用的格式为:#includeintaccess(constchar*path,intamode);其中:参数path指出被检查文件的路径,参数amode指出访问权限。Access判断调用进程的实际用户对文件path是否具有amode所指定的访问权限,若有相应权限,access返回0,否则返回一1。参数amode可取以下值或它们的逻辑或:R_OK检查读权限W_OK检查写权限X_OK检查执行(搜索)权限F_OK检查文件是否存在例如:access(hello.c,R_OK|W_OK),用来检查实际用户对文件hello.c是否具有读/写权;access(hello.c,F_OK)判断文

17、件hello.c是否存在。链接与删除文件-link和unlink系统调用link和unlink系统调用的格式为:#includeintlink(constchar*path1,constchar*path2);intunlink(constchar*path);其中:参数pathl指出已经存在的要被链接的文件路径名,ath2指出要建立的链接文件dink实现path2到pathl的链接,相当于给pathl起了一个别名,同时文件pathl的链接计数加1。若成功则返回0,否则返回-1。参数path指出要被删除的文件路径名。Unlink删除由path指出的文件,若成功则返回0,否则返回一1。从I节点上

18、获取信息一stat与fstat系统调用stat与fstat的调用格式为:#include#includeintstat(constchar*path,structstat*buf);intfstat(intfildes,structstat*buf);说明:stat和fstat都用于获取文件I节点中有关状态信息。stat根据参数path给出的文件路径名,搜索它对应的盘I节点,而fstat则根据参数fildes给出的文件描述字去查找对应的I节点。这两个调用都把从I节点中获取到的信息重组后放入参数buf指向的stat结构中(stat结构的说明在文件/usr/include/sys/stat.h中)

19、。这两个调用成功时均返回0,否则返回-1。stat与fstat调用无论在使用上还是在功能上都是非常类似的,在参数上有一点区别。下面我们来看一个例子。/*statfile.c*/#include#include#include#includemain(argc,argv)intargc;char*argv;intfd;structstatstatbuf;if(argc!=2)printf(usage:statfilefilename!n);exit(1);if(fd=fopen(argv1,O_RDONLY)=-1)fprintf(stderr,Cannotopen%s!n,argv1);if(

20、unlink(argv1)=-1)fprintf(stderr,Cannotunlink%s!n,argv1);if(stat(argv1,&statbuf)=-1)/*byfilename*/fprintf(stderr,stat%sfailsasitshould!n);elseprintf(stat%ssucceed!n,argv1);if(fstat(fd,&statbuf)=-1)/*byfiledescriptor*/fprintf(stderr,fstat%sfails!n,argv1);elseprintf(fstat%ssucceedsasitshould!n,argv1);程

21、序首先打开命令行中指定的文件,然后用unlink将该文件删除,接着分别用stat与fstat系统调用获取该文件的信息。假设当前命令下有一个名为xxx.c的文件,运行%statfilexxx.c后,将会输出如下结果:statxxx.cfailsasitshould!fstatxxx.csucceedsasitshould!从中可知,当一个打开文件被删除后,用stat无法获取该文件的信息。而fstat就可获取该文件的信息。这是由于文件名在unlink之后已从目录中除去,无法找到该文件名,而文件描述字则因文件仍打开而保存下来。因此stat不能成功返回,但fstat仍可成功返回。使用stat调用来判定

22、一个文件为何种文件类型时相当有用。例如下列代码:stat(hello,&statbuf);if(statbuf.st_mod&S_IFMT)=S_IFDIR)printf(Thisisadirectoryfile!n);elseif(statbuf.st_mod&S_IFMT)=S_IFREG)printf(Thisisaregularfile!n);else以上代码可判定当前目录下的hello文件是否为目录文件或其他类型的文件。4.3目录编程介绍UNIX把目录也视为一种文件,称为目录文件,并同普通文件一样进行管理和保护。如open、close、read、lseek等文件操作对目录文件都是有效

23、的。前面讲到的文件编程中的各个系统调用对于目录来说也同样有效。但是与普通文件相比目录文件又具有自身的一些特点:目录文件的读/写/执行访问权限有特殊的含义:读权限允许用户读取目录项的内容;写权限允许用户创建或删除一个文件;执行权限则允许用户检索目录(此时通常称为目录搜索权限)。目录的创建、删除与普通文件也不同,另外,任何用户都不能对目录文件以写方式打开进行文件写操作。目录的创建和删除一mkdir和rmdir系统调用mkdir和rmdir系统调用的格式为:#include#includeintmkdir(constchar*path,mode_tmode);#includeintrmdir(con

24、stchar*path);其中:参数path分别指出要创建和删除的目录文件的文件名。mkdir调用中的参数mode指出新创建目录文件的文件模式。新创建目录后,除.和.两项外,无别的目录项;删除目录时,要求目录中除.和.两项外,也无别的目录项。这两个系统调用Access成功时都返回0,否则返回一1。目录的读取一opendir/readdir/closedir库函数目录文件可以像普通文件一样,先用系统调用open以读方式打开,再用read调用读取其中的内容。同时,由于目录文件是由具有目录结构的目录项组成的,用read读取其内容有些不方便UNIX提供的库函数opendir/readdir/close

25、dir等可以方便地实现目录读取。函数说明如下:#include#includeDIR*opendir(char*filename);structdirect*readdir(DIR*dirp);voidclosedir(DIR*dirp);说明:参数filename指出要打开的目录路径名,库函数opendir返回一个指向结构DIR(在文件/usr/include/sys/dir.h中定义)的指针。库函数readdir和closedir均以这个指针作为参数,其中readdir返回一个指向结构direct的指针。有关目录的操作均可基于这个指针。下面是一个例子,查找当前目录下文件名为name的文件。

26、len=strlen(name);dirp=opendir(.);if(dirp=NULL)returnNOT_FOUND;while(dp=readdir(dirp)!=NULL)if(dp-d_namlen=len&!strcmp(dp-d_name,name)closedir(dirp);returnFOUND;closedir(dirp);returnNOT_FOUND;库函数closedir关闭打开的目录。值得注意的是,上面这一小段代码在编程中很实用,稍加修改即可实现UNIX下类似ls的简单命令。第五章基本进程编程5.1概述UNIX系统为程序员提供了一个强有力的工具:在一个程序中执行

27、另一个程序。执行一个程序最简单的途径就是使用库函数system。该函数使用一个参数:一个包含要被执行的命令的字符串。这一库函数的特点是用法简单,在程序中调用简单的UNIX命令时很有用。但是由于它的调用要由SHELL进程来实现,故效率并不高,在实际的编程中应用并不广泛。本章主要介绍在实际编程中经常使用的有关进程控制和管理方面的系统调用,它们包括:fork创建一子进程exec执行子进程exit终止进程执行wait等待子进程暂停或终止setpgrp设置进程标识符getpid、getppid获取进程标识符setuid、setgid设置进程的用户标识符getuid、geteuid、getgid、gete

28、gid获取进程的用户标识符5.2进程控制1.fork系统调用系统调用fork是UNIX操作系统创建新进程的唯一手段,习惯上将新创建的进程称为子进程,调用fork的进程称为父进程。fork系统调用的格式为:intfork()fork系统调用没有参数,如执行成功,则创建一子进程,子进程继承了父进程的某些属性。当从该系统调用返回时,系统中已有两个用户级环境完全相同的进程存在。这两个进程从fork调用中得到的返回值不同,其中子进程得到的返回值为零,父进程得到的返回值是最新创建的子进程的进程标识符。2.exec系统调用fork系统调用只是将父进程的环境拷贝到新进程中,而没有启动执行一个新的目标程序。UN

29、IX系统提供了exec系统调用,用它更换进程的执行映象,启动新的目标程序。例如:UNIX系统中的所有命令都是通过exec来执行的。exec系统调用有六种不同的使用格式,但在核心中只对应一个调用入口。它们有不同的调用格式和调用参数。这六种调用格式分别为:#includeintexecl(constchar*path,constchar*arg0,.,constchar*argn,(char*)0);intexecv(constchar*path,char*const*argv);intexecle(constchar*path,constchar*arg0,.,constchar*argn,(c

30、har*0),constchar*envp);intexecve(constchar*path,char*const*argv,char*const*envp);intexeclp(constchar*file,constchar*arg0,.,constchar*argn,(char*)0);intexecvp(constchar*file,char*const*argv);说明:参数path指出一个可执行目标文件的路径名;参数file指出可执行目标文件的文件名。arg0作为约定同path一样指出目标文件的路径名;参数argl到argn分别是该目标文件执行时所带的命令行参数;参数argv是一

31、个字符串指针数组,由它指出该目标程序使用的命令行参数表,按约定第一个字符指针指向与path或file相同的字符串;最后一个指针指向一个空字符串,其余的指向该程序执行时所带的命令行参数;参数envp同argv一样也是一个字符指针数组,由它指出该目标程序执行时的进程环境,它也以一个空指针结束。exec的六种格式在以下三点上有所不同:path是一个目标文件的完整路径名,而file是目标文件名,它是可以通过环境变量PATH来搜索的;由path或file指定的目标文件的命令行参数是完整的参数列表或是通过一指针数组argv来给出的;环境变量是系统自动传递或者通过envp来给出的。下图说明了exec系统调用

32、的六种不同格式对以上三点的支持。系统调用参数形式环境传送路径搜索Execl全部列表自动否Execv指针数组自动否Execle全部列表不自动否Execve指针数组不自动否Execlp全部列表自动是Execvp指针数组自动是3.exit、wait系统调用exit系统调用格式如下:#includevoidexit(intstatus);#includevoid_exit(intstatus);说明:exit的功能是终止进程的执行,并释放该进程所占用的某些系统资源。参数status是调用进程终止时传递给其父进程的值。如果调用进程执行exit系统调用时,其父进程正在等待子进程暂停或终止(使用wait系统

33、调用),则父进程可立刻得到该值;如果此时父进程并不处在等待状态,那么一旦父进程使用wait调用,便可立刻得到子进程传过来的status值,注意:只有status的低八位才传递给其父进程。系统调用_exit与exit之间的差异是_exit只做部分的清除,因此建议不要轻易地使用这种调用形式。每个进程在消亡前都要调用该系统调用,没有显示地使用该系统调用,则生成目标文件的装载程序为该进程隐含地做这一工作。wait系统调用格式如下:#include#includepid_twait(int*statptr);说明:wait系统调用将调用进程挂起,直到该进程收到一个被其捕获的信号或者它的任何一个子进程暂停

34、或终止为止。如果在wait调用之前已有子进程暂停或终止,则该调用立刻返回。格式wait(int*)0)的功能是等待所有子进程终止oWait返回时,其返回值为该子进程的进程号。参数statptr的值为该子进程的终止原因:1、如果子进程暂停,statptr目的高八位存放使该子进程暂停的信号值(在第七章中介绍信号)低八位为01772、如果子进程由于调用exit终止,则该值的低八位为0,高八位为子进程终止时,exit系统调用中参数status值的低八位;3、如果子进程因信号终止,该值的高八位为0,低八位为引起终止的信号值。此外如低七位为1,则表示产生了一个core文件。下面我们来看一个例子,该例是一个

35、fork、exec、exit和wait联合使用的一个实例程序,我们称之为feew.c:/*feew.c*/main(argc,argv)intargc;char*argv;intpid,stat;if(argc!=1)if(pid=fork()=0)printf(Childpid=%dn,getpid();execl(argv1,argv1,0);exit(5);pid=wait(&stat);printf(pid=%d,H_stat=%d,L_stat=%dn,pid,stat8,stat&0 xff);当命令行参数的个数不为1时,程序使用fork系统调用产生一个子进程。子进程通过系统调用g

36、etpid获得自己的进程标识符,然后调用exec执行命令行中用户提交的命令,如果exec执行失败,则子进程调用exit(5)终止。父进程使用wait系统调用等待子进程暂停或终止,然后输出从wait中返回的信息。下面以三种方式执行该程序:1不带命令行参数%./feewpid=-1,H_stat=0,L_stat=0%不产生子进程,从运行结果来看,当无子进程时,wait的返回值为一1。2带命令行参数,参数为合法的可执行命令%./feew/bin/dateChildpid=17251998年2月16日(星期一)15时59分14秒CSTpid=1725,H_stat=0,L_stat=0产生子进程。子

37、进程输出其进程标识符后,再调用exec执行从命令行中提交的命令(bin/date),同时父进程等待子进程暂停或终止,然后输出从wait中得到的信息:子进程标识符或状态参数stat的高八位、低八位的内容。从中可以看到:子进程因调用一个隐含的exit(O)而终止,终止时传给父进程的值为0。3带命令行参数,但参数不合法%./feew/etc/shudownChildpid=1760/etc/shutdown:只有超级用户(root)能运行/etc/shutdown。pid=1760,H_stat=2,L_stat=0%子进程创建成功。但由于以普通用户的身份执行/etc/shutdown,因此exec

38、失败,尔后调用exit(5)而终止;父进程调用wait得到返回值:子进程号和状态参数stat的高八位、低八位的内容。从执行结果可以看出:子进程因调用exit(5)而终止,终止时传给父进程的值为5。5.3进程管理进程管理包括的面很广,诸如进程的用户标识符管理、进程标识符管理等。进程的用户标识符有两个:实际用户标识符(realuserid)和有效用户标识符(effectiveuserid),其对应的组标识符分别称为实际组标识符(realgroupid)和有效组标识符(effectivegroudid)。一般而言,进程的实际用户标识符为运行该进程的用户标识符,通常只用于系统记帐,其他功能由有效用户标

39、识符来完成,如用有效用户标识符来完成对新创建文件赋予属性关系、检查文件的存取权限和利用kill系统调用向进程发送信号的权限。一般情况下,一进程的有效用户标识符和实际用户标识符是相等的,但系统允许改变进程的有效用户标识符。进程的用户标识符管理UNIX系统提供了一组系统调用来管理进程的用户标识符,它们的使用形式是:#include#includeuid_tgetuid(void);uid_tgeteuid(void);gid_tgetgid(void);gid_tgetegid(void);intsetuid(uid_tuid);intsetgid(gid_tgid);说明:前四个系统调用没有参数

40、,分别返回调用进程的实际用户标识符、有效用户标识符、实际用户组标识符和有效组标识符。这些系统调用的执行总能获得成功,不会发生任何错误。系统调用setuid和setgid用于设置进程的实际用户(组)标识符和有效用户(组)标识符,如调用进程的有效用户标识符是超级用户标识符,则将调用的进程实际用户(组)标识符和有效用户(组)标识符设置为uid或gid;如调用进程的有效用户标识符不是超级用户标识符,但其实际用户(组)标识符等于uid或gid时,则其有效用户(组)标识符被设置为uid或gid;否则setuid或setgid调用失败。系统调用setuid或setgid调用成功时返回0,失败时返回1。进程标

41、识符管理UNIX系统使用进程标识符来管理当前系统中的进程。为对具有某类似特性的进程统一管理,系统又引入了进程组的概念,以组标识符来区别进程是否同组。进程的组标识符是从父进程继承下来的,所以,通常进程的组标识符就是和它相关联的注册进程的标识符。进程的标识符是由系统为之分配的,不能被修改;组标识符可通过setpgrp系统调用修改。相关系统调用的格式如下:#include#includepid_tgetpid(void);pid_tgetpgrp(void);pid_tgetppid(void);pid_tgetpgid(pid_tpid);说明:前三个系统调用分别返回调用进程的进程标识符、进程组标

42、识符和其父进程标识符。它们总能成功地返回。第四个调用置进程组标识符,它将调用进程的进程组标识符改为调用进程的进程标识符,使其成为进程组首进程,并返回这一新的进程组标识符。下面我们来看一个实例:/*setuid.c*/main(argc,argv)intargc;char*argv;intret,uid;uid=atoi(argv1);printf(Beforeuid=%d,euid=%dn,getuid(),geteuid();ret=setuid(uid);printf(Afteruid=%d,euid=%dn,getuid(),geteuid();printf(ret=%dn,ret);下

43、面分三种情况讨论该程序的执行:1、如果执行该程序的用户为超级用户,则只要命令行所给的用户标识符大于0,无论所给的用户标识符是否存在,执行总能成功。#./setuid3434Beforeuid=0,euid=0Afteruid=3434,euid=3434ret=0结果分析:将进程的实际和有效用户标识符均改为3434。2、如果执行该程序的用户为一般用户,用d命令得到用户uid和gid后,再调用该程序,过程如下:%iduid=1111(yds)gid=20(user)%./setuid3434Beforeuid=1111,euid=1111Afteruid=1111,euid=1111ret=-1

44、%./setuid1111Beforeuid=1111,euid=1111Afteruid=1111,euid=1111ret=0%结果分析:当命令行参数为1111时,setuid执行成功,因为用户的uid就是1111。值得注意的是:注册程序login是个典型的setuid系统调用程序,login进程的有效用户是超级用户,该进程在建立用户的Shell进程前,调用setuid将实际和有效用户标识符调整为注册用户的用户实际和有效标识符。六章设备输入/输出控制6.1概述UNIX将设备看成文件,这是UNIX的一大特色。这里需要介绍一个设备号的概念。设备特别文件与两个设备号有关主设备号和次设备号。主设备

45、号告诉操作系统,当涉及文件名时,将使用哪种设备类型。对于每一种类型的设备都有一段驻留在操作系统中的程序代码,以控制相应类型的设备,这段代码被称为设备驱动程序。次设备号被传递给设备驱动程序,这个号码用来决定使用哪种物理设备。例如,决定在一块多重驱动控制卡上,哪个磁盘驱动器将被访问,以及该磁盘驱动器中哪一部分将被使用;或者,当一个磁盘驱动器所请求的操作已经完成后,应该被恢复原状。几个设备(如同类型的磁盘驱动器)可以用同一个主设备号,但它们将有不同的次设备号。看下面的例子:%ls-l/dev/ttyq*crw-w2ydsuser15,12月17日09时03分ttyq1crw-w2ydsuser15,

46、142月16日17时00分ttyq14%上例中15是主设备号,1和14是次设备号。用户可以使用系统提供的统一而且独立于设备的界面对文件进行操作的系统调用来操作设备,而没有必要涉及设备的具体细节。大部分对文件进行操作的系统调用对它们仍起作用,例如,用open打开设备,用read/write对设备进行读/写,设备操作完成后,用close关闭设备。但有的系统调用在对设备文件进行操作时,其功效有所不同。如create及open的创建方式都不能创建设备文件。6.2设备输入/输出控制一ioctl系统调用ioctl是UNIX系统专门提供的用于设备控制的系统调用。该系统调用与设备类型(即主设备号)相关。不同的

47、设备,系统提供了不同的控制命令。ioctl的调用格式是:ioctl(intfd,intcmd,arg说明:参数fd是一设备文件的文件描述字,cmd是控制命令,它与设备相关,不同类型的设备有不同的控制命令。参数arg没有固定的数据结构,它随cmd的不同而不同。七章高级编程7.1处理信号信号是UNIX进程间最基本的通讯手段,主要作用是实现进程间异步事件的通讯。信号是传送到进程的软中断,它通知进程在它们的环境中出现了非正常事件。进程接收到信号后要进行处理,处理方式为以下四种之一:(1)缺省方式(SIG_DFL):这是进程对信号的一般处理方式,在无特殊情况下,进程在接收到信号后将终止执行。有一些信号,

48、在终止进程运行前需将终止进程的正文段、数据段、user结构和栈段内容写到当前目录的core文件中,以备调试工具分析与使用。(2)忽略方式(SIG_IGN):进程接收到一个已指明忽略的信号,则将该信号清除后,立即返回,不在任何工作。信号SIGKILL不能被忽略。(3)保持方式(SIG_HOLD):当进程处于该方式时,将接收的信号保存起来,等该进程的保持方式解除后,再进行处理。(4)捕获方式(设置信号处理函数):这是用户设置的信号处理方式,当进程接收到这种信号时,执行用户设置的信号处理函数,执行完后,恢复现场,然后继续往下执行。常用信号种类UNIX信号的种类很多,下面介绍一些最常用的信号:SIGH

49、UP挂断。这是当控制终端被挂起时送到进程的信号。SIGINT中断。由键盘产生的中断。SIGQUIT退出。由键盘产生的中断。SIGKILL终止。这个信号不能被捕获、阻塞或忽略。SIGALRM定时信号。SIGTERM软件终止信号。SIGUSR1用户定义的信号1。SIGUSR2用户定义的信号2。这些信号值的声明在/usr/include/sys/signal.h文件中。发送信号一kill系统调用用户传送信号到进程的系统调用是kill,调用格式为:#include#includeintkill(pid_tpid,intsig);说明:该系统调用把一个信号值为sig的信号发送给进程标识符为pid的相关进

50、程。成功时返回0,失败时返回1。该调用执行成功与否,依赖于调用进程的有效用户标识符和参数pid的值,pid值的含义如下:大于0:将信号发送给进程号等于pid的进程。等于0:将信号发送给调用进程的同组进程(0和1进程除外)。等于1:将信号发送给实际用户标识符等于调用进程的有效用户标识符的所有进程(0和1进程除外),如调用进程的有效用户是超级用户,则将信号发送给除0和1进程外的所有进程。非一1的负数:将信号发送给进程组标识符为pid的绝对值的所有进程。在实际编程中,kill系统调用非常有用,具体说来:常用方式kill(pid,SIGUSR1)向进程号为pid的进程发送信号SIGUSR1用来判断进程

51、是否存在:if(kill(pid,0)=0)进程号为pid的进程存在;else进程号为pid的进程不存在!用来杀掉子进程kill(pid,1)杀掉进程号为pid的进程2.处理信号一signal系统调用用户处理信号的系统调用是signal,调用格式为:#includevoid(*signal(intsig,void(*func)()();说明:参数引g是一个信号值,func定义了该信号的处理方式。该系统定义的功能是按func的定义设置调用进程对信号sig的处理方式。执行成功时,返回调用进程先前对信号sig处理方式的值,失败则返回一1。参数取值为SIG_DFL或SIG_IGN或用户信号处理函数的地

52、址时,分别表示缺省方式、忽略方式和捕获方式。3.pause系统调用pause系统调用的格式为:pause()说明:该调用没有参数,其功能为使调用进程睡眠直到其接收到一信号为止。该系统调用的结果依赖于调用进程对接收到的信号的处理方式。缺省方式:终止调用进程,pause无返回值;忽略方式:进程不受该信号的影响,继续睡眠;捕获方式:调用进程从信号处理函数返回后,继续往下执行。4.使用信号定时一alarm系统调用系统调用alarm可以实现定时器的功能,调用格式为:#includeunsignedalarm(unsignedsec);说明:参数sec指定定时的时间间隔,以秒为单位。用户进程可以先通过si

53、gnal调用指定SIGALRM信号对应的捕获函数,然后调用alarm来设定闹钟,在定时这段时间内做自己的工作。定时时间一到,进程就接收到一个SIGALRM信号,并执行该信号对应的捕获函数。系统调用alarm在多进程编程中非常有用。7.2管道通讯用信号来处理异常事件或错误是非常合适的,但它用来处理进程之间的大量信息传送,就非常不适宜。为此,UNIX又提供了一种称为管道的机构,主要处理进程间的大量信息传送。所谓管道是指进程间连接起来的一条通讯通道。它也UNIX文件概念的一种推广,管道通讯的介质是文件,称为管道文件。用户可以用文件操作的有关系统调用来操作管道文件,从而简化管道应用程序的设计。管道的形

54、象描述如下图:write写端读端read管道是UNIX最强大而最有特色的性能之一,特别是在命令行这一级,它允许任意的命令被顺序连接起来。例如:%who|wc-l该命令通过管道把命令who的输出送给字计数程序wc,选项-I告诉wc只计算行数。通过wc最终输出的系统已注册的用户个数。1.管道程序设计在程序中可以用系统调用ipe建立一个管道。如果建立成功,就返回两个文件描述符,一个用于写入管道,一个用于从管道中读出。Pipe调用的格式如下:intfiIedes2,retvaI;retvaI=pipe(fiIedes);其中,fildes是一个含有两个整数的数组,用来存放标识管道的两个文件描述符。如果

55、调用成功,fiIedesO将被打开用于从管道读,fildes1将被打开用于向管道写。管道一旦建立,就能直接用read和write操作它。当管道与系统调用fork联用时,才能体现出管道的真正价值。这时,可以利用父进程已打开的文件,对于其子进程仍保持打开这一事实。下面的程序先建立一个管道,然后调用fork创建子进程,父进程通过管道向子进程发送信息。打开管道在父进程中向管道写入在子进程中从管道读入程序的输入结果如下:ChildRead:hello,world#1ChildRead:hello,world#2ChildRead:hello,world#3管道是在先进先出的基础上处理数据的。所以,首先放

56、入管道的数据,在其另一端首先被读出。这个顺序不能被改变,因为系统调用Iseek不能用于管道。2.命名管道一FIFO我们已经看到,管道是一种功能很强的进程通讯机构。但是,它也存在一些严重的缺点。首先,管道只能用于连接具有共同祖先的进程,如父子进程之间的连接。当要开发一个永远保持存在的,提供为全系统范围服务的程序时,这一缺点就更加突出,例如网络控制服务程序和打印机的假脱机程序等。我们要求调用进程应该能够用管道与任何服务进程进行通讯,然后再脱开。遗憾的是,普通管道不能实现上述功能。其次,管道不能是常设的,在需要时可以建立它们,但是当访问它们的进程终止时,管道也随之被撤销。所以,它们不可能永久存在。事

57、实上,UNIX系统中的FIFO机制(又称命名管道),弥补了上述管道的不足之处。FIFO与管道一样,也是作为进程之间先进先出的通讯通道,但是FIFO是一种永久性的机构,并且具有一个UNIX文件名。FIFO也具有文件主、长度和访问权限。它能象其他UNIX文件那样被打开、关闭和删除。但在读和写时,其性能与管道相同。在讨论FIFO程序设计之前,我们先来看一下FIFO在命令级的使用。UNIX命令mknod可以用来创建一个FIFO文件channeI:%/etc/mknodchanneIp%Is-IchanneIprw-r-r-1ydsuser02月17日14时19分channeI命令Is的输出结果中的首字

58、母p指出channel是一个FIFO类型的文件。从中我们还可以看到其访问权限为文件主可读写,组内及其他用户只读。其用户主是yds,所属组为user,长度为0,此外还有文件建立的时间。FIFO程序设计大部分与管道相同,最主要的区别是在建立方面。FIFO是用mknod调用建立的,而不是用pipe建立的。另夕卜,必须把八进制数010000(定义在文件/usr/include/sys/stat.h的常量S_IFIFO中)加入文件模式中,以指明这是一个FIFO。下面是一个建立FIFO的例子:if(mknod(fifo,010600,0)0)perror(mknod(fifo)call);这个例子建立一个

59、名为fifo的FIFO,其权限为0600,所以此FIFO可以被其文件主读写。一旦建立一个FIFO,必须用系统调用open打开它,例如:#includefd=open(fifo,O_WRONLY);实现打开一个FIFO文件用于写,下面的例子用于以非阻塞方式打开FIFO文件用于读:if(fd=open(fifo,O_RDONLY|O_NDELAY)0)perror(openonfile);下面介绍两个程序,说明FIFO的基本应用。值得注意的是,这两个程序构成了FIFO编程的基本框架,稍微修改即可用于其他场合的FIFO应用。首先是sendfifo.c的程序清单,用于向FIFO文件写入字串:下面是re

60、cvfifo.c的程序清单,实现从FIFO的读入:运行结果如下:%recvfifo&11706%sendfifohelloworldFIFOmessagereceived:helloFIFOmessagereceived:world%首先,运行recvfifo程序创建FIFO文件fifo,并打开文件fifo用于读;然后,运行sendfifo程序发送字符串helloworid,写入文件fifo中。7.3IPC通讯机制IPC概述IPC是UNIX系统V提供的一套新的进程间通讯进制,它大大增强了进程间的通讯功能。IPC机构包括三种:消息、信号量和共享内存。三种IPC机构的程序设计接口比较相似,这说明它

温馨提示

  • 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
  • 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
  • 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
  • 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
  • 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
  • 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
  • 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。

评论

0/150

提交评论