首页 > Linux/Unix > UNIX环境高级编程学习笔记(上)

UNIX环境高级编程学习笔记(上)

2013年4月19日 发表评论 阅读评论
文章作者:Yx.Ac   文章来源:勇幸|Thinking (http://www.ahathinking.com)   转载请注明,谢谢合作。

---

此文作者:冰镇南瓜汁;mail:munantv@qq.com

文件I/O

常用函数:

<fcntl.h>

int open(const char *pathname, int oflag, ... /* mode_t mode */); 

打开或创建一个文件。第一个参数是文件的名字,第二个参数是选项,在O_RDONLY / O_WRONLY / O_RDWR 中选择一个打开的方式,然后可以用或运算指定额外的一些选项(例如O_APPEND写时追加到文件 尾,O_CREAT若文件不存在则创建,O_TRUNC文件截短为0等),第三个参数是创建新文件的访问权限位。返回值是当前最小的未用文件描述符,用于 该文件,出错返回-1。

int creat(const char *pathname, mode_t mode); 

创建一个新文件,等效于 open(pathname, O_WRONLY | O_CREATE | O_TRUNC, mode);

int fcntl(int filedes, int cmd, ... /* int arg */ ); 

改变一个文件的性质。其中cmd的选项包括:F_DUPFD(复制一个现有的描述符,第三个参数为描述符数值的下限)、F_GETFD(获得文件描述符标 志)、F_SETFD(设置文件描述符标志,值为第三个参数)、F_GETFL(获得文件状态标志,可以与O_ACCMODE进行与运算,然后与三种访问 方式标志比较,可以与其他标志进行与运算判断标志位的存在)、F_SETFL(设置文件状态标志,设置为第三个参数,通常先取得现有的标志,然后用或运算 添加标志,用补码的与运算删除标志)、F_GETOWN、F_SETOWN(异步I/O所有权)、F_GETLK、F_SETLK、F_SETLKW(记 录锁)。出错返回-1。
常见的用法:(错误处理略去)val = fcntl(fd, F_GETFL, 0); val |= flags; fcntl(fd, F_SETFL, val);

<unistd.h>

int close(int filedes); 

根据文件描述符filedes关闭已打开的该文件并释放该进程加在该文件上的所有记录锁,成功返回0,出错返回-1。

off_t lseek(int filedes, off_t offset, int whence); 

为一个打开的文件设置偏移量,whence可以取SEEK_SET(距离文件开始处offset字节)、SEEK_CUR(当前值加offset)、SEEK_END(文件长度加offset),成功时返回新的文件偏移量,出错返回-1。

ssize_t read(int filedes, void *buf, size_t nbytes); 

从filedes引用的文件中读取nbytes个字节到buf所指向的内存空间中。成功返回读到的字节数,到达文件尾返回0,出错返回-1。
常见的用法:while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0) {...}

ssize_t write(int filedes, void *buf, size_t nbytes); 

把buf所指的内存内容的count个字节写到filedes所引用的文件内。成功返回写的字节数,出错返回-1。
常见的用法:if (write(STDOUT_FILENO, buf, n) == n) {...}

ssize_t pread(int filedes, void *buf, size_t nbytes, off_t offset); 

带偏移量地读取数据,不会改变文件偏移指针位置,这里偏移操作和读取操作是一个原子操作。

ssize_t pwrite(int filedes, void *buf, size_t nbytes, off_t offset); 

同上,带偏移量地写数据。

int dup(int filedes); 

复制文件描述符filedes,使当前可用文件描述符中最小的描述符与filedes共享一个文件表项,然后返回这一新的描述符,出错返回-1。

int dup2(int filedes, int filedes2); 

复制文件描述符filedes,使当文件描述符filedes2与filedes共享一个文件表项。如果filedes2已经打开,则先关闭,这里关闭操作 与描述符的复制操作是一个原子操作。如果filedes与filedes2相等,不做任何事。成功则返回filedes2,出错返回-1。

void sync(void);
int fsync(int filedes);
int fdatasync(int filedes);

将系统缓冲区的内容写回磁盘确保数据同步。第二个函数只对单一文件作用。第三个函数只对文件的数据部分作用,不影响文件属性。

细节:

与标准输入、标准输出、标准出错关联的文件描述符分别是STDIN_FILENO、STDOUT_FILENO、STDERR_FILENO。

.和..并不总是两个目录,对于根目录来说它们是相同的。

创建文件的函数是creat()而不是create()。

