




版权说明:本文档由用户提供并上传,收益归属内容提供方,若内容存在侵权,请进行举报或认领
文档简介
1、第十三章 UNIX系统程序设计(二),系统调用返回值 (page 323),大多数的系统调用都返回一个值。如打开一个文件的系统调用fd=open(name,mode)返回一个文件描述符fd。 当指定的文件不存在时返回 -1。返回值 -1指示一个系统调用可能失败了,这时系统的全局变量errno值为相应的出错代码。 系统还定义了另外两个外部变量,即对应于出错代码的消息数组sys_errlist和比该数组最大下标大1的整型变量sys_neer。 为了在系统调用失败后获得出错代码和出错信息,我们可以编写一个如下的C函数:,#include void syserr(syscall) char *sysc
2、all; extern int errno,sys_nerr; extern const char const *sys_errlist; fprintf(stderr,ERROR: %s %d,syscall,errno); if(errno0 ,13.1文件系统程序设计,13.1.1获取文件的状态 在C语言程序设计中,有时需要获得有关文件的类型、大小、文件主及时间信息,这可通过系统调用stat和fstat来获取。 include include int stat(pathname,sbuf) char pathname; int fstat(fd,sbuf) int fd; struct
3、stat *sbuf; stat和fstat都是从一个文件的i节点获得有关状态信息的。stat是根据参数pathname给出的文件路径名,通过搜索目录项结构来获取文件的外存i节点,fstat是根据参数fd给出的打开文件描述符,通过打开文件结构获取内存i节点。,stat结构定义如下:,struct stat dev_t st_dev; /* i节点所在设备号*/ ino_t st_ino; /* i节点号(ushort) */ ushort st_mode; /* 文件模式 */ short st_nlink; /* 文件链接数(short)*/ ushort st_uid; /* 文件主用户标
4、识符 */ ushort st_gid; /* 文件用户组标识符 */ dev_t st_rdev; /* 针对设备特别文件的设备号*/ off_t st_size; /* 文件的当前大小,特别文件为0*/ time_t st_atime; /* 文件存取时间(long)*/ time_t st_mtime; /* 文件修改时间(long)*/ time_t st_ctime; /* 文件的创建时间*/ 用fstat可以通过打开的无名管道文件描述符,访问“隐藏”的i节点,获取其状态信息,而stat调用则无能为力。,13.1.2搜索目录树,有时,用户需要在一棵目录树的范围内对文件和目录执行某些操
5、作。例行程序ftw能从指定的目录开始扫描目录树,并对找到的每一个目录项,调用用户定义的函数。ftw函数的格式如下: #include int ftw(path,func,depth) char *path; /* 指向目录路径名 */ int func(); /* 用户定义的处理函数 */ int depth; /* 即可同时打开的文件个数 */,用户定义的函数的格式:,int func(name,statptr,type) char *name; /* 存放ftw找到的目标名 */ struct stat *statptr; /* 指向stat结构指针, ftw在该结构中存放目标的状态信息
6、*/ int type; /* ftw指示目标的类型 和执行状态*/ /* body of function */ ,参数type的目标类型,参数type的目标类型在ftw.h中定义,类型取值为: FTW_F 目标是文件 FTW_D 目标是目录 FTW_DNR 目标是不能读的目录 FTW_NS 目标不能被stat成功地执行 如果目标是不能读的目录,那么此目录的所有下级也不能处理。对于stat不能成功执行的目标,那么传送给用户的stat结构中的内容是无效的。 在调用ftw中,如用户定义的函数返回非0值,ftw就中止扫描,并把此时用户函数的返回值作为ftw的返回值。如ftw在执行中出错,会使ftw
7、返回 -1,同时在errno中设置出错代码。,下面的程序使用ftw及dspstatus函数输出一棵目录树中所有带有路径的目录或文件名,对于文件还显示文件的类型、i节点号及文件链接数,对于特别文件还显示主次设备号。用户也可以在自定义函数中执行其他类型的操作。 主程序从参数中获得一个作为目录树扫描起点的路径名,该参数的缺省值为当前目录。,#incude #incude #incude int dspstatus(name,statptr,type) char *name; struct stat *statptr; int type; switch(type) case FTW_DNR: prin
8、tf(-30st: Dir cant readn,name); case FTW_NS:/* 执行失败 */ return(0); default:/* 正常状态 */ break; printf(-30st:,name);,switch(statprt-st_mode .,main(argc,argv) int argc; char *argv; int dspstatus(); if(argc2) ftw(.,dspstatus,2); else ftw(argv1,dspstatus,2); exit(0); ,13.2用文件的系统调用实现进程通信,13.2.1利用文件的系统调用实现信号
9、灯 可以用creat系统调用生成一个新文件,但如该文件原已存在,则调用进程要对其有写权限才能将该文件的长度截为零。利用这一特性可以将一个预先约定好的文件作为信号灯。 当若干进程要访问临界资源时,规定遵循以下协议:先试图创建同一个没有写权限的文件,这样只有一个进程才能获得成功,允许其进入临界区,其他进程的creat操作将失败(返回值为-1)。 进程从临界区退出时就删除这个作为信号灯的文件,这样正在等待中的进程的一个就能成功地创建它。,13.2.2利用管道实现进程间通信,用C语言实现含有管道符的UNIX复合命令 who | wc l,父进程,子进程1,子进程2,图象改换,图象改换,who_wc()
10、 /* who | wc */ int pfd2; if(pipe(pfd)=-1) syserr(pipe); switch(fork() case -1: syserr(fork); case 0:/* 子进程1 */ if(close(1)=-1) syserr(close); if(dup(pfd1)!=1) fprintf(stderr,ERROR: dupn); exit(1); ,if(close(pfd0)=-1|close(pfd1)=-1) syserr(close2); execlp(who,who,NULL); switch(fork() case 0:/*子进程2 *
11、/ if(close(0)=-1) syserr(close3); if(dup(pfd0)!=0) fprintf(stderr,ERROR: dup2n); if(close(pfd0)=-1|close(pfd1)=-1) syserr(close4); execlp(wc,wc,-l,NULL); syserr(execlp2); if(close(pfd0)=-1|close(pfd1)=-1) syserr(“close5”); /* 父进程 */ while(wait(NULL)!=-1) ; ,系统调用int dup(int fd)复制一个已存在的文件标识字,返回同一个文件或管
12、道的新的文件标识字,该文件标识字是当前可用的最小文件标识字。两个文件标识字共享一个文件读写指针。 利用dup系统调用使用编号最小的可用文件标识字的规则,可以使对任何文件或管道的读写与所希望的文件标识字联系起来,如使用fd为0的标准输入与管道读相联系,fd为1的标准输出与管道写相联系,这就是管道命令的运行机制。 如将文件的输入/输出与标准输入/输出相联系,这就是I/O重定向的运行机制。,为了将前一个命令的标准输出与一个管道的写端相连接,后一个命令的标准输入与同一个管道的读端相连接,可以先关闭文字标识字0且用dup复制读管道的文件标识字,由于0是最小的文件标识字,且又刚刚使它处于“空闲”可用的状态
13、,故dup将返回标识字为0的管道的读端。 同样也可使标识字1成为管道的写端。 接下来如果再关闭管道文件的原先两个文件标识字,就构成标识字为0和1的管道的读写端。 由于子进程和通过exec命令执行的进程继承了父进程的所有文件标识字和打开文件结构,故用这种方法可以建立管道命令符左右两边的命令输出和输入的联系。,父进程首先生成一个管道文件,再产生第一个子进程。 子进程继承了父进程的标准文件标识字和管道文件标识字,接着关闭标准输出,复制写管道文件标识字,其值为1。然后子进程关闭了从父进程继承来的管道文件的原两个标识字,使得该管道文件的写端仅与子进程的标准输出相连接。接下来子进程改换图像,执行UNIX命
14、令who。 在who命令的执行期间,继承了第一个子进程的全部文件标识字和打开文件及管道文件状态,who命令的标准输出就源源不断地写入管道文件中。 为了建立第二个命令的管道读端,原父进程又产生了第二个子进程,类似地,将标准输入与管道文件的读端相连接,并执行UNIX命令wc,使该命令从管道的读端接收数据,从而建立了who和wc两个命令的读写联系。,13.4远程进程间通信,利用管道通信的进程必须属于同一进程族,消息通信、共享内存和信号灯只能用于同一台机器上的进程间通信,它们都不能用于分布在网络上的进程间通信。UNIX提供了Socket(插座或称套接字)的通信机构,主要用于异地进程间的通信,也可用于本
15、地进程间通信。 Socket是在传送层上提供给应用程序的网络通信接口。Socket通过“域”来划分所支持的协议,不同的通信域间不能建立通信连接。目前最常用的是支持TCP/IP协议的INET通信域和支持UNIX系统中进程通信的UNIX通信域。,13.4.1 Socket通信概述,通信域,通信域在sys/socket.h中定义: #define AF_UNIX 1 /* 用于UNIX内部 */ #define AF_INET 2 /* 支持UDP,TCP等 */ 每种通信域又有几种类型方式,它们也在sys/socket.h中定义: #define SOCK_STREAM 1 /* 虚电路方式*/
16、#define SOCK_DGRAM 2 /* 数据报方式 */ Socket模仿UNIX文件操作来实现进程通信操作,将网络通信看成网络上两端进程间的I/O操作。因此定义了类似文件描述符的Socket描述符,以及与文件读写相似的数据接收和发送操作。,Socket通信模型,由于建立打开文件结构,取得文件描述符是建立进程与本地文件静态资源之间的联系,而建立网络的连接需要指定协议,经过多次的握手(请求、应答)过程,故网络上的进程通信要比文件I/O考虑更多的情况。 Socket通信采用了顾客服务员模型,建立连接时,顾客进程与服务员进程所做的工作是不对称的。图13-2给出了Socket实现面向连接的进程
17、通信基本过程。,1建立插座,sockfd=socket(domain,type,protocal) int sockfd; 插座标识字 int domain,type,protocal; 通信域,通信方式、 协议 domain指定通信域,取值主要有AF_UNIX,AF_INET。 type指明通信方式,主要取值有SOCK_STREAM,SOCK_DGRAM。 protocal规定了通信协议,一般可选择0,表示将遵循通信域和插座类型使用的通信协议。当使用INET通信域的数据报方式时,protocal取值为IPPROTO_UDP。 当调用成功,socket返回一个类似于文件描述符的socket描述
18、符号,可为后继的其他的调用使用。,2联系socket地址名和socket描述符,int bind(sockfd,myaddr,addrlen) int sockfd,addrlen /* socket描述字,myaddr的地址长度 */ const struct sockaddr *myaddr; /* 名字(UNIX)或地址(互连网域)*/ 对INET通信域,参数myaddr是一个在netinet/in.h中定义的结构sockaddr_in: struct sockaddr_in short sin_family;/* 通信域值为AF_INET */ u_short sin_port; /*
19、 16位端口号 */ struck in_addr sin_addr;/* 32位IP地址 */ ;,3启动一个连接请求(client),int connect (sockfd, servaddr, addrlen) int sockfd,addrlen; struct sockaddr *servaddr; servaddr是通信信道另一个端点上的进程地址,addrlen是地址长度。 sockaddr用于socket与TCP/IP协议模块交换地址参数,该结构对大多数基于socket的应用程序是透明的,其定义如下: struct sockaddr u_short sa_family; /* 地
20、址类型 */ char sa_data14 /* 14字节地址 */ ;,4接受连接请求(server),int listen(sockfd,queuelen) listen调用为指定的插座规定能接受的连接请求的最大数目,也即队列的最大长度。 newsockfd=accept(sockfd,clieaddr,addrlen) struct sockaddr *clieaddr; 取队列中第一个连接请求,clieaddr和addrlen是accept返回时由系统填入的对方插座地址和地址结构的长度。 返回值newsockfd是一个与sockfd不同的新的插座描述符,这样服务器一方面可以在与sock
21、fd相关的地址上继续监听对方新的服务请求,另一方面可以在与newsockfd相关的另一个信道上与请求连接的client进程进行联系通信。,5数据传送,count=send(sockfd,msg,len,flags) count=write(sockfd,msg,len) count=recv(sockfd,buf,len,flags) count=read(sockfd,buf,len) msg为发送数据的缓冲区, buf是接收数据的缓冲区, flags为0时,表示正常的数据发送或接收,flags等于MSG_OOB时表示发送或接收带外数据(紧急数据);flags等于MSG_PEEK时表示可以“
22、偷看”一个到来的报文,检查其内容,但不把它从队列中移去。当flags等于MSG_DONTROUTE时表示可以在没有网络路由选择的情况下发送数据。 返回值count是实际发送或接收的数据长度。,6关闭插座,可以像关闭打开的文件一样,利用close系统调用可关闭一插座,释放插座描述符。 int close(int sockfd) 插座关闭后,在连接状态下正在进行的信息发送还能继续进行。,13.4.3 socket通信程序设计,一个使用TCP/IP协议进行socket通信的例子。 服务器方使用getsockname获得一未用端口号,并将其打印出来,以供client程序使用该端口号作为命令参数,与服务
23、器建立socket连接。此端口号也可由用户直接定义。 服务器进程在调用accept获得顾客方发来的连接请求后,产生子进程,使子进程使用一个新的插座描述符与顾客程序通信,而父进程继续在原地址上监听。,通信双方程序的头文件,/* sockcom.h */ #include #include #include #include #include ,/* server */ #include “sockcom.h” main() int sockfd,newsockfd,length,count; struct sockaddr_in server; char buf1024; sockfd=sock
24、et(AF_INET,SOCK_STREAM,0); 生成插座 server.sin_family=AF_INET; /* 构成socked名(地址) 和建立联系 */ server.sin_addr.s_addr=INADDR_ANY; server.sin_port=0; /* 选择一个已释放的端口号 */ if(bind(sockfd,(struct sockaddr *) /* 获取并打印端口号 */,/* 获取并打印端口号 */ length=sizeof(server); if(getsockname(sockfd,(strcut sockaddr *) ,while(1) new
25、sockfd=accept(sockfd,(struct sockaddr *)0 ,(int *)0); if(!fork() 子进程 close(sockfd); bzero(buf,sizeof(buf); 调用库函数,清缓冲区 if(count=recv(newsockfd,buf,sizeof(buf),0)0) syserr(“Reading stream message”); printf(“message received: %sn”,buf); exit(0); close(newsockfd); ,调用形式:命令,主机名,端口号,/* client */ #include
26、“sockcom.h” main(argc,argv) int argc; char *argv; int sockfd; struct sockaddr_in server; struct hostent *hp,*gethostbyname(); char msg1024;,sockfd=socket(AF_INET,SOCK_STREAM,0); /* 与由命令行参数指定的主机建立连接 */ hp=gethostbyname(argv1)=NULL) server.sin_family = AF_INET; bcopy(char *)hp-h_addr,(char *) ,while(1
27、) printf(“Enter send message: ”); scanf(“%s”,msg); if(!strlen(msg) break; if(send(sockfd,msg,strlen(msg),0)0) syserr(“sendint message”); bzero(msg,sizeof(msg); printf(“EOF.disconnectn”); close(sockfd); exit(0); ,在顾客方程序中调用了gethostbyname,通过查找/etc/hosts文件,将服务器主机的IP地址填入hostent结构中,并返回指向该结构的指针。hostnet在net
28、db.h中定义: struct hostent char *h_name; /* 主机名 */ char *h_aliases; /* 别名表 */ int h_addrtype; /* 主机地址类型 */ int h_length; /* 地址长度 */ char *h_addr_list; /* 域名服务器地址表,以NULL终止 */ ; bcopy()是字节拷贝函数。,socket进阶,1套接字(插座)的功能 套接字实质上提供了进程通信的端点。进程通信之前,双方首先必须各自创建一个端点,否则是没有办法建立联系并相互通信的。正如打电话之前,双方必须各自拥有一台电话机一样。 有人说:“在UN
29、IX系统中,任何东西都是一个文件。”这句话描述了这样一个事实:在UNIX系统中,任何对I/O的操作,都是通过读或写一个文件描述符来实现的。所以,如果你想通过Internet和另外一个程序通信的话,你将会是通过一个文件的描述符来实现的。 可以用write( )和read( )对套接字描述符进行操作的,但是,通过使用send( )和recv( )函数,你可以对网络数据的传输进行更好的控制。,2Socket是怎样在网络上传输数据的,OSI模型共分为七层。从下到上依次为:物理层、数据链路层、网络层、运输层、会话层、表示层和应用层。这个模型是最一般的模型,但是在UNIX中,真正用到的模型层次是下面这样子
30、的: 应用层(Telnet、FTP等等) 主机间对话层,即传输层(TCP和UDP) 网络层(IP和路由) 网络底层(相当于OSI模型中网络、数据链路和物理层) 对流式套接字我们所需要做的是调用send( )函数来发送数据。UNIX系统内核中已经建立了Transport Layer和Internet Layer。硬件负责NetworkAccess Layer。,3基本转换函数,(1)网络字节顺序 因为每一个机器内部对变量的字节存储顺序不同(有的系统是高位在前,低位在后,而有的系统是低位在前,高位在后),而网络传输的数据顺序是一定要统一的。所以对与内部字节表示顺序和网络字节顺序不同的机器,一定要对
31、数据进行转换(比如IP地址的表示,端口号表示)。 内部字节顺序和网络字节顺序相同的机器也要调用转换函数,但是真正转换还是不转换是由系统函数自己来决定的。,(2) 有关的转换函数,通常使用的有两种数据类型:短型(两个字节)和长型(四个字节)。下面介绍的这些转换函数对于这两种的无符号整型变量都可以进行正确的转换。 如果你想将一个短型数据从主机字节顺序转换到网络字节顺序,有这样一个函数:它以“h”开头,代表“主机”;紧跟着它的是“to”,代表“转换到”;然后是“n”,代表“网络”;最后是“s”,代表“短型数据”。h-to-n-s,就是htons( )函数。,套接字字节转换函数列表:,htons( )
32、主机字节顺序转换为网络字节顺序(对无符号短型进行操作) htonl( )主机字节顺序转换为网络字节顺序(对无符号长型进行操作) ntohs( )网络字节顺序转换为主机字节顺序(对无符号短型进行操作) ntohl( )网络字节顺序转换为主机字节顺序(对无符号长型进行操作),在struct socketaddr_in 中的sin_addr和sin_port的字节顺序都是网络字节顺序,而sin_family却不是网络字节顺序。这是因为sin_addr和sin_port是从IP和UDP协议层取出数据的,而在IP和UDP协议层,是直接和网络相关的,所以它们必须使用网络字节顺序。 sin_family域只
33、是内核用来判断struct socketaddr_in存储的是什么类型的数据,并且,sin_family永远也不会被发送到网络上,所以可以使用主机字节顺序来存储。,(3) IP地址转换,UNIX系统提供很多用于转换IP地址的函数。假设有一个struct sockaddr_in ina,并且IP是2,如果想把它存储到ina中,可以使用函数inet_addr( ),它能够把一个用数字和点表示的IP地址的字符串转换成一个无符号长整型。使用方式为: ina.sin_addr.s_addr=inet_addr(2); inet_addr( )返回的地址已经
34、是网络字节顺序了,没有必要再去调用htonl( )函数。 必须注意,如果inet_addr( )函数执行错误,它将会返回-1,二进制的无符号整数值-1,相当于55,一个广播用的IP地址。,如果有一个struct in_addr,并且想把它代表的地址打印出来(按照数字.数字.数字.数字的格式),可以使用函数inet_ntoa( ),例如: printf(%s,inet_ntoa(ina.sin_addr); 这段代码将会把struct in_addr里面存储的网络地址以数字.数字.数字.数字的格式显示出来。,inet_ntoa( )返回一个字符指针,它指向一个定义在函数inet_ntoa( )中的static类型字符串。所以每次调用inet_ntoa( ),都会改变最后一次调用inet_ntoa( )函数时所得到的结果。例如: char *a1, *a2; a1=inet
温馨提示
- 1. 本站所有资源如无特殊说明,都需要本地电脑安装OFFICE2007和PDF阅读器。图纸软件为CAD,CAXA,PROE,UG,SolidWorks等.压缩文件请下载最新的WinRAR软件解压。
- 2. 本站的文档不包含任何第三方提供的附件图纸等,如果需要附件,请联系上传者。文件的所有权益归上传用户所有。
- 3. 本站RAR压缩包中若带图纸,网页内容里面会有图纸预览,若没有图纸预览就没有图纸。
- 4. 未经权益所有人同意不得将文件中的内容挪作商业或盈利用途。
- 5. 人人文库网仅提供信息存储空间,仅对用户上传内容的表现方式做保护处理,对用户上传分享的文档内容本身不做任何修改或编辑,并不能对任何下载内容负责。
- 6. 下载文件中如有侵权或不适当内容,请与我们联系,我们立即纠正。
- 7. 本站不保证下载资源的准确性、安全性和完整性, 同时也不承担用户因使用这些下载资源对自己和他人造成任何形式的伤害或损失。
最新文档
- 同济大学浙江学院《口腔颌面外科学实验一》2023-2024学年第一学期期末试卷
- 广东省阳江市高中名校2024-2025学年高中高三第二次教学质量检测试题英语试题试卷含解析
- 泉州华光职业学院《工程光学及实验》2023-2024学年第一学期期末试卷
- 晋中职业技术学院《高级汉语写作》2023-2024学年第二学期期末试卷
- 2025年校外托管合同模板
- 湖南省长沙市2025年初三5月中考模拟考试(一)化学试题含解析
- 甘肃省兰州市七里河区重点名校2024-2025学年初三教学质量检测试题(一)英语试题理试题含答案
- 湖南省祁东育英实验学校2025届高三5月质检物理试题含解析
- 内蒙古赤峰市2025届高三(实验班)第三次质检物理试题含解析
- 聘请律师合同尽职调查(2025年版)
- 2024年晋城市城区城市建设投资经营有限公司招聘考试真题
- 社工证笔试题库及答案
- 高考写作专项突破之核心概念阐释要诀 课件
- 2025年全国质量月活动总结参考(2篇)
- 口腔四手操作培训
- 2025年月度工作日历含农历节假日电子表格版
- 第37章 真菌学概论课件
- 总裁助理岗位职责
- 2024年封顶仪式发言稿模版(3篇)
- 癌症治疗协议书范例
- 《中华人民共和国机动车驾驶人科目一考试题库》
评论
0/150
提交评论