Lin 基础技术教程 8_第1页
Lin 基础技术教程 8_第2页
Lin 基础技术教程 8_第3页
Lin 基础技术教程 8_第4页
Lin 基础技术教程 8_第5页
已阅读5页,还剩59页未读 继续免费阅读

下载本文档

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

文档简介

第7章Linux环境编程主要内容系统调用和库函数文件操作进程控制进程通信内存管理7.1系统调用和库函数7.1.1系统调用概念:系统调用是操作系统提供的、与用户程序之间的接口,即操作系统提供给程序员的接口类似于过程调用,都由程序代码构成,使用方式相同差别:过程调用只能在用户态下运行,不能进入核心态;而系统调用可以实现从用户态到核心态的转变系统调用可大致分为五个类别:进程控制、文件管理、设备管理、信息维护和通信7.1.2库函数本身并不属于操作系统的内核部分库函数可以分为6类:①文件管理

②状态信息

③文件修改④程序设计语言的支持

⑤程序装入和执行

⑥通信系统调用与库函数之间的关系7.1.3调用方式C程序中,系统调用与库函数调用方式相同例如,open系统调用可以打开一个指定文件,其函数原型说明如下:

#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>intopen(constchar*path,intoflags);不同的系统调用所需要的头文件(又称前导文件)不同头文件一般放在/usr/include/sys或/usr/include/linux

7.2文件操作7.2.1文件操作的相关系统调用系统调用openclose

系统调用creat

系统调用

link和unlink

系统调用read

和write

lseek、mkdir、rmdir、chdir、chmod等其它系统调用有关文件操作的系统调用格式和功能说明

参见表7-11系统调用openopen是进程存取一个文件中的数据必需首先做的系统调用,打开文件。open系统调用的声明格式如下:intopen(constchar*path,intoflag);intopen(constchar*path,intoflag,mode_tmode);系统调用的头文件:#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>返回:若成功返回为文件描述符,若出错为-1。path是要打开或创建的文件的名字。oflag参数可用来说明此函数的多个选择项。用下列一个或多个常数进行或运算构成oflag参数(这些常数定义在<fcntl.h>头文件中):

O_RDONLY

值为0,只读打开。

O_WRONLY值为1,只写打开。

O_RDWR值为2,读写打开。这三个常数应只指定一个。下列常数则是可选择的:

O_APPEND

每次写时都加到文件的尾端。

O_CREAT

若此文件不存在则创建它。使用此选择项时,需同时说明第三个参数mode,用其说明该新文件的存取许可权位。•O_EXCL如果同时指定了O_CREAT,而文件已经存在,则出错。这可测试一个文件是否存在,如果不存在则创建此文件成为一个原子操作。•O_TRUNC

如果此文件存在,而且为只读或只写成功打开,则将其长度截短为0。open实例:#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<errno.h>#include<stdio.h>externinterrno;intmain(){intfd;if((fd=open("test.c",O_CREAT|O_EXCL,S_IRWXU))==-1){printf("OpenError\nErrorNo=%d\n",errno);if(errno==EEXIST)printf("Openerror,becausefilealreadyexist!\n");}elseprintf("Success\n");return0;}O_CREAT:当文件不存在时,将建立该文件。O_EXCL:O_CREAT一起用S_IRWXU:文件主有读、写、执行的权力。EEXIST:上述标示的文件名已经存在。2系统调用creatcreat是进程新建一个文件时使用的系统调用。新建文件的功能也可以由open调用实现。creat系统调用的声明格式如下:intcreat(constchar*pathname,mode_tmode);系统调用的头文件:#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>creat系统调用中的参数:

pathname和mode的含义与系统调用open中的一样。如果pathname指向的文件不存在,核心就以指定的文件名和许可权创建一个新文件;如果pathname指向的文件存在,核心就将该文件截断,释放以前数据所占用的磁盘块。返回:若成功为只写打开的文件描述符,若出错为-1。此函数等效于:open(pathname,O_WRONLY|O_CREAT|O_TRUNC,mode)在早期的UNIX版本中,open的第二个参数只能是0、1或2。没有办法打开一个尚未存在的文件,因此需要另一个系统调用creat以创建新文件。现在,open函数提供了选择项O_CREAT和O_TRUNC,于是也就不再需要creat函数了。3系统调用read和writeread是从文件中读取指定长度的数据到内存中其声明格式如下:ssize_tread(intfd,void*buf,size_tcount);系统调用用的头文件:#include<unistd.h>功能:从文件描述符fd所指的文件中读取count个字节到buf所指向的内存缓冲中。返回:读到的字节数,若已到文件尾为0,若出错为-1。系统调用read中的数据类型在ANSIC中,类型void*用于表示无类型指针。其返回值必须是一个带符号整数(ssize_t),以返回正字节数、0(表示文件尾端)或-1(出错)。第三个参数在历史上是一个不带符号整数,以允许一个16位的实现可以一次读或写至65534个字节。在1990POSIX.1标准中,引进了新的基本系统数据类型ssize_t以提供带符号的返回值,size_t则被用于第三个参数。write系统调用write是将内存中的数据写入文件

