UNIX高级环境编程头文件apue.h配置详解
- 格式:docx
- 大小:15.51 KB
- 文档页数:2
UnixLinux环境C编程新⼿教程(5)RedHatEnterpriseLinux(RH。
Unix/Linux版本号众多,我们推荐Unix/Linux刚開始学习的⼈选⽤⼏款典型的Unix/Linux操作系统进⾏学习。
Red Hat Enterprise Linux是公司的。
⾯向商业市场。
包含。
红帽公司从Red Hat Enterprise Linux 5開始对企业版LINUX的每⼀个版本号提供10年的⽀持。
⽽Red Hat Enterprise Linux常简作RHEL。
1. 启动Vmware,假设没有安装的话。
请看前⾯VMware安装的视频2.选中⽂件菜单,新建⼀个虚拟机3.这个时候看到新建虚拟机的向导4.我们默认选择⾃⼰定义。
点击下⼀步5.接下来。
我们选择workstation9.0.点击下⼀步6.然后我们选择,我以后再安装操作系统。
点击下⼀步7.然后我们选择linux, 选中红帽64位企业版,也就是 redhat enterprise linux 6 64bit点击下⼀步8.接下来,名称。
位置我们都⽤默认的。
9.处理器配置成1⼀个CPU就可以,点击下⼀步10.然后我们将虚拟机内存设置为2G,点击下⼀步11.接下来,我们选择NAT,也就是⽹络地址翻译。
点击下⼀步12.接下来。
我们选择默认的LSI logic,点击下⼀步13.接下来我们创建⼀个新的磁盘,点击下⼀步14.我们选择SCSI,点击下⼀步15.我们指定硬盘⼤⼩20G,然后选择虚拟磁盘拆分成多个⽂件。
我们能够移植虚拟机。
假设为了追求⾼性能,能够选择马上分配全部磁盘空间16. 点击下⼀步。
我们选择位置来保存虚拟机磁盘17.点击浏览,选择⼀个位置存放虚拟机18.点击下⼀步,完毕虚拟机向导19.单击完毕20 选中虚拟机,右键我们选择设置21 。
我们看到这个界⾯22. 我们选中CD/DVD。
我们选择使⽤ISO镜像⽂件点击浏览button我们选中红帽企业版的镜像iso,23.点击button打开24.然后点击确定,关闭虚拟机设置然后打开虚拟机电源25.看到Vmware启动26.看到红帽安装镜像的启动画⾯27 ,我们选择这个选项,安装⼀个系统。
UNIX简介及其环境配置大型EDA软件大多是在UNIX操作系统上开发的,EDA工具除建立在Sun公司的Solaris 操作系统上之外,也有基于HP公司的HPUX,IBM公司的AIX等操作系统的,另外,随着Linux操作系统的不断成熟,EDA工具在其上已得到了广泛应用,预计将是今后的发展方向。
本章将对在EDA软件使用过程中会经常遇到的相关内容做一个简单的介绍,需要深入了解UNIX系统的请查阅相关的文献资料。
本章将简要介绍UNIX的常用命令、文本编辑器的使用、CShell编程以及终端环境配置、用户环境配置的部分内容。
§2-1 UNIX 常用的命令本节将介绍一些常用UNIX命令的基本用法,读者可以使用man命令获得各个命令的详细手册(manual)。
UNIX命令的一般格式为,command <options> <arguments>其中command为命令名称,options是以减号开始的命令选项,而arguments 则是命令的对象。
所有UNIX命令都是对字母大小写敏感的。
2.1.1 与用户登录相关的命令1. login重新登录本机。
2.rlogin登录到远程主机,后接要登录的主机名。
rlogin hostname <–l username>hostname是需要登录的主机名,username是要求登录的用户。
当登录到远程主机的用户名和当前的用户不同时,需要输入-l username选项。
3.passwdpasswd 命令用来更改用户的登录密码,用法为:passwd <username>缺省情况下修改的是当前用户的密码,在试图修改密码时,系统会让你输入原来的密码,确定无误后才将你的密码改掉。
4.su <username>登录为超级用户或者将当前工作用户转为username。
5.umask当最初登录到系统中时,umask 命令确定了你创建文件的缺省模式。
•文件操作•之前我们都学过如何用C标准I/O库读写文件,这些I/O操作是如何实现的?其实所有的I/O操作最终都要在内核中执行。
只不过我们用的C标准I/O库函数先通过系统调用把I/O操作从用户空间传给内核,然后再让内核去做I/O操作而已。
•现在看看C标准I/O库函数是如何用系统函数实现的。
一、fopen调用open打开指定的文件,返回一个文件描述符(也就是一个int型的编号),分配一个FILE结构体,这个FILE结构体中包含该文件的文件描述符、I/O缓冲区和当前读写位置等信息,最后返回这个FILE结构体的地址。
二、fgetc通过传入的FILE *参数找到该文件的描述符、I/O缓冲区和当前读写位置,判断能否从I/O缓冲区读到下一个字符,如果能读到就直接返回给字符,因为fgetc一次就是只读一个字符,如果读不到就再次调用read函数,read函数将文件描述符传进内核,让内核读取该文件的内容到缓冲区中需要注意的是:对于C标准I/O库函数来说,打开的文件用FILE *指针标识,而对于内核来说,打开的文件由文件描述符标识,文件描述符从open系统调用获得,在使用read,write,close系统调用时都需要传文件描述符三、fputc判断该文件的I/O缓冲区是否有空间再存放一个字符,如果有空间则直接保存在I/O缓冲区中并返回,如果I/O缓冲区已满就调用write,让内核把I/O缓冲区的内容写到文件。
四、fclose如果I/O缓冲区中还有数据没有写回文件,就调用write写回文件,然后调用close关闭文件,释放FILE结构体和I/O缓冲区。
•Open、read、write、close等系统函数称为无缓冲I/O函数,因为它们位于C标准库的I/O 缓冲区的底层,用户程序在读写文件时即可以去调用C标准I/O库函数,也可以直接调用底层的系统函数,标准I/O库函数是有缓冲区的,系统函数是无缓冲区的,究竟用哪一组函数更好一些呢?•1、对于普通文件的读和写,我们最好用C标准库函数,因为C标准I/O库函数已经为我们开辟好了缓冲区,省去了我们自己开辟,自己管理缓冲区的麻烦。
第1章U N I X基础知识1.1 引言所有操作系统都向它们运行的程序提供服务。
典型的服务有执行新程序、打开文件、读文件、分配存储区、获得当前时间等等,本书集中阐述了U N I X操作系统各种版本所提供的服务。
以严格的步进方式、不超前引用尚未说明过的术语的方式来说明U N I X几乎是不可能的(可能也会是令人厌烦的)。
本章从程序设计人员的角度快速浏览U N I X,并对书中引用的一些术语和概念进行简要的说明并给出实例。
在以后各章中,将对这些概念作更详细的说明。
本章也对不熟悉U N I X的程序设计人员简要介绍了U N I X提供的各种服务。
1.2 登录1.2.1 登录名登录U N I X系统时,先键入登录名,然后键入口令。
系统在其口令文件,通常是/ e t c/p a s s w d文件中查看登录名。
口令文件中的登录项由7个以冒号分隔的字段组成:登录名,加密口令,数字用户I D(224),数字组I D(20),注释字段,起始目录( /h o m e/s t e v e n s),以及s h e l l 程序( /b i n/k s h)。
很多比较新的系统已将加密口令移到另一个文件中。
第6章将说明这种文件以及存取它们的函数。
1.2.2 shell登录后,系统先显示一些典型的系统信息,然后就可以向s h e l l程序键入命令。
s h e l l是一个命令行解释器,它读取用户输入,然后执行命令,用户通常用终端,有时则通过文件(称为s h e l l脚本)向s h e l l进行输入。
常用的s h e l l有:• Bourne shell, /bin/sh• C shell, /bin/csh• KornShell, /bin/ksh系统从口令文件中登录项的最后一个字段中了解到应该执行哪一个s h e l l。
自V 7以来,Bourne shell得到了广泛应用,几乎每一个现有的U N I X系统都提供Bourne shell。
最近在读 Richard Stevens 的大作《UNIX环境高级编程》,相信很多初读此书的人都会与我一样遇到这个问题,编译书中的程序实例时会出现问题,提示“错误:apue.h:没有那个文件或目录”。
apue.h 是作者自定义的一个头文件,并不是Unix/Linux系统自带的,此头文件包括了Unix 程序所需的常用头文件及作者Richard自己写的出错处理函数。
所以在默认情况下,gcc在编译时是读不到这个头文件的。
先在这个网站 /src.tar.gz 下载tar.gz格式的源码包,然后解压至某个目录,比如说/home/godsoul/下,然后进入目录apue.2e,把文件 Make.defines.linux 中的WKDIR=/home/xxx/apue.2e 修改为 WKDIR=/home/godsoul/apue.2e ,然后再进入apue.2e目录下的std目录,打开linux.mk,将里面的nawk全部替换为awk,如果是用的vi/vim编辑器,可以使用这个命令 :1.$s/nawk/awk/g (注意前面有冒号)然后在此目录下运行make命令,即回到 /home/godsoul/apue.2e 目录在终端中输入“./make” (不含引号)然后把 /home/godsoul/apue.2e/inlcude 目录下的 apue.h 文件和位于/home/godsoul/apue.2e/lib 目录下的 error.c 文件都复制到 /usr/include 目录下,apue.2e/lib/libapue.a 到/usr/lib/和 /usr/lib64下。
注意复制这文件你需要有root权限。
之所以要这样做,是因为gcc在链接头文件时会到 /usr/include 这个目录下寻找需要的头文件,若找不到则报错。
最终还要编辑一下复制过来的 apue.h 文件在最后一行 #endif 前面添加一行#include “error.c”然后进入apue.2e/std 目录,编辑linux.mk。
1.1文件系统的内部结构硬件:在硬件上,把磁盘划分为一连串的块,块的大小从512到4096个字节都有可能,完全依系统而定文件系统在磁盘上的分布:| 引导块| 超级块| I节点表| 数据块|1.引导块的内容:⑴文件系统的大小⑵可供使用的存储块的总数以及可供使用的块所组成的连接列表.⑶超级块是否被修改过,修改过,当核心程序执行sync指令时,会把主存中的超级块写回磁盘里.⑷文件系统与磁盘的逻辑名称⑸可供使用的I节点总数及能够使用的I节点所组成的连接列表⑹最近一次I节点被修改的时间2.i节点的内容:注:①其中12位到15位是用来判别文件类别,它们在文件建立时就已经设定完成,而且不能被更改②S_ISUID:当一个文件被建立时,文件的用户标识符(user ID)与用户组标符(group ID)被系统设置.当一个用户签到进入UNIX系统时,其用户标识符(user ID)与用户组标识符(group ID)被系统设置,当一个文件被执行时,该进程会被给定两个标识符:真实用户标识符与有效用户标识符.其中真实用户标识符为文件的用户标识符,有效用户标识符随S_ISUID的不同的设定而不同.其中如S_ISUID为off , 则有效用户标识符与真实用户标识符相同.如S_ISUID为on,则有效用户标识符为运行该文件的用户的用户标识符.有效用户标识符标识的用户享有与真实用户标识符标识的用户相同的权利.例如:真实用户标识的用户对文件拥有读写的权利,则有效用户标识的用户也对该文件享有读写的权利.在系统中,S_ISUID能设为on的文件不能超过50个S_ISUID的设定: chmod rwsrwxrwx 第三位不是x而设为s③S_ISGID同S_ISUID,但是指用户组S_ISGID的设定: chmod rwxrwsrwx 第六位不是x而设为s④S_ISVTX(是否被置换):当该位被置为1时,该文件被装入内存后,形同DOS的TSR程序,将常驻内存.S_ISVTX的设定: chmod rwtrwxrwx 第三位不是t而设为s⑤文件存取权限的设置:格式一: rwxrwxrwx注:设置时用其中的八进制数相加值进行设置即可如0400+0200=0600 即为文件主可读写⑵文件的所有者标识⑶文件的实际存储地址⑷文件的存取时间:①最近被修改的时间,指文件最近一次被写入或删除数据的时间②最近被访问的时间,指最近一次被访问的时间③最近文件模式被更改的时间,指I节点的内容被修改的时间,会影响到I节点的指令有chmod,chown等⑸文件有多少个连接⑹文件的大小⑺文件的类型:一般文件,目录文件,字符设备,块设备和FIFO(命名管道).3.内存中的I节点,内存中的I节点除保留了文件中的I节点的内容外,还会增加四项数据:⑴I节点被存取的状态,比如I节点被锁定或明或I节点被修改过⑵有多少个进程在使用该文件及其I节点⑶现在文件指针所指的位置.换言之,下批数据将由此开始读入⑷I节点所在的磁盘设备标识符(device ID)4.如何通过I节点表索引文件在磁盘上的位置(略)1.2文件系统调用int fd ;int open( char * , int , int )fd = open ( fname , flags , perms ) ;fname : 可以是绝对路径,也可以是相对路径flags :O_RDONL Y 打开文件用于读O_WRONL Y 打开文件用于只写O_RDWR 打开文件用于读写O_APPEND 同fopen的”a”模式O_NDELAY 立即打开不做任何耽搁O_TRONC 写入前先删除原有数据O_CREAT 如果该文件不存在,则建立之,并要求给予存取权限参数PermsO_EXCL 如果目的文件已经存在,那么若以写入模式打开该文件,则open( )调用失败O_NOCTTY 如果此标志被设定,而打开的文件为设备文件(主要指终端),则该终端不可以作为调用open( )系统调用的哪个进程的控制终端其中前三项符号常量是互斥的,其余的若以无意义的组合,则系统不做处理perm : 例如0666fd : 成功0 – 19 的整数失败-12.#include <fcntl.h>int creat(char * , int )fd = creat( filename , perms )filename: 是指要打开的文件名称perms : 指打开后的文件的存取权限的设置fd : 打开成功后的文件描述符成功,打开后的蛙文件描述符失败,-1功能: 1.用creat打开的文件只能用于写操作;2.filename可以为一个已存在的文件,此时perms无意义,fd为打开此文件后的文件句柄3.如filename为一个不存在的文件,则creat建立并打开此文件,并把该文件截为空,此时perms为该文件的存取权限3.#include <fcntl.h>int close( int fd )fd :用open或creat打开的文件的句柄creat成功返回值为0,失败返回值为-14.#include <fcntl.h>int num_read = read(int fd,char* buf,int bufsize)int num_write = write(int fd,char* buf,int bufsize)fd : 文件描述字buf : 输入输出缓冲区bufsize : 每次调用read/write时必须读写的字节数量,最小值为1num_read:如果是0表示EOF,若是-1则指读取过程有误.一般情形返回值是读取的字节总数(该总数可能小于bufsize),不过EOF与回车符不算在被读入字节总数中num_write:返回-1,指写的过程出错,否则返回的是写的字节数(该总数可能小于bufsize)补充:1.如果write写入的对象为文本文件时,在遇到换行符时,它会输出换行符与回车符(0D0A),而read的操作在读到换行符与回车符时转换为换行符2.通过设定不同的bufsize可改变读写速度,在stdio.h中的符号常量BUFSIZ是磁盘块的长度3.write在进行写操作时仅能覆盖从当前文件指针到bufsize这么长的一部分字符,文件其余的字符仍然存在4.文件在read/write操作时,系统保留一文件指针指向当前文件操作位置5.#include <fcntl.h>long lseek(int fd,long offset,int origin)fd : 文件描述符offset: 每一读写操作所需移动的距离,这个值可正可负,正值向前移,负值指向后移.origin: 当前位置的基点: 0 当前位置为文件的开头1 指当前位置为前一个位置加上偏移量2 当前位置为文件的尾端返回值:从文件头到现在正要读写的位置之间的字节总数;失败,返回-1补充:1.因为文件的长度可能大于正型变量的值,所以用long2.只有当第三个参数是1或2时,偏移才可以为负值3.新的当前位置可超过文件尾,此时对读是没意义的;但对于写,它将导致文件的扩展,在新位置与原文件尾部之间,在进行读操作时,读到的将是null字符(0x00)4.如打开文件时,存取模式设为只读(O_RDONL Y),则lseek将不允许当前位置设定成超过文件尾5.在unistd.h中定义了SEEK_SET(0) , SEEK_CUR(1),SEEK_END(2)三个符号常量供参数使用6.#include <fcntl.h>int stat=link( char *oldlink ,char *newlink)oldlink : 原文件名newlink: 新文件名stat : 成功, 返回0失败, 返回-17.#include <fcntl.h>int stat=unlink(char *filename)8.#include <fcnt.h>int stat=fcntl(int fd,int cmd,int oparg)9.#include <stat.h>int stat(char *path,struct stat *buf)int fstat(int fd,struct stat *buf)path : 为目标文件的完整路径fd : 为打开的文件描述符struct stat{dev_t st_dev; /* 磁盘设备的标识符*/ino_t st_ino; /* 文件I节点的值*/unsign short st_mode; /* 文件存取权限为的设定状态*/nlink_t st_nlink; /*文件有多少个连接*/uid_t st_uid; /* 文件所有者的用户标识符*/gid_t st_gid; /* 文件所有者的用户组标识符*/dev_t st_rdev; /* 只有当目标文件是设备文件时本栏才有意义.它指示该文件所指向设备的标识符*/ off_t st_size; /*文件的长度,设备文件长度为0 */time_t st_atime; /* 最近一次文件被访问的时间*/time_t st_mtime; /* 最近一次文件被修改的时间*/time_t st_ctime; /* 最近一次文件属性被修改的时间*/ };10.#include <fcntl.h>int stat=access(char *path,int amode )path : 为完整路径名;若无完整路径名,则为当前路径amode : 从0到7. 如图mode binary code check for04 100 read02 010 write01 001 execute/search00 000 existence(存在)将04与02按位”OR”可得06(110),表示可读写stat : 成功0 (表示文件有amode指定的属性)失败–111.#include <fcntl.h>int chmod(const char *path,mode_t mode)补充:1.文件被打开时,不能用chmod改变文件属性2.只有文件主与超级用户才有权执行此系统调用例如: chmod(“/tmp/tdb”,0755)12.#include <fcntl.h>int chown(char *path,int uid,int gid)同chmod( )13.#include <fcntl.h>int chdir(char *path)path : 目标路径名补充:1.chdir的调用者必须有目标目录的搜寻权(x为on),否则调用会失败14.#include <unisd.h>int rmdir(const char path)#include <sys/types.h>#include <sys/stat.h>int mkdir(const char *path,mode_t mode)mode : 为将建立的目录的存取权限2.1标准I/O库1.流和FILE结构.流只是一个程序与一个打开文件之间的数据流.流是数据,不是通道.程序中用一个指向FILE结构类型的指针来标识流,FILE的结构如下: typedef struct _iobuf {int _cnt ;unsigned char *_ptr;unsigned char *_base;char _flag;char _file;}FILE_file : 当流与一个文件发生关联时,用来保存文件描述符_flag : 含有流的控制信息,例如,它可以指出流是否已打开用于读,用于写,用于读写_cnt,_ptr,_base : 描述了与打开的流有关的字符缓冲区._base指出这个缓冲区的开始位置;_ptr指出缓冲区中下一个处理字符的位置;_cnt指出_ptr所指位置后缓冲区中剩下字符的个数.缓冲区的长度为BUFSIZ个字符..对于一个流进行读写的所有数据都要通过缓冲区来传送.如当写数据时,只有当缓冲区满了,才向磁盘写数据;当读数据时,只有缓冲区空了,才从磁盘读数据.以此来提高读写数据2.2标准I/O库函数1.#include <stdio.h>FILE *stream ;char *filename , *type ;int retval ;stream = fopen( filename , type ) ;retval = fclose( stream ) ;type可以取下列基本值:r : 打开filename用于读,如该文件不存在,调用失败,返回NULLw: 建立filename用于写,同时它被截为空a : 打开filename用于只写,被写入的文件被追加到文件尾部.如果文件不存在,则建立文件用于只写一个文件也能被打开用于修改,即程序能同时对这个文件进行读写操作.但因为I/O库的缓冲机制,所以,在输出操作后不能立即进行输入操作,除非中间插入fseek,fflush或rewind操作.r+ : 打开文件filename用于读写,如果文件不存在,fopen将失败w+: 建立filename,或把它截为空文件,并打开它用于修改a+ : 打开文件用于修改,写入的数据被追加到文件尾部.如果文件不存在,它将被建立并打开用于读写以上的只能用于字符操作,以下的能用于二进制操作rb , wb , ab , rb+ , wb+ , ab+stream : 成功,为一指向FILE的指针失败,返回NULL(NULL在stdio.h中定义)补充:1.在进程中,fclose并不是必须的,在进程结束时,系统将自动关闭打开的文件,并且如调用exit( ),则将缓冲区的内容自动写到磁盘上;如不调用exit,则此时在缓冲区的内容不会被写到磁盘上2.在进程中如调用了fopen( ),为了安全性, 则结束时,必须调用fclose( )或exit( );2.#include <stdio.h>FILE *stream;int retval ;retval = fflush( stream ) ;retval : 成功,返回0失败,返回EOF(EOF在stdio.h中定义,表示文件结束)补充:1.调用该函数后,不管缓冲区是否满,将立即把缓冲区的内容写到文件中3.#include <stdio.h>FILE *inf , *outf ;int c ;c = getc( inf );putc( c , outf)c : 失败,返回EOF(-1) , 表示到达文件尾或发生错误注:getc,putc是宏4.#include <stdio.h>FILE *stream;Int c,retval ;retval = ungetc( c,stream);retval : 失败,返回EOF,如果要把EOF插回流中,肯定会失败补充:1.c必须是用getc从缓冲区中读的一个字符(其中EOF是不能被getc读出的,所以把EOF插回流中,必定会失败)2.ungetc主要用于getc多读一个字符后,把该字符回退到缓冲区中5.#include <stdio.h>getchar( ) = getc( stdin ) ;putchar(c) = putc( c ,stdout ).输出到stderr的字符是不经过缓冲区的,但stdin,stdout是需要经过缓冲区6.#include <stdio.h>int retval,fd;FILE *stream ;retval = ferror( stream )retval = feof( stream )clearerr(stream)fd=fileno(stream)ferror :如果前面的输入输出发生错误,则ferror返回一个非零值;返回0,表示没有出错feof : 当在stream中已达到文件末时,返回非0;返回0,则表示没有达到文件末clearerr: 它把stream的错误标志和文件末指示清为0.它能保证将来对这个流的ferror和feof调用,在未发生异常情况返回时返回0值fileno :返回stream所指FILE结构中的文件描述符,但不能用fileno把对文件的系统调用和标准I/O例行程序混合使用7.#include <stdio.h>char *buf , *retstring;FILE *inf ;int nsize ;retstring=gets(buf)retstring=fgets(buf,nsize,inf)gets : 从标准输入流stdin中读取一个字符行,并把它放入buf指向的缓冲区中.gets读出字符至换行符或文件末止,然后把换行符丢掉,把null(\0)符加入缓冲区中,形成一个以null符结尾的字符串.如果gets调用成功,它返回一个指向buf的指针;如果出错或到达文件末,则返回NULLfgets : 从流inf中读出字符,直到读出nsize-1的字符止,如果在此前遇到换行符或文件结束.读出的字符存到缓冲区buf中.fgets不会丢弃换行符,换行符仍被放到缓冲区.fgets成功,返回一个指向buf的指针,否则返回NULL 8.#include <stdio.h>char *string;FILE *outf;int retval ;retval=puts(string);retval=fputs(string,outf);puts : 把string写到stdout中,但末尾的null字符不包括在内,puts在写入部分末尾加上一个换行符,但fputs没有这么做puts,fputs : 出错均返回EOF9.#include <stdio.h>char *buffer;int size,nitems,result;FILE *inf,outf ;result = fread(buffer,size,nitems,inf)result = fwrite(buffer,size,nitems,outf)fread :对应于从inf的输入流中读出nitems个目标数据,读出的字节放入字符型数组buffer中,读出的每个目标是长度为size的字节序列.result中的返回值给出了成功读出的目标个数fwrite :把buffer中的数据写到outf指出的流中,该缓冲区被认为由nitems个长度为size字节的目标组成. result中的返回值给出了成功写入的目标个数补充:1.可以通过强制类型转换把结构等类型转换为字符,然后把结构写入文件中, 其它的变量类型也可以用此方法进行读写操作.例如:struct elem{char d_name[15] ;int start ;int length ;int type ;} elist[10]fwrite((char *)elist ,sizeof(struct elem) , 10 , stream );10.#include <stdio.h>FILE *stream ;long offset,position;int direction,result;result=fseek(stream,offset,direction);rewind(stream);position=ftell(stream);fseek : 同seek类似,fseek成功时,返回0,失败,返回非0rewind : 把文件读写指针置为文件首,不返回任何值ftell : 返回流中的当前位置,从文件首开始的字节数(从0开始计数)11.#include <stdio.h>FILE *oldstream , * newstream;char *type, * filename;int filedes;newstream=freopen(filename,type,oldstream)oldstream=fdopen(filedes,type);freopen : 关闭oldstream标识的流,然后再打开它用作从filename输入.type确定对新流的访问模式,它的取值与fopen中相同,如”r”,”w”等.这个例行程序被用于重新指定stdin,stdout,stderr.例如:freopen(“new.input”,”r”,stdin) ;fdopen : 把一个流与一个文件描述符关连起来,这个文件描述符取自前面的系统调用open,creat,pipe,dup等freopen,fdopen : 如出错,均返回NULL,它门打开的文件的关闭均用fclose12.#include <stdio.h>FILE *stream;char buf1[BUFSIZ],buf2[SOMEV ALUE];int type,size,res;setbuf(stream,buf1);res=setvbuf(stream,buf2,type,size);setbuf : 用buf1取代标准I/O库正常分配的缓冲区,buf1的长度由stdio.h中定义的常数BUFSIZ决定.如果把一个NULL字符型指针传给setbuf,那么就使输入和输出不在经过缓冲.在对程序调试时,常常需要这样做. setvbuf : buf2给出了一个新的缓冲区的值,size为该缓冲区的长度.如果用NULL 来做buf2之值,就标示用系统默认的缓冲区.type为确定stream被缓冲的方法,可以用它来调整流,使之更适合磁盘文件和终端设备的使用type的取值如下:⑴_IOFBF:流完全被缓冲,这是不与终端关联的默认方式.所以,以BUFSIZ个字节为单位来读写数据,可获得最高效率⑵_IOLBF:输出以行被缓冲,只要写入换行符,缓冲区就被冲刷清.当缓冲区满时,或请求输入时,缓冲区也将被刷清.这是终端的默认方式,它适合交互使用.⑶_IOBNF:这导致输入输出都不被缓冲,在这种情况下,buf2和size均被忽略如果type或size的值为非法值,那么setvbuf就返回一个非0值;成功,就返回03.1进程控制从上图可看出,父子进程共享同一个程序段,但各自拥有自己的堆栈,数据段,用户空间及进程控制块.当核心程序收到fork( )需求时,操作系统会给子进程一个进程标识符,并且设定CPU时间,设定与父进程共享的区段,同时将父进程的I节点拷贝一份到子进程的用户区域里,然后子进程的数据段与堆栈会被重新指定一份给子进程使用,最终子进程会返回数值0,以标识它是子进程..exec系统调用后,新进程将代替老进程,原进程的堆栈,数据段与程序段都会被修改,只有用户区和进程标识维持不变.1.fork,文件,数据首先,用fork生成的子进程的所有变量均是父进程的变量的值的拷贝;其次,在父进程中打开的文件,在子进程中也被打开.但是,被打开的文件的文件指针是由系统保存的,在系统中仅有一份拷贝,当在子进程中移动文件指针时,也等于移动了父进程的文件指针.在父进程中移动文件指针时, 也等于移动了子进程的文件子针2.exec和打开文件当调用exec时,在原进程中打开的文件,在新进程中也被打开.但通过fcntl( )函数设置文件的”执行关闭”位,可在exec调用时,关闭打开的文件(但保留stdin, stdout,stderr的打开),对”执行关闭”位的操作如下:#include <fcntl.h>int fd ;fd=open(“file”,O_RDONL Y);设置:fcntl(fd,F_SETFD,1);关闭,并取得返回值:res=fcntl(fd,F_GETFD,0);其中如果文件描述符fd对应的文件的”执行关闭”位被设置.则res之值为1, 否则res之值为03.exec和fork的联用进程首先调用fork( )生育一个子进程,然后在子进程中调用exec再生育一个进程,这样就实现了父进程运行一个与其不同的子进程,并且父进程不会被覆盖4.进程终止的特殊情况⑴子进程终止时,父进程并不正在执行wait调用⑵当子进程尚未终止时,父进程却终止在第一种情况,有终止的进程处于一种过度状态,处于这种状态的进程不使用任何内核资源,但要占用内核进程中的进程处理标内的一项.当其父进程执行wait 等待子进程时,它会进入睡眠状态.然后把这种处于过渡状态的进程从系统内删除, 父进程仍将能得到该子进程的结束状态.在第二种情况中,一般允许父进程结束,并把它的子进程(包括处于过渡状态的进程)交归系统初始化进程所属.3.2进程控制的系统调用1.int pid ;pid = fork( )pid : 在父进程中:①成功,pid为子进程的的进程标识符②失败,pid为一负数在子进程中:①成功,pid为0②失败,pid为一负数2.int execl(path,arg0,arg1,…,argn,(char *)0)int execv(path,argv)int execle(path,arg0,arg1,…,argn,(char *)0,envp)int execve(path,argv,envp)int execlp(file,arg0,arg1,..,argn,(char *)0)int execvp(file,argv)char *path,*arg0,*arg1,…,*argn;char *file, *argv[],envp[];path : 指向被执行程序的完整路径名, 例如: /bin/lsfile : 指向被执行的程序的文件名,例如: lsarg0,..,argn : 是一组要传给新进程的参数,它门的数据类型是字符串argv[] , envp[] : 同main( ) 函数的argv[]和envp[],此时envp为新进程的新环境; exec执行失败的返回值为-1补充:1.可以通过main( )函数的argc,argv[]变量来访问execl传给它们的参数2.execl 与execlp的主要区别是:execlp的第一个参数是一个简单的文件名,而不是一个路径名.它们通过检索SHELL环境变量的PATH指出的目录,来得到该文件的路径前缀部分.3. int status ;exit(status);status : 0到255,其中0表示程序的正常终止,1到255则指程序因错误而终止功能:1.终止进程,关闭所有打开的文件2.完成一些系统内部的清理工作,如缓冲区的清除3.当父进程因执行wait而睡眠时,子进程执行exit会重新启动父进程,同时,父进程能使用exit的参数status的低8位4.int retval,statusretval=wait(&statuts)或:retval=wait((int *)0)补充:1.当父进程执行wait时,父进程进入睡眠状态,此时当父进程的多个子进程中的第一个子进程结束时, 恢复父进程执行,而子进程的终止有两种可能:①子进程正常结束或调用exit();②子进程被另一个进程用一种称为信号的通信机构停止,而不是通过exit()retval : 成功,结束进程的标识符:①子进程正常结束或调用exit结束,status的低8位0,第8位到第15为子进程exit(status)的退出码status②子进程被另一个进程用一种称为信号的通信机构停止,此时status的低8位不为0,8到15位为终止此进程的信号失败, 返回-1,表示没有子进程结束,errno的值为ECHILD4.1信号处理1.中断与自陷的区别:⑴中断:外部设备和中央处理机并行工作时,若外部设备完成了某一预定的输入,输出(I/O)操作,它就要求处理机暂停正在执行的程序,转而对它进行必要的处理(例如,检查在I/O过程中是否发生过某种错误,如若出错则进行出错处理;如未出错则通知与该I/O有关地进程,然后启动外设执行下一个I/O请求),这种显现称为中断.⑵中断的类型:①I/O中断外部设备完成预定的I/O操作或执行I/O操作时出错②进管中断执行进入系统调用的指令时引起的中断③断点跟踪中断为了调试程序,有些系统设置了断点指令以及断点状态位等,使得程序在运行中达到了某些预定点后能自动停止下来,并有中断方式通知系统,这种中断称为断点跟踪中断④硬件故障中断它是机器故障或由机器故障造成出错时产生的中断⑤程序性中断由程序中的错误引起的中断,如使用非法指令,浮点运算出错,地址越界其中①称为中断,②③④⑤称为自陷⑶陷入处理:①等待系统管理员干预如陷入前为核心态,则除浮点溢出外,皆为故障,用户态下的电源失效也属于此类.对于这些故障,系统只有进入死循环等待系统操作人员干预②按用户规定方式进行处理在用户态下发生的陷入,除电源失效,系统调用,段违例外,皆按陷入类型转换为信号.然后将信号送入现运行近程的p-sig③用户栈自动扩充在发生段违例时,首先是力图恢复造成段违例的指令执行前的现场.如成功,则检查用户栈是否溢出.若溢出,则扩充用户栈.如扩充成功,则处理结束;否则将信号SIGSEG送p-sig并按第二种方式处理.④系统调用处理如陷入前为用户态,则进入系统调用2.信号与信号机构.信号的产生见上②;.信号机构指的是系统中围绕信号的产生,传送和处理而构成的一套机制.信号处理机制除故障和特殊陷入指令处理外,还处理以下两种情况:①用户使用键盘上的特定功能键对相关进程进行控制②用户态进程之间以传送信号方式进行简单通信3.信号类型(略)4.信号的产生,传送和同步⑴信号的产生: ①在陷入处理子程序中,针对用户态下产生的各种故障以及使用的各种陷入指令产生不同类型的信号.然后将此信号送入该进程proc中的p-sig项.②进程之间传送信号.进程之间通过系统调用kill进行信号传送.③用户在终端机键盘上击特定功能键”DELETE”和”QUIT”.终端机输入中断处理子程序将信号SIGINT或SIGQIT送入与该终端相关的所有进程④进程将信息写入通讯文件pipe时,发现该文件读通道已经关闭,也就是没有一个进程从中读取信息,则向本进程发送SIGPIPE信号⑤父进程对接到一个信号子进程进行跟踪处理时,可用跟踪命令向子进程传送一信号上述五种情况除最后一种外,都使用psignal程序实施信号传送.⑵信号的传送psignal(int p,const char *sig)将信号sig送到p指向的进程的proc中的p-sig项.一个进程只有一个p-sig项,它在任何时候只能记住接到的一个信号.如果进程对上一次接到的信号处理尚未结束时,又接到另一个信号,则一般后一个信号将取代前一个信号(无论同种还是不同种信号).但SIGKIL信号例外,它不被后来的信号取代.若接到信号的进程的优先数大于PUSER(100),则将其置为PUSER;若该进程处于低优先全睡眠状态,则立即将其唤醒.⑶信号的同步进程用kill向另一进程传送信号时,为了保持进程间的同步,接收进程调用sleep或pause系统调用,使接收进程处理低优先权睡眠状态.当发送进程发送信号时,会将处于低优先权睡眠状态的接收进程唤醒5.信号的处理方式,进程接收到信号后的处理方式有三种:⑴终止进程,这是针对用户态下故障信号最常用的一种方法⑵忽略该信号,不做任何处理⑶执行预先编写好的位于虚地址空间的信号处理程序,这种处理方式通过调用系统调用signal(sig,func)来设定.但SIGKIl信号不能用此方法来设定,其缺省方式是为终止接收到该信号的进程注:进程对信号的处理方式记录在信号处理表u-signal[NSIG]中,相应元素值为0,采用第一种处理方式;为奇数, 采用第二种处理方式;为偶数, 采用第三种处理方式4.2信号系统调用1. #include <stdlib.h>void abort ()功能:向调用进程发送一个信号,产生一个非正常终止,导致coredump2.#include <signal.h>int func( ), (* was)( ) , sig ;was = signal( sig,func );sig : 可以为信号系统中除SIGKIL和SIGSTOP外的任何信号func : 为一返回值为整型,入口参数为sig的函数was : 成功,为一指向信号原来相关连的函数的函数指针失败,传送非法信号或明或SIGKIL,返回值为(int(*)( ))-1,同时置errno为。
unix环境高级编程:UNIX学习之UNIX编程资料大收集一疯狂代码 / ĵ:http://UnixFreeBsd/Article26398.html 第一章 概述1.1UNIX的版本 本教材的目的是讲解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等。
由于NT的异军突起,对UNIX的市场形成巨大的威胁,各大UNIX厂商不得不联合起来,在工作站市场上,统一以系统V版作为标准,加入BSD版本中的一些优点,支持统一的CDE(CommonDesktop Environment)窗口环境,以与 NT进行对抗。
1.2 UNIX编程环境 UNIX操作系统通过Shell程序实现系统与用户的交互,在Shell提示符下,用户键入UNIX命令,即可得到操作系统的输出结果。
BSD系统的常用Shell是C Shell,缺省提示符是"%",系统V的常用Shell是BourneShell(现在多为KornShell),缺省提示符是"$",有关Shell的编程,我们在后面的章节中进行介绍。
UNIX上的标准编译器是cc。
在Shell提示符下(以C Shell为例)键入下列命令: %cc -o hello hello.c 即将C文件hello.c编译为可执行文件hello。
linux下apue.h及相关出错文件的处理(zz)摸了两天的unix环境下的编程,其中的开山之碍是对apue.h及相关文档的处理。
先来小览一下这两天的著多,成果吧。
本打算低低调调的做coding,没想到一开始就遇到了麻烦。
涮涮的编了个程序,可是在linuxgcc下一调,真是warning and error一长列,那个郁闷啊,当时很是惊慌,我堕落于此呼?还是好好的解决吧。
apue.h以及一些相关的出错处理文件找不着,对啊,我一看是没有啊,SO长时间没有编程,但不致于大意如此吧,心中一阵冷汗。
后来补上了这些文档,可是编译,未果!想想问题是出自哪了?怎么就是找不到这个文件和相关的函数呢。
于是深思,找了两个方法解决:1.将在编译中error的函数,用man命令找出,然后分别再包括进应用程序中,并将相关的提示出错处理函数改用printf()输出。
这样就可以通过编译。
但是之个方法,对于效率来说是大大的打了折扣,一是要在每个文件中都要增加这些系统头文件,另一方面,如果有一个修改了,所有的引用此文件的都要修改。
工作量大且烦。
这就相当于你如果有事要通知,你是发N个消息给每一个人呢,还是建立一个群,由这个群的相关机制帮你完成通知工作,这就是1:N与1:1:N这两者关系的优劣的问题。
于是想到了另一个方法。
2.就是将apue.h和apue_err.c、apue_log.c编译成一个库。
以后都是对这个库的引用,以后如果以上这几个文件要有修改,只要重新编译这个库就可以了,而应用程序与库之间的接口不变,不用更改,至于库是怎么实现的,这就不关应用程序的事了。
很好的实现了“四两博千斤”之势。
突然醒悟为什么国际上那么多的组织为什么花那么大的心思在做接口的标准化工作。
接口的标准化横向上让不同系统之间的通信变得平坦,纵向上让系统构架上将接口与实现分开,尽量减少修改时的“遍地开花”的局面。
下面针对第二点,看看将多个文件编译成库的方法,在此之前要先了解系统一般默认的头文件和库文件的收搜位置是:/usr/include 和/usr/lib。
【转】apue《UNIX环境⾼级编程第三版》第⼀章答案详解⼤家好,从这周开始学习apue《UNIX环境⾼级编程第三版》,在此,我要感谢⽹易的⼀个⼯程师朋友和室友,没有他们,我不会开始真正的学习这本书,希望⼤家以后开始慢慢进步。
废话少说,直接上课后习题了。
UNIX⾼级编程第⼀章习题答案:1.1在系统上验证,除根⽬录外,⽬录l和l l是不同的。
答:这个验证有很多⽅法可使⽤命令ls 、cd、vim等,⽬录.指向当前⽬录,⽬录..指向⽗⽬录,在根⽬录中这两个是相同的。
⾸先是根⽬录可见在根⽬录下,⽬录.和⽬录..都是指向本⽬录的。
结果显然易见。
有兴趣可以使⽤vim .与vim..1.2分析图1-6程序的输出,说明进程ID为852和853的进程发⽣了什么情况?答:UNIX是多任务系统,在程序运⾏的同时其他两个进程也在运⾏,编程运⾏如下图所⽰由上图可见进程PID是⼀直增⼤的,⼩⽔试图使⽤lsof -p 15122去查询中间的那个进程,可是⼀⽆所获,因此可以判定这个进程在⼩⽔试图去抓它的时候已经结束了。
为什么进程的PID⼀直在增⼤,会增⼤到什么程度?1.3在1.7节中,perror的参数是⽤ISO C的属性const定义的,⽽strerror的整形参数没有⽤此属性定义,为什么?答:因为perror的msg参数是个指针,perror就可以改变msg指向的字符串。
然⽽使⽤限定符const限制了perror不能修改msg指针指向的字符串。
⽽对于strerror,其错误号参数是整数类型,并且C是按值传递所有参数,因此即使strerror函数想修改参数的值也修改不了,也就没有必要使⽤const属性。
以我的理解是这样的:可以从两个函数的定义去理解char *strerror(int errnum);void perror(const char *msg);strerror函数将errnum (通常就是errno值)映射为⼀个出错消息字符串,并返回此字符串的指针,⽽errno的值通常是不定的(可参考errno的规则),存在多种不同的errno,当然不能使⽤cons修饰符了。
1.去该网站/下载源码 src.tar.bz
2.解压到某目录,假定为/home/user,进入解压后的文件修改Make.defines.linux中的WKDIR=/home/user/apue.2e
3.返回至apue.2e/std目录下面,修改linux.mk,将里面的nawk全部改为awk,可以使用这个命令:1,$s/nawk/awk/g
4.make后将会遇见两个问题。
a)gcc -DLINUX -ansi -I/tmp/apue.2e/include -Wall -D_GNU_SOURCE
-DDEBUG -c -o printd.oprintd.c
In file included from /usr/include/i386-linux-gnu/bits/time.h:86:0,
from /usr/include/time.h:42,
from /usr/include/pthread.h:26,
from printd.c:11:
/usr/include/i386-linux-gnu/bits/timex.h:31:7: 错误:expected ‘:’, ‘,’, ‘;’, ‘}’or ‘__attribute__’before ‘.’token
原因:
在apue.2e/ipp.h中定义了一个宏定义status和
/usr/include/i386-linux-gnu/bits/timex.h中的成员status冲突
解决方法:
修改这个apue.2e/ipp/ipp.h文件中的宏名称,例如改为Status
然后将apue.2e/ipp/printd.c中977行的hp->status 改为hp->Status
b)ARG_MAX未定义
在apue.2e/include/apue.h中添加一行:
#define ARG_MAX 4096
打开apue.2e/threadctl/getenv1.c 和apue.2e/threadctl/getenv3.c,添加一行: #include "apue.h"
5.解决完以上两个问题后,就可以make成功。
6.把头文件apue.h放到/usr/include/中,注意要以root用户操作以下命令
cp ~/apue.2e/include/apue.h /usr/include
cp ~/apue.2e/lib/libapue.a /usr/lib/
7.编译时候可以使用 gcc 源程序.c -o 可执行程序名/usr/lib/libapue.a
ps,如果c++引用此头文件和静态库要使用extern "C"{include "apue.h"}。