使用O_APPEND标志打开一个文件后,仍可以用lseek在任意位置开始读,但不能用lseek在任意位置写入。

./a.out > outfile 2>&1 : 执行后1和2都指向outfile

./a.out 2>&1 > outfile : 执行后只有1指向outfile

dup和dup2例子解释:
//声明及异常处理代码略去

fd = open("somefile", O_RDWR|O_CREAT, S_IRUSR|S_IWUSR);
save_fd = dup(STDOUT_FILENO);
dup2(fd, STDOUT_FILENO);
close(fd);
write(STDOUT_FILENO, msg, strlen(msg));
dup2(save_fd, STDOUT_FILENO);
write(STDOUT_FILENO, msg, strlen(msg));
close(save_fd);

具体执行细节如图所示:

两处重点:
第3幅图,要执行dup2(fd, 1);,文件描述符1原本指向tty,现在要指向新的文件somefile,就把原来的关闭了,但是tty这个文件原本有2个引用计数,还有文件描述符save_fd也指向它,所以只是将引用计数减1,并不真的关闭文件。
第5幅图,要执行dup2(save_fd, 1);,文件描述符1原本指向somefile,现在要指向新的文件tty,就把原来的关闭了,somefile原本只有1个引用计数,所以这次减到0,是真的关闭了。

文件和目录

常用函数:

<sys/stat.h>

int stat(const char *restrict pathname, struct stat *restrict buf);
int fstat(int filedes, struct stat *buf);
int lstat(const char *restrict pathname, struct stat *restrict buf);

将文件有关的信息结构通过参数buf返回。成功返回0,出错返回-1。
stat和lstat通过路径获取,fstat通过文件描述符获取。如果文件是一个符号链接,lstat返回该符号链接的信息而不是链接指向文件的信息。

mode_t umask(mode_t cmask); 

设置文件模式创建屏蔽字,并返回之前的值,没有出错返回。参数是由S_IRUSR、S_IWUSR、S_IXUSR、S_IRGRP、S_IWGRP、S_IXGRP、S_IROTH、S_IWOTH、S_IXOTH中的若干位或运算构成的。

int chmod(const char *pathname, mode_t mode);
int fchmod(int filedes, mode_t mode);

更改文件的访问权限,这里的参数mode除了umask的9个之外还有6个:S_ISUID、S_ISGID、S_ISVTX、S_IRWXU、S_IRWXG、S_IRWXO。

int mkdir(const char *pathname, mode_t mode);
int rmdir(const char *pathname);

创建和删除目录。成功返回0,出错返回-1。

<unistd.h>

int access(const char *pathname, int mode); 

测试对文件的访问能力,mode可以取R_OK、W_OK、X_OK、F_OK,分别测试读、写、执行的权限以及文件是否存在。成功返回0,出错返回-1。

int chown(const char *pathname, uid_t owner, gid_t group);
int fchown(int filedes, uid_t owner, gid_t group);
int lchown(const char *pathname, uid_t owner, gid_t group);

更改文件的用户ID和组ID。对于符号链接,lchown修改的是符号链接本身的信息而不是指向文件的信息。成功返回0,出错返回-1。

int truncate(const char *pathname, off_t length);
int ftruncate(int filedes, off_t length);

将文件截短为length字节。成功返回0,出错返回-1。

int link(const char *existingpath, const char *newpath); 

创建一个新目录项newpath,它引用现有的文件existingpath。成功返回0,出错返回-1。

int unlink(const char *pathname); 

删除目录项,并使其所引用文件的链接计数减1。成功返回0,出错返回-1。

int symlink(const char *actualpath, const char *sympath); 

创建符号链接:指向actualpath的新目录项sympath。成功返回0,出错返回-1。

ssize_t readlink(const char *restrict pathname, char *restrict buf, size_t bufsize); 

打开符号链接本身进行读取,成功返回读的字节数,出错返回-1。

int chdir(const char *pathname);
int fchdir(int filedes);

更改当前工作目录。成功返回0,出错返回-1。

char *getcwd(char *buf, size_t size); 

获取当前工作目录的绝对路径,成功返回buf,出错返回空指针。

<stdio.h>

int remove(const char *pathname); 

对于文件,remove的功能与unlink相同,对于目录,remove的功能与rmdir相同。成功返回0,出错返回-1。

int rename(const char *oldname, const char *newname); 

为文件或目录更名。成功返回0,出错返回-1。