其声明格式如下:

ssize_twrite(intfd,constvoid*buf,size_tcount);系统调用加入头文件:#include<unistd.h>功能:write将buf所指内存中的count个字节写入文件描述符fd所指的文件。返回:若成功为已写的字节数,若出错为-1write出错的一个常见原因是:磁盘已写满,或者超过了对一个给定进程的文件长度限制。对于普通文件,写操作从文件的当前位移量处开始。如果在打开该文件时,指定了O_APPEND选择项,则在每次写操作之前,将文件位移量设置在文件的当前结尾处。在一次成功写之后,该文件位移量增加实际写的字节数。例如:将标准输入复制到标准输出#include<unistd.h>#defineBUFFSIZE1024intmain(){intn;charbuf[BUFFSIZE];while((n=read(STDIN_FILENO,buf,BUFFSIZE))>0)if(write(STDOUT_FILENO,buf,n)!=n)printf("writeerror\n");if(n<0)printf("readerror\n");exit(0);}说明很多应用程序假定标准输入是文件描述符0,标准输出是文件描述符1。本例中则用两个在<unistd.h>中定义的名字STDIN_FILENO和STDOUT_FILENO。考虑到进程终止时,UNIX会关闭所有打开文件描述符,所以此程序并不关闭输入和输出文件4lseek系统调用每个打开文件都有一个与其相关联的“当前文件位移量”。它是一个非负整数,用以度量从文件开始处计算的字节数。

lseek用来重新定位下一次read/write系统调用所用的文件指针(也叫文件偏移量:fileoffset)进程可以使用系统调用lseek来指定I/O的位置,从而实现文件的随机存取。也可以形成空洞文件lseek声明格式如下:off_tlseek(intfildes,off_toffset,intwhence);系统调用加入头文件:#include<sys/types.h>#include<unistd.h>作用:系统调用根据whence指定的位置将文件描述符fildes指向文件的文件指针偏移offset长度的字节数。返回:若成功为新的文件位移,若出错为-1。对参数offset

的解释与参数whence的值有关。若whence是SEEK_SET,则将该文件的位移量设置为距文件开始处offset个字节。若whence是SEEK_CUR,则将该文件的位移量设置为其当前值加offset,offset可为正或负。若whence是SEEK_END,则将该文件的位移量设置为文件长度加offset,offset可为正或负。若lseek成功执行,则返回新的文件位移量,为此可用下列方式确定一个打开文件的当前位移量:

off_tcurrpos;currpos=lseek(fd,0,SEEK_CUR);

这种方法也可用来确定所涉及的文件是否可以设置位移量。对于普通文件,则其位移量必须是非负值。因为位移量可能是负值,所以在比较lseek的返回值时应当谨慎,不要测试它是否小于0,而要测试它是否等于-1。例如:创建一个具有空洞的文件#include<sys/types.h>#include<sys/stat.h>#include<fcntl.h>#include<errno.h>#include<unistd.h>charbuf1[]="abcdefghij";charbuf2[]="ABCDEFGHIJ";intmain(){intfd;if((fd=creat(“file.hole”,3))<0)/*3是FILE_MODE*/printf("createrror");

if(write(fd,buf1,10)!=10)printf("buf1writeerror");/*offsetnow=10*/if(lseek(fd,40,SEEK_SET)==-1)printf("lseekerror"); /*offsetnow=40*/if(write(fd,buf2,10)!=10)printf("buf2writeerror");

