




已阅读5页,还剩35页未读, 继续免费阅读
版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
第一章 概述 1.1 UNIX的版本 UNIX操作系统是贝尔实验室于六十年代末用C语言研制开发的。经过几十年的发展,已经成为流行于从大型机、小型机到工作站甚至微机等多种平台的操作系统。UNIX的成功同时也推动了C语言的普及。本教材的目的是讲解UNIX系统下的C程序设计,使C程序员快速掌握UNIX系统下的编程开发。作者在进行UNIX编程开发的实践过程中,深感实例的重要性,一个简短的C语言实例往往胜过长篇累牍的文字说明,当然了,文字说明也是必不可少的。本教材将本着实例优先的原则,使您能够对UNIX编程开发快速入门。 UNIX的版本不统一是出了名的,从UNIX的发展历史来看,主要有两大流派:AT&T的UNIX系统V版本和加州大学伯克利分校的BSD版本,在此基础上,各家UNIX厂商均开发了各自的UNIX操作系统。如:工作站厂商中有 HP的hpux、SUN的solaris、SGI的irix、IBM的AIX等,小型机有VAX上的Ultrix, 微机上有SCO UNIX、微软的Xenix以及随着Internet而风靡全球的Linux等。由于Windows NT的异军突起,对UNIX的市场形成巨大的威胁,各大UNIX厂商不得不联合起来,在工作站市场上,统一以系统V版作为标准,加入BSD版本中的一些优点,支持统一的CDE(Common Desktop Environment)窗口环境,以与Windows NT进行对抗。1.2 UNIX编程环境 UNIX操作系统通过Shell程序实现系统与用户的交互,在Shell提示符下,用户键入UNIX命令,即可得到操作系统的输出结果。BSD系统的常用Shell是C Shell,缺省提示符是%,系统V的常用Shell是Bourne Shell(现在多为Korn Shell),缺省提示符是$,有关Shell的编程,我们在后面的章节中进行介绍。 UNIX上的标准编译器是cc。在Shell提示符下(以C Shell为例)键入下列命令:$ cc -o hello hello.c即将C文件hello.c编译为可执行文件hello。在编译多个文件生成一个可执行文件时,UNIX提供命令make。用户需要针对多个C文件,按照一定的格式编写一个叫做Makefile的文本文件。下面是SGI上的一个Makefile的例子:CC = ccCFLAGS = $(DEBUG) -cckr -I$(INC)/X11 -DSYSVDEBUG = -gINC = /usr/includeLDFLAGS = -lXext -lXm -lXt -lX11 -lPW -lcOBJS = initx.o windowx.oTGTS = showxwinall: $(TGTS)showxwin: $(OBJS)$(CC) -o $ $(OBJS) $(CFLAGS) $(LDFLAGS)大写字母的字串是一些宏,CC是编译器的名字、CFLAGS定义cc的编译开关、DEBUG是调试宏、INC是头文件所在目录、LDFLAGS定义了编译连接库、OBJ定义了目标文件名、TGTS定义了可执行文件名。在Shell提示符下直接键入:$ make即可将Makefile中指定的所有C文件进行编译并生成可执行文件。1.3 UNIX编程中的基本概念 在讨论UNIX编程开发前,首先需要阐明系统调用和库函数这两个概念。一个系统调用指一个需要操作系统代表用户程序来执行某些任务的请求。例如:read是一个系统调用,它请求操作系统存储在一个磁盘设备(或其他设备)上的数据去填充一个缓冲区。如果任何人在他们想执行任务的时候都能随便访问设备,那么后果将是不可预测的。所以,这种服务必须请求操作系统来做,它(经常是透明地)记录所有处理每个设备的请求。而一个库函数,并不经常需要操作系统来执行其任务。例如数学库函数中的sin(),cos()等,这些计算只需要简单地对一个有限序列求和,所以并不需要操作系统干预。在UNIX操作系统中,有一个常用的命令man,可用来查阅命令、库函数和系统调用等的具体使用方法。传统 Unix联机帮助手册的分节法为:1 用户级命令(User-level commands)2 系统调用(System calls)3 库函数(Library functions)4 设备及驱动程序(Devices and device drivers)5 文件格式(File formats)6 游戏(Games)7 杂项(Various miscellaneous stuff - macro packages etc.)8 系统维护及操作命令(System maintenance and operation commands)第二章 标准输入/输出库2.1 概述本章介绍UNIX的标准输入/输出库,UNIX提供一些库函数完成高级输入/输出,为程序员提供了三方面的主要功能:自动开辟缓冲区。即使一次读或写的数据只有几个字节,库函数仍然在大到由数千个字节组成的块中执行实际输入或输出(缓冲区大小通常由头文件stdio.h中的常量BUFSIZ定义)。这个缓冲区在内部开辟给库函数使用,对于程序员来说是透明的;自动执行输入和输出转换。输入输出被自动格式化。以上两点在C语言的教程中一般均以讲到。在标准输入/输出库中,一个文件被称为一串字符流,并且被一个指向类型为FILE的目标指针所描述,该指针被称为文件指针。在UNIX中文件指针stdin、stdout、stderr是预先定义好的,分别对应标准输入(键盘)、标准输出(终端屏幕)和标准错误输出。2.2 库函数介绍文件创建和关闭fopen()用于打开已存在的文件或创建新文件文件读写1、 一次处理一个字符 getc(), putc()2、 一次处理多个字符 fgets(), fputs()3、 文件的二进制读写 fread(), fwrite()4、 文件的格式化输入/输出 fscanf(), fprintf()5、 字符串的格式化输入/输出 sscanf(), sprintf()文件移动定位用于在文件中移动的标准输入/输出库函数是fseek(),它接收三个参数:一个文件指针指向一个打开的字符流;一个整数指明要移动的字节数,称为offset;一个整数指明从文件中什么位置移动。第三章 低级输入/输出3.1 概述与第二章内容相对应,本章介绍UNIX系统中通过系统调用来实现的输入/输出,通常称之为低级输入/输出。这些系统调用能够直接实现对设备(如磁带驱动器等)的输入和输出,程序员能够决定要使用的缓冲区的大小,而不象标准输入/输出库函数那样透明设定缓冲区大小。在标准输入/输出库中,一个文件是由一个文件指针来对应的。当使用低级界面时,则用一个文件描述字对应一个文件。文件描述字是一个小的整数。有3个事先定义的文件描述字0、1和2,分别对应标准输入、标准输出和标准错误输出。一般说来,文件描述字都是作为系统调用的第一个参数给出的。3.2 相关系统调用介绍文件创建和关闭open()用于为读写而打开一个文件,或用它来创建新文件。int open (const char *path, int oflag, . /* mode_t mode */);open使用三个参数:一个字符串path包含要打开的文件名;一个整数oflag指明文件将被如何打开;整数mode在创建文件时使用。常用的oflag包括:O_RDONLY 打开文件仅用于读。O_WRONLY 打开文件仅用于写。O_RDWR 打开文件用于读写。O_CREAT 如果文件不存在,则创建,此时mode作为第三个参数给出。close()用于关闭一个已经打开的文件。文件读写read()用于读文件,格式为:read(int fildes, void *buf, size_t nbyte);三个参数说明如下:filedes是文件描述字;指针buf指向一个数据将被读入的缓冲区;整数nbytes指明要读的字节个数。成功时返回实际读入的字节数,出错则返回-1。write()用于写文件,与read类似,格式为:write(int fildes, void *buf, size_t nbyte);三个参数说明如下:filedes是文件描述字;指针buf指向一个数据将被写入的缓冲区;整数nbytes指明要写的字节个数。成功时返回实际写入的字节数,出错则返回-1。文件移动定位用于在文件中移动的低级输入/输出系统调用是lseek(),与fseek()类似,它也接收三个参数:一个文件描述字对应一个打开的文件;一个整数指明要移动的字节数,称为offset;一个整数指明从文件中什么位置移动。复制文件描述字有时候有不只一个文件描述字对应一个文件。当创建子进程时(参加后面关于进程开发的章节),这一点很常用。为了获得一个新的文件描述字,并保证其与fd对应同一个文件,应调用fd2 = dup(fd)fd2现在和fd对应同一个文件,并且和fd一样在文件中有相同的位置。四章 文件与目录编程4.1 基本概念文件目录概述文件系统是UNIX对计算机技术的一大贡献!UNIX系统的文件管理十分灵活、功能强大,许多首次在UNIX系统中出现的概念被其他操作系统所采用,如MSDOS等。UNIX系统提供了一种层次目录方案。目录就象存放一组文件的柜子一样,目录也可以包括在其他目录中,这样就形成了一种庞大的、具有分支的组织方式,这种结构通常被称为树状结构。目录实际上也是一种特殊的文件。命令、数据文件、其他命令甚至设备(特别文件)都可以作为目录中的项(文件)。I标识号、I列表和I节点一个目录是由一系列结构组成的;每个结构包含一个文件名和一个指向文件自身的指针,该指针是一个整数,称为文件的I标识号。当文件被访问时,它的I标识号用来作为索引打开一个系统表(I列表),系统中存放着文件(I节点)的实体。I节点包含了对文件的描述:文件自身的用户和用户组ID文件的保护码文件内容所在的物理磁盘地址文件的大小最后一次I节点改变的时间,最后一次使用和修改的时间连接该文件的次数,即它出现在其他目录中的次数一个指明文件类型的标记(目录、普通文件、特别文件)文件的三级保护UNIX把使用文件的用户分成三个等级:文件所有者、同组用户和其他用户。文件所有者也称文件主,是文件的创建者,对该文件拥有所有权限;同组用户是具有相同组标识号的所有用户,文件主可以决定一个文件属于哪个组以及该组用户对文件的存取权;其他用户是指与文件主无关的用户,他们与文件主不属于同一个用户组,其他用户对一个文件的访问权限也由该文件主决定的。一个文件的访问权限存放在该文件I节点的di_mode域中,di_mode的08位表示文件主、文件组用户和其他用户对该文件的存取权限。举个例子,用ls -l命令可列出文件hello.c的模式和属性:-rwxr-xr-x 1 yds user 58 9月 25日 10时54分 hello.c最左面一栏显示了该文件的模式:文件主对该文件可读(r)、可写(w)、可执行(x),同组用户对该文件可读、可执行,其他用户对该文件可读、可执行。相应的,di_mode的08位为111101101(0755)。4.2 文件编程介绍检查访问权限access系统调用access系统调用的格式为:#include int access(const char *path, int amode);其中:参数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) 判断文件hello.c是否存在。链接与删除文件link和unlink系统调用link和unlink系统调用的格式为:#include int link (const char *path1, const char *path2);int unlink(const char *path);其中:参数path1指出已经存在的要被链接的文件路径名,path2指出要建立的链接文件。link实现path2到path1的链接,相当于给path1起了一个别名,同时文件path1的链接计数加1。若成功则返回0,否则返回1。参数path指出要被删除的文件路径名。Unlink删除由path指出的文件,若成功则返回0,否则返回1。从I节点上获取信息stat与fstat系统调用stat与fstat的调用格式为:#include #include int stat(const char *path, struct stat *buf);int fstat(int fildes, struct stat *buf);说明:stat和fstat都用于获取文件I节点中有关状态信息。stat根据参数path给出的文件路径名,搜索它对应的盘I节点,而fstat则根据参数fildes给出的文件描述字去查找对应的I节点。这两个调用都把从I节点中获取到的信息重组后放入参数buf指向的stat结构中(stat结构的说明在文件/usr/include/sys/stat.h中)。这两个调用成功时均返回0,否则返回1。stat与fstat调用无论在使用上还是在功能上都是非常类似的,在参数上有一点区别。下面我们来看一个例子。/* statfile.c */#include #include #include #include main(argc,argv)int argc;char *argv;int fd;struct stat statbuf;if (argc!=2)printf(usage: statfile filename!n);exit(1);if (fd = fopen(argv1, O_RDONLY) = -1)fprintf(stderr, Cannot open %s!n, argv1);if (unlink(argv1) = -1)fprintf(stderr,Cannot unlink %s!n, argv1);if (stat(argv1, &statbuf) = -1) /* by file name */fprintf(stderr, stat %s fails as it should !n);elseprintf(stat %s succeed!n, argv1);if (fstat(fd, &statbuf) = -1) /* by file descriptor */fprintf(stderr, fstat %s fails!n, argv1);elseprintf(fstat %s succeeds as it should!n, argv1);程序首先打开命令行中指定的文件,然后用unlink将该文件删除,接着分别用stat与fstat系统调用获取该文件的信息。假设当前命令下有一个名为xxx.c的文件,运行%statfile xxx.c后,将会输出如下结果:stat xxx.c fails as it should!fstat xxx.c succeeds as it should!从中可知,当一个打开文件被删除后,用stat无法获取该文件的信息。而fstat就可获取该文件的信息。这是由于文件名在unlink之后已从目录中除去,无法找到该文件名,而文件描述字则因文件仍打开而保存下来。因此stat不能成功返回,但fstat仍可成功返回。使用stat调用来判定一个文件为何种文件类型时相当有用。例如下列代码:stat(hello, &statbuf);if (statbuf.st_mod & S_IFMT) = S_IFDIR)printf(This is a directory file!n);else if (statbuf.st_mod & S_IFMT) = S_IFREG)printf(This is a regular file !n);else 以上代码可判定当前目录下的hello文件是否为目录文件或其他类型的文件。4.3 目录编程介绍UNIX把目录也视为一种文件,称为目录文件,并同普通文件一样进行管理和保护。如open、close、read、lseek等文件操作对目录文件都是有效的。前面讲到的文件编程中的各个系统调用对于目录来说也同样有效。但是与普通文件相比目录文件又具有自身的一些特点:目录文件的读/写/执行访问权限有特殊的含义:读权限允许用户读取目录项的内容;写权限允许用户创建或删除一个文件;执行权限则允许用户检索目录(此时通常称为目录搜索权限)。目录的创建、删除与普通文件也不同,另外,任何用户都不能对目录文件以写方式打开进行文件写操作。目录的创建和删除mkdir和rmdir系统调用mkdir 和rmdir系统调用的格式为:#include #include int mkdir (const char *path, mode_t mode);#include int rmdir(const char *path);其中:参数path分别指出要创建和删除的目录文件的文件名。mkdir调用中的参数mode指出新创建目录文件的文件模式。新创建目录后,除.和.两项外,无别的目录项;删除目录时,要求目录中除.和.两项外,也无别的目录项。这两个系统调用Access成功时都返回0,否则返回1。目录的读取opendir/readdir/closedir库函数目录文件可以像普通文件一样,先用系统调用open以读方式打开,再用read调用读取其中的内容。同时,由于目录文件是由具有目录结构的目录项组成的,用read读取其内容有些不方便。UNIX提供的库函数opendir/readdir/closedir等可以方便地实现目录读取。函数说明如下:#include #include DIR *opendir(char *filename);struct direct *readdir(DIR *dirp);void closedir(DIR *dirp);说明:参数filename指出要打开的目录路径名,库函数opendir返回一个指向结构DIR(在文件/usr/include/sys/dir.h中定义)的指针。库函数readdir和closedir均以这个指针作为参数,其中readdir返回一个指向结构direct的指针。有关目录的操作均可基于这个指针。下面是一个例子,查找当前目录下文件名为name的文件。len = strlen(name);dirp = opendir(.);if (dirp = NULL) return NOT_FOUND;while (dp = readdir(dirp) != NULL) if (dp-d_namlen = len & !strcmp(dp-d_name, name) closedir(dirp);return FOUND;closedir(dirp);return NOT_FOUND;库函数closedir关闭打开的目录。值得注意的是,上面这一小段代码在编程中很实用,稍加修改即可实现UNIX下类似ls的简单命令。第五章 基本进程编程5.1 概述UNIX系统为程序员提供了一个强有力的工具:在一个程序中执行另一个程序。执行一个程序最简单的途径就是使用库函数system。该函数使用一个参数:一个包含要被执行的命令的字符串。这一库函数的特点是用法简单,在程序中调用简单的UNIX命令时很有用。但是由于它的调用要由SHELL进程来实现,故效率并不高,在实际的编程中应用并不广泛。本章主要介绍在实际编程中经常使用的有关进程控制和管理方面的系统调用,它们包括:fork 创建一子进程exec 执行子进程exit 终止进程执行wait 等待子进程暂停或终止setpgrp 设置进程标识符getpid、getppid 获取进程标识符setuid、setgid 设置进程的用户标识符getuid、geteuid、getgid、getegid 获取进程的用户标识符5.2 进程控制1. fork系统调用系统调用fork是UNIX操作系统创建新进程的唯一手段,习惯上将新创建的进程称为子进程,调用fork的进程称为父进程。fork系统调用的格式为:int fork()fork系统调用没有参数,如执行成功,则创建一子进程,子进程继承了父进程的某些属性。当从该系统调用返回时,系统中已有两个用户级环境完全相同的进程存在。这两个进程从fork调用中得到的返回值不同,其中子进程得到的返回值为零,父进程得到的返回值是最新创建的子进程的进程标识符。2. exec系统调用fork系统调用只是将父进程的环境拷贝到新进程中,而没有启动执行一个新的目标程序。UNIX系统提供了exec系统调用,用它更换进程的执行映象,启动新的目标程序。例如:UNIX系统中的所有命令都是通过exec来执行的。exec系统调用有六种不同的使用格式,但在核心中只对应一个调用入口。它们有不同的调用格式和调用参数。这六种调用格式分别为:#include int execl (const char *path, const char *arg0, ., const char *argn, (char *)0);int execv (const char *path, char *const *argv);int execle (const char *path, const char *arg0, ., const char *argn,(char *0), const char *envp);int execve (const char *path, char *const *argv, char *const *envp);int execlp (const char *file, const char *arg0, ., const char *argn, (char *)0);int execvp (const char *file, char *const *argv);说明:参数path指出一个可执行目标文件的路径名;参数file指出可执行目标文件的文件名。arg0作为约定同path一样指出目标文件的路径名;参数arg1到argn分别是该目标文件执行时所带的命令行参数;参数argv是一个字符串指针数组,由它指出该目标程序使用的命令行参数表,按约定第一个字符指针指向与path 或file相同的字符串;最后一个指针指向一个空字符串,其余的指向该程序执行时所带的命令行参数;参数envp同argv一样也是一个字符指针数组,由它指出该目标程序执行时的进程环境,它也以一个空指针结束。exec的六种格式在以下三点上有所不同:(1) path是一个目标文件的完整路径名,而file是目标文件名,它是可以通过环境变量PATH来搜索的;(2) 由path或file指定的目标文件的命令行参数是完整的参数列表或是通过一指针数组argv来给出的;(3) 环境变量是系统自动传递或者通过envp来给出的。下图说明了exec系统调用的六种不同格式对以上三点的支持。系统调用 参数形式 环境传送 路径搜索Execl 全部列表 自动 否Execv 指针数组 自动 否Execle 全部列表 不自动 否Execve 指针数组 不自动 否Execlp 全部列表 自动 是Execvp 指针数组 自动 是3. exit、wait系统调用(1) exit系统调用格式如下: #include void exit(int status);#include void _exit(int status);说明:exit的功能是终止进程的执行,并释放该进程所占用的某些系统资源。参数status是调用进程终止时传递给其父进程的值。如果调用进程执行exit系统调用时,其父进程正在等待子进程暂停或终止(使用wait系统调用),则父进程可立刻得到该值;如果此时父进程并不处在等待状态,那么一旦父进程使用wait调用,便可立刻得到子进程传过来的status值,注意:只有status的低八位才传递给其父进程。系统调用_exit与exit之间的差异是_exit只做部分的清除,因此建议不要轻易地使用这种调用形式。每个进程在消亡前都要调用该系统调用,没有显示地使用该系统调用,则生成目标文件的装载程序为该进程隐含地做这一工作。(2) wait系统调用格式如下: #include #include pid_t wait (int *statptr);说明:wait系统调用将调用进程挂起,直到该进程收到一个被其捕获的信号或者它的任何一个子进程暂停或终止为止。如果在wait调用之前已有子进程暂停或终止,则该调用立刻返回。格式wait(int *)0) 的功能是等待所有子进程终止。Wait返回时,其返回值为该子进程的进程号。参数statptr的值为该子进程的终止原因:1、 如果子进程暂停,statptr目的高八位存放使该子进程暂停的信号值(在第七章中介绍信号),低八位为01772、 如果子进程由于调用exit终止,则该值的低八位为0,高八位为子进程终止时,exit系统调用中参数status值的低八位;3、 如果子进程因信号终止,该值的高八位为0,低八位为引起终止的信号值。此外如低七位为1,则表示产生了一个core文件。下面我们来看一个例子,该例是一个fork、exec、exit和wait联合使用的一个实例程序,我们称之为feew.c:/* feew.c */main(argc, argv)int argc;char *argv;int pid, stat;if (argc != 1)if (pid = fork() = 0)printf(Child pid = %dn, getpid();execl(argv1, argv1, 0);exit(5);pid = wait(&stat);printf(pid=%d, H_stat=%d, L_stat=%dn, pid, stat8, stat&0xff);当命令行参数的个数不为1时,程序使用fork系统调用产生一个子进程。子进程通过系统调用getpid获得自己的进程标识符,然后调用exec执行命令行中用户提交的命令,如果exec执行失败,则子进程调用exit(5)终止。父进程使用wait系统调用等待子进程暂停或终止,然后输出从wait中返回的信息。下面以三种方式执行该程序:1 不带命令行参数% ./feewpid=-1, H_stat=0, L_stat=0%不产生子进程,从运行结果来看,当无子进程时,wait的返回值为1。2 带命令行参数,参数为合法的可执行命令% ./feew /bin/dateChild pid = 17251998年 2月16日(星期一) 15时59分14秒 CSTpid=1725, H_stat=0, L_stat=0产生子进程。子进程输出其进程标识符后,再调用exec执行从命令行中提交的命令(/bin/date),同时父进程等待子进程暂停或终止,然后输出从wait中得到的信息:子进程标识符或状态参数stat的高八位、低八位的内容。从中可以看到:子进程因调用一个隐含的exit(0)而终止,终止时传给父进程的值为0。3 带命令行参数,但参数不合法%./feew /etc/shudownChild pid = 1760/etc/shutdown: 只有超级用户(root)能运行 /etc/shutdown。pid=1760, H_stat=2, L_stat=0%子进程创建成功。但由于以普通用户的身份执行/etc/shutdown,因此exec失败,尔后调用exit(5)而终止;父进程调用wait得到返回值:子进程号和状态参数stat的高八位、低八位的内容。从执行结果可以看出:子进程因调用exit(5)而终止,终止时传给父进程的值为5。5.3 进程管理进程管理包括的面很广,诸如进程的用户标识符管理、进程标识符管理等。进程的用户标识符有两个:实际用户标识符(real user id)和有效用户标识符(effective user id),其对应的组标识符分别称为实际组标识符(real group id)和有效组标识符(effective groud id)。一般而言,进程的实际用户标识符为运行该进程的用户标识符,通常只用于系统记帐,其他功能由有效用户标识符来完成,如用有效用户标识符来完成对新创建文件赋予属性关系、检查文件的存取权限和利用kill系统调用向进程发送信号的权限。一般情况下,一进程的有效用户标识符和实际用户标识符是相等的,但系统允许改变进程的有效用户标识符。1. 进程的用户标识符管理UNIX系统提供了一组系统调用来管理进程的用户标识符,它们的使用形式是:#include #include uid_t getuid (void);uid_t geteuid (void);gid_t getgid (void);gid_t getegid (void); int setuid(uid_t uid);int setgid(gid_t gid);说明:前四个系统调用没有参数,分别返回调用进程的实际用户标识符、有效用户标识符、实际用户组标识符和有效组标识符。这些系统调用的执行总能获得成功,不会发生任何错误。系统调用setuid和setgid用于设置进程的实际用户(组)标识符和有效用户(组)标识符,如调用进程的有效用户标识符是超级用户标识符,则将调用的进程实际用户(组)标识符和有效用户(组)标识符设置为uid或gid;如调用进程的有效用户标识符不是超级用户标识符,但其实际用户(组)标识符等于uid或gid时,则其有效用户(组)标识符被设置为uid或gid;否则setuid或setgid调用失败。系统调用setuid或setgid调用成功时返回0,失败时返回1。2. 进程标识符管理UNIX系统使用进程标识符来管理当前系统中的进程。为对具有某类似特性的进程统一管理,系统又引入了进程组的概念,以组标识符来区别进程是否同组。进程的组标识符是从父进程继承下来的,所以,通常进程的组标识符就是和它相关联的注册进程的标识符。进程的标识符是由系统为之分配的,不能被修改;组标识符可通过setpgrp系统调用修改。相关系统调用的格式如下:#include #include pid_t getpid(void);pid_t getpgrp(void);pid_t getppid(void);pid_t getpgid(pid_t pid);说明:前三个系统调用分别返回调用进程的进程标识符、进程组标识符和其父进程标识符。它们总能成功地返回。第四个调用置进程组标识符,它将调用进程的进程组标识符改为调用进程的进程标识符,使其成为进程组首进程,并返回这一新的进程组标识符。下面我们来看一个实例:/* setuid.c */main(argc, argv)int argc;char *argv;int ret, uid;uid = atoi(argv1);printf(Before uid=%d, euid=%dn, getuid(), geteuid();ret = setuid(uid);printf(After uid=%d, euid=%dn, getuid(), geteuid();printf(ret = %dn, ret);下面分三种情况讨论该程序的执行:1、 如果执行该程序的用户为超级用户,则只要命令行所给的用户标识符大于0,无论所给的用户标识符是否存在,执行总能成功。#./setuid 3434Before uid=0, euid=0After uid=3434, euid=3434ret = 0结果分析:将进程的实际和有效用户标识符均改为3434。2、 如果执行该程序的用户为一般用户,用id命令得到用户uid和gid后,再调用该程序,过程如下:%iduid=1111(yds) gid=20(user)%./setuid 3434Before uid=1111, euid=1111After uid=1111, euid=1111ret = -1%./setuid 1111Before uid=1111, euid=1111After uid=1111, euid=1111ret = 0%结果分析:当命令行参数为1111时,setuid执行成功,因为用户的uid就是1111。值得注意的是:注册程序login 是个典型的setuid系统调用程序,login进程的有效用户是超级用户,该进程在建立用户的Shell进程前,调用setuid将实际和有效用户标识符调整为注册用户的用户实际和有效标识符。六章 设备输入/输出控制6.1 概述UNIX将设备看成文件,这是UNIX的一大特色。这里需要介绍一个设备号的概念。设备特别文件与两个设备号有关主设备号和次设备号。主设备号告诉操作系统,当涉及文件名时,将使用哪种设备类型。对于每一种类型的设备都有一段驻留在操作系统中的程序代码,以控制相应类型的设备,这段代码被称为设备驱动程序。次设备号被传递给设备驱动程序,这个号码用来决定使用哪种物理设备。例如,决定在一块多重驱动控制卡上,哪个磁盘驱动器将被访问,以及该磁盘驱动器中哪一部分将被使用;或者,当一个磁盘驱动器所请求的操作已经完成后,应该被恢复原状。几个设备(如同类型的磁盘驱动器)可以用同一个主设备号,但它们将有不同的次设备号。看下面的例子:%ls -l /dev/ttyq*crw-w- 2 yds user 15, 1 2月 17日 09时03分 ttyq1crw-w- 2 yds user 15, 14 2月 16日 17时00分 ttyq14%上例中15是主设备号,1和14是次设备号。用户可以使用系统提供的统一而且独立于设备的界面对文件进行操作的系统调用来操作设备,而没有必要涉及设备的具体细节。大部分对文件进行操作的系统调用对它们仍起作用,例如,用open打开设备,用read/write对设备进行读/写,设备操作完成后,用close关闭设备。但有的系统调用在对设备文件进行操作时,其功效有所不同。如create及open的创建方式都不能创建设备文件。6.2 设备输入/输出控制ioctl系统调用ioctl是UNIX系统专门提供的用于设备控制的系统调用。该系统调用与设备类型(即主设备号)相关。不同的设备,系统提供了不同的控制命令。ioctl的调用格式是:ioctl(int fd, int cmd,arg)说明:参数fd是一设备文件的文件描述字,cmd是控制命令,它与设备相关,不同类型的设备有不同的控制命令。参数arg没有固定的数据结构,它随cmd的不同而不同。七章 高级编程7.1 处理信号信号是UNIX进程间最基本的通讯手段,主要作用是实现进程间异步事件的通讯。信号是传送到进程的软中断,它通知进程在它们的环境中出现了非正常事件。进程接收到信号后要进行处理,处理方式为以下四种之一:(1) 缺省方式(SIG_DFL):这是进程对信号的一般处理方式,在无特殊情况下,进程在接收到信号后将终止执行。有一些信号,在终止进程运行前需将终止进程的正文段、数据段、user结构和栈段内容写到当前目录的core文件中,以备调试工具分析与使用。(2) 忽略方式(SIG_IGN):进程接收到一个已指明忽略的信号,则将该信号清除后,立即返回,不在任何工作。信号SIGKILL不能被忽略。(3) 保持方式(SIG_HOLD):当进程处于该方式时,将接收的信号保存起来,等该进程的保持方式解除后,再进行处理。(4) 捕获方式(设置信号处理函数):这是用户设置的信号处理方式,当进程接收到这种信号时,执行用户设置的信号处理函数,执行完后,恢复现场,然后继续往下执行。1. 常用信号种类UNIX信号的种类很多,下面介绍一些最常用的信号:SIGHUP 挂断。这是当控制终端被挂起时送到进程的信号。SIGINT 中断。由键盘产生的中断。SIGQUIT 退出。由键盘产生的中断。SIGKILL 终止。这个信号不能被捕获、阻塞或忽略。SIGALRM 定时信号。SIGTERM 软件终止信号。SIGUSR1 用户定义的信号1。SIGUSR2 用户定义的信号2。这些信号值的声明在/usr/include/sys/signal.h文件中。2. 发送信号kill系统调用用户传送信号到进程的系统调用是kill,调用格式为:#include #include int kill (pid_t pid, int sig);说明:该系统调用把一个信号值为sig的信号发送给进程标识符为pid的相关进程。成功时返回0,失败时返回1。该调用执行成功与否,依赖于调用进程的有效用户标识符和参数pid的值,pid值的含义如下:大于0:将信号发送给进程号等于pid的进程。等于0:将信号发送给调用进程的同组进程(0和1进程除外)。等于1:将信号发送给实际用户标识符等于调用进程的有效用户标识符的所有进程(0和1进程除外),如调用进程的有效用户是超级用户,则将信号发送给除0和1进程外的所有进程。非1的负数:将信号发送给进程组标识符为pid的绝对值的所有进程。在实际编程中,kill系统调用非常有用,具体说来:常用方式kill(pid,SIGUSR1) 向进程号为pid的进程发送信号SIGUSR1用来判断进程是否存在:if (kill(pid,0) = 0)进程号为pid的进程存在;else进程号为pid的进程不存在!用来杀掉子进程kill(pid,1) 杀掉进程号为pid的进程2. 处理信号signal系统调用用户处理信号的系统调用是signal,调用格式为:#include void (*signal (int sig, void (*func)()();说明:参数sig是一个信号值,func定义了该信号的处理方式。该系统定义的功能是按func的定义设置调用进程对信号sig的处理方式。执行成功时,返回调用进程先前对信号sig处理方式的值,失败则返回1。参数取值为SIG_DFL或SIG_IGN或用户信号处理函数的地址时,分别表示缺省方式、忽略方式和捕获方式。3.pause系统调用pause系统调用的格式为:pause()说明:该调用没有参数,其功能为使调用进程睡眠直到其接收到一信号为止。该系统调用的结果依赖于调用进程对接收到的信号的处理方式。缺省方式:终止调用进程,pause无返回值;忽略方式:进程不受该信号的影响,继续睡眠;捕获方式:调用进程从信号处理函数返回后,继续往下执行。4. 使用信号定时alarm系统调用
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 难点详解贵州省兴义市七年级上册有理数及其运算专题测评试卷(含答案详解)
- 难点详解甘肃省临夏市中考数学真题分类(数据分析)汇编单元测评练习题
- 辽宁沈阳副食集团及所属企业招聘笔试题库及答案详解(必刷)
- 2025年教师职称-河南-河南教师职称(基础知识、综合素质、小学音乐)历年参考题库典型考点含答案解析
- 难点详解自考专业(行政管理)试卷及参考答案(夺分金卷)
- 月供购车合同范本
- 【英语】湖北省黄冈市黄梅县2025届某高级中学模拟押题卷(一)(解析版)
- 2025年法学本科测试题及答案
- 2025年度禁毒知识竞赛试题库及答案
- 2025年医药行业研发外包(CRO)模式下的临床试验风险管理策略报告
- 粮食仓储(粮库)安全生产标准化管理体系全套资料汇编(2019-2020新标准实施模板)
- 喜茶运营管理手册和员工操作管理手册
- 比亚迪汉DM-i说明书
- 心肾综合征及其临床处理
- 普通高中课程方案
- 2022年山东高考生物试卷真题及答案详解(精校版)
- GB/T 38936-2020高温渗碳轴承钢
- 高考地理一轮复习课件 【知识精讲+高效课堂】 农业区位因素及其变化
- 教师专业发展与名师成长(学校师范专业公共课)
- 互通立交设计课件
- 生物竞赛辅导 动物行为学第七章 行为发育(38)课件
评论
0/150
提交评论