<utime.h>

int utime(const char *pathname, const struct utimbuf *times); 

修改文件的访问时间(atime)和修改时间(mtime),其中utimbuf的结构包括actime和modtime两个成员。调用utime时,文件的更改状态时间(ctime)自动更新。成功返回0,出错返回-1。

<dirent.h>

DIR *opendir(const char *pathname); 

打开一个目录,失败时返回空指针。

struct dirent *readdir(DIR *dp); 

读取目录一次,返回目录中下一个dirent结构体指针,顺序与实现有关,失败时返回空指针。

void rewinddir(DIR *dp); 

将目录的读取位置重置为开头。

int closedir(DIR *dp); 

关闭参数dir所指的目录,成功返回0,出错返回-1。

long telldir(DIR *dp); 

返回目录读取的当前位置偏移量,出错返回-1。

void seekdir(DIR *dp, long loc); 

设置参数dir目录流目前的读取位置。

细节:

文件的信息用stat结构保存,可以用lstate函数获取。

文件的类型信息保存在stat结构的st_mode成员中,可以用S_ISREG(),S_ISDIR(),S_ISLNK()等宏进行判断。

文件的所有者和所有组用stat结构中的st_uid和st_gid成员表示。

文件是否有SUID和SGID属性可以用S_ISUID和S_ISGID测试。

对于umask中为1的位,在文件的mode中相应位(S_IR/W/XUSR/GRP/OTH)一定被关闭。

SUID,SGID,SBIT对应的mode中的位分别是S_ISUID,S_ISGID,S_ISVTX(saved-text bit)。

创建符号链接时,可以指向一个不存在的文件。

普通文件的长度可以是0。目录的长度不会是0,因为至少包含.和..两项。符号链接的长度是路径名中的实际字节数,也不会是0。

dirent结构中表示文件名的成员是d_name。

标准I/O库

常用函数:

<stdio.h>

void setbuf(FILE *restrict fp, char *restrict buf); 

打开或关闭缓冲机制。如果buff非空,则fp的缓冲为长度为BUFSIZ的缓冲buf,如果buff为空,则fp为无缓冲。

int setvbuf(FILE *restrict fp, char *restrict buf, int mode, size_t size); 

精确指定缓冲类型。mode参数可选_IOFBF(长度为size的用户buf全缓冲,buff为空则自动分配)、_IOLBF(长度为size的用户buf行缓冲,buff为空则自动分配)、_IONBF(无缓冲,忽略buf和size)。

int fflush(File *fp); 

强制冲洗一个流,该流所有未写的数据都传送至内核。

FILE *fopen(const char *restrict pathname, const char *restrict type);
FILE *freopen(const char *restrict pathname, const char *restrict type, FILE *restrict fp);
FILE *fdopen(int filedes, const char *type);

fopen 打开一个指定的文件;freopen在指定的流上打开文件,若该流已经定向则先清除;fdopen打开一个现有的文件描述符,并与一个标准的I/O流结 合。参数type指定读写方式:r或rb(读,文件必须已存在)、w或wb(删除之前的内容,写)、a或ab(在文件末尾写)、r+或r+b或rb+(读 写,文件必须已存在)、w+或w+b或wb+(删除之前的内容,读写)、a+或a+b或ab+(读写,只能在末尾写)。成功返回文件指针,出错返回空指 针。

int fclose(FILE *fp); 

关闭一个打开的流。成功返回0,出错返回EOF。

int getc(FILE *fp);
int fgetc(FILE *fp);
int getchar(void);

一次读一个字符。getc可被实现为宏,fgetc不能被实现为宏。getchar等价于getc(stdin)。成功返回下一个字符,到达文件结尾或出错返回EOF。
常见的用法:while ((c = getc(stdin)) != EOF) {...}

int ungetc(int c, FILE *fp); 

将字符压回流中。成功返回c,出错返回EOF。

int putc(int c, FILE *fp);
int fputc(int c, FILE *fp);
int putchar(int c);

一次输出一个字符,其之间区别与getc函数类似。成功返回c,出错返回EOF。

char *fgets(char *restrict buf, int n, FILE *restrict fp);
char *gets(char *buf);

每次读入一行到缓冲区buf,gets从标准输入读,fgets从指定的流读。fgets读到换行符为止,但是不超过n-1个字符,放入buf后以null字符结尾。gets有可能造成缓冲区溢出。成功返回buf,到达文件尾或出错返回EOF。
常见的用法:while (fgets(buf, MAXLINE, stdin) != NULL) {...}