/*offsetnow=50*/exit(0);}/*输出结果:#a.out#od-cfile.hole//以字符方式观察文件的实际内容

0000000abcdefghIj

\0\0\0\0\0\0

0000020\0\0\0\0\0\0\0\0\0\0\0\0\0\0\0\00000040\0\0\0\0\0\0\0\0

ABCDEFGH0000060IJ0000062每一行开始的一个七位数是以八进制形式表示的字节位移量。5系统调用closeclose系统调用:当进程不再使用一个打开的文件时,就应该关闭该文件。其声明格式如下:intclose(intfd);返回:若成功为0,若出错为-1/*其中fd是一个已经打开的文件的文件描述符。*/系统调用的头文件:#include<unistd.h>当一个进程终止时,它所有的打开文件都由内核自动关闭。很多程序都使用这一功能而不显式地用close关闭打开的文件。6.文件链接的概念文件链接:在不同目录文件中的某些文件名所对应的inode都指向同一个索引节点。即:多个文件对应一个文件,叫硬链接。硬链接不能跨文件系统。在inode(磁盘索引节点)的结构中,文件链接数字段的作用:有多少文件名指向该文件(>1or=0)。系统调用的头文件:#include<unistd.h>系统调用link系统调用声明形式:intlink(constchar*oldpath,constchar*newpath);其中:

oldpath:已存在的文件。

newpath:被链接的新文件。功能:系统调用link对一个已经存在的文件创建一个新的链接,是硬链接。给文件一个新的文件名字,一个文件多个名字,链接文件不开新的空间。

只有超级用户进程可以创建指向一个目录的新链接。其理由是这样做可能在文件系统中形成循环,大多数处理文件系统的公用程序都不能处理这种情况。

POSIX1.1允许支持跨越文件系统的链接的实现。symlink系统调用声明形式:

intsymlink(constchar*oldpath,constchar*newpath);

返回:若成功则为0,若出错则为-1系统调用头文件:

#include<unistd.h>作用:系统调用symlink创建一个包含字符串oldpath的名为newpath的符号链接。符号链接在运行时被解释,仿佛链接的内容被随后找到的文件或目录替代。符号链接中可以包含上级目录“..”,作为符号链接中路径的组成部分。说明:(1)符号链接(软链接):可以指向一个已存在的文件或还不存在的文件。(2)可以给一个目录做链接,可以跨文件系统作链接。(3)符号链接的许可权是没有意义的。其权限是由其链接的目标确定的。注意:符号链接与硬链接不同,删除符号链接所指的文件,将导致真正的删除。

符号链接是对一个文件的间接指针,它与硬链接有所不同,硬链接直接指向文件的i节点。引进符号链接的原因是为了避免硬链接的一些限制:

(a)硬链接通常要求链接和文件位于同一文件系统中,

(b)只有超级用户才能创建到目录的硬链接。对符号链接以及它指向什么没有文件系统限制,任何用户都可创建指向目录的符号链接。符号链接一般用于将一个文件或整个目录结构移到系统中其他某个位置。unlink是清除文件的连接(一个目录表项)。声明格式:intunlink(constchar*pathname);返回:若成功则为0,若出错则为-1系统调用的头文件:#include<unistd.h>作用:unlink从文件系统中删除目录项,并将pathname所引用文件的链接计数减1。当链接计数为0并且打开此文件的进程数为0时,此链接被删除。释放索引节点及其指向的数据块。若pathname指符号链接,则该链接被删除。系统调用unlink

只有当链接计数达到0时,该文件的内容才可被删除。另外只要有进程打开了该文件,其内容也不能被删除。关闭一个文件时,内核首先检查使该文件打开的进程计数。如果该计数达到0,然后内核检查其链接计数,如果也是0,那么就删除该文件的内容。其他有关文件操作的系统调用格

式功

能#include<sys/types.h>#include<sys/stat.h>intmkdir(constchar*path,mode_tmode);创建目录。目录名由path指出,mode表示赋予该目录的权限。如果成功,则返回值为0;否则,返回值为

1,并由errno变量记录错误码#include<unistd.h>#include<sys/types.h>#include<sys/stat.h>intrmdir(constchar*path);删除由path所指示的子目录(该目录必须是空目录)。如果成功,则返回值为0;否则,返回值为

1,并由errno变量记录错误码#include<unistd.h>intchdir(constchar*path);将当前工作目录改到path所指示的目录上。如果成功,则返回值为0;否则,返回值为

1#include<unistd.h>#include<sys/types.h>intchmod(constchar*path,mode_tmode);修改由path所指示的文件或子目录的访问权限,新权限由mode参数给出。如果成功,则返回值为0;否则,返回值为

