资源预览内容
第1页 / 共168页
第2页 / 共168页
第3页 / 共168页
第4页 / 共168页
第5页 / 共168页
第6页 / 共168页
第7页 / 共168页
第8页 / 共168页
第9页 / 共168页
第10页 / 共168页
亲,该文档总共168页,到这儿已超出免费预览范围,如果喜欢就下载吧!
资源描述
授课教师 李平勇成都大学信息科学与技术学院第1章 网络编程基础网络操作系统(Network Operating System,NOS) 网络操作系统是使网络上的计算机能方便而有效地共享网络资源,为网络用户提供所需的各种服务软件和有关规程的集合。它除了具有一般桌面操作系统的全部功能外,还应该满足用户使用网络的需要,尤其要提供数据在网上的安全传输,管理网络中的共享资源,实现用户通信以及方便用户使用网络。网络操作系统是作为网络用户与网络系统之间的接口。网络操作系统的任务有两项:(1)常规任务,主要负责文件管理、存储管理、进程管理、任务管理、I/O管理、CPU调度等;(2)网络任务,主要功能为资源共享(文件、设备、数据)、安全管理、远程过程调用、网络I/O等。1.1 网络操作系统网络操作系统网络操作系统网络操作系统目前主流的四种主要网络操作系统是:nUNIX或Linux。其中,UNIX操作系统主要有SUN公司的Solaris,IBM公司的AIX等。Linux操作系统主要有Redhat,红旗Linux等。nWindows操作系统:Microsoft公司的Windows系列,如Microsoft的NT或Windows2003AdvancedServer等。nNovell公司的Novell网(NetWare)nSantaCruzOperation公司(SCO)的UnixWareUNIXnUNIX是一个多用户、多任务的分时操作系统,在计算机网络尤其是Internet的发展中发挥了极其重要的作用。在Internet中提供服务的各类节点计算机中,90%以上都使用UNIX或类UNIX操作系统。UNIX系统结构图UNIXnUNIX系统主要由以下4个部分组成:n内核。是组成操作系统的核心,它控制任务的调度运行,管理计算机存储器,维护文件系统,并在用户中分配计算机资源。它对用户是透明的。n外壳(Shell)。Shell是一个程序(类似于DOS中的COMMAND.COM),它解释用户所提交的命令并把该命令提交给内核执行,执行结果再返回给用户。Shell也是一种程序设计语言,用户可以使用Shell命令来设计程序(类似于DOS中的batch命令)。n文件系统。文件系统是指在用户终端上可为用户所用的全部文件的集合,它使信息的存储和检索更为容易。n命令。命令是一组实用程序的名称。UNIX系统提供的命令包括:文本编辑、文件管理、软件开发工具、系统配置、通信等。UNIXnUNIX中包含3个主要的网络包:nTCP/IP 包括TCP、UDP、IP、DNS等标准协议。 n基本网络实用程序(BNU) 提供UUCP、远程登录、远程执行、发送邮件、连接远地终端、串行通信等功能。n网络文件系统(Network File System,NFS)实现不同系统间文件和目录的透明访问,这种文件共享方式独立于计算机、操作系统和网络体系结构。LinuxnUNIX系统是一个非常成熟的网络操作系统,但对计算机硬件的要求比较高,对于一般的个人用户来说,想要在PC机上运行UNIX是比较困难的。nLinux是按照UNIX风格设计的操作系统,所以在源代码级上兼容绝大部分的UNIX标准。相当多的网络安全人员在自己的机器上运行的正是Linux。LinuxnLinux是一个充满生机的操作系统,具有巨大的用户群和广泛的应用领域,在软件业中有着重要地位,是惟一能与UNIX和Windows较量和抗衡的操作系统。从技术上讲,Linux有如下特点:(1)继承了UNIX的优点,又有了许多更好的改进,其开放、协的开发模式是集体智慧的结晶,能紧跟技术发展潮流,具有极强的生命力;(2)是通用的操作系统,可用于各种PC机和工作站;(3)内置通信联网功能,可让异种机联网;(4)具有开放的源代码,有利于发展各种特色的操作系统;(5)符合POSIX标准,各种UNIX应用可方便地移植到Linux下;(6)提供庞大的管理功能和远程管理功能;(7)支持大量外部设备;(8)支持32种文件系统;(9)提供GUI;(10)支持并行处理和实时处理,能充分发挥硬件性能;(11)在Linux平台上软件开发成本低。WindowsnMicrosoft(微软)公司的Windows系统不仅在个人操作系统中占有绝对优势,它在网络操作系统中也具有非常强劲的竞争力。Windows操作系统配置在整个局域网配置中是最常见的,但由于它对服务器的硬件要求较高,且稳定性能不是很高,所以Windows网络操作系统一般只是用在中低档服务器中,高端服务器通常采用UNIX、Linux或Solaris等非Windows操作系统。n在局域网中,Windows网络操作系统主要有:WindowsNT4.0Server、Windows2000Server/AdvanceServer,以及Windows2003Server/AdvanceServer等。工作站系统可以采用任何Windows或非Windows操作系统,包括个人操作系统,如Windows9x/ME/XP等。WindowsNT系统结构WindowsnWindowsNT作为一款成功的网络操作系统,其网络功能强大,采用OSI/RM网络体系结构,在多个层次之间提供接口规范,如NDIS、TDI、ProviderInterface、Socket、NETBIOS等,支持TCP/IP、IPX/SPX、NETBEUI等网络协议,如下页图中所示。WindowsNT网络体系结构Windows API函数函数nWIN32API是MicrosoftWindows32位平台的应用程序编程接口。WIN32API函数是构筑Windows应用框架的基石,在它的下面是Windows的操作系统核心,上面则是所有的Windows应用程序,如下图所示。Windows API函数函数nWindowsAPI函数根据功能不同,分为很多种类,具体如下:网络函数消息函数文件处理函数打印函数文本和字体函数菜单函数位图、图标和光栅运算函数绘图函数设备场景函数硬件与系统函数进程和线程函数控件与消息函数Windows API函数函数n目前常用的可视化编程环境(如VB、VC+、DELPHI等)中提供了大量的类库和各种控件,它们替代了API的神秘功能,事实上这些类库和控件都是构架在WIN32API函数基础之上的,是封装了的API函数的集合。它们把常用的API函数组合在一起成为一个控件或类库,并赋予其方便的使用方法,极大地加速了Windows应用程序开发的过程。n实际上如果要开发出更灵活、更实用、更具效率的应用程序,必然要涉及到直接使用API函数,虽然类库和控件使应用程序的开发简单的多,但它们只提供WINDOWS的一般功能,对于比较复杂和特殊的功能来说,使用类库和控件是非常难以实现的,这时就需要采用API函数来实现。1.2文件系统n在网络通信中,许多通信方式(如Socket通信、管道通信、邮路通信等)的基本原理都与UNIX/Linux的文件操作相同,所以,了解UNIX/Linux下的文件操作是学习网络通信的基础。LinuxLinux支持多种文件系统,如支持多种文件系统,如ext2ext2、ext3ext3、minixminix、iso9660iso9660、fatfat、vfatvfat、nfsnfs等。在这些具体文件系统的上层,等。在这些具体文件系统的上层,LinuxLinux提供了虚拟文件系统(提供了虚拟文件系统(VFSVFS)来隐)来隐藏各种文件系统的具体细节,为访问它藏各种文件系统的具体细节,为访问它们提供统一的接口。们提供统一的接口。1.2文件系统文件系统文件系统虚拟文件系统虚拟文件系统应用程序应用程序Ext2设备文件设备文件FATNFS文件编程文件编程LinuxLinux中对文件编程可以使用两类函数:中对文件编程可以使用两类函数: nLinuxLinux系统调用系统调用nC C语言库函数语言库函数 前者依赖于前者依赖于LinuxLinux系统,后者与操作系统是独立的,系统,后者与操作系统是独立的,在任何操作系统下,使用在任何操作系统下,使用C C语言库函数操作文件的语言库函数操作文件的方法都是相同的。方法都是相同的。系统调用系统调用-创建创建int creat(const char *filename, int creat(const char *filename, mode_t mode)mode_t mode)nFilename:Filename:要创建的文件名要创建的文件名( (包含路径,缺省为包含路径,缺省为当前路径当前路径) )nMode:Mode:创建模式创建模式系统调用系统调用-创建创建 Mode Mode 含义含义S_IRUSR S_IRUSR 可读可读S_IWUSR S_IWUSR 可写可写S_IXUSR S_IXUSR 可执行可执行S_IRWXU S_IRWXU 可读、写、执行可读、写、执行S_IRGRP S_IRGRP 组可以读组可以读S_IWGRP S_IWGRP 组可以写组可以写S_IXGRP S_IXGRP 组可以执行组可以执行系统调用系统调用-创建创建 Mode Mode 含义含义S_IRWXG S_IRWXG 组可以读写执行组可以读写执行S_IROTH S_IROTH 其他人可以读其他人可以读S_IWOTH S_IWOTH 其他人可以写其他人可以写S_IXOTH S_IXOTH 其他人可以执行其他人可以执行S_IRWXO S_IRWXO 其他人可以读、写、执行其他人可以读、写、执行S_ISUID S_ISUID 设置用户执行设置用户执行IDIDS_ISGID S_ISGID 设置组的执行设置组的执行IDID系统调用系统调用-创建创建除了可以通过上述宏进行标志以外,也可以用除了可以通过上述宏进行标志以外,也可以用数字来表示,数字来表示,LinuxLinux共用共用5 5个数字来表示文件的个数字来表示文件的各种权限:各种权限:第一位:用户第一位:用户IDID;第二位:组第二位:组IDID;第三位:用户自己的权限;第三位:用户自己的权限;第四位:组的权限;第四位:组的权限;第五位:其他人的权限。第五位:其他人的权限。系统调用系统调用-创建创建 每个数字可以取每个数字可以取1(1(执行权限执行权限) )、2(2(写权限写权限) )、4(4(读权限读权限) )、0(0(无任何权限无任何权限) )或者或者这些值的和这些值的和。例例: : 要创建一个用户可读、可写、可执行,要创建一个用户可读、可写、可执行,但是组没有权限,其他人可以读、可以执行但是组没有权限,其他人可以读、可以执行的文件,并设置用户的文件,并设置用户IDID位。位。 1070510705 :1(1(用户用户ID)ID)、0(0(不设置组不设置组ID)ID)、7(1+2+47(1+2+4,读、写、执行,读、写、执行) )、0(0(没有权限没有权限) )、5(1+45(1+4,读、执行,读、执行) )系统调用系统调用-创建创建实实 例例文件描述文件描述在在LinuxLinux系统中,所有打开的文件都对应系统中,所有打开的文件都对应一个一个文件描述符文件描述符。文件描述符是一个。文件描述符是一个非负非负整数整数。当打开一个文件时,内核向进程返。当打开一个文件时,内核向进程返回一个文件描述符。回一个文件描述符。文件描述符的范围是文件描述符的范围是0 - OPEN_MAX 0 - OPEN_MAX 。早。早期的期的UNIXUNIX版本版本OPEN_MAX =19OPEN_MAX =19,即允许每个,即允许每个进程同时打开进程同时打开2020个文件,现在很多系统则个文件,现在很多系统则将其增加至将其增加至10241024。系统调用系统调用-打开打开nint open(const char *pathname, int flags)int open(const char *pathname, int flags)nint open(const char *pathname, int flags, int open(const char *pathname, int flags, mode_t mode)mode_t mode)PathnamePathname:要打开的文件名:要打开的文件名( (包含路径,缺省为当前包含路径,缺省为当前路径路径) )FlagsFlags:打开标志:打开标志系统调用系统调用-打开打开Flags Flags 含义含义O_RDONLY O_RDONLY 只读方式打开只读方式打开O_WRONLY O_WRONLY 只写方式打开只写方式打开O_RDWR O_RDWR 读写方式打开读写方式打开O_APPEND O_APPEND 追加方式打开追加方式打开O_CREAT O_CREAT 创建一个文件创建一个文件系统调用系统调用-打开打开 Flags Flags 含义含义O_NOBLOCK O_NOBLOCK 非阻塞方式打开非阻塞方式打开O_TRUNC O_TRUNC 如果文件已经存在,如果文件已经存在, 则删除文件的内容则删除文件的内容O_EXEC O_EXEC 如果使用了如果使用了O_CREAT,O_CREAT, 且文件已存在,则报错且文件已存在,则报错系统调用系统调用-打开打开nO_RDONLYO_RDONLY、O_WRONLYO_WRONLY、O_RDWRO_RDWR三个标志不能同时三个标志不能同时使用。使用。n如果使用了如果使用了O_CREATEO_CREATE标志,则使用的函数是标志,则使用的函数是int int open(const char *pathname,int flags,mode_t open(const char *pathname,int flags,mode_t mode); mode); 这时还要指定这时还要指定modemode来表示文件的访问权限。来表示文件的访问权限。系统调用系统调用-打开打开实实 例例系统调用系统调用-关闭关闭 当我们操作完文件以后当我们操作完文件以后, ,需要关闭需要关闭文件:文件:int close(int fd)int close(int fd)Fd:Fd:文件描述符,来源?文件描述符,来源? 系统调用系统调用-读读int read(int fd, const void *buf, size_t length)int read(int fd, const void *buf, size_t length)功能:功能: 从文件描述符从文件描述符fdfd所指定的文件所指定的文件中中读取读取lengthlength个字节个字节到到bufbuf所指向的缓冲区所指向的缓冲区中,中,返回值为实际读取的字节数。返回值为实际读取的字节数。系统调用系统调用-写写int write(int fd, const void *buf, size_t length)int write(int fd, const void *buf, size_t length)功能:功能: 把把lengthlength个字节个字节从从bufbuf指向的缓冲区指向的缓冲区中写中写到文件描述符到文件描述符fdfd所指向的文件所指向的文件中,返回中,返回值为实际写入的字节数。值为实际写入的字节数。系统调用系统调用-定位定位int lseek(int fd, offset_t offset, int whence)int lseek(int fd, offset_t offset, int whence)功能:将文件读写指针相对功能:将文件读写指针相对whencewhence移动移动offsetoffset个字节个字节。操作成功时,返回文件。操作成功时,返回文件指针相对于文件头的位置。指针相对于文件头的位置。系统调用系统调用-定位定位whencewhence可使用下述值:可使用下述值:SEEK_SETSEEK_SET:相对文件开头:相对文件开头SEEK_CURSEEK_CUR:相对文件读写指针的当前位置:相对文件读写指针的当前位置SEEK_ENDSEEK_END:相对文件末尾:相对文件末尾 offsetoffset可取可取负值负值,例如下述调用可将文件指针相,例如下述调用可将文件指针相对当前位置对当前位置向前向前移动移动5 5个字节:个字节:lseek(fd, -5, SEEK_CUR)lseek(fd, -5, SEEK_CUR)系统调用系统调用-定位定位 如何利用如何利用lseeklseek来计算文件长度?来计算文件长度? 系统调用系统调用-定位定位由于由于lseeklseek函数的返回值为文件指针相函数的返回值为文件指针相对于文件头的位置,因此下列调用的对于文件头的位置,因此下列调用的返回值就是文件的长度:返回值就是文件的长度:lseek(fd, 0, SEEK_END)lseek(fd, 0, SEEK_END)系统调用系统调用-访问判断访问判断 有时侯我们要判断文件是否可以进行某种操作有时侯我们要判断文件是否可以进行某种操作( (读读, ,写等写等) ),这个时候我们可以使用,这个时候我们可以使用accessaccess函数。函数。int access(const char*pathname,int mode)int access(const char*pathname,int mode)PathnamePathname:文件名称文件名称ModeMode:要判断的属性。可以取以下值或者是他们的组合要判断的属性。可以取以下值或者是他们的组合. . R_OKR_OK文件可读文件可读,W_OK,W_OK文件可写文件可写,X_OK,X_OK文件可执行文件可执行,F_OK,F_OK文文件存在。件存在。返回值:返回值:当我们测试成功时当我们测试成功时, ,函数返回函数返回0,0,否则如果一个条否则如果一个条件不符时件不符时, ,返回返回-1-1。系统调用系统调用-访问判断访问判断例:例:#include#includeint main()int main() if (access(“/etc/passwd”,R_OK) = =0) if (access(“/etc/passwd”,R_OK) = =0) printf(“/etc/passwd can be printf(“/etc/passwd can be read!n”);read!n”); 系统调用系统调用-属性属性文件具有各种属性文件具有各种属性, ,除了常见的访问权除了常见的访问权限以外限以外, ,文件还有创建时间文件还有创建时间, ,大小等属大小等属性,文件属性使用性,文件属性使用 Struct StatStruct Stat 结构结构描述。描述。系统调用系统调用-属性属性struct stat struct stat dev_t st_dev; /* dev_t st_dev; /* 设备设备 */ */ ino_t st_ino; /* ino_t st_ino; /* 节点节点 */ */ mode_t st_mode; /* mode_t st_mode; /* 模式模式 */ */ nlink_t st_nlink; /* nlink_t st_nlink; /* 硬连接硬连接 */ */ uid_t st_uid; /* uid_t st_uid; /* 用户用户ID */ ID */ gid_t st_gid; /* gid_t st_gid; /* 组组ID */ ID */ dev_t st_rdev; /* dev_t st_rdev; /* 设备类型设备类型 */ */ off_t st_off; /* off_t st_off; /* 文件字节数文件字节数 */ */ unsigned long st_blksize; /* unsigned long st_blksize; /* 块大小块大小 */ */ unsigned long st_blocks; /* unsigned long st_blocks; /* 块数块数 */ */ time_t st_atime; /* time_t st_atime; /* 最后一次访问时间最后一次访问时间 */ */ time_t st_mtime; /* time_t st_mtime; /* 最后一次修改时间最后一次修改时间 */ */ time_t st_ctime; /* time_t st_ctime; /* 最后一次改变时间最后一次改变时间( (指属性指属性) */ ) */ ; ; 系统调用系统调用-属性属性使用最多的属性是使用最多的属性是st_modest_mode,通过属性我们可以判断给定的文件是,通过属性我们可以判断给定的文件是一个普通文件还是一个目录一个普通文件还是一个目录, ,连接等等,可以结合下面几个宏来判连接等等,可以结合下面几个宏来判断。断。 S_ISLNK(st_mode):S_ISLNK(st_mode):是否是一个连接是否是一个连接S_ISREG(st_mode) S_ISREG(st_mode) :是否是一个常规文件:是否是一个常规文件S_ISDIR(st_mode) S_ISDIR(st_mode) :是否是一个目录:是否是一个目录S_ISCHR(st_mode) S_ISCHR(st_mode) :是否是一个字符设备:是否是一个字符设备. .S_ISBLK(st_mode) S_ISBLK(st_mode) :是否是一个块设备:是否是一个块设备S_ISFIFO(st_mode) S_ISFIFO(st_mode) :是否是一个:是否是一个FIFOFIFO文件文件S_ISSOCK(st_mode) S_ISSOCK(st_mode) :是否是一个:是否是一个SOCKETSOCKET文件文件系统调用系统调用-属性属性 如果我们要获取文件的属性如果我们要获取文件的属性, ,我们可以我们可以使用函数使用函数: :int stat(const char *file_name,struct stat *buf)int stat(const char *file_name,struct stat *buf)int fstat(int filedes,struct stat *buf)int fstat(int filedes,struct stat *buf)StatStat:判断没有打开的文件:判断没有打开的文件FstatFstat:判断打开的文件:判断打开的文件系统调用系统调用-文件文件综综 合合 实实 例例LinuxLinux目录文件操作目录文件操作路径获取路径获取在编写程序的时候,有时候需要得到当在编写程序的时候,有时候需要得到当前路径。前路径。C C库函数提供了库函数提供了getcwdgetcwd来解决这来解决这个问题。个问题。 char *getcwd(char *buffer,size_t size)char *getcwd(char *buffer,size_t size)我们提供一个我们提供一个sizesize大小的大小的buffer,getcwdbuffer,getcwd会把当前的路径名会把当前的路径名copy copy 到到bufferbuffer中中. .如如果果bufferbuffer太小太小, ,函数会返回函数会返回-1-1。 路径获取路径获取#include#includemain()main() char buf80;char buf80;getcwd(buf,sizeof(buf);getcwd(buf,sizeof(buf);printf(“current working directory : printf(“current working directory : %sn”,buf);%sn”,buf); 创建创建#include #include int mkdir(char * dir, int mode)int mkdir(char * dir, int mode)功能:创建一个新目录。功能:创建一个新目录。返回值:返回值:0 0表示成功,表示成功,-1-1表述出错。表述出错。打开打开#include#include#include#includeDIR * opendir(const char * name)DIR * opendir(const char * name)功能:打开参数功能:打开参数namename指定的目录指定的目录返回值:成功则返回返回值:成功则返回DIR* DIR* 型态的目录流,型态的目录流,打开失败则返回打开失败则返回NULLNULL。读取读取#include #include #include #include struct dirent * readdir(DIR * dir)struct dirent * readdir(DIR * dir)功能:功能: 返回参数返回参数dirdir目录流的下个目录进入点目录流的下个目录进入点读取读取struct direntstruct dirent ino_t ino_t d_ino; d_ino; ff_t ff_t d_off; d_off; signed signed short short int int d_reclen; d_reclen; unsigned unsigned char char d_type; d_type; har har d_name256; d_name256;d_ino d_ino 此目录进入点的此目录进入点的inodeinoded_offd_off 目录文件开头至此目录进入点的位移目录文件开头至此目录进入点的位移d_reclen _named_reclen _name的长度,不包含的长度,不包含NULLNULL字符字符d_type d_name d_type d_name 所指的文件类型所指的文件类型d_named_name 文件名文件名读取位置读取位置#include#includeoff_toff_ttelldir(DIRtelldir(DIR*dir)*dir)功能:功能: 返回返回dirdir目录流目前的读取位置。目录流目前的读取位置。读取位置读取位置#include#include#include#include#include#includemain()main() DIR DIR*dir; *dir; struct structdirentdirent*ptr;*ptr; int intoffset;offset; dir=opendir(/etc/rc.d); dir=opendir(/etc/rc.d); while(ptr = readdir(dir) != NULL) while(ptr = readdir(dir) != NULL) offset offset= telldir(dir);= telldir(dir);printf(d_name : %sprintf(d_name : %soffset : %d n,offset : %d n,ptr-d_name, offset);ptr-d_name, offset); closedir(dir);定位定位#include#includevoid seekdir(DIR * dir,off_t offset)void seekdir(DIR * dir,off_t offset)功能:功能: 设置参数设置参数dirdir目录流目前的读取位置,在调用目录流目前的读取位置,在调用readdirreaddir()()时便从此新位置开始读取。参数时便从此新位置开始读取。参数offset offset 代表距代表距离目录文件开头的偏移量。离目录文件开头的偏移量。定位定位#include#include#include#include#include#includemain()main() DIRDIR*dir;*dir;structstructdirentdirent*ptr;*ptr;int offset,int offset,offset_5,offset_5,i=0;i=0;dir=opendir(/etc/rc.d);dir=opendir(/etc/rc.d);while(ptr=readdir(dir)!=NULL)while(ptr=readdir(dir)!=NULL) offsetoffset= telldir(dir);= telldir(dir);if(+i = 5)if(+i = 5)offset_5=offset; offset_5=offset; printf(d_name : %sprintf(d_name : %soffset :%d n, ptr-d_name, offset);offset :%d n, ptr-d_name, offset); seekdir(dir.offset_5); seekdir(dir.offset_5); printf(Readdir again!n); printf(Readdir again!n); while(ptr = readdir(dir) != NULL) while(ptr = readdir(dir) != NULL) offset = telldir(dir);offset = telldir(dir);printf(d_name : %sprintf(d_name : %soffset : %dn,ptr-d_name, offset);offset : %dn,ptr-d_name, offset); closedir(dir); closedir(dir); 目录操作目录操作综综 合合 实实 例例文件加锁n对一个文件,一个时刻只能进行一个写操作,所以在写的同时,要对文件加锁,防止别的用户也进行写操作。文件加锁n一般对要写的文件进行锁定用flock函数#includeintflock(intfd,intoperation);其中,fd是一个已打开文件的文件描述符;operation表示封锁方式,可以取下列4个值之一:(1)LOCK_SH:共享封锁。多个进程可同时对同一个文件作共享锁定。(2)LOCK_EX:专有封锁。一个文件同时只有一个互斥锁定。(3)LOCK_UN:解除封锁。(4)LOCK_NB:无法建立锁定时,此操作可不被阻断,马上返回进程。n返回值返回值:返回0表示成功,若有错误则返回-1,错误代码存于errno。1.3I/O模型n在通信过程中,必须明确所采用的I/O方式。在Winsock等通信中,常用的通信方式分为阻塞方式和非阻塞方式。(1)阻塞调用方式是指调用结果返回之前,当前线程会被挂起,函数只有在得到结果之后才会返回。(2)非阻塞和阻塞的概念相对应,指在不能立刻得到结果之前,该函数不会阻塞当前线程,而会立刻返回。Q Q:为什么进程间需要通信?:为什么进程间需要通信?A A:1 1、数据传输、数据传输 一个进程需要将它的数据发送给另一个进一个进程需要将它的数据发送给另一个进程。程。2 2、资源共享、资源共享 多个进程之间共享同样的资源。多个进程之间共享同样的资源。1.4套接字与管道3 3、通知事件、通知事件 一个进程需要向另一个或一组进程发送消息,通知一个进程需要向另一个或一组进程发送消息,通知它们发生了某种事件。它们发生了某种事件。4 4、进程控制、进程控制 有些进程希望完全控制另一个进程的执行(如有些进程希望完全控制另一个进程的执行(如DebugDebug进程),此时控制进程希望能够拦截另一个进进程),此时控制进程希望能够拦截另一个进程的所有操作,并能够及时知道它的状态改变。程的所有操作,并能够及时知道它的状态改变。1.4套接字与管道现在现在linuxlinux使用的进程间通信方式包括:使用的进程间通信方式包括:1 1、套接字(套接字(socketsocket)2 2、管道(管道(pipepipe)和有名管道()和有名管道(FIFOFIFO)3 3、消息队列、消息队列4 4、共享内存、共享内存5 5、信号量、信号量6 6、信号(、信号(signalsignal)1.4套接字与管道套接字nSocketSocket是进程间通信(IPC)的方法。Socket接口是主要的TCP/IP网络的API之一,Socket接口定义了许多函数或例程。网络的Socket数据传输是一种特殊的I/O,Socket也是一种文件描述符。nTCP/IP的socket提供下列三种类型套接字。(1)流式套接字(SOCK_STREAM)提供了一个面向连接、可靠的数据传输服务,数据无差错、无重复地发送,且按发送顺序接收。内设流量控制,避免数据流超限;数据被看作是字节流,无长度限制。文件传送协议(FTP)即使用流式套接字。(2)数据报式套接字(SOCK_DGRAM)提供了一个无连接服务。数据包以独立包形式被发送,不提供无错保证,数据可能丢失或重复,并且接收顺序混乱。网络文件系统(NFS)使用数据报式套接字。(3)原始式套接字(SOCK_RAW)该接口允许对较低层协议,如IP、ICMP直接访问。常用于检验新的协议实现或访问现有服务中配置的新设备。管道通信管道通信Q:什么是管道?:什么是管道?A:管道是:管道是单向的、先进先出单向的、先进先出的字节流,它的字节流,它把一个进程的输出和另一个进程的输入连接把一个进程的输出和另一个进程的输入连接在一起。在一起。写进程写进程在管道的在管道的尾部尾部写入数据,写入数据,读读进程进程从管道的从管道的头部头部读出数据。读出数据。管道通信管道通信数据读出后将从管道中移走,其它读进数据读出后将从管道中移走,其它读进程都不能再读到这些数据。管道提供了简程都不能再读到这些数据。管道提供了简单的流控制机制,进程试图读空管道时,单的流控制机制,进程试图读空管道时,进程将阻塞。同样,管道已经满时,进程进程将阻塞。同样,管道已经满时,进程再试图向管道写入数据,进程将阻塞。再试图向管道写入数据,进程将阻塞。管道创建管道创建管道包括管道包括无名管道无名管道和和有名管道有名管道两种,前者用于父两种,前者用于父进程和子进程间的通信,后者用于运行于同一台机进程和子进程间的通信,后者用于运行于同一台机器上的任意两个进程间的通信。器上的任意两个进程间的通信。 无名管道由无名管道由pipepipe()函数创建:()函数创建: int pipe(int filedis2) int pipe(int filedis2);当一个管道建立时,它会创建两个文件描述符:当一个管道建立时,它会创建两个文件描述符:filedis0 filedis0 用于读管道,用于读管道, filedis1 filedis1 用于写管道。用于写管道。管道通信管道通信管道关闭管道关闭关闭管道只需将这两个文件描述符关闭管道只需将这两个文件描述符关闭即可,可以使用普通的关闭即可,可以使用普通的closeclose函数函数逐个关闭。逐个关闭。#include #include #include #include #include #include #include #include int main()int main() int pipe_fd2;int pipe_fd2;if(pipe(pipe_fd)0)if(pipe(pipe_fd)0) printf(pipe create errorn); printf(pipe create errorn); return -1; return -1; else else printf(pipe create successn);printf(pipe create successn);close(pipe_fd0);close(pipe_fd0);close(pipe_fd1);close(pipe_fd1); 管道通信管道通信管道读写管道读写管道用于不同进程间通信。通常先创建一个管道,再管道用于不同进程间通信。通常先创建一个管道,再通过通过forkfork函数创建一个子进程,该子进程会继承父进程函数创建一个子进程,该子进程会继承父进程所创建的管道。所创建的管道。注意事项注意事项必须在系统调用必须在系统调用fork( )fork( )前调用前调用pipe( )pipe( ),否则子进程将不会继承文件描述符。否则子进程将不会继承文件描述符。 管道实例见:管道实例见:pipe_rw.cpipe_rw.c实例实例pipe_rw.cpipe_rw.c命名管道(命名管道(FIFOFIFO) 命名管道和无名管道基本相同,但也命名管道和无名管道基本相同,但也有不同点:无名管道只能由父子进程使有不同点:无名管道只能由父子进程使用;但是通过命名管道,不相关的进程用;但是通过命名管道,不相关的进程也能交换数据。也能交换数据。创建创建#include #include #include #include int mkfifo(const char * pathname, mode_t mode)int mkfifo(const char * pathname, mode_t mode)nPathnamePathname:FIFOFIFO文件名文件名nModeMode:创建标志(见文件操作章节):创建标志(见文件操作章节)一旦创建了一个一旦创建了一个FIFOFIFO,就可用,就可用openopen打开它,一般打开它,一般的文件访问函数(的文件访问函数(closeclose、readread、writewrite等)都可等)都可用于用于FIFOFIFO。当打开当打开FIFOFIFO时,非阻塞标志(时,非阻塞标志(O_NONBLOCKO_NONBLOCK)将)将对以后的读写产生如下影响:对以后的读写产生如下影响:1 1、没有使用没有使用O_NONBLOCKO_NONBLOCK:访问要求无法满足时:访问要求无法满足时进程将阻塞。如试图读取空的进程将阻塞。如试图读取空的FIFOFIFO,将导致进程,将导致进程阻塞。阻塞。2 2、使用使用O_NONBLOCKO_NONBLOCK:访问要求无法满足时不阻:访问要求无法满足时不阻塞,立刻出错返回,塞,立刻出错返回,errnoerrno是是ENXIOENXIO。操作操作FIFOFIFO相关出错信息:相关出错信息:n EACCES ( EACCES (无存取权限无存取权限) )n EEXIST ( EEXIST (指定文件已存在指定文件已存在) )n ENAMETOOLONG ( ENAMETOOLONG (路径名太长路径名太长) )n ENOENT ( ENOENT (包含的目录不存在包含的目录不存在) )n ENOSPC ( ENOSPC (文件系统剩余空间不足文件系统剩余空间不足) )n ENOTDIR ( ENOTDIR (文件路径无效文件路径无效) )n EROFS ( EROFS (指定的文件存在于只读文件系统中指定的文件存在于只读文件系统中) )操作操作实例实例fifo_write.c fifo_write.c fifo_read.cfifo_read.cWindows管道n1.管道与邮路管道是进程用来通信的共享内存区域。管道是进程间数据交流的通道。邮路(mailslot)的功能与管道类似,但它使用数据包广播消息。因此,命名管道最适合用来两个进程间消息传递,邮路则更适合一个进程向多个进程广播消息。匿名管道只能单向传送数据,而命名管道可以双向传送。Windows管道n2.Windows中管道的使用管道通常涉及两个进程:客户进程和服务器进程。服务器进程负责创建管道,并且可以创建一个管道的多个实例;客户进程连接到管道。在Windows环境下,匿名管道的创建函数:BOOLCreatePipe()命名管道的创建函数:HANDLECreateNamePipe()以及其他管道函数。例1-3命名管道通信的实现(自学了解)1.5进程与线程理论:理论:n1.5.1 进程概念进程概念n1.5.2 进程编程进程编程实验:实验:n进程创建进程创建n进程等待进程等待n进程删除进程删除进程定义进程定义 进程:进程: 通常被定义为一个正在运行的程序的通常被定义为一个正在运行的程序的实例,是一个具有一定独立功能的程序在实例,是一个具有一定独立功能的程序在其自身的地址空间中的其自身的地址空间中的一次运行活动一次运行活动。进程进程进程由2部分组成:1.内核对象操作系统用来管理进程的内核对象。内核对象也就是系统用来存放有关进程的统计信息的地方。2.地址空间地址空间包含所有可执行模块或DLL模块的代码和数据,还包含动态内存分配的空间。比如:线程堆栈和堆分配空间进程进程u进程从来不执行任何东西,它只是线程的容器。u若要使进程完成某项操作,它必须拥有一个在它环境里运行的线程,此线程负责执行包含在进程的地址空间中的代码。u单个进程可能包含若干线程,这些线程都是“同时”执行进程地址空间中的代码。u每个进程至少拥有一个线程,来执行地址空间中的代码。当创建一个进程时,系统会自动创建这个进程的第一个线程,此线程称为主线程。此后,主线程再创建其它线程进程特点进程特点n动态性动态性n并发性并发性n独立性独立性n异步性异步性进程状态进程状态进程控制块PCB(ProcessControlBlock)n存放进程的管理和控制信息的数据结构称为进程控制块。它是进程管理和控制的最重要的数据结构,每一个进程均有一个PCB,在创建进程时,建立PCB,伴随进程运行的全过程,直到进程撤消而撤消。n在不同的操作系统中对进程的控制和管理机制不同,PCB中的信息多少也不一样,但PCB中基本信息是一样。进程进程IDID进程进程IDID(PID)PID):标识进程的唯一数字:标识进程的唯一数字父进程的父进程的IDID(PPID)PPID)启动进程的用户启动进程的用户IDID(UIDUID)进程互斥进程互斥进程互斥是指当有若干进程都要使用某进程互斥是指当有若干进程都要使用某一共享资源时,任何时刻最多允许一个一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必进程使用,其他要使用该资源的进程必须等待,直到占用该资源者释放了该资须等待,直到占用该资源者释放了该资源为止。源为止。临界资源临界资源操作系统中将一次只允许一个进程操作系统中将一次只允许一个进程访问的资源称为临界资源。访问的资源称为临界资源。临界区临界区进程中访问临界资源的进程中访问临界资源的那段程序代码那段程序代码称称为临界区。为实现对临界资源的互斥访为临界区。为实现对临界资源的互斥访问,应保证诸进程互斥地进入各自的临问,应保证诸进程互斥地进入各自的临界区。界区。进程同步进程同步一组一组并发进程按并发进程按一定的顺序一定的顺序执行的过执行的过程称为进程间的同步。具有同步关系程称为进程间的同步。具有同步关系的一组并发进程称为合作进程,合作的一组并发进程称为合作进程,合作进程间互相发送的信号称为消息或事进程间互相发送的信号称为消息或事件。件。进程调度进程调度概念:概念:按一定算法,从一组待运行的进程按一定算法,从一组待运行的进程中选出一个来占有中选出一个来占有CPU运行。运行。调度方式:调度方式:抢占式抢占式非抢占式非抢占式调度算法调度算法n先来先服务调度算法先来先服务调度算法n短进程优先调度算法短进程优先调度算法n高优先级优先调度算法高优先级优先调度算法n时间片轮转法时间片轮转法死锁死锁多个进程因竞争资源而形成一种僵局,多个进程因竞争资源而形成一种僵局,若无外力作用,这些进程都将永远不若无外力作用,这些进程都将永远不能再向前推进。能再向前推进。1.5.2Linux多进程编程的系统调用多进程编程的系统调用获取获取IDID#include #include #include #include npid_t getpid(void)pid_t getpid(void) 获取本进程获取本进程IDID。npid_t getppid(void)pid_t getppid(void) 获取父进程获取父进程IDID。获取获取ID# #includeinclude # #includeinclude # #includeinclude intint main main( (voidvoid) ) printfprintf( ( PID = %dnPID = %dn, , getpid getpid()() ););printfprintf( ( PPID = %dnPPID = %dn, , getppid getppid()() ););returnreturn 0 0; ; 进程创建进程创建#include#includepid_t fork(void)pid_t fork(void)功能:创建子进程功能:创建子进程forkfork的奇妙之处在于它被调用一次,却返回两次,它可能有三种不的奇妙之处在于它被调用一次,却返回两次,它可能有三种不同的返回值:同的返回值:1.1.在父进程中,在父进程中,forkfork返回新创建的子进程的返回新创建的子进程的PIDPID; 2.2.在子进程中,在子进程中,forkfork返回返回0 0; 3.3.如果调用失败,如果调用失败,forkfork返回返回-1-1给父进程,不生成子进程。给父进程,不生成子进程。进程创建进程创建#include#include#inlcude#inlcudemain()main() pid_t pid;pid_t pid;/*/*此时仅有一个进程此时仅有一个进程*/*/pid=fork();pid=fork(); /* /*此时已经有两个进程在同时运行此时已经有两个进程在同时运行*/*/if(pid0)if(pid0) printf(error in fork!); printf(error in fork!);else if(pid=0)else if(pid=0) printf(I am the child process, ID is %dn,getpid(); printf(I am the child process, ID is %dn,getpid();elseelse printf(I am the parent process,ID is %dn,getpid(); printf(I am the parent process,ID is %dn,getpid(); ?执行后的结果?执行后的结果 ?进程创建进程创建$./fork_test$./fork_testI am the parent process, my process ID is 1991I am the parent process, my process ID is 1991I am the child process, my process ID is 1992I am the child process, my process ID is 1992在在pid=fork()pid=fork()之前,只有一个进程在执行,但在这条语句执行之后,之前,只有一个进程在执行,但在这条语句执行之后,就变成两个进程在执行了,这两个进程的代码部分完全相同,就变成两个进程在执行了,这两个进程的代码部分完全相同,将要将要执行的下一条语句都是执行的下一条语句都是if(pid=0)if(pid=0)。 两个进程中,原先就存在的两个进程中,原先就存在的那个进程被称作那个进程被称作“父进程父进程”,新出现的那个进程被称作,新出现的那个进程被称作“子进程子进程”,”,父子进程的区别在于进程标识符(父子进程的区别在于进程标识符(PIDPID)不同。)不同。进程创建进程创建思考运行结果?思考运行结果?#include #include #include #include int main(void)int main(void) pid_t pid;pid_t pid;int count=0;int count=0;pid = fork();pid = fork();printf( This is first time, pid = %dn, pid );printf( This is first time, pid = %dn, pid );printf( This is second time, pid = %dn, pid );printf( This is second time, pid = %dn, pid );count+;count+;printf( count = %dn, count );printf( count = %dn, count );if ( pid0 )if ( pid0 ) printf( This is parent process,the child has the pid:%dn, pid ); printf( This is parent process,the child has the pid:%dn, pid );else if ( !pid )else if ( !pid )printf( This is the child process.n);printf( This is the child process.n);else else printf( fork failed.n );printf( fork failed.n );printf( This is third time, pid = %dn, pid );printf( This is third time, pid = %dn, pid );printf( This is fouth time, pid = %dn, pid );printf( This is fouth time, pid = %dn, pid );return 0return 0; 进程创建进程创建思考运行结果?思考运行结果?This is first time, pid = 0This is first time, pid = 0This is second time, pid = 0This is second time, pid = 0count = 1count = 1This is the child process.This is the child process.This is third time, pid = 0This is third time, pid = 0This is fouth time, pid = 0This is fouth time, pid = 0This is first time, pid = 3512This is first time, pid = 3512This is second time, pid = 3512This is second time, pid = 3512count = 1count = 1This is the parent process,the child has pid:3512This is the parent process,the child has pid:3512This is third time, pid = 3512This is third time, pid = 3512This is fouth time, pid = 3512This is fouth time, pid = 3512问题:问题:为何为何count+count+执行了两次,第执行了两次,第2 2次打印次打印countcount确为确为1 1?进程创建进程创建思考运行结果?思考运行结果?父进程的数据空间、堆栈空间都会给子父进程的数据空间、堆栈空间都会给子进程一个拷贝,而不是共享这些内存。进程一个拷贝,而不是共享这些内存。在子进程中对在子进程中对countcount进行自加进行自加1 1的操作,的操作,但是并没有影响到父进程中的但是并没有影响到父进程中的countcount值,值,父进程中的父进程中的countcount值仍然为值仍然为0 0。进程创建进程创建#include #include #include #include pid_t vfork(void)pid_t vfork(void)功能:创建子进程。功能:创建子进程。创建进程创建进程区别:区别:1.1.forkfork要拷贝父进程的数据段;而要拷贝父进程的数据段;而vforkvfork则则不需要完全拷贝父进程的数据段,子进程不需要完全拷贝父进程的数据段,子进程与父进程共享数据段。与父进程共享数据段。2.fork2.fork不对父子进程的执行次序进行任何限不对父子进程的执行次序进行任何限制;而在制;而在vforkvfork调用中,子进程先运行,调用中,子进程先运行,父进程挂起。父进程挂起。进程创建进程创建1.1.#include#include 2.2.#include#include 3.3.#include#include 4.4.5.5.main()main() 6.6.intintcountcount= =1; 1; 7.7.intintchild; child; 8.8.9.9.printf(Beforeprintf(Beforecreatecreateson,son,thethefathersfatherscountcountis:%dn,is:%dn,count); count); if(!(childif(!(child= =vforkvfork()()10.10. 11.11.printf(Thisprintf(Thisisisson,son,hishispidpidis:is:%d%dandandthethecountcountis:is:%dn,%dn,getpidgetpid(),(),+ +count); +count); 12.12.exit(1); exit(1); 13.13. elseelse 14.14.printf(Afterprintf(Afterson,son,ThisThisisisfather,father,hishispidpidis:is:%d%dandandthethecountcountis:is:%d,%d,andandthethechildchildis:is:%dn,%dn,getpidgetpid(),(),count,count,child); child); 15.15. 16.16. 执行程序执行程序 exec exec用用被执行的程序被执行的程序替换替换调用它的程序调用它的程序。区别:区别: fork fork创建一个新的进程,产生一个新的创建一个新的进程,产生一个新的PIDPID。execexec启动一个新程序,替换原有的进程,因此启动一个新程序,替换原有的进程,因此进程的进程的PIDPID不会改变,和调用不会改变,和调用execexec函数的进程函数的进程一样。一样。执行程序执行程序#include#includeint execl(const char * path,const int execl(const char * path,const char * arg,.)char * arg,.)功能:功能:运行参数运行参数pathpath所指定的可执行文件,接下所指定的可执行文件,接下来的参数代表执行该文件时传递过去的来的参数代表执行该文件时传递过去的argv0 argv0 、argv1argv1,最后一个参数必,最后一个参数必须用空指针须用空指针(NULL)(NULL)作结束。作结束。执行程序执行程序#include#includemain()main() execl(“/bin/ls”,”-al”,”/etc/passwd”,(char * )0);execl(“/bin/ls”,”-al”,”/etc/passwd”,(char * )0); 执行程序执行程序#include#includeint execlp(const char * file,const char int execlp(const char * file,const char * arg,)* arg,)功能:功能:从从PATH PATH 环境变量所指的目录中查找符合参数环境变量所指的目录中查找符合参数filefile的文件名,找到后便执行该文件,然后将的文件名,找到后便执行该文件,然后将第二个以后的参数当做该文件的第二个以后的参数当做该文件的argv0argv0、argv1argv1,最后一个参数必须用空指针,最后一个参数必须用空指针(NULL)(NULL)作结束。作结束。执行程序执行程序#include#includemain()main() execlp(”ls”,”-al”,”/etc/passwd”,(char *)0);execlp(”ls”,”-al”,”/etc/passwd”,(char *)0); 执行程序执行程序#include#include int execv (const char * path, char * int execv (const char * path, char * const argv )const argv )功能:功能: 执行参数执行参数pathpath所指定的文件,与所指定的文件,与execlexecl()()不同的地方在于不同的地方在于execve()execve()只需两个只需两个参数,第二个参数利用数组指针来传参数,第二个参数利用数组指针来传递给执行文件。递给执行文件。执行程序执行程序#include#includemain()main() char * argv =“ls”,”-al”,”/etc/passwd”,(char*) char * argv =“ls”,”-al”,”/etc/passwd”,(char*) ;execv(“/bin/ls”,argv);execv(“/bin/ls”,argv); 执行程序执行程序#include#includeintsystem(constchar*string)intsystem(constchar*string)功能:功能:调用调用fork()fork()产生子进程,由子进程来调用产生子进程,由子进程来调用命令解释器命令解释器/bin/sh-cstring/bin/sh-cstring来执行参数来执行参数stringstring字符串所代表的命令。字符串所代表的命令。执行程序执行程序includeincludemain()main() system(“ls -al /etc/passwd system(“ls -al /etc/passwd /etc/shadow”);/etc/shadow”); 等待等待#include#include#include#includepid_twait(int*status)pid_twait(int*status)功能:功能:进程一旦调用了进程一旦调用了waitwait,就立即阻塞自己,就立即阻塞自己,直到自己的直到自己的某个某个子进程退出,如果没有找子进程退出,如果没有找到这样一个子进程,到这样一个子进程,waitwait就会一直阻塞在就会一直阻塞在这里,直到有一个出现为止。这里,直到有一个出现为止。等待等待#include #include #include #include #include #include #include #include main()main() pid_t pc,pr;pid_t pc,pr;pc=fork();pc=fork();if(pc0) /* if(pc0) /* 如果出错如果出错 */ */printf(error ocurred!n);printf(error ocurred!n);else if(pc=0) /* else if(pc=0) /* 如果是子进程如果是子进程 */ */ printf(This is child process with pid of %dn,getpid();printf(This is child process with pid of %dn,getpid();sleep(10); /* sleep(10); /* 睡眠睡眠1010秒钟秒钟 */ */ else /* else /* 如果是父进程如果是父进程 */ */pr=wait(NULL); /* pr=wait(NULL); /* 在这里等待在这里等待 */ */printf(I catched a child process with pid of %dn),pr);printf(I catched a child process with pid of %dn),pr); exit(0);exit(0); 等待等待#include#include#include#includepid_t waitpid(pid_t pid,int *status,int options)pid_t waitpid(pid_t pid,int *status,int options)功能:功能:进程一旦调用了进程一旦调用了waitwait,就立即阻塞自己,直到自,就立即阻塞自己,直到自己的己的某个某个子进程退出,如果没有找到这样一个子子进程退出,如果没有找到这样一个子进程,进程,waitwait就会一直阻塞在这里,直到有一个出就会一直阻塞在这里,直到有一个出现为止。现为止。等待等待pidpid 但当但当pidpid取不同的值时,在这里有不同的意义:取不同的值时,在这里有不同的意义:1.1.pid0pid0时,只等待进程时,只等待进程IDID等于等于pidpid的子进程,不管其它已经有多少子的子进程,不管其它已经有多少子进程运行结束退出了,只要指定的子进程还没有结束,进程运行结束退出了,只要指定的子进程还没有结束,waitpidwaitpid就就会一直等下去。会一直等下去。 2.2.pidpid=-1=-1时,等待任何一个子进程退出,没有任何限制,此时时,等待任何一个子进程退出,没有任何限制,此时waitpidwaitpid和和waitwait的作用一模一样。的作用一模一样。 3.3.pidpid=0=0时,等待同一个进程组中的任何子进程。时,等待同一个进程组中的任何子进程。 4.4.pidpid-1-1时,等待一个指定进程组中的任何子进程,这个进程组的时,等待一个指定进程组中的任何子进程,这个进程组的IDID等于等于pidpid的绝对值。的绝对值。等待等待参数参数 option option 可以为可以为 0 0 或下面的或下面的 OR OR 组合组合: :WNOHANG WNOHANG : 如果没有任何已经结束的子进程则马上返如果没有任何已经结束的子进程则马上返回回, , 不予以等待。不予以等待。WUNTRACEDWUNTRACED: 如果子进程进入暂停执行情况则马上返回。如果子进程进入暂停执行情况则马上返回。发送信号发送信号#include#include#include#includeint kill(pid_t pid,int sig)int kill(pid_t pid,int sig)功能:功能:发送参数发送参数 sig sig 指定的信号给参数指定的信号给参数 pid pid 指定的进程指定的进程pid0 pid0 将信号传给进程识别码为将信号传给进程识别码为 pid pid 的进程的进程pid=0 pid=0 将信号传给和目前进程相同进程组的所有进程将信号传给和目前进程相同进程组的所有进程pid=-1 pid=-1 将信号广播传送给系统内所有的进程将信号广播传送给系统内所有的进程pid0 pid0:将信号sig发送到进程号为pid的进程npid=0:信号sig将发送给当前进程所属进程组里的所有进程npid=-1:信号sig将发送给除了进程1和自身以外的所有进程npid-1:信号sig将发送给属于进程组-pid的所有进程n如果参数sig为0,将不发送信号该调用执行成功时,返回值为0;错误时,返回-1,并设置相应的错误代码errno。信号的系统调用nraise系统调用intraise(intsig);raise调用给自己发关一个sig信号。因此,raise(sig)等价于kill(getpid(),sig);信号的系统调用npause和sleep系统调用#includeintpause(void);intsleep(unsignedintseconds);sleep让进程睡眠seconds秒,pause让进程永远睡眠。如果在睡眠过程中被信号打断,它们将返回-1。信号的系统调用nalarm和setitimer系统调用系统调用alarm的功能是设置一个定时器,当定时器计时到达时,将发出一个信号给进程。该调用的声明格式如下:unsignedintalarm(unsignedintseconds);n在seconds秒后对本进程发送一个SIGALRM信号。一般情况下返回0;如果已经有一个定时器被设置且还没有到时间,否返回上一个定时器剩余的时间。n注意,在使用时,alarm只设定为发送一次信号,如果要多次发送,就要多次使用alarm调用。n现在的系统中很多程序不再使用alarm调用,而是使用setitimer调用来设置定时器,用getitimer来得到定时器的状态,这两个调用的声明格式如下:intgetitimer(intwhich,structitimerval*value);intsetitimer(intwhich,conststructitimerval*value,structitimerval*ovalue);信号的系统调用n信号初始化操作函数参见教材P52页。1.7应用程序与设备驱动程序之间的接口1.7.1 VxD的概念的概念nWindows环境下驱动程序共有三类:一类是VxD(VirtualDeviceDriver,虚拟设备驱动程序),起源于Windows3.1,用于Windows95/98/Me操作系统中;一类是KMD(KernelModeDriver,内核模式驱动程序),用于WindowsNT下;还有一类就是WDM(Win32DriverMode,Win32驱动程序模型),是微软从Windows98开始,推出的一个新的驱动类型,它是一个跨平台的驱动程序模型,不仅如此WDM驱动程序还可以在不修改源代码的情况下经过重新编译后在非Intel平台上运行。n由于Windows对系统底层操作采取了屏蔽的策略,因而对用户而言,系统变得更为安全,但这却给众多的硬件或者系统软件开发人员带来了不小的困难,因为只要应用中涉及到底层的操作,开发人员就不得不深入到Windows的内核去编写属于系统级的虚拟设备驱动程序VxD。1.7.2应用程序与硬件之间的接口n应用程序和硬件设备之间的沟通是由驱动程序来完成的,设备驱动程序为应用程序屏蔽外设硬件的细节,这样对应用程序来说,外设只是一个设备文件,应用程序可以像操作普通文件一样来操作外设。n在Windows环境下应用程序通常使用Windows32API函数像访问文件那样来访问设备,通常用到的函数有CreatFile,ReadFile,WriteFile,CloseHandle和DeviceIoControl等等。应用程序与硬件之间的接口n对Win32应用程序来说,它必须首先调用CreateFile()函数得到一特定VxD的句柄。通常这函数是用于创建打开磁盘文件的,但如果程序在调用它时,在文件名前加上前缀.DeviceName,系统就会识别出此文件名对应于一个VxD名。(注意在C/C+中,字符串中的反斜杠字符必须加更多的反斜杠前缀,因此.就成了.DeviceName)DeviceName必须与设备驱动程序内定义的设备名称一致。应用程序与硬件之间的接口n函数CreateFile()返回一句柄,这里是一个VxD的句柄。应用程序可用这个句柄调用函数DeviceIoControl()来发消息给VxD。n函数DeviceIoControl()提供了通知VxD执行何种功能的参数,同时提供用于在应用程序和VxD间传送数据的输入输出缓冲区指针。n然后,通过调用函数ReadFile和WriteFile进行设备数据的读写。n当应用程序终止时,它要调用函数CloseHandle()释放这个VxD的句柄应用程序与硬件之间的接口n在Windows中,应用程序通过win32API函数DeviceIoControl来实现对设备的访问获取信息、发送命令、交换数据等。利用该接口函数向指定的设备驱动发送正确的控制码及数据,然后分析它的响应,就可以实现应用程序与VxD之间的通信。nDeviceIoControl的函数原型为:BOOLDeviceIoControl(HANDLEhDevice,/设备句柄DWORDdwIoControlCode,/控制码LPVOIDlpInBuffer,/输入数据缓冲区指针DWORDnInBufferSize,/输入数据缓冲区长度LPVOIDlpOutBuffer,/输出数据缓冲区指针DWORDnOutBufferSize,/输出数据缓冲区长度LPDWORDlpBytesReturned,/输出数据实际长度单元长度LPOVERLAPPEDlpOverlapped/重叠操作结构指针);应用程序与硬件之间的接口其中,n设备句柄用来标识你所访问的设备。n发送不同的控制码,可以调用设备驱动程序的不同类型的功能。在头文件winioctl.h中,预定义的标准设备控制码,都以IOCTL或FSCTL开头。例如,IOCTL_DISK_GET_DRIVE_GEOMETRY是对物理驱动器取结构参数(介质类型、柱面数、每柱面磁道数、每磁道扇区数等)的控制码,FSCTL_LOCK_VOLUME是对逻辑驱动器的卷加锁的控制码。n输入输出数据缓冲区是否需要,是何种结构,以及占多少字节空间,完全由不同设备的不同操作类型决定。在头文件winioctl.h中,已经为标准设备预定义了一些输入输出数据结构。如果重叠操作结构指针设置为NULL,DeviceIoControl将进行阻塞调用;否则,应在编程时按异步操作应用程序与硬件之间的接口n串口通信程序实例,参见教材P60。注:要在PC机上实现串口通信,需要使用教材P55表1-4所示的API函数。该程序利用WindowsAPI提供的通信函数编写,可移植性较高。读数据采用事件驱动方式,用一个线程处理相应事件,收到的字符用消息发往父窗口;由于软件中写串口的数据不多,所以采用直接写的方式,而没有使用线程。1.8Byteorder(字节顺序)nLittleEndiannthelow-orderbyteofthenumberisstoredinmemoryatthelowestaddress,andthehigh-orderbyteatthehighestaddress.(Thelittleendcomesfirst.)nIntelprocessors(thoseusedinPCs)nBigEndiannthehigh-orderbyteofthenumberisstoredinmemoryatthelowestaddress,andthelow-orderbyteatthehighestaddress.(Thebigendcomesfirst.)nMotorolaprocessors(thoseusedinMacs)n4bytesLongInteger0x99887766Byte3Byte2Byte1Byte099887766nBigEndianBaseAddress+0Byte399BaseAddress+1Byte288BaseAddress+2Byte177BaseAddress+3Byte066nLittleEndianBaseAddress+0Byte066BaseAddress+1Byte177BaseAddress+2Byte288BaseAddress+3Byte399Example一般的PC采用 Little Endian 网络通信采用 Big Endiann目前一般的PC机(Intel、AMD等x86CPU)采用LittleEndian字节顺序,而Internet(采用TCP/IP网络传输协议)通信使用的是BigEndian,因而存在一个不同字节顺序之间相互转换的问题。channel.sin_addr.s_addr=htonl(INADDR_ANY);channel.sin_port=htons(SERVER_PORT);UtilityroutinesnConvertbetweennetworkbyteorderandthelocalhostsbyteordernhtons(hosttonetworkshort)nntohs(networktohostshort)nhtonl(hosttonetworklong)nntohl(networktohostlong)如何判断本机的字节顺序?Thank You! 结束语结束语若有不当之处,请指正,谢谢!若有不当之处,请指正,谢谢!
收藏 下载该资源
网站客服QQ:2055934822
金锄头文库版权所有
经营许可证:蜀ICP备13022795号 | 川公网安备 51140202000112号