int fputs(const char *restrict str, FILE *restrict fp);
int puts(const char *str);

每次输出一行。尾端的null字符不输出。成功返回非负值,出错返回EOF。

int ferror(FILE *fp);
int feof(FILE *fp);

检查是否出错/是否到达文件尾,返回一个非0值表示真,返回0表示假。

size_t fread(void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);
size_t fwrite(const void *restrict ptr, size_t size, size_t nobj, FILE *restrict fp);

二进制读写,size为每个元素的长度,nobj为要读写的元素个数。返回读或写的元素个数。
常见的用法:float data[10]; if (fwrite(&data[2], sizeof(float), 4, fp) != 4) {/*error*/}

long ftell(FILE *fp); 

成功返回当前文件位置指示,出错返回-1L。

int fseek(FILE *fp, long offset, int whence); 

成功返回非0值,出错返回0。
二进制文件的字节位置,whence的取值可以有SEEK_SET、SEEK_CUR和SEEK_END。

off_t ftello(FILE *fp);
int fseeko(FILE *fp, off_t offset, int whence);

与ftell和fseek对比,只是返回值类型不同。

int fgetpos(FILE *restrict fp, fpos_t *restrict pos);
int fsetpos(FILE *fp, const fpos_t *pos);

文件位置标记使用fpos_t结构存放。

int printf(const char *restrict format, ...);
int fprintf(FILE *restrict fp, const char *restrict format, ...);
int sprintf(char *restrict buf, const char *restrict format, ...);
int snprintf(char *restrict buf, size_t n, const char *restrict format, ...);

printf将格式化数据写到标准输出;fprintf写到指定的流;sprintf将格式化的字符送入数组buf;snprintf超过缓冲区长度的字符会被丢弃。
printf和fprintf成功返回输出字符数,出错返回负值。sprintf和snprintf成功返回存入数组的字符数,出错返回负值。

int scanf(const char *restrict format, ...);
int fscanf(FILE *restrict fp, const char *restrict format, ...);
int sscanf(const char *restrict buf, const char *restrict format, ...);

格式化输入。返回输入项数,出错或到达文件尾返回EOF。

int fileno(FILE *fp); 

获得一个流的文件描述符。

char *tmpnam(char *ptr); 

产生一个与现有文件名不同的有效路径名字符串,返回路径名的指针。

FILE *tmpfile(void); 

创建一个临时二进制文件,在关闭文件或程序结束时自动删除。成功返回文件指针,失败返回NULL。

细节:

默认情况下,标准出错是不带缓冲的,打开至终端设备的流是行缓冲的,其他流是全缓冲的。

只有两个函数可以改变流的定向:freopen函数清除流的定向,fwide函数设置流的定向。

printf不输出字符时,例如printf("");返回0。

对标准I/O流使用fsync函数,先调用fflush,将数据从内存缓冲区传至内核,然后通过fileno获得fsync的参数。

系统数据文件和信息

常用函数:

<pwd.h>

struct passwd *getpwuid(uid_t uid);
struct passwd *getpwnam(const char *name);

根据用户ID或者用户名获取用户对应的passwd结构(登录相关信息),出错返回NULL。

struct passwd *getpwent(void);
void setpwent(void);
void endpwent(void);

getpwent打开口令文件(第一次调用时),读取口令文件中的下一个记录项,每调用一次返回一个记录项,出错或到达文件结尾时返回NULL。setpwent重置读取口令文件的位置。endpwent关闭口令文件。

<shadow.h>

struct spwd *getspnam(const char *name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);

访问阴影口令文件。

<grp.h>

struct group *getgrgid(gid_t gid);
struct group *getgrnam(const char *name);
struct group *getgrent(void);
void setgrent(void);
void endgrent(void);

访问组信息。

<time.h>

time_t time(time_t *calptr); 

返回当前时间和日期,如果参数不为空,也存放在calptr指向的内存中。出错返回-1。

<sys/time.h>

int gettimeofday(struct timeval *restrict tp, void *restrict tzp); 

返回0,当前时间存放在tp指向的内存中,tzp参数取NULL。timeval结构包含两个成员,time_t tv_sec存放秒,long tv_usec存放微秒。

细节:

口令文件是/etc/passwd,包含用户登录的信息,但不包含口令。

口令加密后存放在阴影文件/etc/shadow中。

组信息存放在/etc/group中。

(全文完)