1。只有文件属主或超级用户才能修改该文件的权限#include<sys/stat.h>mode_tumask(mode_tnewmask);把进程的新权限掩码(umask)设置为newmask所指定的掩码。掩码是新建文件和目录应关闭的权限位。使用该调用时,只能使掩码更严格,而不能放宽。无论成功与否,均返回原来的掩码值7.3进程控制进程的创建声明格式:pid_tfork(void);头文件:#include<unistd.h>说明:调用成功,父进程中返回子进程的PID值(pid>0)及proc结构中的某些参数;子进程中返回0且子进程是父进程的副本。调用失败时,给父进程返回-1,没有子进程创建。由fork创建的新进程被称为子进程(childprocess)。该函数被调用一次,但返回两次:子进程的返回值是0父进程的返回值则是新子进程的进程ID。从fork()调用以后,两个代码相同的进程并发执行。由于子进程继承了父进程的程序指针,子进程也从fork()调用后的语句开始执行。使fork()失败的两个主要原因是:(1)系统中已经有了太多的进程(通常意味着某个方面出了问题)。(2)该ID用户的进程总数超过了系统限制。CHILD_MAX规定了每个实际用户在任一时刻可具有的最大进程数。#include

<stdio.h>#include

<unistd.h>main(){

pid_tpid;/*直接用int定义也可以*/

printf("Just1processnow.\n");

printf("Callingfork()now.\n");

pid=fork();

if(pid==0)

printf("I’mthechildprecess.\n");

elseif(pid>0)

printf("I’mtheparentprocess.\n");

elseprintf("forkfailed.\n");

printf("programendnow.\n");}fork系统调用举例:结果:Just1processnow.Callingfork()now.I’mthechildprecess.I’mtheparentprocess.programendnow.programendnow.

在fork之后是父进程先执行还是子进程先执行是不确定的。这取决于内核所使用的调度算法。如果要求父、子进程之间相互同步,则要求某种形式的进程间通信:父进程使自己睡眠几秒钟,以使子进程先执行在fork之后用信号使父、子进程同步。2进程的执行exec()系统调用exec实现“对其他程序的引用”。具体内容:(1)该调用将一个可执行的程序文件读入,代替发出调用的进程执行。(2)核心读入程序文件的正文,清除原先进程的数据区,清除原先用户软中断信号处理函数的地址,当exec调用返回时,进程执行新的正文。系统调用exec是用来执行一个可执行文件来代替当前进程的执行映像。注意:该调用并没有生成新的进程,而是在原有进程的基础上,替换原有进程的正文,调用前后是同一个进程,进程号PID不变。但执行的程序变了(执行的指令序列改变了)。exec有六种调用形式(系统不同有变化)intexecl(constchar*path,constchar*arg,...);intexeclp(constchar*file,constchar*arg,...);

intexecle(constchar*path,constchar*arg,...,char*constenvp[

]);intexecv(constchar*path,char*constargv[

]);intexecvp(constchar*file,char*constargv[

]);

intexecve(constchar*path,char*constargv[

],

char*constenvp[

]);在BSD系统中还有:intexect(constchar*path,char*constargv[

],char*constenvp[

]);

这六种系统调用的功能相似,都是通过一个新的程序段覆盖原有内存空间的内容来实现进程执行程序的转换,其差别仅在于参数形式不同。它们用系统调用名的后两个字母区别调用方式:

l:长格式调用,每个参数指针独立传递并以空指针0结束;如:

execl("/bin/ls","ls","-l","abc.c",0);

表示运行ls–labc.c命令。

v:表示可利用argv指针数组传递运行参数;

execv("pathname",argv[0],argv[1],argv[2],0);

✦e:从envp指针数组传递环境参数;这种调用方法只有在将要执行的程序需要新的运行环境时才被使用。为了使与以前程序运行环境不同的新程序可以运行,需要用execve或execle来传递新程序的运行环境变量。✦p:在环境变量PATH指定的目录中搜索指定的文件,否则在当前目录中搜索。

如:

execl("/bin/ls","ls",

"-l",

"abc.c",0);

可写成:

execlp("ls",

"ls",

"-l",

"abc.c",0);进程控制的系统调用格

式功

能#include<unistd.h>#include<sys/types.h>pid_tgetpid(void);pid_tgetppid(void);getpid返回当前进程的PID,而getppid返回父进程的PID#include<unistd.h>void_exit(intstatus);#include<stdlib.h>voidexit(intstatus);终止调用的程序(用于程序运行出错)。参数status表示进程退出状态(又称退出值、返回码、返回值等),它传递给系统,用于父进程恢复。_exit函数比exit函数简单些,前者使进程立即终止;后者在进程退出之前,要检查文件的打开情况,执行清理I/O缓冲的工作#include<sys/types.h>#include<sys/wait.h>pid_twait(int*status);

pid_twaitpid(pid_tpid,int*status,intoption);wait()等待任何要僵死的子进程;有关子进程退出时的一些状态保存在参数status中。如成功,返回该终止进程的PID;否则,返回

1而waitpid()等待由参数pid指定的子进程退出。参数option规定了该调用的行为:WNOHANG表示如没有子进程退出,则立即返回0;WUNTRACED表示返回一个已经停止但尚未退出的子进程的信息。可以对它们执行逻辑“或”#include<unistd.h>unsignedintsleep(unsignedintseconds);使进程挂起指定的时间,直至指定时间(由seconds表示)用完或者收到信号#include<unistd.h>intnice(intinc);改变进程的优先级。普通用户调用nice时,只能增加进程的优先数(inc为正值);只有超级用户才能减少进程的优先数(inc为负数)。如成功,返回0;否则,返回

1【例7.3】该示例说明如何使用有关进程操作的系统调用。每个进程都有唯一的进程ID号(PID)。PID通常在数值上逐渐增大。因此,子进程的PID一般要比其父进程大。当然,PID的值不可能无限大,当它超过系统规定的最大值时,就反转回来使用最小的尚未使用的PID值。如果父进程死亡或退出,则子进程会被指定一个新的父进程init(其PID为1)。本程序利用fork()创建子进程,利用getpid()和getppid()分别获得进程的PID和父进程PID,使用sleep()将相关进程挂起几秒钟。/*proc1.c演示有关进程操作*/#include<unistd.h>#include<sys/types.h>#include<stdio.h>#include<errno.h>

#include<string.h>#include<stdlib.h>intmain(intargc,char**argv){pid_tpid,old_ppid,new_ppid;pid_tchild,parent;

parent=getpid(); /*获得本进程的PID*/if((child=fork())<0){fprintf(stderr,"%s:forkofchildfailed:%s\n",argv[0],strerror(errno));exit(1);}elseif(child==0){ /*此时是子进程被调度运行*/old_ppid=getppid();sleep(2);new_ppid=getppid();}else{sleep(1);exit(0); /*父进程退出*/}/*下面仅子进程运行*/printf("Originalparent:%d\n",parent);printf("Child:%d\n",getpid());printf("Child'soldppid:%d\n",old_ppid);printf("Child'snewppid:%d\n",new_ppid);exit(0);}程序运行结果如下:$./proc1Originalparent:2009Child:2010Child'soldppid:2009Child'snewppid:17.4进程通信7.4.1进程通信的相关函数

式功

能管

道#include<unistd.h>intpipe(intfiledes[2]);创建管道。参数filedes[2]是有2个整数的数组,存放打开文件的描述符,其中,filedes[0]表示管道读端,filedes[1]表示写端。若成功,返回值为0;否则,返回值为

1#include<sys/types.h>#include<sys/stat.h>intmkfifo(constchar*pathname,mode_tmode);创建FIFO文件(即有名管道)。pathname是要创建的FIFO文件名,mode是给FIFO文件设定的权限。如执行成功,返回值为0;否则,返回值为

1,并将出错码存入errno变量【例7.5】本程序简单地演示如何使用管道机制进行I/O控制。首先用pipe函数创建一个管道,然后用write将消息写入管道,最后用read读取管道中的内容。程序如下:/*pipedemo.c演示使用管道机制进行I/O控制*/#include<unistd.h>#include<stdio.h>#include<errno.h>

#include<string.h>#include<stdlib.h>intmain(intargc,char**argv){staticconstcharmesg[]="HappyNewYearstoyou!";charbuf[BUFSIZ];size_trcount,wcount;intp_fd[2];size_tn;

if(pipe(p_fd)<0){/*创建管道*/fprintf(stderr,"%s:pipefailed:%s\n",argv[0],strerror(errno));exit(1);}printf("Readend=fd%d,writeend=fd%d\n",p_fd[0],p_fd[1]);n=strlen(mesg);if((wcount=write(p_fd[1],mesg,n))!=n){/*写入数据*/fprintf(stderr,"%s:writefailed:%s\n",argv[0],strerror(errno));exit(1);}if((rcount=read(p_fd[0],buf,BUFSIZ))!=wcount){/*读出数据*/fprintf(stderr,"%s:readfailed:%s\n",argv[0],strerror(errno));

温馨提示

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

评论

0/150

提交评论