错误提示 strerror 打印错误整型值对应的错误信息
1 2 #include <string.h> char *strerror (int errnum) ;
参数一:errnum
,传入错误整型值
返回值:返回错误整型值对应的错误信息
perror 根据errno
值打印相应的错误信息
1 2 #include <stdio.h> void perror (const char *msg) ;
文件操作 open与openat open
与openat
函数通过参数fd
进行区分
情况一:参数pathname
为绝对路径,参数fd
可以被忽略
情况二:参数pathname
为相对路径,参数fd
为文件描述符,此时fd
描述符所在的文件夹为相对路径的起始位置
情况三:参数pathname
为相对路径,参数fd
为 AT_FDCWD
,此时当前目录为相对路径的起始位置,此时open
与openat
功能相似
openat
对open
的改进
使得线程可以操作其他目录下的文件
防止TOCTTOU
漏洞
1 2 3 4 #include <fcntl.h> int open (const char *path, int oflag, ...) ;int openat (int fd,const char *path,int oflag,..)
creat create
函数用于创建只写文件
1 2 3 4 5 #include <fcntl.h> int create (const char *path,mode_t mode) ; open(path,O_WROLY | O_CREAT | O_TRHUNK,mode);
close 关闭文件
1 2 #include <unistd.h> int close (int fd) ;
lseek 搜索文件偏移,该函数执行记录了内核中记录文件偏移的数据,不需要执行I/O
操作
1 2 #include <unistd.h> off_t lseek (int fd,off_t offset,int whence) ;
fd
文件描述符
offset
偏移
whence
为指定offset
的位置
whence = SEEK_SET
,偏移从文件起始位置计算
whence = SEEK_CUR
,偏移从文件当前偏移开始计算
whence = SEEK_END
,偏移从文件末尾开始计算
lseek
函数的两个功能
获取当前文件的偏移
1 2 off_t currpos; currpos = lseek(fd, 0 , SEEK_CUR);
判断当前文件是否可以搜索偏移
pipe
、FIFO
以及socket
无法使用lseek
函数找偏移量
1 2 3 4 5 6 7 8 9 10 11 #include <unistd.h> int main (int argc,char *argv[]) { if (lseek(STDIN_FILENO, 0 , SEEK_CUR) == -1 ) printf ("cannot seek\n" ); else printf ("seek OK\n" ); exit (0 ); }
read 1 2 #include <unistd.h> int read (int fd, char *buf, unsigned nbytes) ;
read
函数有以下五种情况读取字节少于参数指定的字节
读到文件尾,返回0
从终端设备读取数据,一次只返回一行
从网络读取的数据大小取决于网络的缓冲区
从管道或FIFO
读取数据时取决于管道内的数据大小
被信号所中断
write 将数据写入打开的文件里
1 2 #include <unistd.h> ssize_t write (int fd, const void *buf, size_t nbytes) ;
如果写入字节数超过了文件大小或者磁盘大小则会发生错误。
I/O效率 1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include "apue.h" #define BUFFSIZE 4096 int main (void ) { int n;char buf[BUFFSIZE];while ((n = read(STDIN_FILENO, buf, BUFFSIZE)) > 0 )if (write(STDOUT_FILENO, buf, n) != n) err_sys("write error" );if (n < 0 ) err_sys("read error" );exit (0 ); }
如何选择文件输入输出的字节的大小
buffsize为4096的效果最好
pread和pwrite pread
与pwrite
函数相当于先调用lseek
函数后调用write
和read
函数,但是两个操作视作为原子操作并且当前的文件偏移不会改变
1 2 3 #include <unistd.h> ssize_t pread (int fd, void *buf, size_t nbytes, off_t offset) ;ssize_t pwrite (int fd, void *buf, size_t nbytes, off_t offset) ;
dup和dup2 dup
和dup2
函数用于复制文件描述符,并且会清空close-on-exec
标志位
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <unistd.h> int dup (int oldfd) ;int dup2 (int oldfd,int newfd) ; error, -1 is returned, and errno is set appropriately
dup
函数复制参数fd
的描述符,返回最小未被使用的文件描述符
dup2
函数将参数oldfd
的描述符复制到newfd
的描述符
若oldfd
是非法文件描述符,那么dup2
函数调用失败,newfd
不会被关闭
若oldfd
与newfd
指向同一个文件描述符,则不会执行任何操作,并返回newfd
若newfd
已经被使用,那么dup2
会先关闭newfd
,然后再使用
sysnc、fsync和fdatasync 磁盘写入需要经过内核缓存区再写入磁盘中,该行为被称之为延迟写入。为了确保缓冲区内的数据与写入磁盘内的数据一致性,因此提供了sysnc
、fsync
和fdatasync
函数
1 2 3 4 #include <unistd.h> int fsync (int fd) ;int fdatasync (int fd) ;void sync (void ) ;
sync
函数只是简单的将修改后的缓冲区数据写入队列中,并不等待磁盘的写入操作
fsync
函数则是需要等待fd
文件描述符指定的文件写入完后
fdatasync
函数与fsync
函数类似,但是只影响数据部分
fcntl 用于修改以及打开的文件属性
1 2 3 #include <fcntl.h> int fcntl (int fd, int cmd,...) ;
ioctl 上述不能操作的文件都可以使用iotcl
函数进行操作
1 2 3 4 5 #include <unistd.h> #include <sys/ioctl.h> int ioctl (int fd, int request, ...) ;
每个设备驱动程序可以定义它自己专用的一组ioctl
命令,系统则为不同种类的设备提供通用的ioctl
命令。
类别
常量名
头文件
ioctl数
盘标号
DIOxxx
<sys/disklabel.h>
4
文件I/O
FIOxxx
<sys/filio.h>
14
磁盘I/O
MTIOxxx
<sys/mtio.h>
11
套接字I/O
STOxxx
<sys/sockio.h>
73
终端I/O
TIOxxx
<sys/ttycom.h>
43
文件与目录 stat函数 1 2 3 4 5 6 7 8 #include <sys/stat.h> int stat (const char *restrict pathname,struct stat *restrict buf) ;int fstat (int fd, struct stat *buf) ;int lstat (const char *restrict pathname, struct stat *restrict buf) ;int fstatat (int fd, const char *restrict pathname,struct stat *restrict buf,int flag) ;
stat函数
参数一:文件路径
参数二:存储stat结构的缓存区
fstat函数
参数一:文件描述符
参数二:存储stat结构的缓存区
lstat函数
参数一:文件路径
参数二:存储stat结构的缓存区
与stat
的区别是,当pathname
传入的是符号链接,那么会返回符号链接的信息而不是链接文件的信息
fstatat函数
参数一:文件描述符,若该参数使用了标志位AT_FDCWD
,pathname
则采用相对路径
参数二:pathname
,如果该参数是绝对路径,则忽略fd
参数
参数三:存储stat
结构的缓存区
参数四:若该标志位设置了AT_SYMLINK_NOFOLLOW
则fstatat
函数返回符号链接的信息,否则返回链接的原文件
stat
结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct stat {mode_t st_mode; ino_t st_ino; dev_t st_dev; dev_t st_rdev; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; off_t st_size; struct timespec st_atim ; struct timespec st_mtim ; struct timespec st_ctim ; blksize_t st_blksize; blkcnt_t st_blocks; };
文件类型
常规文件
目录文件
块特殊文件:每个块作为单位处理
字节特殊文件:每个字节作为单位处理
FIFO:管道文件
Socket:网络通信文件
符号链接
文件访问权限
access和faccessat函数 用于判断当前进程对文件的权限
1 2 3 4 #include <unistd.h> int access (const char *pathname, int mode) ;int faccessat (int fd, const char *pathname, int mode, int flag) ;
access函数
faccessat函数
参数一:文件描述符,该参数标志了AT_FDCWD
后,pathname
为相对路径
参数二:文件路径,若该参数是绝对路径则fd
参数忽略
访问权限
umask 设置默认创建文件的权限
1 2 3 #include <sys/stat.h> mode_t umask (mode_t cmask) ;
chomd,fchmod和fchmodat 用于改变文件的访问权限
1 2 3 4 5 #include <sys/stat.h> int chmod (const char *pathname, mode_t mode) ;int fchmod (int fd, mode_t mode) ;int fchmodat (int fd, const char *pathname, mode_t mode,int flag) ;
chmod
函数
fchmod
函数
参数一:文件描述符
参数二:权限
与chmod
函数区别为,该函数用于修改打开了的文件
fchmodat
函数
参数一:文件描述符,如果fd
含有AT_FDCWD
,那么文件路径为相对路径
参数二:文件路径,如果该参数是绝对路径,则忽略fd
参数
参数三:权限
参数四:标志位,设置AT_SYMLINK_NOFOLLOW
则不返回符号链接
chown,fchown,fchownat,和lchown chown
函数允许我们更改文件的用户ID和组ID
1 2 3 4 5 #include <unistd.h> int chown (const char *pathname, uid_t owner, gid_t group) ;int fchown (int fd,uid_t owner,gidd_t group) ;int fchownat (int fd, const char *pathname,uid_t owner, gid_t group, int flag) ;int lchown (const char *pathname,uid_t owner,gid_t group) ;
chown
函数
参数一:文件路径
参数二:用户id
参数三:组id
fchown
函数
参数一:文件描述符
参数二:用户id
参数三:组id
与chown
的区别是fchown
函数用于修改已经打开了的文件,并且不能修改符号链接文件
fchownat
函数
参数一:文件描述符,若该值含有AT_FDCWD
标志位,则pathname
采用相对路径
参数二:文件路径,若该值采用绝对路径则忽略文件描述符
参数三:用户id
参数四:组id
参数五:标志,若该标志设置为AT_SYMLINK_NOFOLLOW
则更改符号链接本身的所有者,否者更改符号链接指向的文件
lchown
函数用于修改符号链接的文件的权限
truncate和ftruncate 用于截断文件
1 2 3 4 #include <unistd.h> int truncate (const char *pathname, off_t length) ;int ftruncate (int fd, off_t length) ;
link,linkat,unlink,unlinkat,和 remove 创建现有文件的链接
1 2 3 4 #include <unistd.h> int link (const char *existingpath, const char *newpath) ;int linkat (int efd, const char *existingpath, int nfd, const char *newpath, int flag) ;
linkat
函数
efd
与existingpath
指出被链接的文件,nfd
与newpath
为链接后的文件
若使用绝对路径则忽略文件描述符
若使用相对路径,则相对于文件描述符
若文件描述含有AT_FDCWD
标志,那么相当于当前目录
若flag
设置为AT_SYMLINK_FOLLOW
则链接符号链接本身,否则指向符号链接指向的文件
解除链接
1 2 3 4 #include <unistd.h> int unlink (const char *pathname) ;int unlinkat (int fd,const char *pathname,int flag) ;
unlinkat
函数
flag
参数设置了AT_REMOVEDIR
标志后,unlinkat
函数用于删除目录
删除文件
1 2 3 #include <stdio.h> int remove (const char *pathname) ;
rename 和 renameat 修改文件或目录名称
1 2 3 #include <stdio.h> int rename (const char *oldname, const char *rewname) ;int renameat (int oldfd,const char *oldname,int newfd,const char *newname) ;
symlink 和 symlinkat 1 2 3 4 #include <unistd.h> int symlink (const char *actualpath, const char *sympath) ;int symlinkat (const char *actualpath,int fd, const char *sympath) ;
readlink和readlinkat 用于打开与读取符号链接本身
1 2 3 4 #include <unistd.h> ssize_t readlink (const char * restrict pathname, char *restrict buf, size_t bufsize) ;ssize_t readlinkat (int fd, const char *restrict pathname, char *restrict buf, size_t bufsize) ;
文件时间
futimens,utimensat和utimes 用于修改文件的访问时间与修改时间
1 2 3 #include <sys/stat.h> int futimnes (int fd, const struct timespec times[2 ]) ;int utimensat (int fd, const char *path, const struct timespec times[2 ],int flag) ;
以秒或微妙修改文件时间
1 2 #include <sys/time.h> int utimes (const char *pathname, const struct timeval times[2 ]) ;
mkdir,mkdirat和rmdir mkdir
与mkdriat
函数创建目录
1 2 3 #include <sys/stat.h> int mkdir (const char *pathname, mode_t mode) ;int mkdirt (int fd,const char *pathname, mode_t mode) ;
rmdir
函数删除目录
1 2 #include <unistd.h> int rmdir (const char *pathname) ;
目录读写操作 1 2 3 4 5 6 7 8 9 10 11 12 #include <dirent.h> DIR *opendir (const char *pathhname) ; DIR *fdopendir (int fd) ;struct dirent *readdir (DIR *dp) ;void rewinddir (DIR *dp) ;int closedir (DIR *dp) ;long telldir (DIR *dp) ;void seekdir (DIR *dp, long loc) ;
dirent
1 2 ino_t d_ino; char d_name[];
chdir和fchdir 更改当前的工作目录
1 2 3 4 5 #include <unistd.h> int chdir (const char *pathname) ;int fchidr (int fd) ;
getcwd 获取当前目录的路径
1 2 #include <unistd.h> char *getcwd (char *buf, size_t size) ;
标准I/O库 fwide 用于设置流的定向,流的定向绝对了流是读或写,是单字节或多字节,若流的定向已经被决定则不能修改
1 2 3 4 5 6 7 8 9 10 #include <stdio.h> #include <wchar.h> int fwide (FILE *fp, int mode) ;
Buffering 有三种缓冲类型
Fully buffered
:全缓冲,直到缓冲区满才执行I/O
操作,可以通过flush
函数刷新缓冲区,执行I/O
操作
Line buffered
:直到遇到回车符才会执行I/O
操作
Unbuffered
:不缓存,直接输出
ISO C
对缓冲类型的要求
标准输入与标准输出是完全缓存的,除非它们与相关设备交互
标准错误流是不缓存的
缓冲知识点扩展 参考链接不带缓存I/O和标准I/O(带缓存)之间的区别
printf.c
1 2 3 4 5 6 7 8 9 10 11 12 13 #include <stdio.h> #include <stdlib.h> int main (void ) { printf ("This Line Should be Cached..." ); sleep(3 ); printf ("\nThis Line Should be Cached Again" ); sleep(3 ); printf ("This Line Should Not be Cached Agin\n" ); sleep(3 ); }
标准输出流默认为行缓冲输出,因此只有遇到回车符时才会将缓冲区的字符打印,因此第一条输出语句不带回车符时屏幕上不会有任何信息,而是等待回车符出现后才输出到屏幕上。
普通情况下,使用printf
函数,不带回车符,程序也会将字符串打印在屏幕上,这是因为main
函数会经过exit
函数退出,exit
函数会刷新缓冲区,使得字符串打印在屏幕上,但是通过_Exit
函数则不会打印,则不会刷新。
1 2 3 4 5 6 7 8 9 #include <stdio.h> #include <stdlib.h> int main (void ) { printf ("This Line Should be Cached..." ); _Exit(0 ); }
当标准输出被重定向其余文件时,会从行缓冲转化为全缓存,那么只有缓冲区满或者通过fflush
或fclose
函数函数刷新缓冲区时才会在屏幕上打印字符串。
FILE文件结构体
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 struct _IO_FILE {int _flags;#define _IO_file_flags _flags char * _IO_read_ptr; char * _IO_read_end; char * _IO_read_base; char * _IO_write_base; char * _IO_write_ptr; char * _IO_write_end; char * _IO_buf_base; char * _IO_buf_end; char *_IO_save_base;char *_IO_backup_base;char *_IO_save_end;struct _IO_marker *_markers ;struct _IO_FILE *_chain ;int _fileno; };
FILE结构测试代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdlib.h> #include <stdio.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> int main (void ) { char buf[5 ]; char output[5 ] = "abcd" ; FILE *myfile = fopen("./test.txt" ,"r+" ); printf ("before reading\n" ); printf ("read buffer base %p\n" , myfile->_IO_read_base); printf ("read buffer length %d\n" , myfile->_IO_read_end - myfile->_IO_read_base); printf ("write buffer base %p\n" , myfile->_IO_write_base); printf ("write buffer length %d\n" , myfile->_IO_write_end - myfile->_IO_write_base); printf ("buf buffer base %p\n" , myfile->_IO_buf_base); printf ("buf length %d\n" , myfile->_IO_buf_end - myfile->_IO_buf_base); fgets(buf, 5 , myfile); printf ("\n" ); printf ("after reading\n" ); printf ("before reading\n" ); printf ("read buffer base %p\n" , myfile->_IO_read_base); printf ("read buffer length %d\n" , myfile->_IO_read_end - myfile->_IO_read_base); printf ("write buffer base %p\n" , myfile->_IO_write_base); printf ("write buffer length %d\n" , myfile->_IO_write_end - myfile->_IO_write_base); printf ("buf buffer base %p\n" , myfile->_IO_buf_base); printf ("buf length %d\n" , myfile->_IO_buf_end - myfile->_IO_buf_base); return 0 ; }
在读之前结构体还未初始化,只开辟了一段空间对FILE
结构体进行存储
可以看到即使代码中写的是读取5
字节,但是缓冲区中还是存在着42
字节的数据,因此带缓存的I/O
是一次性读取1024
个字节大小的数据(_IO_buf
的大小)到读缓存区中,接着从缓存区中取出5
个字节到指定的变量中。
全缓冲
setbuf和setvbuf 用于设置是否缓存以及缓存的方式
1 2 3 #include <stdio.h> void setbuf (FILE *restrict fp, char *restrict buf) ; int setvbuf (FILE *restrict fp, char *restrict buf, int mode, size_t size) ;
setvbuf
函数可以指定缓存的方式,若mode
选择_IONBF
则采用无缓存模式
打开流 fopen、freopen和fdopen 1 2 3 4 5 #include <stdio.h> 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 fd, const char *type) ;
fopen
函数用打开指定路径的文件,与open
函数的区别为open
函数为系统调用而fopen
函数为更高层的函数
freopen
函数为将文件描述符与指定路径的文件联系起来
fdopen
函数用于打开管道或者socket
文件,因为常规的I/O无法打开,则可以借助fdopen
函数将上述文件描述符与标准I/O联系起来
fclose 关闭流
1 2 #include <stdio.h> int fclose (FILE *fp)
对流进行读写
一次一字符输入函数 1 2 3 4 5 #include <stdio.h> int getc (FILE *fp) ;int fgetc (FILE *fp) ;int getchar (void ) ;
ferror和feof 用于判断读失败是由于到达文件末尾,还是读文件出错
1 2 3 4 5 #include <stdio.h> int ferror (FILE *fp) ; int feof (FILE *fp) ; void clearerr (FILE *fp) ;
ungetc 用于将字符压回流中,但是只能压入一个字符
1 2 3 #include <stdio.h> int ungetc (int c, FILE *fp) ;
一次一字符输出函数 1 2 3 4 #include <stdio.h> int puc (int c, FILE *fp) ;int fputc (int c,FILE *fp) ;int putchar (int c) ;
一次一行输入函数 1 2 3 #include <stdio.h> char *fgets (char *restrict buf, int n, FILE *restrict fp) ;char *gets (char *buf)
一次一行输出函数 1 2 3 #include <stdio.h> int fputs (const char *restrict str, FILE *restrict fp) ;int puts (const char *str) ;
二进制流I/O 可以每次读取/写入一个对象
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> size_t fread (void *resrict 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) ;
ftell、fseek和rewind 1 2 3 4 5 6 7 #include <stdio.h> long ftell (FILE *fp) ;int fseek (FILE *fp,long offset, int whence) ;void rewind (FILE *fp)
ftello和fseeko 1 2 3 #include <stdio.h> off_t ftello (FILE *fp) ; int fseeko (FILE *fp, off_t offset, int whence) ;
fgetops和fsetops 1 2 3 4 #include <stdio.h> int fgetspos (FILE *restrict fp, fpos_t *restrict pos) ;int fsetpos (FILE *fp, const fpos_t *pos) ;
格式化的输出 1 2 3 4 5 6 7 8 9 10 #include <stdio.h> int printf (const char *restrict format, ...) ; int fprintf (FILE *restrict fp, const char *restrict format, ...) ; int dprintf (int fd, const char *restrict format, ...) ; return :number of characters output if OK,negative value if output errorint sprintf (char *restrict buf, const char *restrict format, ...); return :number of characters stored in array if OK,negative value if encoding errorint snprintf (char *restrict buf, size_t n, const char *restrict format, ...); return :number of characters that would have been stored in array if buffer was large enough,negative value if encoding error
格式化字符串的格式
flags
字符
描述
+
总是表示有符号数值的’+’或’-‘号,缺省情况是忽略正数的符号。仅适用于数值类型。
空格
使得有符号数的输出如果没有正负号或者输出0个字符,则前缀1个空格。如果空格与’+’同时出现,则空格说明符被忽略。
-
左对齐。缺省情况是右对齐。
#
对于’g’与’G’,不删除尾部0以表示精度。对于’f’, ‘F’, ‘e’, ‘E’, ‘g’, ‘G’, 总是输出小数点。对于’o’, ‘x’, ‘X’, 在非0数值前分别输出前缀0, 0x, and 0X表示数制。
0
如果width选项前缀以0,则在左侧用0填充直至达到宽度要求。例如printf(“%2d”, 3)输出” 3”,而printf(“%02d”, 3)输出”03”。如果0与-均出现,则0被忽略,即左对齐依然用空格填充。
fldwidth
指定宽度
precision
指定精度
lenmodifier
字符
描述
hh
对于整数类型,printf期待一个从char提升的int尺寸的整型参数。
h
对于整数类型,printf期待一个从short提升的int尺寸的整型参数。
l
对于整数类型,printf期待一个long尺寸的整型参数。 对于浮点类型,printf期待一个double尺寸的整型参数。 对于字符串s类型,printf期待一个wchar_t指针参数。 对于字符c类型,printf期待一个wint_t型的参数。
ll
对于整数类型,printf期待一个long long尺寸的整型参数。Microsoft也可以使用I64
L
对于浮点类型,printf期待一个long double尺寸的整型参数。
z
对于整数类型,printf期待一个size_t尺寸的整型参数。
j
对于整数类型,printf期待一个intmax_t尺寸的整型参数。
t
对于整数类型,printf期待一个ptrdiff_t尺寸的整型参数。
convtype
转换类型,必选字段
字符
描述
d,i
有符号十进制数值int。’%d’与’%i’对于输出是同义;但对于scanf()输入二者不同,其中%i在输入值有前缀0x或0时,分别表示16进制或8进制的值。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
u
十进制unsigned int。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
f,F
double型输出10进制定点表示。’f’与’F’差异是表示无穷与NaN时,’f’输出’inf’, ‘infinity’与’nan’;’F’输出’INF’, ‘INFINITY’与’NAN’。小数点后的数字位数等于精度,最后一位数字四舍五入。精度默认为6。如果精度为0且没有#标记,则不出现小数点。小数点左侧至少一位数字。
e,E
double值,输出形式为10进制的([-]d.ddd e[+/-]ddd). E版本使用的指数符号为E(而不是e)。指数部分至少包含2位数字,如果值为0,则指数部分为00。Windows系统,指数部分至少为3位数字,例如1.5e002,也可用Microsoft版的运行时函数_set_output_format 修改。小数点前存在1位数字。小数点后的数字位数等于精度。精度默认为6。如果精度为0且没有#标记,则不出现小数点。
g,G
double型数值,精度定义为全部有效数字位数。当指数部分在闭区间 [-4,精度] 内,输出为定点形式;否则输出为指数浮点形式。’g’使用小写字母,’G’使用大写字母。小数点右侧的尾数0不被显示;显示小数点仅当输出的小数部分不为0。
x,X
16进制unsigned int。’x’使用小写字母;’X’使用大写字母。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
o
8进制unsigned int。如果指定了精度,则输出的数字不足时在左侧补0。默认精度为1。精度为0且值为0,则输出为空。
s
如果没有用l标志,输出null结尾字符串直到精度规定的上限;如果没有指定精度,则输出所有字节。如果用了l标志,则对应函数参数指向wchar_t型的数组,输出时把每个宽字符转化为多字节字符,相当于调用wcrtomb函数。
c
如果没有用l标志,把int参数转为unsigned char型输出;如果用了l标志,把wint_t参数转为包含两个元素的wchart_t数组,其中第一个元素包含要输出的字符,第二个元素为null宽字符。
p
void *型
a,A
double型的16进制表示,”[−]0xh.hhhh p±d”。其中指数部分为10进制表示的形式。例如:1025.010输出为0x1.004000p+10。’a’使用小写字母,’A’使用大写字母。(C++11流使用hexfloat输出16进制浮点数)
n
不输出字符,但是把已经成功输出的字符个数写入对应的整型指针参数所指的变量。
%
‘%’字面值,不接受任何flags, width, precision or length。
printf家族变体 用于自定义printf
函数的时候使用
1 2 3 4 5 6 7 #include <stdarg.h> #include <stdio.h> int vprintf (const char *restrict format, va_llist arg) ;int vfprintf (FILE *restrict fp, const char *restrict format, va_list arg) ;int vdprintf (int fd, const char *restrict buf,const char *restrict format, va_list arg) ;int vsnprintf (char *restrict buf, size_t n,const char *restrict format, va_list arg) ;
格式化输入 1 2 3 4 5 6 #include <stdio.h> int scanf (const char *restrict format, ...) ;int fscanf (FILE *restrict fp, const char *restrict format, ...) ;int sscanf (const char r*restrict buf, const char *restrict format, ...) ;
*
:用于跳过当前的输入
fldwidth
:指定字段宽度
lenmodifier
:字段大小
convtyoe
:转换类型
scanf家族变体 1 2 3 4 5 6 #include <stdarg.h> #include <stdio.h> int vscanf (const char *restrict format, va_list arg) ;int vfscanf (FILE *restrict fp, const char *restrict format, va_list arg) ;int vsscanf (const char *restrict buf, const char *restrict format, va_list arg) ;
fileno 1 2 3 #include <stdio.h> int fileno (FILE *fp) ; return :流相关联文件描述符
tmpname和tempfile 1 2 3 4 #include <stdio.h> char *tmpname (char *ptr) ; FILE *tmpfile (void ) ;
mkdtemp和mkstemp 1 2 3 #include <stdlib.h> char *mkdtemp (char *template) ; int mkstemp (char *template) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <stdio.h> #include <errno.h> #include <sys/stat.h> #include <stdlib.h> void make_temp (char *template) ;int main () { char good_template[] = "/tmp/dirXXXXXX" ; char *bad_template = "/tmp/dirXXXXXX" ; printf ("tring to create first temp file...\n" ); make_temp(good_template); printf ("trying to create second temp file...\n" ); make_temp(bad_template); exit (0 ); }void make_temp (char *template) { int fd; struct stat sbuf ; if ((fd = mkstemp(template)) < 0 ) fprintf (stderr ,"%s" ,"can't create temp file" ); printf ("temp name = %s\n" ,template); close(fd); if (stat(template,&sbuf) < 0 ){ if (errno == ENOENT) printf ("file doesn't exit\n" ); else printf ("stat failed" ); } else { printf ("file exists\n" ); unlink(template); } }
内存流 用于创建内存流
1 2 #include <stdio.h> FILE *fmemopen (void *restrict buf, size_t size, const char *restrict type) ;
用于创建内存流
1 2 3 4 5 #include <stdio.h> FILE *open_memstream (char **bufp, size_t *sizep) ;#include <wchar.h> FILE *open_wmemstream (wchar_t **bufp, size_t *sizep) ;
密码文件
每个成员使用冒号隔开
getpwuid和getpwname 通过用户id
与用户名获取密码
1 2 3 4 #include <pwd.h> struct passwd *getpwuid (uid_t uid) ;struct passwd *getpwnam (const char *name) ;
用于遍历整个密码文件
1 2 3 4 5 6 #include <pwd.h> struct passwd *getpwent (void ) ; void setpwent (void ) ; void endpwent (void ) ;
6-2
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <pwd.h> #include <stddef.h> #include <string.h> struct passwd *getpwname (const char *name) { struct passwd *ptr ; setpwent(); while ((ptr = getpwent()) != NULL ) if (strcmp (name, ptr->pw_name) == 0 ) break ; endpwent(); return (ptr); }
影子密码文件
用于获取影子密码文件的函数
1 2 3 4 5 6 7 #include <shadow.h> struct spwd *getspname (const char *name) ;struct spwd *getspent (void ) ;void setspent (void ) ;void endspent (void ) ;
组文件
1 2 3 #include <grp.h> struct group *getgrgid (gid_t gid) ;struct group *getgrname (const char *name) ;
用于遍历整个组文件
1 2 3 4 #include <grp.h> struct group *getgrent (void ) ;void setgrent (void ) ;void endgrent (void ) ;
补充组ID 1 2 3 4 5 6 7 8 9 10 11 12 13 #include <unistd.h> int getgroups (int gidsetsize, gid_t grouplist[]) ;#include <grp.h> #include <unistd.h> int setgroups (int ngroups, const gid_t grouplist[]) ;#include <grp.h> #include <unistd.h> int initgroups (const char *username, gid_t basegid) ;
其他系统文件
登录记录 utmp
用于跟踪过当前登录的所有用户
系统识别 获取当前主机与操作系统的信息
1 2 3 #include <sys/utsname.h> int uname (struct utsname *name)
获取TCP/IP网络上的主机名称
1 2 3 #include <unistd.h> int gethosstname (char *name,int namelen) ;
时间与日期 获取当前时间和日期
1 2 3 #include <time.h> time_t time (time_t *calptr) ;
用于获取指定时钟的时间
1 2 3 #include <sys/time.h> int clock_gettime (clockid_t clock_id, struct timespec *tsp) ;
用于获取指定精度时间
1 2 3 #include <sys/time.h> int clock_getres (clockid_t clock_id, struct timespec *tsp) ;
用于设置特定的时间
1 2 3 #include <sys/time.h> int clock_settime (clockid_t clock_id, const struct timespec *tsp) ;
比time
函数更高的精度获取时间
1 2 #include <sys/time.h> int gettimeofday (struct timeval *restrict tp, void *restrict tzp) ;
时间之间的关系
时间转化
1 2 3 4 #include <time.h> struct tm *gmtime (const time_t *calptr) ;struct tm*localtime (const time_t *calptr)
函数mktime获取一个表示为本地时间的分解时间,并将其转换为time_t值
1 2 3 #include <time.h> time_t mktime (struct tm*tmptr) ;
以格式化字符串的形式转换时间
1 2 3 4 5 #include <time.h> size_t strftime (charr *restrict buf,size_t maxsize, const char *restrict format,const struct tm*restrict tmptr) ;size_t strftime_l (char *restrict buf,size_t maxsize,const char *restrict format,const struct tm*restrict tmptr, locale_t locale) ;
将字符串转化为中断时间
1 2 3 #include <time.h> char *strptime (const char *restrict buf, const char *restrict format, struct tm *restrct tmptr) ;
Exit函数 exit、_Exit和_exit
函数 _exit
和_Exit
会马上返回内核,而exit
会执行清理过程再返回内核
1 2 3 4 5 6 7 8 9 #include <stdlib.h> void exit (int status) ;void _Exit(int status);#include <unistd.h> void _exit(int status);
atexit 一个进程可以设置至少32个退出时自动调用的函数,通过atexit
函数可以进行注册,调用顺序与执行顺序相反
1 2 3 #include <stdlib.h> int atexxit (void (*func)(void )) ;
内存分配 malloc、calloc和realloc函数 1 2 3 4 5 #include <stdlib.h> void *malloc (size_t size) ;void *calloc (size_t nobj, size_t size) ;void *realloc (void *ptr, size_t newsize) ;
环境变量 环境变量的形式为name = value
getenv 1 2 3 4 #include <stdlib.h> char *getenv (const char *name) ; Returns value;
putenv、setenv和unsetenv 1 2 3 4 5 #include <stdlib.h> int putenv (char *str) ;int setenv (const char *name, const char *value,int rewrite) ;int unsetenv (const char *name) ;
setjmp和longjmp 1 2 3 4 #include <setjmp.h> int setjmp (jmp_buf env) ;void longjmp (jmp_buf env,int cal) ;
getrlimit和setrlimit 通过这两个函数可以查询和设置资源限制
1 2 3 4 #include <sys/resource.h> int getrlimit (int resource, struct rlimit *rlptr) ;int setrlimit (int resource, const struct rlimit *rlptr) ;
获取进程id 1 2 3 4 5 6 7 8 #include <unistd.h> pid_t getpid (void ) ; pid_t getppid (void ) ; uid_t getuid (void ) ; uid_t geteuid (void ) ; gid_t getgid (void ) ; gid_t getegid (void ) ;
fork函数 现有进程可以通过调用fork函数来创建新进程。
1 2 3 4 5 #include <unistd.h> pid_t fork (void ) ;
wait和waitpid 等待子进程完成执行
1 2 3 4 5 #include <sys/wait.h> pid_t wait (int *statloc) ;pid_t waitpid (pid_t pid,int *statloc, int options) ;
宏
描述
WIFEXITED(status)
如果子进程正常结束则返回True
WIFSIGNALED(status)
如果子进程异常结束则返回True,通过WTERMSIG(status)可以获取信号的序号,通过WCOREDUMP(status)可以判断是否生成core文件
WIFSTOPPED(status)
如果子进程被停止返回True。通过WSTOPSIG(status)可以获取导致子进程停止的信号序号
WIFCONTINUED(status)
如果在作业控制停止后继续的子进程返回True
waitid函数 1 2 #include <sys/wait.h> int waitid (idtype_t idtype, id_t id, siginfo_t *infop,int options) ;
类型
P_PID:等待特定的进程id,id用于标识特定的进程id
P_GID:等待特定的进程组id,id用于标识特定的组id
P_ALL:等待热河子进程,id可以被忽略
选项
WCONTINUED:等待先前已停止并已继续且其状态尚未报告的进程
WEXITED:等待已退出的进程
WNOHANG:如果没有可用的子退出状态,则立即返回而不是阻止
WSTOPPED:等待已停止且状态尚未报告的进程
wait3和wait4 1 2 3 4 5 6 7 f#include <sys/wait.h> #include <sys/time.h> #include <sys/resource.h> pid_t wait3 (int *statloc,int options, struct rusage *resage) ;pid_t wait4 (pid_t pid,int *statloc,int options, struct rusage *rusage) ;
exec函数 exec用磁盘上的一个新程序替换了当前进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 #include <unistd.h> int execl (const char *pathname, const char *arg0, ...) ;int execv (const char *pathname,char *const argv[]) ;int execle (const char *pathname, const char *arg0,...) ;int execve (const char r*pathname,char *const argv[],char *const envp[]) ;int execlp (const char *filename,const char *arg0,...) ;int execvp (const char *filename, char *const argv[]) ;int fexecve (int fd,char *consst argv[],char *const envp[]) ;
system函数 1 2 3 #include <stdlib.h> int system (const char *cmdstring) ;
用户识别 1 2 #include <unistd.h> char *getlogin (void ) ;
进程调度 获取调度优先级
1 2 #include <unistd.h> int nice (int incr) ;
获取一组进程的调度优先级
1 2 #include <sys/resource.h> int getpriority (int which, id_t who) ;
设置进程的调用优先级
1 2 #include <sys/resource.h> int setpriority (int which, id_t who, int value) ;
进程时间 1 2 #include <sys/times.h> clock_t times (struct tms *buf) ;
进程组 获取进程组id
1 2 3 #include <unistd.h> pid_t getpgrp (void ) ;
1 2 3 #include <unistd.h> pid_t getpgid (pid_t pid) ;
加入或创建组
1 2 3 #include <unistd.h> int setpgid (pid_t pid,pid_t pgid) ;
会话 setsid 建立会话
1 2 3 #include <unistd.h> pid_t setsid (void ) ;
getsid 获取会话id
1 2 #include <unistd.h> pid_t getsid (pid_t pid) ;
tcgetpgrp,tcsetpgrp and tcgetsid 获取前台进程
1 2 3 #include <unistd.h> pid_t tcgetpgrp (int fd) ; int tcsetpgrp (int fd,pid_t pgrpid) ;
通过TTY
文件获取会话负责人的进程组ID
1 2 #include <termios.h> pid_t tcgetsid (int fd) ;
信号 信号处理函数
1 2 #include <signal.h> void (*signal(int signo, void (*func)(int )))(int );
参数func
SIG_IGN
忽略信号
SIG_DFL
默认值操作
函数地址,自定义操作
kill和raise kill
函数用于给进程与进程组发送信号
1 2 3 4 5 #include <signal.h> int kill (pid_t pid,int signo) ;#raise = kill(getpid(),signo); int raise (int signo) ;
pid > 0
,发送的进程ID
为pid
pid == 0
,向该进程组所有进程发送信号
pid < 0
,向指定的进程组发送信号
pid == -1
,向系统所有进程发送信号
alarm和pause alarm
允许设置一个计时器,当计时器到期时,生成SIGALRM
信号。默认操作是终止进程。
1 2 #include <unistd.h> unsigned int alarm (unsigned int seconds) ;
pause
函数暂停调用过程,直到捕捉到信号。
1 2 3 #include <unistd.h> int pause (void ) ;
信号设置 1 2 3 4 5 6 7 #include <signal.h> int sigemptyset (sigset_t *set ) ; int sigfillset (sigset_t *set ) ; int sigaddset (sigset_t *set ,int signo) ;int sigdelset (sigset_t *set ,int signo) ; return 0 :if OK, -1 on errorint sigismember(conset sigset_t *set ,int signo);
sigprocmask 用于修改信号掩码
1 2 3 #include <signal.h> int sigprocmask (int how,const sigset_t *restrict set , sigset_t *restrict oset) ;
oset
用于存储当前进程的信号掩码
set
若不为空,则how
参数指示当前信号掩码如何修改
how
参数选项如下
SIG_BLOCK
:set
指向需要阻塞的信号
SIG_UNBLOCK
:set
指向需要解除的信号
SIG_SETMASK
:新信号的掩码被set
替换
sigpending sigpending
函数用于返回被阻塞的信号或者等待被调用的信号集,返回的信号集存放在set
1 2 3 #include <signal.h> int sigpending (sigset_t *set ) ; Ruturns:0 if OK, -1 on error
例子
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <stdio.h> #include <stdlib.h> #include <signal.h> static void sig_quit (int ) ;int main (void ) { sigset_t newmask, oldmask, pendmask; if (signal(SIGQUIT, sig_quit) == SIG_ERR) { fprintf (stderr ,"can't catch SIGQUIT" ); exit (-1 ); } sigemptyset(&newmask); sigaddset(&newmask, SIGQUIT); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0 ) { fprintf (stderr ,"SIG_BLOCK error" ); exit (-1 ); } sleep(5 ); if (sigpending(&pendmask) < 0 ) { fprintf (stderr ,"sigpending error" ); exit (-1 ); } if (sigismember(&pendmask, SIGQUIT)) printf ("\nSIGQUIT pending\n" ); if (sigprocmask(SIG_SETMASK, &oldmask, NULL ) < 0 ) { fprintf (stderr ,"SIG_SETMASK error" ); exit (-1 ); } printf ("SIGQUIT unblocked\n" ); sleep(5 ); exit (0 ); }static void sig_quit (int signo) { printf ("caught SIGQUIT\n" ); if (signal(SIGQUIT, SIG_DFL) == SIG_ERR) { fprintf (stderr ,"can't reset SIGQUIT" ); exit (-1 ); } }
sigaction 用于检查或修改特定信号相关动作
1 2 3 #include <signal.h> int sigaction (int signo, const struct sigaction *restrict act,struct sigaction *restrict oact) ; Retruns:0 if OK, -1 on error
signo
为正在检查或修改动作的信号
act
为修改的动作
oact
为原先的动作
1 2 3 4 5 6 struct sigaction { void (*sa_handler)(int ); sigset_t sa_mask; int sa_flags; void (*sa_sigaction)(int , siginfo_t *, void *); };
1 2 3 4 5 6 7 8 9 10 11 struct siginfo {int si_signo; int si_errno; int si_code; pid_t si_pid; uid_t si_uid; void *si_addr; int si_status; union sigval si_value ; };
1 2 3 4 5 6 7 ucontext_t *uc_link; sigset_t uc_sigmask; stack_t uc_stack; mcontext_t uc_mcontext;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <signal.h> Sigfunc * signal (int signo, Sigfunc *func) { struct sigaction act , oact ; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0 ; if (signo == SIGALRM){#ifdef SA_INTERRUPT act.ssa_flags |= SA_IINTERRUPT;#endif }else { act.sa_flags |= SA_RESTART; } if (sigaction(signo,&act, &ocat) < 0 ) return (SIG_ERR); retuurn(ocat.sa_handler); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <singal.h> Sigfunc * signal_intr (int signo, Sigfunc *func) { struct sigaction act , oact ; act.sa_handler = func; sigemptyset(&act.sa_mask); act.sa_flags = 0 ;#ifdef SA_INTERRUPT act.sa_flags |= SA_INTERRUPT;#endif if (sigaction(signo, &act, &oact) < 0 ) return (SIG_ERR); return (oact.sa_handler); }
sigsetjmp和siglongjmp 1 2 3 4 #include <setjmp.h> int sigsetjmp (sigjmp_buf env,int savemask) ; Returns:0 if called directly,nonzero if returning from a call to siglong jmpvoid siglongjmp (sigjmp_buf env,int val) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 #include <stdio.h> #include <setjmp.h> #include <time.h> #include <stdlib.h> #include <signal.h> static void sig_usr1 (int ) ;static void sig_alrm (int ) ;static sigjmp_buf jmpbuf;static volatile sig_atomic_t canjump;int main (void ) { if (signal(SIGUSR1, sig_usr1) == SIG_ERR) { fprintf (stderr ,"signal(SIGUSR1) error" ); exit (-1 ); } if (signal(SIGALRM, sig_alrm) == SIG_ERR) { fprintf (stderr ,"signal(SIGALRM) error" ); exit (-1 ); } printf ("starting main: " ); if (sigsetjmp(jmpbuf,1 )){ printf ("ending main: " ); exit (0 ); } canjump = 1 ; for ( ; ; ) pause(); }static void sig_usr1 (int signo) { time_t starttime; if (canjump == 0 ) return ; printf ("starting sig_usr1: " ); alarm(3 ); starttime = time(NULL ); for ( ; ; ) if (time(NULL ) > starttime + 5 ) break ; printf ("finishing sig_ussr1: " ); canjump = 0 ; siglongjmp(jmpbuf, 1 ); }static void sig_alrm (int signo) { printf ("in sig_alrm: " ); }
sigsuspend 恢复信号掩码
1 2 3 #include <signal.h> int sigsuspend (const sigset_t *sigmask) ; Returns:-1 with errno set to EINTR
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <signal.h> #include <stdlib.h> #include <stdio.h> volatile sig_atomic_t quitflag;static void sig_int (int signo) { if (signo == SIGINT) printf ("\ninterrupt\n" ); else if (signo == SIGQUIT) quitflag = 1 ; }int main (void ) { sigset_t newmask, oldmask, zeromask; if (signal(SIGINT, sig_int) == SIG_ERR) { fprintf (stderr ,"signal(SIGINT) error" ); exit (-1 ); } if (signal(SIGQUIT, sig_int) == SIG_ERR) { fprintf (stderr ,"signal(SIGQUIT) error" ); exit (-1 ); } sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGQUIT); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0 ) { fprintf (stderr ,"SIG_BLOCK error" ); exit (-1 ); } while (quitflag == 0 ) sigsuspend(&zeromask); quitflag = 0 ; if (sigprocmask(SIG_SETMASK, &oldmask, NULL )) { fprintf (stderr ,"SIG_SETMASK error" ); exit (-1 ); } exit (0 ); }
abort 终止函数会导致程序异常退出
1 2 #include <stdlib.h> void abort (void ) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <signal.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> void abort (void ) { sigset_t mask; struct sigaction action ; sigaction(SIGABRT,NULL ,&action); if (action.sa_handler == SIG_IGN){ action.sa_handler = SIG_DFL; sigaction(SIGABRT,&action,NULL ); } if (action.sa_handler == SIG_DFL) fflush(NULL ); sigfillset(&mask); sigdelset(&mask,SIGABRT); sigprocmask(SIG_SETMASK,&mask,NULL ); kill(getpid(),SIGABRT); fflush(NULL ); action.sa_handler = SIG_DFL; sigaction(SIGABRT, &action, NULL ); sigprocmask(SIG_SETMASK, &mask, NULL ); kill(getpid(), SIGABRT); exit (1 ); }
sleep,nanosleep和clock_nanosleep 暂停当前进程
1 2 3 #include <unistd.h> unsigned int sleep (unsigned int seconds) ; Returns:0 or number of unslept seconds
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <stdio.h> #include <stdlib.h> #include <signal.h> static void sig_alrm (int signo) { }unsigned int sleep (unsigned int seconds) { struct sigaction newact , oldact ; sigset_t newmask, oldmask, suspmask; unsigned int unslept; newcat.sa_handler = sig_alrm; sigemptyset(&newact.sa_mask); newact.sa_flags = 0 ; sigaction(SIGALRM, &newact, &oldact); sigemptyset(&newmask); sigaddset(&newmask, SIGALRM); sigprocmask(SIG_BLOCK, &newmask, &oldmask); alarm(seconds); suspmask = oldmask; sigdelset(&suspmask, SIGALRM); sigsuspend(&suspmask); unslept = alarm(0 ); sigaction(SIGALRM, &oldact, NULL ); sigprocmask(SIG_SETMASK, &oldmask, NULL ); return (unslept); }
纳秒级别休眠
1 2 3 #include <time.h> int nanosleep (const struct timespec *reqtp, struct timespec *remtp) ; Returns: 0 if slept for requested time or -1 on error
指定时间类型的休眠函数
1 2 3 #include <time.h> int clock_nanosleep (clockid_t clock_id, int flags, const struct timespec *reqtp, struct timespec *remtp) ; Returns: 0 if slept for requeested time or error number on failure
clock_id
用于指定计算时间延迟所依据的时钟。
flags
用于标记是绝对延迟还是相对延迟
相对时间:想要休眠多长时间,0为相对时间
绝对时间:休眠到哪个时间点
sigqueue 用于排序的信号
1 2 3 #include <signal.h> int sigqueue (pid_t pid,int signo, const union sigval value) Returns: 0 if OK, -1 on error
使用排队信号的条件
使用sigaction
函数时,需要使用SA_SIGINFO
标志
sigaction
结构需要使用sa_sigation
成员中提供信号处理程序
使用sigqueue
函数发送信号
psignal和psiginfo 打印信号对应的字符串
1 2 3 #include <signal.h> void psignal (int signo, const char *msg) ;void psiginfo (const siginfo_t *info,const char *msg)
返回信号对应的字符串,不打印
1 2 #include <string.h> char *strsignal (int signo) ;
将信号与字符串进行映射
1 2 3 #include <signal.h> int sig2str (int signo, char *str) ;int str2sig (const char *str, int *signop) ;
线程 pthread_equal 比较线程id
1 2 3 4 #include <pthread.h> int pthread_equal (pthread_t tid1, pthread_t tid2) ;
pthread_self 获取线程id
1 2 3 #include <pthread.h> pthread_t pthread_self (void ) ;
pthread_create 线程创建
1 2 3 4 5 6 #include <pthread.h> int pthread_create (pthread_t *restrict tidp, const pthread_attr_t *restrict attr, void *(*start_rtn)(void *), void *restrict arg) ;
tidp
被分配的线程ID
attr
自定义各种线程属性
start_trn
,函数地址
arg
运行的参数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_t ntid;void printids (const char *s) { pid_t pid; pthread_t tid; pid = getpid(); tid = pthread_self(); printf ("%s pid %lu tid %lu (0x%lx)\n" ,s,(unsigned long )pid,(unsigned long )tid,(unsigned long )tid); }void *thr_fn (void *arg) { printids("new thread: " ); return ((void *)0 ); }int main (void ) { int err; err = pthread_create(&ntid, NULL , thr_fn, NULL ); if (err != 0 ) { fprintf (stderr ,"can't create thread" ); exit (-1 ); } printids("main thread:" ); sleep(1 ); exit (0 ); }
pthread_exit 线程退出函数
1 2 3 #include <pthread.h> void pthread_exit (void *rval_ptr)
pthread_join 用于等待线程的结束
1 2 3 #include <pthread.h> int pthread_join (pthread_t thread, void **rval_ptr)
thread
等待的线程id
rval_ptr
用于存储线程的返回值
pthread_cancel 取消线程
1 2 #include <pthread.h> int pthread_cancel (pthread_t tid) ;
pthread_cleanup_push和pthread_cleanup_pop 线程的清理处理函数
1 2 3 #include <pthread.h> void pthread_cleanup_push (void (*rtn)(void *),void *arg) ;void pthread_cleanup_pop (int execute) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <stdio.h> #include <stdlib.h> #include <pthread.h> void cleanup (void *arg) { printf ("cleanup: %s\n" , (char *)arg); }void *thr_fn1 (void *arg) { printf ("thread 1 start\n" ); pthread_cleanup_push(cleanup,"thread 1 first handler" ); pthread_cleanup_push(cleanup,"thread 1 second handler" ); printf ("thread 1 push complete\n" ); if (arg) return ((void *)1 ); pthread_cleanup_pop(0 ); pthread_cleanup_pop(0 ); return ((void *)1 ); }void *thr_fn2 (void *arg) { printf ("thread 2 start\n" ); pthread_cleanup_push(cleanup,"thread 2 first handler" ); pthread_cleanup_push(cleanup,"thread 2 second handler" ); printf ("thread 2 push complete\n" ); if (arg) pthread_exit((void *)2 ); pthread_cleanup_pop(0 ); pthread_cleanup_pop(0 ); pthread_exit((void *)2 ); }int main (void ) { int err; pthread_t tid1, tid2; void *tret; err = pthread_create(&tid1, NULL , thr_fn1,(void *)1 ); if (err != 0 ) { fprintf (stderr ,"can't create thread 1" ); exit (-1 ); } err = pthread_create(&tid2, NULL , thr_fn2, (void *)1 ); if (err != 0 ) { fprintf (stderr ,"can't create thread 2" ); exit (-1 ); } err = pthread_join(tid1, &tret); if (err != 0 ) { fprintf (stderr ,"can't join thread 1" ); exit (-1 ); } err = pthread_join(tid2,&tret); if (err != 0 ) { fprintf (stderr ,"can't join thread 2" ); exit (-1 ); } exit (0 ); }
pthread_detach 分离线程
1 2 #include <pthread.h> int pthread_detach (pthread_t tid) ;
线程互斥锁 pthread_mutex_init和pthread_mutex_destroy 1 2 3 4 5 #include <pthread.h> int pthread_mutext_init (pthread_mutex_t *restrict mutex,const pthreaad_mutexattr_t *retrict attr) ; int pthread_mutext_destroy (pthread_mutex_t *mutex) ; Both return : 0 if OKk, error number on failure
锁定互斥锁 1 2 3 4 5 #include <pthread.h> int pthread_mutex_loc (pthread_mutex_t *mutex) ; int pthread_mutex_trylock (pthread_mutex_t *mutex) ;int pthread_mutex_unlock (pthread_mutex_t *mutex) ; All return :0 if OK, error number on failure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <stdio.h> #include <pthread.h> #include <stdlib.h> struct foo { int f_count; pthread_mutex_t f_lock; int f_id; };struct foo *foo_alloc (int id) { struct foo *fp ; if ((fp = malloc (sizeof (struct foo))) != NULL ){ fp->f_count = 1 ; fp->f_id = id; if (pthread_mutex_init(&foo->f_lock,NULL ) != 0 ){ free (fp); return (NULL ); } } return (fp); }void foo_hold (struct foo *fp) { pthread_mutex_lock(&fp->f_lock); fp->f_count++; pthread_mutex_unlock(&fp->f_lock); }void foo_rele (struct foo *fp) { pthread_mutex_lock(&fp->f_lock); if (--fp->f_count == 0 ){ pthread_mutex_unlock(&fp->f_lock); pthread_mutex_destroy(&fp->f_lock); free (fp); fp = NULL ; } else { pthread_mutex_unlock(&fp->f_lock); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <stdlib.h> #include <pthread.h> #define NHASH 29 #define HASH(id) ((((unsigned long)id)%NHASH) struct foo *fh [NHASH ];pthread_mutext_t hashlock = PTHREAD_MUTEX_INITALIZER; struct foo { int f_count; pthread_mutext_t f_lock; int f_id; struct foo *f_next ; };struct foo *foo_alloc (int id) { struct foo *fp ; int idx; if ((fp = malloc (sizeof (struct foo))) != NULL ){ fp->f_count = 1 ; fp->f_id = id; if (pthread_mutext_init(&fp->f_lock, NULL ) != 0 ){ free (fp); return (NULL ); } idx = HASH(id); pthread_mutex_lock(&hashlock); fp->f_next = fh[idx]; fh[idx] = fp; pthread_mutext_lock(&fp->f_lock); pthread_mutext_unlock(&hashlock); pthread_mutext_unlock(&fp->f_lock); } return (fp); }void foo_hold (struct foo *fp) { pthread_mutex_lock(&fp->f_lock); fp->f_count++; pthread_mutext_unlock(&fp->f_lock); }struct foo *foo_find (int id) { struct foo *fp ; pthread_mutex_lock(&hashlock); for (fp = fh[HASH(id)]; fp != NULL ; fp = fp->f_next){ if (fp->f_id == id){ foo_hold(fp); break ; } } pthread_mutext_unlock(&hashlock); return (fp); }void foo_rele (struct foo *fp) { struct foo *tfp ; int idx; pthread_mutex_lock(&fp->f_lock); if (fp->f_count == 1 ){ pthread_mutext_unlock(&fp->f_lock); pthread_mutex_lock(&hashlock); pthread_mutext_lock(&fp->f_lock); if (fp->f_count != 1 ){ fp->f_count--; pthread_mutext_unlock(&fp->f_lock); pthread_mutext_unlock(&hashlock); return ; } idx = HASH(fp->f_id); tfp = fh[idx]; if (tfp == fp){ fh[idx] = fp->f_next; } else { while (tfp->f_next != fp) tfp = tfp->f_next; tfp->f_next = fp->f_next; } pthread_mutext_unlock(&hashlock); pthread_mutext_unlock(&fp->f_lock); pthread_mutext_destroy(&fp->f_lock); free (fp); } else { fp->f_count--; pthread_mutext_unlock(&fp->f_lock); } }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <stdlib.h> #include <pthread.h> #define NHASH 29 #define HASH(id) (((unsigned long)id) %NHASH)) struct foo *fh [NHASH ];pthread_mutext_t hashlock = PTHREAD_MUTEX_INITALIZER;struct foo { int f_count; pthread_mutext_t f_lock; int f_id; struct foo *f_next ; };struct foo *foo_alloc (int id) { struct foo *fp ; int idx; if ((fp = malloc (sizeof (struct foo))) != NULL ){ fp->f_count = 1 ; fp->f_id = id; if (pthread_mutext_init(&fp-f_lock, NULL ) != 0 ){ free (fp); return (NULL ); } idx = HASH(id); pthread_mutex_lock(&hashlock); fp->f_next = fh[idx]; fh[idx] = fp; pthread_mutext_lock(&fp->f_lock); pthread_mutext_unlock(&hashlock); pthread_mutext_unlock(&fp->f_lock); } return (fp); }void foo_rele (struct foo *fp) { struct foo *tfp ; int idx; pthread_mutex_lock(&hashlock); if (--fp->f_count == 0 ){ idx = HASH(fp->f_id); tfp = fh[idx]; if (tfp == fp){ fh[idx] = fp->f_next; } else { while (tfp->f_next != fp) tfp = tfp->f_next; tfp->f_next = fp->f_next; } pthread_mutext_unlock(&hashlock); pthread_mutext_destroy(&fp->f_lock); free (fp); } else { pthread_mutext_unlock(&hashlock); } }
pthread_mutex_tiimedlock 等待指定时间在加锁
1 2 3 4 5 #include <pthread.h> #include <time.h> int pthread_mutex_timedlock (pthread_mutex_t *restrict mutex,const struct timespec *restrict tsptr) ; Returns:0 if OK, error number on failure
读写器锁 初始化读写器锁
1 2 3 4 5 6 #include <pthread.h> int pthread_rwlock_init (pthread_rwlock_t *restrict rwlock,const pthread_rwlockattr_t *restrict attr) ;int pthread_rwlock_destroy (pthread_rwlock_t *rwlock) ; Both return : 0 if OK, error number on failure
锁读写器锁
1 2 3 4 5 #include <pthread.h> int pthread_rwlock_rdlock (pthread_rwlock_t *rwlock) ; int pthread_rwlock_wrlock (pthread_rwlock_t *rwlock) ; int pthread_rwlock_unlock (pthread_rwlock_t *rwlock) ; All return : 0 if OK, error number on failure
读写器锁定原语的条件版本
1 2 3 4 #include <pthread.h> int pthread_rwlock_tryrdlock (pthread_rwlock_t *rwlock) ;int pthread_rwlock_trywrlock (pthread_rwlock_t *rwlock) ; Both return : 0 if OK, error number on failure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 #include <stdlib.h> #include <pthread.h> struct job { struct job *j_next ; struct job *j_prev ; pthread_t j_id; };struct queue { struct job *q_head ; struct job *q_tail ; pthread_rwlock_t q_lock; };int queue_init (struct queue *qp) { int err; qp->q_head = NULL ; qp->q_tail = NULL ; err = pthread_rwlock_init(&qb->q_lock,NULL ); if (err != 0 ) return (err); return (0 ); }void job_insert (struct queue *qp, struct job *jp) { pthread_rwlock_wrlock(&qb->q_lock); jp->j_next = qp->q_head; jp->j_prev = NULL ; if (qb->q_head != NULL ) qb->q_head->j_prev = jp; else qb->q_tail = jp; qb->q_head = jp; pthread_rwlock_unlock(&qb->q_lock); }void job_append (struct queue *qp, struct job *jp) { pthread_rwlock_wrlock(&qb->q_lock); jp->j_next = NULL ; jp->j_prev = qp->q_tail; if (qp->q_tail != NULL ) qp->q_tail->j_next = jp; else qp->q_head = jp; qp->q_tail = jp; pthread_rwlock_unlock(&qb->q_lock); }void job_remove (struct queue *qp,struct job *jp) { pthread_rwlock_wrlock(&qb->q_lock); if (jp == qb->q_head){ qp->q_head = jp->j_next; if (qp->q_tail == jp) qp->q_tail = NULL ; else jp->j_next->j_prev = jp->j_prev; } else if (jp == qb->q_tail){ qp->q_tail = jp->j_prev; jp->j_prev->j_next = jp->j_next; } else { jp->j_prev->j_next = jp->j_next; jp->j_next->j_prev = jp->j_prev; } pthread_rwlock_unlock(&qb->q_lock); }struct job *job_find (struct queue *qp, pthread_t id) { struct job *jp ; if (pthread_rwlock_rdlock(&qp->q_lock) != 0 ) return (NULL ); for (jp = qp->q_head; jp != NULL ; jp = jp->j_next) if (pthread_equal(jp->j_id, id)) break ; pthread_rwlock_unlock(&qp->q_lock); return (jp); }
指定时间阻塞锁定读写器 1 2 3 4 5 6 #include <pthread.h> #include <time.h> int pthread_rwlock_timedrdlock (pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr) ;int pthread_rwlock_timedwrlock (pthread_rwlock_t *restrict rwlock, const struct timespec *restrict tsptr) ; Both return : 0 if OK, error number on failure
状态变量 利用状态加锁
1 2 3 4 #include <pthread.h> int pthread_cond_init (pthread_cont_t *restrict cond, const pthread_condattr_t *restrict attr) ;int pthread_cond_destroy (pthread_cond_t *cond) ; Both return : 0 if OK, error number on failure
状态等待
1 2 3 4 #include <pthread.h> int pthread_cond_wait (pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex) ;int pthread_cond_timedwait (pthread_cond_t *restricct cond, pthread_mutex_t *restrict mutex,const struct timespec *restrict tsptr) ; Both return : 0 if OK, error number on failure
获取超时的绝对时间
1 2 3 4 5 6 7 8 9 10 11 12 #include <sys/time.h> #include <stdlib.h> void maketimeout (struct timespec *tsp, long minutes) { struct timeval now ; gettimeofday(&now, NULL ); tsp->tv_sec = now.tv_sec; tsp->tv_nsec = now.tv_usec * 1000 ; tsp->tv_sec += minutes * 60 ; }
唤醒线程
1 2 3 4 #include <pthread.h> int pthread_cond_signal (pthread_cond_t *cond) ;int pthread_cond_broadcast (pthread_cont_t *cond) ; Both return :0 if OK, error number on failure
自旋锁 1 2 3 4 5 #include <pthread.h> int pthread_spin_init (pthread_spinlock_t *lock, int pshared) ;int pthread_spin_destroy (pthread_spinlock_t *lock) ; Both return :0 if OK, error number on failure
1 2 3 4 5 6 #inlucde <pthread.h> int pthread_spin_lock (pthread_spinlock_t *lock) ;int pthread_spin_trylock (pthread_spinlock_t *lock) ;int pthread_spin_unlock (pthread_spinlock_t *lock) ; All return : 0 if OK, error number on failure
屏障 1 2 3 4 5 #include <pthread.h> int pthread_barrier_init (pthread_barrier_t *restrict barrier, const pthread_barrierattr_t *restrict attr,unsigned int count) ;int pthread_barrier_destory (pthread_barrier_t *barrier) ; Both return : 0 if OK, error number on failure
等待其他线程
1 2 3 #include <pthread.h> int pthread_barrier_wait (pthread_barrier_t *barrier) ; Returns:0 or PTHREAD_BARRIER_SERIAL_THREAD if OK, error number on failure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 #include <pthread.h> #include <limits.h> #include <sys/time.h> #include <stdio.h> #include <stdlib.h> #define NTHR 8 #define NUMNUM 8000000L #define TNUM (NUMNUM/NTHR) long nums[NUMNUM];long snums[NUMNUM];pthread_barrier_t b; #ifdef SOLARIS #define heapsort qsort #else extern int heapsort (void *, size_t , size_t , int (*)(const void *, const void *)) ; #endif int complong (const void *arg1, const void *arg2) { long l1 = *(long *)arg1; long l2 = *(long *)arg2; if (l1 == l2) return 0 ; else if (l1 < l2) return -1 ; else return 1 ; }void *thr_fn (void *arg) { long idx = (long )arg; heapsort(&nums[idx], TNUM, sizeof (long ), complong); pthread_barrier_wait(&b); return ((void *)0 ); }void merge () { long idx[NTHR]; long i, minidx, sidx, num; for (i = 0 ; i < NTHR; i++) idx[i] = i *TNUM; for (sidx = 0 ; sidx < NUMNUM; sidx++){ num = LONG_MAX; for (i = 0 ; i < NTHR; i++){ if ((idx[i] < (i+1 )*TNUM) && (nums[idx[i]] < num)){ num = nums[idx[i]]; minidx = i; } } snums[sidx] = nums[idx[minidx]]; idx[minidx]++; } }int main () { unsigned long i; struct timeval start , end ; long long startusec, endusec; double elapsed; int err; pthread_t tid; srandom(1 ); for (i = 0 ; i < NUMNUM; i++) nums[i] = random(); gettimeofday(&start, NULL ); pthread_barrier_init(&b, NULL , NTHR+1 ); for (i = 0 ; i < NTHR; i++){ err = pthread_create(&tid, NULL , thr_fn, (void *)(i * TNUM)); if (err != 0 ) { fprintf (stderr ,"can't create thread" ); exit (-1 ); } } pthread_barrier_wait(&b); merge(); gettimeofday(&end,NULL ); startusec = start.tv_sec * 1000000 + start.tv_usec; endusec = end.tv_sec * 1000000 + end.tv_usec; printf ("sort took %.4f seconds \n" ,elapsed); for (i = 0 ; i < NUMNUM; i++) printf ("%ld\n" ,snums[i]); exit (0 ); }
线程控制 线程属性 1 2 3 4 #include <pthread.h> int pthread_attr_init (pthread_attr_t *attr) ;int pthread_attr_destroy (pthread_attr_t *attr) ;
获取线程的分离状态 1 2 3 4 #include <pthread.h> int pthread_attr_getdetachstate (const pthread_attr_t *restrict attr, int *detachstate) ;int pthread_attr_setdetachstate (pthread_attr_t *attr, int detachstate) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <pthread.h> int makethread (void *(*fn)(void *), void *arg) { int err; pthread_t tid; pthread_attr_t attr; err = pthread_attr_init(&attr); if (err != 0 ) return (err); err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (err == 0 ) err = pthread_create(&tid, &attr, fn, arg); pthread_attr_destroy(&attr); return (err); }
线程堆栈属性 设置线程的堆栈
1 2 3 4 5 6 7 8 #include <pthread.h> int pthread_attr_getstack (const pthread_attr_t *restrict attr, void **restrict stackaddr, size_t *restrict stacksize) ;int pthread_attr_setstack (pthread_attr_t *attr, void *stackaddr, size_t stacksize) ;
获取与设置堆栈大小
1 2 3 4 #include <pthread.h> int pthread_attr_getstacksize (const pthread_attr_t *restrict attr,size_t *restrict stacksize) ;int pthread_attr_setstacksize (pthread_attr_t , size_t stacksize) ;
设置与获取guardsize
,guardsize
用于设置保护缓存区大小
1 2 3 4 #include <pthread.h> int pthread_attr_getguardsize (const pthread_attr_t *restrict attr,size_t *restrict guaradsize) ;int pthread_attr_setguardsize (pthread_attr_t *attr, size_t guardsize) ;
同步属性 锁属性 初始化锁属性
1 2 3 4 #include <pthread.h> int pthread_mutextattr_init (pthread_mutexattr_t *attr) ;int pthread_mutexattr_destroy (pthread_mutexattr_t *attr) ;
获取锁共享属性
1 2 3 4 5 6 7 8 #include <pthread.h> int pthread_mutexattr_getpshared (const pthread_mutexattr_t * restrict attr, int *restrict pshared) ;int pthread_mutexattr_setpshared (pthread_mutexattr_t *attr, int pshared) ;
获取健壮互斥体属性
1 2 3 4 5 6 7 #include <pthread.h> int pthread_mutexattr_getrobust (const pthread_mutexattr_t * restrict attr, int *restrict robust) ;int pthread_mmutexattr_setrobust (pthread_mutexattr_t *attr, int robust) ;
保持互斥锁的状态
1 2 3 #include <pthread.h> c int pthread_mutex_consistent (pthread_mutex_t * mutex) ;
mutex类型的可选值
PTHREAD_MUTEX_NORMAL
:标准互斥类型,不进行任何特殊的错误检查或死锁检测
PTHREAD_MUTEX_ERRORCHECK
:提供错误检查的互斥类型
PTHREAD_MUTEX_RECURSIVE
:一种互斥锁类型,允许同一线程在不首先解锁的情况下多次锁定它。递归互斥锁保持一个锁计数,直到它被锁定的次数相同时才被释放。因此,如果您锁定递归互斥锁两次,然后解锁它,则互斥锁将保持锁定状态,直到第二次解锁。
PTHREAD_MUTEX_DEFAULT
:提供默认特性和行为的互斥类型。实现可以自由地将其映射到其他互斥类型之一
获取与更改锁属性
1 2 3 4 5 #include <pthread.h> int pthread_mutexattr_gettype (const pthread_mutexattr_t * restrict attr, int *restrict type) ;int pthread_mutexattr_settype (pthread_mutexattr_t *attr, int type) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 #include <pthread.h> #include <time.h> #include <sys/time.h> #include <stdio.h> #include <stdlib.h> int makethread (void *(*)(void *), void *) ;struct to_info { void (*to_fn)(void *); void *to_arg; struct timespec to_wait ; };#define SECTONSEC 1000000000 #if !defined(CLOCK_REALTIME) || defined(BSD) #define clock_nanosleep(ID, FL, REQ, REM) nanosleep((REQ), (REM)) #endif #ifndef CLOCK_REALTIME #define CLOCK_REALTIME 0 #define USECTONSEC 1000 void clock_gettime (int id, struct timespec *tsp) { struct timeval tv ; gettimeofday(&tv, NULL ); tsp->tv_sec = tv.tv_sec; tsp->tv_nsec = tv.tv_usec * USECTONSEC; }#endif int makethread (void *(*fn)(void *), void *arg) { int err; pthread_t tid; pthread_attr_t attr; err = pthread_attr_init(&attr); if (err != 0 ) return (err); err = pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_DETACHED); if (err == 0 ) err = pthread_create(&tid, &attr, fn, arg); pthread_attr_destroy(&attr); return (err); }void *timeout_helper (void *arg) { struct to_info *tip ; tip = (struct to_info *)arg; clock_nanosleep(CLOCK_REALTIME, 0 , &tip->to_wait, NULL ); (*tip->to_fn)(tip->to_arg); free (arg); return (0 ); }void timeout (const struct timespec *when, void (*func)(void *), void *arg) { struct timespec now ; struct to_info *tip ; int err; clock_gettime(CLOCK_REALTIME, &now); if ((when->tv_sec > now.tv_sec) || (when->tv_sec == now.tv_sec && when->tv_nsec > now.tv_nsec)){ tip = malloc (sizeof (struct to_info)); if (tip != NULL ){ tip->to_fn = func; tip->to_arg = arg; tip->to_wait.tv_sec = when->tv_sec - now.tv_sec; if (when->tv_nsec >= now.tv_nsec){ tip->to_wait.tv_nsec = when->tv_nsec - now.tv_nsec; } else { tip->to_wait.tv_sec--; tip->to_wait.tv_nsec = SECTONSEC - now.tv_nsec + when->tv_nsec; } err = makethread(timeout_helper, (void *)tip); if (err == 0 ) return ; else free (tip); } } }pthread_mutexattr_t attr;pthread_mutex_t mutex;void retry (void *arg) { pthread_mutex_lock(&mutex); pthread_mutex_unlock(&mutex); }int main (void ) { int err, condition, arg; struct timespec when ; if ((err = pthread_mutexattr_init(&attr)) != 0 ) { fprintf (stderr , "pthread_mutexattr_init failed" ); exit (-1 ); } if ((err = pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE)) != 0 ) { fprintf (stderr , "can't set recursive type" ); exit (-1 ); } if ((err = pthread_mutex_init(&mutex, &attr)) != 0 ) { fprintf (stderr , "can't create recursive mutex" ); exit (-1 ); } pthread_mutex_lock(&mutex); if (condition) { clock_gettime(CLOCK_REALTIME, &when); when.tv_sec += 10 ; timeout(&when, retry, (void *)((unsigned long )arg)); } pthread_mutex_unlock(&mutex); exit (0 ); }
读写锁属性 读写锁属性初始化
1 2 3 4 5 #include <pthread.h> int pthread_rwlockattr_init (pthread_rwlockattr_t *attr) ;int pthread_rwlockattr_destroy (pthread_rwlockattr_t *attr) ;
获取读写锁共享属性
1 2 3 4 5 6 7 8 #include <pthread.h> int pthread_rwlockattr_getpshared (const pthread_rwlockattr_t * restrict attr, int *restrict pshared) ;int pthread_rwlockattr_setpshared (pthread_rwlockattr_t *attr, int pshared) ;
状态变量属性 状态变量属性初始化
1 2 3 4 5 #include <pthread.h> int pthread_condattr_init (pthread_condattr_t *attr) ;int pthread_condattr_destroy (pthread_condattr_t *attr) ;
共享属性
1 2 3 4 5 6 7 8 #include <pthread.h> int pthread_condattr_getpshared (const pthread_condattr_t * restrict attr, int *restrict pshared) ;int pthread_condattr_setpshared (pthread_condattr_t *attr, int pshared) ;
获取时钟
1 2 3 4 5 6 7 8 #include <pthread.h> int pthread_condattr_getclock (const pthread_condattr_t * restrict attr, clockid_t *restrict clock_id) ;int pthread_condattr_setclock (pthread_condattr_t *attr, clockid_t clock_id) ;
围栏属性 初始化属性
1 2 3 4 #include <pthread.h> int pthread_barrierattr_init (pthread_barrierattr_t *attr) ;int pthread_barrierattr_destroy (pthread_barrierattr_t *attr) ;
获取共享属性
1 2 3 4 5 6 7 #include <pthread.h> int pthread_barrierattr_getpshared (const pthread_barrierattr_t * restrict aattr, int *reestrict pshared) ;int pthread_barrierattr_setpshared (pthread_barrierattr_t *attr, int pshared) ;
flockfile和ftrylockfile 线程安全方式管理FILE
对象的方法,flockfile
和ftrylockfile
用于获取与给定FILE
对象关联的锁
1 2 3 4 5 6 #include <stdio.h> int ftrylockfile (FILE *fp) ; void flockfile (FILE *fp) ;void funlockfile (FILE *fp) ;
字符锁 基于字符的标准I/O
例程的锁
1 2 3 4 5 6 #include <stdio.h> int getchar_unlocked (void ) ;int getc_unlocked (FIILE *fp) ; int putchar_unlocked (int c) ;int putc_unlocked (int c, FILE *fp) ;
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 #include <string.h> #include <errno.h> #include <pthread.h> #include <stdlib.h> #include <stdio.h> extern char **environ;pthread_mutex_t env_mutex;static phtread_once_t init_done = PTHREAD_ONCE_INIT;static void thread_init (void ) { pthread_mutexattr_t attr; pthread_mutexattr_init(&attr); pthread_mutexattr_settype(&attr, PTHREAD_MUTEX_RECURSIVE); pthread_mutex_init(&env_mutex, &attr); pthread_mutexattr_destroy(&attr); }int getenv_r (const char *name, char *buf, int buflen) { int i, len, olen; pthread_once(&init_done, thread_init); len = strlen (name); pthread_mutex_lock(&env_mutex); for (i = 0 ; environ[i] = NULL ; i++){ if ((strncmp (name, environ[i], len) == 0 ) && (environ[i][len] == '=' )){ olen = strlen (&environ[i][len+1 ]); if (olen >= buflen){ pthread_mutex_unlock(&env_mutex); return (ENOSPC); } strcpy (buf, &environ[i][len+1 ]); pthread_mutex_unlock(&env_mutex); return (0 ); } } pthread_mutex_unlock(&env_mutex); return (ENOENT); }
线程特定数据 创建数据关联的键
1 2 3 #include <pthread.h> int pthread_key_create (pthread_key_t *keyp, void (*destructor)(void *)) ;
删除键的关联
1 2 3 #include <pthread.h> int pthread_key_delete (pthread_key_t key) ;
一次初始化
1 2 3 #include <pthread.h> pthread_once_t initflag = PTHREAD_ONCE_INIT;int pthread_once (phtread_once_t *initflag, void (*initfn)(void )) ;
特定数据与键关联
1 2 3 4 5 6 #include <pthread.h> void *pthread_getspecific (pthread_key_t key) ; Returns:thread-specific data value or NULL if no value has benn associated with the keyint pthread_setspecific (pthread_key_t key, const void *value) ; Returns:0 if OK,error nnumber on failure
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 #include <limits.h> #include <string.h> #include <pthread.h> #include <stdlib.h> #define MAXSTRINGSZ 4096 static pthread_key_t key;static pthread_once_t init_done = PTHREAD_ONCE_INIT;pthread_mutex_t env_mutex = PTHREAD_MUTEX_INITIALIZER;extern char **environ;static void thread_init (void ) { pthread_key_create(&key, free ); }char *getenv (const char *name) { int i, len; char *envbuf; pthread_once(&init_done, thread_init); pthread_mutex_lock(&env_mutex); envbuf = (char *)pthread_getspecific(key); if (envbuf == NULL ) { envbuf = malloc (MAXSTRINGSZ); if (envbuf == NULL ){ pthread_mutex_unlock(&env_mutex); return (NULL ); } pthread_setspecific(key, envbuf); } len = strlen (name); for (i = 0 ; environ[i] != NULL ; i++){ if ((strncmp (name, environ[i], len) == 0 ) && (environ[i][len] == '=' )){ strncpy (envbuf, &environ[i][len+1 ], MAXSTRINGSZ-1 ); pthread_mutex_unlock(&env_mutex); return (envbuf); } } pthread_mutex_unlock(&env_mutex); return (NULL ); }
取消选项 pthread_attr_t
结构中不包含两个线程属性,需要通过pthread_cancel
函数进行设置
可取消状态
PTHREAD_CANCEL_ENABLE
PTHREAD_CANCEL_DISABLE
可取消类型
1 2 3 #include <pthread.h> int pthread_setcancelstate (int state, int *oldstate) ;
设置取消点 自定义取消点
1 2 #include <pthread.h> void pthread_testcancel (void ) ;
设置取消类型 1 2 3 #include <pthread.h> int pthread_setcanceltype (int type, int *oldtype) ;
线程与信号 修改进程的信号阻塞行为
1 2 3 #include <signal.h> int pthread_sigmask (int how, const sigset_t *restrict set , sigset_t *restrict oset) ;
等待信号
1 2 3 #include <signal.h> int sigwait (const sigset_t *restrict set , int *restrict signop) ;
发送信号
1 2 3 #include <signal.h> int pthread_kill (pthread_t thread, int signo) ;
线程与fork 线程的fork
函数
1 2 3 #include <pthread.h> int pthread_atfork (void (*prepare)(void ), void (*parent)(void ), void (*child)(void )) ;
prepare
:获取父级定义的所有锁
parent
:在父进程中,解锁preparefork
获取的锁
child
:在子进程中,解锁prepparefork
获取的锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 #include <pthread.h> #include <stdio.h> #include <stdlib.h> pthread_mutex_t lock1 = PTHREAD_MUTEX_INITIALIZER;pthread_mutex_t lock2 = PTHREAD_MUTEX_INITIALIZER;void prepare (void ) { int err; printf ("preparing locks...\n" ); if ((err = pthread_mutex_lock(&lock1)) != 0 ) { fprintf (stderr , "can't lock lock1 in prepare handler" ); exit (-1 ); } if ((err = pthread_mutex_lock(&lock2)) != 0 ) { fprintf (stderr , "can't lock lock2 in prepare handler" ); exit (-1 ); } }void parent (void ) { int err; printf ("parent unlocking locks...\n" ); if ((err = pthread_mutex_unlock(&lock1)) != 0 ) { fprintf (stderr ,"can't unlock lock1 in parent handler" ); exit (-1 ); } if ((err = pthread_mutex_unlock(&lock2)) != 0 ) { fprintf (stderr ,"can't unlock lock2 in parent handler" ); exit (-1 ); } }void child (void ) { int err; printf ("child unlocking locks...\n" ); if ((err = pthread_mutex_unlock(&lock1)) != 0 ) { fprintf (stderr , "can't unlock lock1 in child handler" ); exit (-1 ); } if ((err = pthread_mutex_unlock(&lock2)) != 0 ) { fprintf (stderr , "can't unlock lock2 in child handler" ); exit (-1 ); } }void *thr_fn (void *arg) { printf ("thread started...\n" ); pause(); return (0 ); }int main (int argc, char *argv[]) { int err; pid_t pid; pthread_t tid; if ((err = pthread_atfork(prepare, parent, child)) != 0 ) { fprintf (stderr , "can't install fork handlers" ); exit (-1 ); } if ((err = pthread_create(&tid, NULL , thr_fn, 0 )) != 0 ) { fprintf (stderr , "can't create thread" ); exit (-1 ); } sleep(2 ); printf ("parent about to fork...\n" ); if ((pid = fork()) < 0 ) { fprintf (stderr , "fork failed" ); exit (-1 ); } else if (pid == 0 ) { printf ("child returned from fork\n" ); exit (-1 ); } else printf ("parent returned from fork\n" ); exit (0 ); }
线程与I/O pread与pwrite 用于处理线程间读写
守护进程
读取日志信息的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <syslog.h> void openlog (const char *ident, int option, int facility) ; void syslog (int priority, const char *format, ...) ;void closelog (void ) ; int setlogmask (int maskpri) ;
自定义的日志打印函数
1 2 3 4 #include <syslog.h> #include <stdarg.h> void vsyslog (int priority, const char *format, va_list arg) ;
单独实例守护进程 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <syslog.h> #include <string.h> #include <errno.h> #include <stdio.h> #include <sys/stat.h> #define LOCKFILE "/var/run/daemon.pid" #define LOCKMODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) extern int lockfile (int ) ;int already_running (void ) { int fd; char buf[16 ]; fd = open(LOCKFILE, O_RDWR | O_CREAT, LOCKMODE); if (fd < 0 ){ syslog(LOG_ERR, "can't open %s: %s" , LOCKFILE, strerror(errno)); exit (1 ); } if (lockfile(fd) < 0 ){ if (errno == EACCES || errno == EAGAIN){ close(fd); return (1 ); } syslog(LOG_ERR,"can't lock %s: %s" , LOCKFILE, strerror(errno)); exit (1 ); } ftruncate(fd, 0 ); sprintf (buf, "%ld" , (long )getpid()); write(fd, buf, strlen (buf)+1 ); return (0 ); }
守护进程重读配置文件
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 #include <pthread.h> #include <syslog.h> #include <signal.h> #include <stdio.h> #include <stdlib.h> sigset_t mask;extern int already_runniing (void ) ;void reread (void ) { }void *thr_fn (void *arg) { int err, signo; for (;;){ err = sigwait(&mask, &signo); if (err != 0 ){ syslog(LOG_ERR, "sigwait failed" ); exit (1 ); } switch (signo) { case SIGHUP: syslog(LOG_INFO, "Re-reading configuration file" ); reread(); break ; case SIGTERM: syslog(LOG_INFO, "got SIGTERM; exiting" ); exit (0 ); default : syslog(LOG_INFO, "unexpected signal %d\n" , signo); break ; } return (0 ); } }int main (int argc, char *argv[]) { int err; pthread_t tid; char *cmd; struct sigaction sa ; if ((cmd = strrchr (argv[0 ], '/' ) == NULL )) cmd = argv[0 ]; else cmd++; daemonize(cmd); if (already_runniing()){ syslog(LOG_ERR, "daemon already running" ); exit (1 ); } sa.sa_handler = SIG_DFL; sigemptyset(&sa.sa_mask); sa.sa_flags = 0 ; if (sigaction(SIGHUP, &sa, NULL ) < 0 ) { fprintf (stderr , "can't restore SIGHUP default" ); exit (-1 ); } sigfillset(&mask); if ((err = pthread_sigmask(SIG_BLOCK, &mask, NULL )) != 0 ) { fprintf (stderr ,"SIG_BLOCK error" ); exit (-1 ); } err = pthread_create(&tid, NULL , thr_fn, 0 ); if (err != 0 ) { fprintf (stderr ,"can't create thread" ); exit (-1 ); } exit (0 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <fcntl.h> #include <stdio.h> #include <stdlib.h> int set_cloexec (int fd) { int val; if ((val = fcntl(fd, F_GETFD, 0 )) < 0 ) return (-1 ); val |= FD_CLOEXEC; return (fcntl(fd, F_SETFD,val)); }
高级I/O 非阻塞I/O
调用open
函数时使用O_NONBLOCK
标志
对于已经打开的文件,可以使用fcntl
函数打开O_NONBLOCK
标志
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <unistd.h> char buf[500000 ];int main (void ) { int ntowrite, nwrite; char *ptr; ntowrite = read(STDIN_FILENO, buf, sizeof (buf)); fprintf (stderr , "read %d bytes\n" , ntowrite); set_fl(STDOUT_FILENO, O_NONBLOCK); ptr = buf; while (ntowrite > 0 ){ errno = 0 ; nwrite = write(STDOUT_FILENO, ptr, ntowrite); fprintf (stderr , "nwrite = %d, errno = %d\n" , nwrite, errno); if (nwrite > 0 ){ ptr += nwrite; ntowrite -= nwrite; } } clr_fl(STDERR_FILENO, O_NONBLOCK); exit (0 ); }
记录锁 fcntl Record Locking
1 2 3 4 #include <fcntl.h> int fcntl (int fd, int cmd, ... ) Returns:depends on cmd if OK (see follwing) , -1 on error
对于记录锁,cmd
的取值为:F_GETLK
、F_SETLK
或F_SETLKW
1 2 3 4 5 6 7 struct flock { short l_type; short l_whence; off_t l_start; off_t l_len; pid_t l_pid; };
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 #include <fcntl.h> int lock_reg (int fd, int cmd, int type, off_t offset, int whence, off_t len) { struct flock lock ; lock.l_type = type; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; return (fcntl(fd, cmd, &lock)); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <fcntl.h> #include <stdio.h> #include <stdlib.h> pid_t lock_test (int fd, int type, off_t offset, int whence, off_t len) { struct flock lock ; lock.l_type = type; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; if (fcntl(fd, F_GETLK, &lock) < 0 ) { fprintf (stderr , "fcntl error" ); exit (-1 ); } if (lock.l_type == F_UNLCK) return (0 ); return (lock.l_pid); }
死锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include "apue.h" static void lockabyte (const char *name, int fd, off_t offset) { if (writew_lock(fd, offset, SEEK_SET, 1 ) < 0 ) { fprintf (stderr , "%s: writew_lock error" , name); exit (-1 ); } printf ("%s: got the lock, byte %lld\n" ,name, (long long )offset); }int main (void ) { int fd; pid_t pid; if ((fd = creat("templock" , FILE_MODE)) < 0 ) { fprintf (stderr , "creat error" ); exit (-1 ); } if (write(fd, "ab" , 2 ) != 2 ) { fprintf (stderr , "write error" ); exit (-1 ); } TELL_WAIT(); if ((pid = fork()) < 0 ){ fprintf (stderr ,"fork error" ); exit (-1 ); }else if (pid == 0 ){ lockabyte("child" , fd, 0 ); TELL_PARENT(getppid()); WAIT_PARENT(); lockabyte("child" , fd, 1 ); }else { lockabyte("parent" , fd, 1 ); TELL_CHILD(pid); WAIT_CHILD(); lockabyte("parent" , fd, 0 ); } exit (0 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 #include <unistd.h> #include <fcntl.h> int lockfile (int fd) { struct flock fl ; fl.l_type = F_WRLCK; fl.l_start = 0 ; fl.l_whence = SEEK_SET; fl.l_len = 0 ; return (fcntl(fd, F_SETLK, &fl)); }
强制锁 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 #include <errno.h> #include <fcntl.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <signal.h> #include <unistd.h> #include <sys/types.h> #include <sys/wait.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) #define SIGHUP 1 #define SIGINT 2 #define SIGQUIT 3 #define SIGILL 4 #define SIGTRAP 5 #define SIGABRT 6 #define SIGIOT 6 #define SIGBUS 7 #define SIGFPE 8 #define SIGKILL 9 #define SIGUSR1 10 #define SIGSEGV 11 #define SIGUSR2 12 #define SIGPIPE 13 #define SIGALRM 14 #define SIGTERM 15 #define SIGSTKFLT 16 #define SIGCHLD 17 #define SIGCONT 18 #define SIGSTOP 19 #define SIGTSTP 20 #define SIGTTIN 21 #define SIGTTOU 22 #define SIGURG 23 #define SIGXCPU 24 #define SIGXFSZ 25 #define SIGVTALRM 26 #define SIGPROF 27 #define SIGWINCH 28 #define SIGIO 29 #include <fcntl.h> #define read_lock(fd, offset, whence, len) \ lock_reg((fd), F_SETLK, F_RDLCK, (offset), (whence), (len)) #define readw_lock(fd, offset, whence, len) \ lock_reg((fd), F_SETLKW, F_RDLCK, (offset), (whence), (len)) #define write_lock(fd, offset, whence, len) \ lock_reg((fd), F_SETLK, F_WRLCK, (offset), (whence), (len)) #define writew_lock(fd, offset, whence, len) \ lock_reg((fd), F_SETLKW, F_WRLCK, (offset), (whence), (len)) #define un_lock(fd, offset, whence, len) \ lock_reg((fd), F_SETLK, F_UNLCK, (offset), (whence), (len)) int lock_reg (int fd, int cmd, int type, off_t offset, int whence, off_t len) { struct flock lock ; lock.l_type = type; lock.l_start = offset; lock.l_whence = whence; lock.l_len = len; return (fcntl(fd, cmd, &lock)); }static sigset_t newmask, oldmask, zeromask;static volatile sig_atomic_t sigflag;static void sig_usr (int signo) { sigflag = 1 ; }void TELL_WAIT (void ) { if (signal(SIGUSR1, sig_usr) == SIG_ERR) printf ("signal(SIGUSR1) error" ); if (signal(SIGUSR2, sig_usr) == SIG_ERR) printf ("signal(SIGUSR2) error" ); sigemptyset(&zeromask); sigemptyset(&newmask); sigaddset(&newmask, SIGUSR1); sigaddset(&newmask, SIGUSR2); if (sigprocmask(SIG_BLOCK, &newmask, &oldmask) < 0 ) printf ("SIG_BLOCK error" ); }void TELL_PARENT (pid_t pid) { kill(pid, SIGUSR2); }void WAIT_PARENT (void ) { while (sigflag == 0 ) sigsuspend(&zeromask); sigflag = 0 ; if (sigprocmask(SIG_SETMASK, &oldmask, NULL ) < 0 ) printf ("SIG_SETMASK error" ); }void TELL_CHILD (pid_t pid) { kill(pid, SIGUSR1); }void WAIT_CHILD (void ) { while (sigflag == 0 ) sigsuspend(&zeromask); sigflag = 0 ; if (sigprocmask(SIG_SETMASK, &oldmask, NULL ) < 0 ) printf ("SIG_SETMASK error" ); }void set_fl (int fd, int flags) { int val; if ( (val = fcntl(fd, F_GETFL, 0 )) < 0 ) { printf ("fcntl F_GETFL error" ); exit (1 ); } val |= flags; if (fcntl(fd, F_SETFL, val) < 0 ) { printf ("fcntl F_SETFL error" ); exit (1 ); } }int main (int argc, char *argv[]) { int fd; pid_t pid; char buf[5 ]; struct stat statbuf ; if (argc != 2 ){ fprintf (stderr , "usage: %s filename\n" , argv[0 ]); exit (1 ); } if ((fd = open(argv[1 ], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0 ) { fprintf (stderr , "open error" ); exit (-1 ); } if (write(fd, "abcdef" , 6 ) != 6 ) { fprintf (stderr , "write error" ); exit (-1 ); } if (fstat(fd, &statbuf) < 0 ) { fprintf (stderr , "fstat error" ); exit (-1 ); } if (fchmod(fd, (statbuf.st_mode & ~S_IXGRP) | S_ISGID) < 0 ) { fprintf (stderr , "fchmod error" ); exit (-1 ); } TELL_WAIT(); if ((pid = fork()) < 0 ){ fprintf (stderr , "fork error" ); exit (-1 ); } else if (pid > 0 ) { if (write_lock(fd, 0 , SEEK_SET, 0 ) < 0 ) { fprintf (stderr , "write_lock error" ); exit (-1 ); } TELL_CHILD(pid); if (waitpid(pid, NULL , 0 ) < 0 ){ fprintf (stderr , "waitpid error" ); exit (-1 ); } } else { WAIT_PARENT(); set_fl(fd, O_NONBLOCK); if (read_lock(fd, 0 , SEEK_SET, 0 ) != -1 ) { fprintf (stderr , "child: read_lock succeeded" ); exit (-1 ); } printf ("read_lock of already-locked region returns %d\n" ,errno); if (lseek(fd, 0 , SEEK_SET) == -1 ) { fprintf (stderr , "lseek error" ); exit (-1 ); } if (read(fd, buf, 2 ) < 0 ) { fprintf (stderr , "read failed (mandatory locking works)" ); exit (-1 ); } else printf ("read OK (o mandatory locking), buf = %2.2s\n" ,buf); } exit (0 ); }
IO多路复用 从一个文件描述符中读取数据,并写入到另一个文件描述符中,通过阻塞I/O
实现
1 2 3 while ((n = read(STDIN_FILENO, buf, BUFSIZE)) > 0 ) if (write(STDOUT_FILENO, buf, n) != n) err_sys("write error" );
上述实现方法不能应用于从两个文件描述符当中读取数据,因为我们没有办法同时阻塞两个read
使用两个不同进程分别进行读与写能够解决上述情况,但是当父子进程被终止时需要相互使用信号进行通知,会增加程序的复杂性
使用同个进程两个不同线程进行处理,但是需要同步两个线程之间的信息。
轮询:读取文件描述符,若无数据直接返回,等一段时间再进行读取
缺点:浪费CPU时间
应该避免在多任务系统上进行使用
同步I/O
:当从文件描述符通过I/O
读取时,内核需要通知用户
缺点:兼容性不高、单一的信号无法标记所有的文件描述符
I/O
复用:通过构建描述符列表,并调用一个函数,直到其中一个描述符准备好I/O
时才返回
select 和 pselect函数 select
函数实现I/O
复用,参数指明
哪个文件描述符我们感兴趣
文件描述符的哪个状态我们感兴趣
我们需要等待多长时间
return
总共有多少个文件描述符已就绪
三个条件(读、写和异常)中的每一个都准备好了哪些描述符
总的来说,select
函数用于获取我们对某个文件描述符感兴趣的状态
1 2 3 4 5 6 7 8 #include <sys/select.h> int select (int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, struct timeval *restricct tvptr) ;
tvptr == NULL
无限制的等待。当描述符就绪以及被信号中断则返回。若被信号中断则select
返回-1
以及errno
被设置为EINTR
tvptr->tv_sec == 0 && tvptr->tv_usec == 0
tvptr->tv_sec != 0 || tvptr->tv_usec != 0
等待指定秒数或毫秒数。当指定的描述符之一就绪或超时返回。
fd_set数据类型的操作
1 2 3 4 5 6 7 #include <sys/select.h> int FD_ISSET (int fd, fd_set *fdset) ; Returns:nonzero if fd is in set ,0 otherwisevoid FD_CLR (int fd, fd_set *fdset) ;void FD_SET (int fd, fd_set *fdset) ;void FD_ZERO (fd_set *fd_set) ;
example1
1 2 3 4 5 6 7 8 9 10 fd_set rset;int fd; FD_ZERO(&rset); FD_SET(fd, &rset); FD_SET(STDIN_FILENO, &rset);if (FD_ISSET(fd, &rset)){ ... }
example2
1 2 3 4 5 6 7 8 9 fd_set readset, writeset; FD_ZERO(&readset); FD_ZERO(&writeset); FD_SET(0 , &readset); FD_SET(3 , &readset); FD_SET(1 , &writeset); FD_SET(2 , &WRITESET); select(4 , &readset, &writeset, NULL , NULL );
返回值可能的情况
返回值为-1意味着错误发生。例如被信号中断
返回值为0,意味着等待超时,此时所有的描述符集会被清空
非负数,意味着对应的描述符已经就绪。该值是三个集合中所有描述符的总和。
pselect
1 2 3 4 5 6 7 8 9 #include <sys/select.h> int pselect (int maxfdp1, fd_set *restrict readfds, fd_set *restrict writefds, fd_set *restrict exceptfds, const struct timespec *restrict tsptr, const sigset_t *restrict sigmask) ; Returns:count of ready descriptors, 0 on timeout, -1 on error
与select区别
时间采用timespec
结构体,提供了秒与纳米级别的粒度
提供了信号集,可以指定安装对应的信号掩码
poll 1 2 3 4 #include <poll.h> int poll (struct pollfd fdarray[], nfds_t nfds, int timeout) ; Returns:count of ready descriptors, 0 on timeout, -1 on error
1 2 3 4 5 struct pollfd { int fd; short events; short revents; };
timeout == -1
一直等待。如果捕获到信号,轮询将返回-1,errno设置为EINTR
timeout == 0
不等待。所有描述符都将被测试并且立即返回。这是一种poll
找出多个描述符状态的方法,而不会阻塞轮询调用。
timeout > 0
等到指定的毫秒数。当指定描述符就绪或者超时则返回。超时时返回0。
异步I/O 异步操作可能产生的错误
System V 异步I/O 在流设备与流管道上工作。
BSD异步I/O BSD
驱动系统需要结合两个信号
SIGIO
:通常为异步信号
SIGURG
:用于通知进程带外数据已到达网络连接
为了接收SIGIO
信号,需要执行三个步骤
建立基于SIGIO
的信号处理函数
设置进程ID或进程组ID为了接收 信号的描述符通过函数fcntl
并设置参数F_SETOWN
通过fcntl
设置O_ASYNC
文件状态标志,启用描述符上的异步I/O
POSIX异步I/O POSIX
的异步I/O
接口使用AIO
块去描述I/O
操作
AIO
块通过aiocb
结构体定义
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 struct aiocb { int aio_fildes; off_t aio_offset; volatile void *aio_buf; size_t aio_nbytes; int aio_reqprio struct sigevent aio_sigevent ; int aio_lio_opcode; };struct sigevent { int sigev_notify; int sigev_signo; union sigval sigev_value ; void (*sigev_notify_function)(union sigval); pthread_attr_t *sigev_notify_attributes; };
sigev_notify
类型有三个值可选
SIGEV_NONE
:异步I/O
请求完成时不会通知进程
SIGEV_SIGNAL
:当异步I/O
请求完成时,将生成sigev_signo
字段指定的信号
SIGEV_THREAD
:当异步I/O
请求完成时,调用sigev_notify_function
字段指定的函数。传递sigev_value
字段作为其唯一参数。除非sigev_notify_attributes
字段设置为pthread
属性结构的地址,否则该函数将在分离状态的单独线程中执行
在执行异步I/O
前,需要初始化AIO
控制块,并调用AIO_read
函数进行异步读取,或者调用AIO_write
函数进行非同步写入
1 2 3 4 5 #include <aio.h> int aio_read (struct aiocb *aiocb) ;int aio_write (struct aiocb *aiocb) ; Both return :0 if OK, -1 on error
1 2 3 4 5 #include <aio.h> int aio_fsync (int op, struct aiocb *aiocb) ; Returns:0 if OK, -1 on error
op
设置为O_DSYNC
类似于fdatasync
的调用
op
设置为O_SYNC
类似于fsync
的调用,需要你在入参的位置上传递给他一个fd
,然后系统调用就会对这个fd
指向的文件起作用。fsync
会确保一直到写磁盘操作结束才会返回。
为了确定异步读、写或同步操作的完成状态,则通过aio_error
函数
1 2 3 4 5 #include <aio.h> int aio_error (const struct aiocb *aiocb) ; Returns:(see following)
返回值
0,异步操作已完成,需要通过aio_return
函数从操作中获取返回值
-1,异步操作失败,errno
中存储了原因
EINPROGRESS
,异步读取、写入或同步仍处于挂起状态
其他值,任何其他返回值都会为我们提供与失败的异步操作相对应的错误代码。
通过aio_return
获取操作的返回值
1 2 3 4 5 #include <atio.h> ssize_t aio_return (const struct aiocb *aiocb) ; Returns:(see following)
-1,设置errno
则返回错误
返回异步操作的结果
当完成其他处理但仍然有其他异步操作未完成时,调用aio_suspend
函数进行阻塞
1 2 3 4 5 #include <aio.h> int aio_suspend (const struct aiocb *const list [], int nent, const struct timespec *timeout) ; Returns:0 if OK, -1 on error
当被信号中断时,返回-1且将errno
设置为EINTR
超时,返回-1且将errno
设置为EAGAIN
如果异步操作完成,则返回0
如果在调用aio_suspend
时所有异步I/O
操作都已完成,那么aio_susend
将返回而不会阻塞
取消异步操作
1 2 3 4 5 6 7 #include <aio.h> int aio_cancel (int fd, struct aiocb *aiocb) ; Returns:(see following)
AIO_ALLDONE
:所有操作在取消前已经全部完成
AIO_CANCELED
:所有请求的操作都已经被取消
AIO_NOTCANCELED
:至少有一个请求未被取消
-1:调用aio_cancel
函数失败,错误代码被存在errno
1 2 3 4 5 6 7 8 #include <aio.h> int lio_listio (int mode, struct aiocb *restrict const list [restrict ], int nent, struct sigevent *restrict sigev ) ; Returns:0 if OK, -1 on error
此函数可以控制一个AIO
控制块列表来描述IO
请求
mode
LIO_WAIT
:直到list
指定的所有操作完成时才会返回,sigev
参数将会被忽略
LIO_NOWAIT
:当I/O
请求排队时,lio_listio
函数立即返回。根据sigev
参数的指定,当所有I/O
操作完成时,将异步通知进程。不想通知时,sigev
可以被设置为NULL
AIO
控制块中的aio_lio_opcoded
区域制定了操作是否为读、写或者忽略。读被认为是传入aio_read
函数,写被认为是传入aio_write
函数
以下是限制允许执行的异步I/O
操作的数量
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 #include <ctype.h> #include <fcntl.h> #include <aio.h> #include <errno.h> #include <sys/stat.h> #include <stdio.h> #include <stdlib.h> #define BSZ 4096 #define NBUF 8 #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) enum rwop { UNUSERD = 0 , READ_PENDING = 1 , WRITE_PENDING = 2 };struct buf { enum rwop op ; int last; struct aiocb aiocb ; unsigned char data[BSZ]; };struct buf bufs [NBUF ];unsigned char translate (unsigned char c) { if (isalpha (c)){ if (c >= 'n' ) c -= 13 ; else if (c >= 'a' ) c += 13 ; else if (c >= 'N' ) c -= 13 ; else c += 13 ; } return (c); }int main (int argc, char * argv[]) { int ifd, ofd, i, j, n, err, numop; struct stat sbuf ; const struct aiocb *aiolist [NBUF ]; off_t off = 0 ; if (argc != 3 ) { fprintf (stderr , "usage: rot13 infile outfile" ); exit (-1 ); } if ((ifd = open(argv[1 ], O_RDONLY)) < 0 ) { fprintf (stderr , "can't open %s" , argv[1 ]); exit (-1 ); } if ((ofd = open(argv[2 ], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE)) < 0 ) { fprintf (stderr , "can't create %s" , argv[2 ]); exit (-1 ); } if (fstat(ifd, &sbuf) < 0 ) { fprintf (stderr ,"fstat failed" ); exit (-1 ); } for (i = 0 ;i < NBUF; i++){ bufs[i].op = UNUSERD; bufs[i].aiocb.aio_buf = bufs[i].data; bufs[i].aiocb.aio_sigevent.sigev_notify = SIGEV_NONE; aiolist[i] = NULL ; } numop = 0 ; for (;;){ for (i = 0 ; i < NBUF; i++){ switch (bufs[i].op) { case UNUSERD: printf ("UNUSERD\n" ); if (off < sbuf.st_size) { printf ("%d\n" ,sbuf.st_size); bufs[i].op = READ_PENDING; bufs[i].aiocb.aio_fildes = ifd; bufs[i].aiocb.aio_offset = off; off += BSZ; if (off >= sbuf.st_size) bufs[i].last = 1 ; bufs[i].aiocb.aio_nbytes = BSZ; if (aio_read(&bufs[i].aiocb) < 0 ) { fprintf (stderr , "aio_read failed" ); exit (-1 ); } aiolist[i] = &bufs[i].aiocb; numop++; } break ; case READ_PENDING: printf ("READ\n" ); if ((err = aio_error(&bufs[i].aiocb)) == EINPROGRESS) continue ; if (err != 0 ){ if (err == -1 ) { fprintf (stderr , "aio_error failed" ); exit (-1 ); } else { fprintf (stderr ,"read failed" ); exit (0 ); } } if ((n = aio_return(&bufs[i].aiocb)) < 0 ) { fprintf (stderr , "aio_return failed" ); exit (-1 ); } if ( n != BSZ && !bufs[i].last) { fprintf (stderr , "short read (%d/%d)" ,n, BSZ); exit (0 ); } for (j = 0 ; j < n; j++) bufs[i].data[j] = translate(bufs[i].data[j]); printf ("%s\n" ,bufs[i].data); bufs[i].op = WRITE_PENDING; bufs[i].aiocb.aio_fildes = ofd; bufs[i].aiocb.aio_nbytes = n; if (aio_write(&bufs[i].aiocb) < 0 ) { fprintf (stderr ,"aio_write failed" ); exit (-1 ); } break ; case WRITE_PENDING: printf ("WRITE\n" ); if ((err = aio_error(&bufs[i].aiocb)) == EINPROGRESS) continue ; if (err != 0 ){ if (err == -1 ) { fprintf (stderr ,"aio_error faiiled" ); exit (-1 ); } else { fprintf (stderr ,"write failed" ); exit (-1 ); } } if ((n = aio_return(&bufs[i].aiocb)) < 0 ) { fprintf (stderr ,"aio_return failed" ); exit (-1 ); } if (n != bufs[i].aiocb.aio_nbytes) { fprintf (stderr ,"short write (%d/%d)" ,n , BSZ); exit (-1 ); } aiolist[i] = NULL ; bufs[i].op = UNUSERD; numop--; break ; } } if (numop == 0 ){ if (off >= sbuf.st_size) break ; } else { if (aio_suspend(aiolist, NBUF, NULL ) < 0 ) { fprintf (stderr , "aio_suspend failed" ); exit (-1 ); } } } bufs[0 ].aiocb.aio_fildes = ofd; if (aio_fsync(O_SYNC, &bufs[0 ].aiocb) < 0 ) { fprintf (stderr , "aio_fsync failed" ); exit (-1 ); } exit (0 ); }
readn和writen 管道、FIFOs和一些设备,特别是终端和网络,会有两个特点
读取的数据会比要求的少,这不是错误,而需要继续读
写的数据比预期的少,造成这样的原因是写缓冲区已经满了,需要继续写剩余的数据。导致这样的原因通常是不阻塞的文件描述符以及捕获了信号。
1 2 3 4 5 6 #include "apue.h" ssize_t readn (int fd, void *buf size_t nbytes) ;ssize_t writen (int fd, void *buf, size_t nbytes) ; Both returns:number of bytes read or written, -1 on error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <stdio.h> #include <stdlib.h> #include <unistd.h> ssize_t readn (int fd, void *ptr, size_t n) { size_t nleft; ssize_t nread; nleft = n; while (nleft > 0 ){ if ((nread = read(fd, ptr, nleft)) < 0 ){ if (nleft == n) return (-1 ); else break ; }else if (nread == 0 ){ break ; } nleft -= nread; ptr += nread; } return (n - nleft); }ssize_t writen (int fd,const void *ptr, size_t n) { size_t nleft; ssize_t nwritten; nleft = n; while (nleft > 0 ){ if ((nwritten = write(fd, ptr, nleft)) < 0 ){ if (nleft == n) return (-1 ); else break ; }else if (nwritten == 0 ){ break ; } nleft -= nwritten; ptr += nwritten; } return (n - nleft); }
Memory-Mapped I/O mmap 内存映射I/O
允许我们将磁盘上的文件映射到内存中的缓冲区,当从缓存区获取字节时,文件的相应字节就会被读取。
1 2 3 4 #include <sys/mmanh> void *mmap (void *addr, size_t len, int prot, int flag, int fd, off_t off) ; Returns:starting address of mapped region if OK,MAP_FAILED on error
flag
可选值
MAP_FIXED
:返回值必须等于addr
MAP_SHARED
:对映射内存的操作会同步到文件中
MAP_PRIVATE
:对映射内存的操作会同步到私有副本中
addr
与off
的值需要与页对齐
有两个信号常用于映射区域
SIGSEGV
:试图访问对我们不可用的内存
SIGBUS
:访问没有意义的部分内存
内存映射区域可以通过fork
继承,但是不会通过exec
继承
mprotect 可以通过mprotect
函数修改现存的映射区域的权限
1 2 3 4 5 #include <sys/mman.h> int mprotect (void *addr, size_t len, int prot) ; Returns:0 if OK,-1 on error
prot
参数与mmap
的一样,addr
需要与页对齐
msync 用于将映射区域内的数据同步到文件中
1 2 3 4 #include <sys/mman.h> int msync (void *addr, size_t len, int flags) ; Returns:0 if OK, -1 on error
flag
参数
MS_ASYNC
简单地安排要写入的页面
MS_SYNC
在返回之前等待写入完成
可选标志MS_INVALIDATE
允许告诉操作系统丢弃与底层存储不同步的任何页面。
munmap 取消映射
1 2 3 4 #include <sys/mman.h> int munmap (void *addr, size_t len) ; Returns:0 if OK, -1 on error
example
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <fcntl.h> #include <sys/mman.h> #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> #define FILE_MODE (S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH) #define COPYINCR (1024*1024*1024) int main (int argc, char *argv[]) { int fdin, fdout; void *src, *dst; struct stat sbuf ; size_t copysz; off_t fsz = 0 ; if (argc != 3 ) { fprintf (stderr ,"usage: %s <froomfiile> <tofiile>" ,argv[0 ]); exit (-1 ); } if ((fdin = open(argv[1 ], O_RDONLY)) < 0 ) { fprintf (stderr , "can't create %s for writing" , argv[2 ]); exit (-1 ); } if ((fdout = open(argv[2 ], O_RDWR | O_CREAT | O_TRUNC, FILE_MODE))< 0 ) { fprintf (stderr , "can't create %s for writing" ,argv[2 ]); exit (-1 ); } if (fstat(fdin, &sbuf) < 0 ) { fprintf (stderr , "fstat error" ); exit (-1 ); } if (ftruncate(fdout, sbuf.st_size) < 0 ) { fprintf (stderr , "ftruncate error" ); exit (-1 ); } while (fsz < sbuf.st_size){ if ((sbuf.st_size - fsz) > COPYINCR) copysz = COPYINCR; else copysz = sbuf.st_size - fsz; if ((src = mmap(0 , copysz, PROT_READ, MAP_SHARED, fdin, fsz)) == MAP_FAILED) { fprintf (stderr , "mmap error for input" ); exit (-1 ); } if ((dst = mmap(0 , copysz, PROT_READ | PROT_WRITE,MAP_SHARED,fdout,fsz)) == MAP_FAILED) { fprintf (stderr , "mmap error for output" ); exit (-1 ); } memcpy (dst, src, copysz); munmap(src, copysz); munmap(dst, copysz); fsz += copysz; } exit (0 ); }
进程间通信(IPC) pipe 管道有两个限制
半双工通信,数据流只有一个方向
管道只能在具有共同祖先的进程之间使用
FIFOs
绕过了第二个限制,而Socket
绕过了上诉两个限制
1 2 3 4 5 #include <unistd.h> int pipe (int fd[2 ]) ; Returns: if OK, -1 on error
fstat
函数针对管道文件返回FIFO
类型,可以通过S_ISFIFP
宏获取。
父进程关闭fd[0]
读管道,子进程关闭fd[1]
写管道,则父进程只往管道中写,而子进程只从管道中读数据。
两个应用于管道的规则
当写管道关闭时,读管道会读到文件末尾,并返回已经读了的数据。
当读管道关闭时,写管道往管道中写会发出SIGPIPE
信号。默认的信号处理为返回-1
并设置errno
为EPIPE
PIPE_BUF
为管道缓冲区的常量值,写入少于该值时不会覆盖其他管道的缓冲区,但是超过该值则会覆盖其余管道的缓冲区。
父进程传字符串到子进程
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define MAXLINE 1024 int main (void ) { int n; int fd[2 ]; pid_t pid; char line[MAXLINE]; if (pipe(fd) < 0 ) { fprintf (stderr , "pipe error!\n" ); exit (-1 ); } if ((pid = fork()) < 0 ){ fprintf (stderr ,"fork error!\n" ); exit (-1 ); }else if (pid > 0 ){ close(fd[0 ]); write(fd[1 ], "hello world\n" ,12 ); }else { close(fd[1 ]); n = read(fd[0 ], line, MAXLINE); write(STDOUT_FILENO, line, n); } exit (0 ); }
实现管道功能,读指定文件并输入到more程序中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 #include <sys/wait.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #include <string.h> #define DEF_PAGE "/bin/more" #define MAXLINE 1024 int main (int argc, char *argv[]) { int n; int fd[2 ]; pid_t pid; char *pager, *argv0; char line[MAXLINE]; FILE *fp; if (argc != 2 ) { fprintf (stderr , "usgae: a.out <pathname>" ); exit (-1 ); } if ((fp = fopen(argv[1 ], "r" )) == NULL ) { fprintf (stderr , "can't open %s" , argv[1 ]); exit (-1 ); } if (pipe(fd) < 0 ) { fprintf (stderr , "pipe error\n" ); exit (-1 ); } if ((pid = fork()) < 0 ){ fprintf (stderr , "fork error!\n" ); exit (-1 ); }else if (pid > 0 ){ close(fd[0 ]); while (fgets(line, MAXLINE, fp) != NULL ){ n = strlen (line); if (write(fd[1 ], line, n) != n) { fprintf (stderr , "write error to pipe" ); exit (-1 ); } } if (ferror(fp)) { fprintf (stderr , "fgets error" ); exit (-1 ); } close(fd[1 ]); if (waitpid(pid, NULL , 0 ) < 0 ) { fprintf (stderr , "waitpid error" ); exit (-1 ); } exit (0 ); } else { close(fd[1 ]); if (fd[0 ] != STDIN_FILENO) { if (dup2(fd[0 ], STDIN_FILENO) != STDIN_FILENO) { fprintf (stderr , "dup2 error!\n" ); exit (-1 ); } close(fd[0 ]); } if ((pager = getenv("PAGER" ) == NULL )) pager = DEF_PAGE; if ((argv0 = strrchr (pager, '/' )) != NULL ) argv0++; else argv0 = pager; if (execl(pager, argv0, (char *)0 ) < 0 ) { fprintf (stderr , "execl error for %s" , pager); exit (-1 ); } } exit (0 ); }
利用管道实现父子进程信号同步
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 #include <stdio.h> #include <stdlib.h> static int pfd1[2 ], pfd2[2 ];void TELL_WAIT (void ) { if (pipe(pfd1) < 0 || pipe(pfd2) < 0 ) { fprintf (stderr , "pipe error!\n" ); exit (-1 ); } }void TELL_PARENT (pid_t pid) { if (write(pfd2[1 ], "c" , 1 ) != 1 ) { fprintf (stderr , "write error" ); exit (-1 ); } }void WAIT_PARENT (void ) { char c; if (read(pfd1[0 ], &c, 1 ) != 1 ) { fprintf (stderr , "read error" ); exit (-1 ); } if (c != 'p' ) { fprintf (stderr , "WAIT_PARENT: incorrect data" ); exit (-1 ); } }void TELL_CHILD (pid_t pid) { if (write(pfd1[1 ], "p" , 1 ) != 1 ) { fprintf (stderr , "write error" ); exit (-1 ); } }void WAIT_CHILD (void ) { char c; if (read(pfd2[0 ], &c, 1 ) != 1 ) { fprintf (stderr , "read error" ); exit (-1 ); } if (c != 'c' ) { fprintf ("WAIT_CHILD: incorrect data" ); exit (-1 ); } }
popen 和 pclose popen
用于创建管道、fork
子管道、关闭管道未使用的端、执行shell
命令以及等待命令终止。
1 2 3 4 5 6 7 #include <stdio.h> FILE *popen (const char *cmdstring, const char *type) ; Returns:file pointer if OK, NULL on errorint pclose (FILE *fp) ; Returns:termination status of cmdstring, or -1 on error
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #define PAGER "${PAGER:-more}" #define MAXLINE 1024 int main (int argc, char *argv[]) { char line[MAXLINE]; FILE *fpin, *fpout; if (argc != 2 ) { fprintf (stderr , "usage: a.out <pathname>" ); exit (-1 ); } if ((fpin = fopen(argv[1 ], "r" )) == NULL ) { fprintf (stderr , "can't open %s" , argv[1 ]); exit (-1 ); } if ((fpout = popen(PAGER, "w" )) == NULL ) { fprintf (stderr , "popen error" ); exit (-1 ); } while (fgets(line, MAXLINE, fpin) != NULL ) { if (fputs (line, fpout) == EOF) { fprintf (stderr , "fputs error to pipe" ); exit (-1 ); } } if (ferror(fpin)) { fprintf (stderr , "fgets error" ); exit (-1 ); } if (pclose(fpout) == -1 ) { fprintf (stderr , "pclose error" ); exit (-1 ); } exit (0 ); }
使用管道实现字符大小写转换
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 #include <ctype.h> #include <stdio.h> #include <stdlib.h> int main (void ) { int c; while ((c = getchar()) != EOF){ if (isupper (c)) c = tolower (c); if (putchar (c) == EOF) { fprintf (stderr , "output error\n" ); exit (-1 ); } if (c == '\n' ) fflush(stdout ); } exit (0 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #define MAXLINE 1024 int main (void ) { char line[MAXLINE]; FILE *fpin; if ((fpin = popen("./myuclc" , "r" )) == NULL ) { fprintf (stderr , "popen error\n" ); exit (-1 ); } for (;;){ fputs ("prompt> " ,stdout ); fflush(stdout ); if (fgets(line, MAXLINE, fpin) == NULL ) break ; if (fputs (line, stdout ) == EOF) { fprintf (stderr , "fputs error to pipe" ); exit (-1 ); } } if (pclose(fpin) == -1 ) { fprintf (stderr , "pclose error" ); exit (-1 ); } putchar ('\n' ); exit (0 ); }
协作进程
使用协作进程实现加法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define MAXLINE 1024 int main (void ) { int n, int1, int2; char line[MAXLINE]; while ((n = read(STDIN_FILENO, line, MAXLINE)) > 0 ) { line[n] = 0 ; if (sscanf (line, "%d%d" , &int1, &int2) == 2 ){ sprintf (line, "%d\n" , int1 + int2); n = strlen (line); if (write(STDOUT_FILENO, line, n) != n) { fprintf (stderr , "write error" ); exit (-1 ); } } else { if (write(STDOUT_FILENO, "invalid args\n" ,13 ) != 13 ) { fprintf (stderr , "write error" ); exit (-1 ); } } } exit (0 ); }
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <string.h> #define MAXLINE 1024 static void sig_pipe (int ) ;int main (void ) { int n, fd1[2 ], fd2[2 ]; pid_t pid; char line[MAXLINE]; if (signal(SIGPIPE, sig_pipe) == SIG_ERR) { fprintf (stderr , "signal error!\n" ); exit (-1 ); } if (pipe(fd1) < 0 || pipe(fd2) < 0 ) { fprintf (stderr , "fork error!\n" ); } if ((pid = fork()) < 0 ) { fprintf (stderr , "fork error" ); exit (-1 ); } else if (pid > 0 ){ close(fd1[0 ]); close(fd2[1 ]); while (fgets(line, MAXLINE, stdin ) != NULL ){ n = strlen (line); if (write(fd1[1 ], line, n) != n) { fprintf (stderr , "write error to pipe\n" ); exit (-1 ); } if ((n = read(fd2[0 ], line, MAXLINE)) < 0 ) { fprintf (stderr , "read error from pipe" ); exit (-1 ); } if (n == 0 ) { fprintf (stderr , "child closed pipe" ); break ; } line[n] = 0 ; if (fputs (line, stdout ) == EOF) { fprintf (stderr , "fputs error" ); exit (-1 ); } } if (ferror(stdin )) { fprintf (stderr , "fgets error on stdin" ); exit (-1 ); } exit (0 ); } else { close(fd1[1 ]); close(fd2[0 ]); if (fd1[0 ] != STDIN_FILENO) { if (dup2(fd1[0 ], STDIN_FILENO) != STDIN_FILENO) { fprintf (stderr , "dup2 error to stdin" ); exit (-1 ); } close(fd1[0 ]); } if (fd2[1 ] != STDOUT_FILENO){ if (dup2(fd2[1 ], STDOUT_FILENO) != STDOUT_FILENO) { fprintf (stderr , "dup2 error to stdout" ); exit (-1 ); } close(fd2[1 ]); } if (execl("./add2" , "add2" , (char *)0 ) < 0 ) { fprintf (stderr , "execl error" ); exit (-1 ); } } exit (0 ); }static void sig_pipe (int signo) { fprintf (stderr , "SIGPIPE caught\n" ); exit (1 ); }
改写add2文件,使用标准I/O库函数实现对管道的读写,会造成死锁
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <stdio.h> #include <stdlib.h> #define MAXLINE 1024 int main (void ) { int int1, int2; char line[MAXLINE]; while (fgets(line, MAXLINE, stdin ) != NULL ){ if (sscanf (line, "%d%d" , &int1, &int2) == 2 ){ if (printf ("%d\n" , int1 + int2) == EOF) { fprintf (stderr , "printf error!\n" ); exit (-1 ); } } else { if (printf ("invalid args\n" ) == EOF) { fprintf (stderr , "printf error" ); exit (-1 ); } } } exit (0 ); }
采用fgets函数从标准输入流中读取数据,由于标准输入流指向管道,因此在Linux下会默认为全缓冲,那么调用fgets函数时会进行阻塞,等到管道中的数据被填充满时才进行数据的读取,在父进程中同样通过fgets函数进行数据读取,同样为全缓冲,那么造成父进程也在等到管道中的数据被填满后才读取数据,导致了父子进程互相等待的局面,从而导致了死锁
改进方法,将标准输入输出流修改为行缓冲或无缓冲模式
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <stdio.h> #include <stdlib.h> #define MAXLINE 1024 int main (void ) { int int1, int2; char line[MAXLINE]; if (setvbuf(stdin , NULL , _IOLBF, 0 ) != 0 ) { fprintf (stderr , "setvbuf error\n" ); exit (-1 ); } if (setvbuf(stdout , NULL , _IOLBF, 0 ) != 0 ) { fprintf (stderr , "setvbuf error\n" ); exit (-1 ); } while (fgets(line, MAXLINE, stdin ) != NULL ){ if (sscanf (line, "%d%d" , &int1, &int2) == 2 ){ if (printf ("%d\n" , int1 + int2) == EOF) { fprintf (stderr , "printf error!\n" ); exit (-1 ); } } else { if (printf ("invalid args\n" ) == EOF) { fprintf (stderr , "printf error" ); exit (-1 ); } } } exit (0 ); }
FIFOs FIFOs
有时也被称之为命名管道。未命名管道只能是相关的进程间进行数据交互,即由共同祖先创造的子进程之间。但是命名管道可以使得不相关的进程之间也交换数据。
创建FIFO文件
1 2 3 4 #include <sys/stat.h> int mkfifo (const char *path, mode_t mode) ;int mkfifoat (int fd, const char *path, mode_t mode) ; Both return : 0 if OK, -1 on error
如果path
为绝对路径,则忽略fd
参数,此时mkfifoat
与mkfifo
行为类似
path
如果为相对路径,并且fd
指向合法的被打开的目录文件描述符,那么path
则相对于fd
指向目录进行寻址
如果fd
的值为AT_FDCWD
,那么path
则相对于当前目录进行寻址
O_NONBLOCK
标志会影响FIFO
文件的行为
如果为设置该标志,那么以只读方式打开FIFO
文件会进行阻塞直到有以只写方式打开FIFO
文件的进程出现,相反以只写方式打开FIFO
文件的进程也会等到只读形式打开FIFO
文件的进程出现。
FIFOs
可以用于以下两个场景
FIFOs
被用于shell
从一个管道传递数据给另一个,并且不需要创建中间的临时文件
1 2 3 mkfifo fifo1 prog3 < fifo1 & prog1 < infile | tee fifo1 | prog2
FIFOs
被用于客户-服务应用程序中传递数据的中间集合点
客户 经过FIFO
发送请求给服务 ,并且FIFO
的路径是所有客户都知道的。
但是为了使得服务能够给每个客户发送回应包,客户在给服务发送请求包时需要携带自己的进程号,便于让服务识别。当客户关闭FIFO
时会给服务发送SIGPIPE
的信号,因此服务必须处理该信号。
XSI IPC 有三种类型的进程间通信被称之为XSI IPC
Identifiers与Keys Identifiers
用于内核空间使用,用于标记IPC
对象
Keys
用于用户空间使用
有许多方法可以使得服务与客户使用相同的IPC
结构
使用IPC_PRIVATE
的键值新建IPC
的结构,并且会返回identifiers
给客户,但是该方法的缺陷为客户需要利用文件操作去读取identifiers
服务与客户使用相同的键值,但是该方法的问题是键值可能已经被使用
服务与客户通过文件名与对象ID
生成键值,然后使用上述第二个方法。
通过路径与id值生成键
1 2 3 4 5 #include <sys/ipc.h> key_t ftok (const char *path, int id) ; Returns:key if OK, (key_t )-1 on error
Permission Structure ipc_perm
结构与每个IPC
相关联的。这个结构定义了权限和拥有者。
1 2 3 4 5 6 7 8 struct ipc_perm {uid_t uid; gid_t gid; uid_t cuid; gid_t cgid; mode_t mode; ... };
消息队列 消息队列是存储在内核中并被链表链接的信息,通过identified
识别队列,也称之为queue ID
消息队列相关的结构体msqid_ds
1 2 3 4 5 6 7 8 9 10 11 struct msqid_ds {struct ipc_perm msg_perm ; msgqnum_t msg_qnum; msglen_t msg_qbytes; pid_t msg_lspid; pid_t msg_lrpid; time_t msg_stime; time_t msg_rtime; time_t msg_ctime; ... };
msgget 1 2 3 4 #include <sys/msg.h> int msgget (key_t key, int flag) ; Returns:meessage queue ID if OK, -1 on error
当队列被创建完毕后,msqid_ds
的成员会进行初始化
ipc_perm
结构体将被初始化,并且权限相关的标志位将会被设置。
msg_qnum
、msg_lspid
、msg_stime
和msg_rtime
被设置为0
msg_ctime
被设置为当前时间
msg_qbytes
被设置为系统的限制
msgctl 针对消息队列执行操作
1 2 #include <sys/msg.h> int msgctl (int msqid, int cmd, struct msqid_ds *buf) ;
IPC_STAT
:获取msqid_ds
结构体信息
IPC_SET
:将buf
指向的msg_perm.uid
、msg_perm.gid
、msg_prerm.mode
和msg_gbytes
拷贝到与消息队列相关的msqid_ds
结构
IPC_RMID
:从系统中删除消息队列以及队列中所有数据
msgsnd 将数据放入消息队列中
1 2 #include <sys/msg.h> int msgnd (int msqid, const void *ptr, size_t nbytes, int flag) ;
如果发送的最大数据为512字节,则可以使用下面的结构体
1 2 3 4 struct mymesg { long mtype; char mtext[512 ]; };
msgrcv 检索消息队列中的消息
1 2 3 4 5 #include <sys/msg.h> ssize_t msgrcv (int msgid, void *ptr, size_t nbytes, long type, int flag) ; Returns:size of data portion of message if OK, -1 on error
type
参数指定需要返回哪个参数
type == 0
:队列中的第一个消息返回
type > 0
:返回与type
一致的消息
type < 0
:第一个小于或等于type
绝对值的消息
信号量 为了获得共享资源,进程需要做以下操作
测试控制资源的信号量
如果信号量的值是正数,则进程获得该资源,并且把信号量的值减去一
如果信号量的值为0,则进程需要休眠等待直到信号量的的值大于一,当进程被唤醒,重复操作一
信号量结构体semid_ds
1 2 3 4 5 6 7 struct semid_ds {struct ipc_perm sem_perm ; unsigned short sem_nsems; time_t sem_otime; time_t sem_ctime; ... };
每个信号量由至少包含以下成员的匿名结构表示
1 2 3 4 5 6 7 struct {unsigned short semval; pid_t sempid; unsigned short semncnt; unsigned short semzcnt; ... };
semget 获取信号量的id值
1 2 3 #include <sys/sem.h> int semget (key_t key, int nsems, int flag) ; Returns:semaphore ID if OK, -1 on error
semctl 操作信号量
1 2 3 4 5 6 7 8 9 #include <sys/sem.h> int semctl (int semid, int semnum, int cmd, ...)
第四个为可选参数,若存在则为senum类型
1 2 3 4 5 union semun {int val; struct semid_ds *buf ; unsigned short *array ; };
cmd
为执行的命令,命令选择如下
IPC_STAT
:获取此集合的semid_ds
结构,将其存储在arg.buf
指向的结构中。
IPC_SET
:从与此集关联的semid_ds
结构中arg.buf
指向的结构中设置sem_perm.uid
、sem_perm.gid
和sem_perm.mode
字段。此命令只能由有效用户ID等于sem_perm.cuid
或sem_perm.uid
的进程或具有超级用户权限的进程执行
IPC_RMID
:从系统中删除信号量集。此删除是立即的。任何其他仍在使用该信号量的进程在其下一次尝试对该信号量进行操作时都会收到EIDRM
错误。
此命令只能由有效用户ID
等于sem_perm.cuid
或sem_perm.uid
的进程或具有超级用户权限的进程执行。
GETVAL
:返回成员semnum
的semval
值
SETVAL
:设置成员semnum
的semval
值。该值由arg.val
指定
GETPID
:返回成员semnum
的sempid
值
GETNCNT
:返回成员semnum
的semncnt
值
GETZCNT
:返回成员semnum
的semzcnt
值
GETALL
:获取集合中的所有信号量值。这些值存储在arg.array
指向的数组中
SETALL
:将集合中的所有信号量值设置为arg.array
指向的值
semop 原子地对信号集执行一系列操作
1 2 3 #include <sys/sem.h> int semop (int semid, struct sembuf semoparray[], size_t nops) ; Returns: 0 if OK, −1 on error
semoparray
参数是指向信号量操作数组的指针,由sembuf
结构表示
1 2 3 4 5 struct sembuf {unsigned short sem_num; short sem_op; short sem_flg; };
结构体sembuf
中地sem_op
变量定义了对信号集的操作
sem_op
为正值,则对应的进程归还资源。sem_op
的值被加到信号量的值上。如果指定undo
标志,则还将该进程的信号量调整值减去sem_op
sem_op
为负值,希望获得信号量控制的资源。则从信号量值中减去sem_op
绝对值。这保证了生成的信号量值大于或等于0。如果指定了undo
标志,则sem_op
的绝对值也会添加到此进程的信号量调整值中。
如果信号量的值小于sem_op
的绝对值,则以下条件使用
指定了IPC_NOWAIT
,则semop
返回错误EAGAIN
未指定IPC_NOWAIT
,则信号量的semncnt
值递增,并且调用过程暂停,直到出现以下情况
信号量的值大于或等于sem_op
的绝对值,此时信号量的semncnt
值减少,并且从信号量的值中减去sem_op
的绝对值。如果指定了undo
标志,则sem_op
的绝对值也会添加到此进程的信号量调整值中。
信号量从系统中被移除。函数返回EIDRM
信号被捕捉,信号量中的semncnt
值递减,并且函数返回EINTR
sem_op
值为0,代表进程需要等待直到信号量的值为0
如果信号量的值当前为0,则函数立即返回
如果信号量的值是非零,则发生以下的情况
如果IPC_NOWAIT
被指定,则error
被标记为EAGIN
如果IPC_NOWAIT
被指定,则semzcnt
的值递增,并且调用进程悬挂直到以下的情况发生
信号量的值变为0。信号量的semzcnt
递减
信号量从系统中被移除。函数返回并将error
设置为EIDRM
信号被捕捉。信号量的semzcnt
递减,并且函数返回并将error
设置为EINTR
共享内存 共享内存允许两个或多个进程共享给定的内存区域。
共享内存的结构体
1 2 3 4 5 6 7 8 9 10 11 struct shmid_ds {struct ipc_perm shm_perm ; size_t shm_segsz; pid_t shm_lpid; pid_t shm_cpid; shmatt_t shm_nattch; time_t shm_atime; time_t shm_dtime; time_t shm_ctime; ... };
shmget 获取共享内存的identifier
1 2 3 4 #include <sys/shm.h> int shmget (key_t key, size_t size, int flag) ; Returns:shared memory ID if OK, -1 on error
shmctl 操作共享内存
1 2 3 4 5 #include <sys/shm.h> int shmctl (int shmid, int cmd, struct shmid_ds *buf) ; Returns:0 if OK, -1 on error
cmd
指示了操作的行为
IPC_STAT
:从段中获取shmid_ds
结构体,并且将这个结构存入buf
IPC_SET
:从与此共享内存段关联的shmid_ds
结构中buf
指向的结构中设置以下三个字段:shm_perm.uid
、shm_perm_gid
和shm_perm.mode
。此命令只能由有效用户ID等于shm_permcuid
或shm_perk.uid
的进程或具有超级用户权限的进程执行。
IPC_RMID
:从系统中删除共享内存段集。由于为共享内存段(shmid_ds
结构中的shm_attch
字段)保留了一个附加计数,因此在使用该段的最后一个进程终止或分离该段之前,不会删除该段。无论该段是否仍在使用,都会立即删除该段的标识符,以便shmat
不再附加该段。此命令只能由有效用户ID
等于shm_perm.uid
或shm_perm.uid
的进程或具有超级用户权限的进程执行。
LINUX额外增加的命令
SHM_LOCK
:将共享内存段锁定在内存中。此命令只能由超级用户执行。
SHM_UNLOCK
:解锁共享内存段。此命令只能由超级用户执行。
shmat 共享存储段连接到调用进程的哪个地址上与addr
参数以及flag
中是否制定SHM_RND
位有关
1 2 3 4 #include <sys/shm.h> void *shmat (int shmid, const void *addr, int flag) ;
addr
为0:则此段链接到由内核选择的第一个可用地址上。
addr
非0并且SHM_RND
没被指定,该段被附着的地址为addr
addr
非0并且指定SHM_RND
,该段被附着的地址为addr
-(addr
% SHMLBA
).SHM_RND
为取整。
若flag
的指定了SHM_RDONLY
位,则以只读形式连接此段,否则以读写形式连接此段。
shmdt 使用shmdt
函数可以将地址从共享存储段中分离。并不是删除 ,直到带IPC_RMID
命令的调用shmctl
特地删除。
1 2 3 4 #include <sys/shm.h> int shmdt (const vidd *addr) ;
example1
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <sys/shm.h> #include <stdio.h> #include <stdlib.h> #define ARRAY_SIZE 40000 #define MALLOC_SIZE 100000 #define SHM_SIZE 1000000 #define SHM_MODE 0600 char array [ARRAY_SIZE];int main (void ) { int shmid; char *ptr, *shmptr; printf ("array[] from %p to %p\n" ,(void *)&array [0 ], (void *)&array [ARRAY_SIZE]); printf ("stack around %p\n" ,(void *)&shmid); if ((ptr = malloc (MALLOC_SIZE)) == NULL ) { fprintf (stderr , "malloc error" ); exit (-1 ); } printf ("malloced from %p to %p\n" , (void *)ptr, (void *)ptr+MALLOC_SIZE); if ((shmid = shmget(IPC_PRIVATE, SHM_SIZE, SHM_MODE)) < 0 ) { fprintf (stderr , "shmget error" ); exit (-1 ); } if ((shmptr = shmat(shmid, 0 , 0 )) == (void *)-1 ) { fprintf (stderr , "shmat error" ); exit (-1 ); } printf ("shared memory attached from %p to %p\n" , (void *)shmptr, (void *)shmptr+SHM_SIZE); if (shmctl(shmid, IPC_RMID, 0 ) < 0 ) { fprintf (stderr , "shmctl error" ); exit (-1 ); } exit (0 ); }
example2
使用/dev/zero
设备使得两个相关进程共享一段匿名内存
使用/dev/zero
设备的好处
创建一个未命名的存储区,长度为mmap
的第二个参数,页对齐
存储区都初始化为0
若指定了MAP_SHARED
则进程共享此区域
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #include <sys/mman.h> #include <unistd.h> #define NLOOPS 1000 #define SIZE sizeof(long) static int pfd1[2 ], pfd2[2 ];void TELL_WAIT (void ) { if (pipe(pfd1) < 0 || pipe(pfd2) < 0 ) { fprintf (stderr , "pipe error!\n" ); exit (-1 ); } }void TELL_PARENT (pid_t pid) { if (write(pfd2[1 ], "c" , 1 ) != 1 ) { fprintf (stderr , "write error" ); exit (-1 ); } }void WAIT_PARENT (void ) { char c; if (read(pfd1[0 ], &c, 1 ) != 1 ) { fprintf (stderr , "read error" ); exit (-1 ); } if (c != 'p' ) { fprintf (stderr , "WAIT_PARENT: incorrect data" ); exit (-1 ); } }void TELL_CHILD (pid_t pid) { if (write(pfd1[1 ], "p" , 1 ) != 1 ) { fprintf (stderr , "write error" ); exit (-1 ); } }void WAIT_CHILD (void ) { char c; if (read(pfd2[0 ], &c, 1 ) != 1 ) { fprintf (stderr , "read error" ); exit (-1 ); } if (c != 'c' ) { fprintf ("WAIT_CHILD: incorrect data" ); exit (-1 ); } }static int update (long *ptr) { return ((*ptr)++); }int main (void ) { int fd, i, counter; pid_t pid; void *area; if ((fd = open("/dev/zero" , O_RDWR)) < 0 ) { fprintf (stderr , "open error" ); exit (-1 ); } if ((area = mmap(0 , SIZE, PROT_READ | PROT_WRITE, MAP_SHARED, fd, 0 )) == MAP_FAILED) { fprintf (stderr , "mmap error" ); exit (-1 ); } close(fd); TELL_WAIT(); if ((pid = fork()) < 0 ){ fprintf (stderr , "fork error" ); exit (-1 ); } else if (pid > 0 ){ for (i = 0 ; i < NLOOPS; i += 2 ){ if ((counter = update((long *)area)) != i) { fprintf (stderr , "parent: expected %d, got %d" ,i, counter); exit (-1 ); } TELL_CHILD(pid); WAIT_CHILD(); } } else { for (i = 1 ; i < NLOOPS + 1 ; i += 2 ){ WAIT_PARENT(); if ((counter = update((long *)area)) != i) { fprintf (stderr , "child: expected %d, got %d" , i, counter); exit (-1 ); } TELL_PARENT(getppid()); } } exit (0 ); }
匿名映射 MAP_ANON
为建立匿名映射区域
1 mmap(0 , SIZE, PROT_READ | PROT_WRITE, MAP_ANON | MAP_SHARED, -1 , 0 )
POSIX信号量 POSX
信号量分为命名信号量与匿名信号量
sem_open 创建和使用命名信号量
1 2 3 4 #include <semaphore.h> sem_t *sem_open (const char *name, int oflag, mode_t mode, unsigned int value) ;
当使用现有的信号量时,只需要指定name
和oflag
参数为0即可。
当使用oflag
为O_CREAT
标志时,若信号量不存在则会创建,若存在则直接使用
需要额外指定两个参数
mode
:谁可以访问该信号量
value
:指定信号量的初始化值,范围为0-SEM_VALUE_MAX
若想确保信号量为新建,则可以指定oflag
为O_CREAT|O_EXCL
,若信号量已存在则会导致sem_open
失败
sem_close 关闭信号量
1 2 3 #include <semaphore.h> int sem_close (sem_t *sem) ;
sem_unlink 销毁命名信号量,若没有对信号量的引用则直接销毁,若存在则等待到没引用时进行销毁。
1 2 3 4 5 #include <semaphore.h> int sem_unlink (const char *name) ;
sem_wait和sem_trywait 对信号量进行减一操作
1 2 3 4 5 6 #include <semaphore.h> int sem_trywait (sem_t *sem) ;int sem_wait (sem_t *sem) ;
sem_wait
,若信号量为0会阻塞
sem_trywait
,若信号量为0不会阻塞,而是直接返回-1并且将errno
设置为EAGAIN
sem_timedwait 阻塞一段时间
1 2 3 4 #include <semaphore.h> #include <time.h> int sem_timedwait (sem_t *restrict sem,const struct timespec *restrict tsptr) ;
若超时则直接返回-1,将errno
设置为ETIMEDOUT
sem_post 将信号量值增加1
1 2 3 4 #include <semaphore.h> int sem_post (sem_t *sem) ;
sem_init 创建未命名的信号量
1 2 3 4 #include <semaphore.h> int sem_init (sem_t *sem, int pshared, unsigned int value) ;
pshared
:表明是否再多个进程中使用变量
value
:指定了信号量的初始值
sem_destory 用于丢弃信号量
1 2 3 4 #include <semaphore.h> int sem_destroy (sem_t *sem) ;
sem_getvalue 检索信号量值
1 2 3 4 #include <semaphore.h> int sem_getvalue (sem_t *restrict sem, int *restrict valp) ;
15-35.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <errno.h> #include <semaphore.h> #include <fcntl.h> struct slock { sem__t *semp; char name[_POSIX_NAME_MAX]; };struct slock *s_alloc () { struct slock *sp ; static int cnt; if ((sp = malloc (sizeof (struct slock))) == NULL ) return (NULL ); do { snprintf (sp->name, sizeof (sp->name), "/%ld.%d" , (long )getpid(), cnt++); sp->semp = sem_open(sp->name, O_CREAT|O_EXCL, S_IRWXU, 1 ); } while (((sp->semp == SEM_FAILED) && (errno == EEXIST)); if (sp->semp == SEM_FAILED){ free (sp); return (NULL ); } sem_unlink(sp->name); return (sp); }void s_free(struct slock *sp) { sem_close(sp->semp); free (sp); }int s_lock(struct slock *sp) { return (sem_wait(sp->semp)); }int s_trylock(struct slock *sp) { return (sem_trywait(sp->semp)); }int s_unlock(struct slock *sp) { return (sem_post(sp->semp)); }
网络IPC:套接字 socket 套接字是一个端点的抽象
1 2 3 4 #include <sys/socket.h> int socket (int domain, int type, int protocol) ;
domain
:通信的特性,包括地址格式,各个域的常数以AF_
开头,表示地址族
AF_INET
:IPv4
因特尔域
AF_INET6
:IPv6
因特尔域
AF_UNIX
:UNIX
域
AF_UNSPEC
:未指定
type
:套接字类型
SOCK_DGRAM
:固定长度、无连接的、不可靠的报文传递
SOCK_RAW
:IP
协议的数据报接口
SOCK_SEQPACKET
:固定长度的、有序的、可靠的、面向连接的报文传递
SOCK_STREAM
:有序的、可靠的、双向的、面向连接的字节流
protocol
:协议类型,若为0则使用默认协议
IPPROTO_IP
:IPv4
网际协议
IPPROTO_IPV6
:IPv6
网际协议
IPPROTO_ICMP
:控制报文协议
IPPROTO_TCP
:传输控制协议
IPPROTO_UDP
:用户数据包协议
在Linux
内部,套接字被视为文件描述符,但是并不是所有对文件的操作都可以应用在套接字上
函数
使用套接字时的行为
close
释放套接字
Dup和dup2
和一般文件描述符一样复制
fchdir
失败,并且将errno
设置为ENOTDIR
fchomod
未指定
fchown
由实现定义
fcntl
支持一些命令,包括F_DUPFD、F_DUPFD_CLOEXEC、F_GETFD、F_GETEFL、F_GETOWN、F_SETFD、F_SETFL和F_SETOWN
Fdatasync和fsync
由实现定义
fstat
支持部分stat结构体成员,具体由实现定义
ftruncate
未指定
ioctl
支持部分命令,依赖于底层设备驱动
lseek
由实现的定义(通常失败时会将errno设为ESPIPE)
mmap
未指定
poll
正常工作
Pread和Pwrite
失败是会将errno设为ESPIPE
read和readv
与没有任何标志位的recv等价
select
正常工作
write和writev
与没有任何标志位的send等价
shutdown 用来禁止一个套接字的I/O
1 2 3 4 #include <sys/socket.h> int shutdown (int sockfd, int how) ;
how
:用于指定禁止的行为
SHUT_RD
:关闭读端,无法从套接字读取数据
SHUT_WR
:关闭写端,无法从套接字发送数据
SHUT_RDWR
:关闭读写端,即无法从套接字发送数据也无法读取数据
字节序转换 1 2 3 4 5 6 7 8 9 10 #include <arpa/inet.h> uint32_t htonl (uint32_t hostint32) ;uint16_t htons (uint16_t hostint16) ;uint32_t ntohl (uint32_t netint32) ;uint16_t ntohs (uint16_t netint16) ;
地址格式 为了使不同的地址都能够传入socket
中,Linux
使用相同的结构体管理地址
1 2 3 4 struct sockaddr { sa_family_t sa_family; char sa_data[14 ]; }
IPv4
因特网域,套接字使用sockaddr_in
表示
1 2 3 4 5 6 7 8 9 struct in_addr { in_addr_t s_addr; };struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr ; };
IPv6
因特网域,套接字使用sockaddr_in6
表示
1 2 3 4 5 6 7 8 9 10 struct_in6_addr{ uint8_t sa_addr[16 ]; };struct sockaddr_in6 { sa_faimly_t sin6_family; in_port_t sin6_port; uint32_t sin6_flowinfo; struct in6_addr sin6_addr ; uint32_t sin6_scope_id; };
在Linux
中,sockaddr_in
的定义如下
1 2 3 4 5 6 struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr ; unsigned char sin_zero[8 ]; };
inet_ntop和inet_pton 用于二进制地址格式与点分十进制字符表示之间的相互转换
1 2 3 4 5 6 7 #include <arpa/inet.h> const char *inet_ntop (int domain, const void *restrict addr, char *restrict str, socklen_t size) ;int inet_pton (int domain, const char * restrict str, void *restrict addr) ;
gethostent 网络配置信息被存放在许多地方,例如在静态文件中/etc/hosts
和/etc/services
,名字服务管理,如域名系统或者网络信息服务。通过gethostent
,都可以找到给定计算机系统的主机信息。
1 2 3 4 5 6 7 #include <netdb.h> struct hostent *gethostent (void ) ;void sethostent (int stayopen) ;void endhostent (void ) ;
hostent
结构体
1 2 3 4 5 6 7 8 struct hostent { char *h_name, char **h_aliases; int h_addrtype; int h_length; char **h_addr_list; ... };
getnetbyaddr和getnetbyname 1 2 3 4 5 6 7 8 9 #include <netdb.h> struct netent *getnetbyaddr (uint32_t net, int type) ;struct netent *getnetbyname (const char *name) ;struct netent *getnetent (void ) ;void setnetent (int stayopen) ;void endnetent (void ) ;
netent
结构包含以下字段
1 2 3 4 5 6 7 struct netent { char *n_name; char **n_aliases; int n_addrtype; uint32_t n_net; ... };
协议名字和协议编号之间映射 1 2 3 4 5 6 7 8 9 #include <netdb.h> struct protoent *getprotobyname (const char *name) ;struct protoent *getprotobynumber (int proto) ;struct protoent *getprotoent (void ) ;void setprotoent (int stayopen) ;void endprotoent (void ) ;
protoent
结构
1 2 3 4 5 6 struct protoent { char *p_name; char **p_aliases; int p_proto; ... }
端口与服务映射 1 2 3 4 5 6 7 8 9 #include <netdb.h> struct servent *getservbyname (const char *name, const char *proto) ;struct servent *getserbyport (int port, const char *proto) ;struct servent *getsetvent (void ) ;void setservent (int stayopen) ;void endservent (void ) ;
servent
结构
1 2 3 4 5 6 7 struct servent { char *s_name; char **s_aliases; int s_port; char *s_proto; ... };
getaddrinfo getaddrinfo
函数允许将一个主机名和一个服务名映射到一个地址
1 2 3 4 5 6 7 8 9 10 #include <sys/socket.h> #include <netdb.h> int getaddrinfo (const char *restrict host, const char *restrict service, const struct addrinfo *restrict hint, struct addrinfo **restrict res) ;void freeaddrinfo (struct addrinfo *ai) ;
getaddrinfo
函数返回一个链表结构addrinfo
。可以用freeaddrinfo
来释放一个或多个这种结构,取决于用ai_next
字段链接起来的结构有多少。
addrinfo
结构
1 2 3 4 5 6 7 8 9 10 11 struct addrinfo { int ai_flags; int ai_family; int ai_socktype; int ai_protocol; socklen_t ai_addrlen; struct sockaddr *ai_addr ; char *ai_canonname; struct addrinfo *ai_next ; ... }
标志
描述
AI_ADDRCONFIG
查询配置的地址类型(IPv4或IPv6)
AI_ALL
查询IPv4和IPv6地址
AI_CANONNAME
需要一个规范的名字
AI_NUMERICHOST
以数字格式指定主机地址,不翻译
AI_NUMERICSERV
将服务指定为数字端口号,不翻译
AI_PASSIVE
套接字地址用于监听绑定
AI_V4MAPPED
如没有找到IPv6地址,返回映射到IPv6格式的IPv4地址
gai_strerror 用于打印getaddrinfo
的失败消息
1 2 3 #include <netdb.h> const char *gai_strerror (int error) ;
getnameinfo getnameinfo
函数将一个地址转换成一个主机名和一个服务名
1 2 3 4 5 6 7 #include <sys/socket.h> #include <netdb.h> int getnameinfo (const struct sockaddr *restrict addr, socklen_t alen, char *restrict host, socklen_t hostlen, char *restrict service, socklen_t servlen, int flags) ;
flag
参数提供了控制翻译的方式
标志
描述
NI_DGRAM
服务基于数据包而非基于流
NI_NAMEREQD
如果找不到主机名,将其作为一个错误对待
NI_NOFQDN
对于本地主机,仅返回全限定域名的节点名部分
NI_NUMERICHOST
返回主机地址的数字形式
NI_NUMERICSCOPE
对于IPv6,返回范围ID的数字形式
NI_NUMERICSERV
返回服务地址的数字形式,即端口号
16-9.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 #include <stdio.h> #include <stdlib.h> #if defined(SOLARIS) #include <netinet/in.h> #endif #include <netdb.h> #include <arpa/inet.h> #if defined(BSD) #include <sys/socket.h> #include <netinet/in.h> #endif void print_family (struct addrinfo *aip) { printf (" family \n" ); switch (aip->ai_family){ case AF_INET: printf ("inet\n" ); break ; case AF_INET6: printf ("inet6\n" ); break ; case AF_UNIX: printf ("unix\n" ); break ; case AF_UNSPEC: printf ("unspecified\n" ); break ; default : printf ("unknown\n" ); break ; } }void print_type (struct addrinfo *aip) { printf (" type \n" ); switch (aip->ai_socktype){ case SOCK_STREAM: printf ("stream\n" ); break ; case SOCK_DGRAM: printf ("datagram\n" ); break ; case SOCK_SEQPACKET: printf ("seqpacket\n" ); break ; case SOCK_RAW: printf ("raw\n" ); break ; default : printf ("unknow (%d)\n" , aip->ai_socktype); } }void print_protocol (struct addrinfo *aip) { printf (" protocol \n" ); switch (aip->ai_protocol) { case 0 : printf ("default\n" ); break ; case IPPROTO_TCP: printf ("TCP\n" ); break ; case IPPROTO_UDP: printf ("UDP\n" ); case IPPROTO_RAW: printf ("raw\n" ); break ; default : printf ("unknow (%d)\n" , aip->ai_protocol); break ; } }void print_flags (struct addrinfo *aip) { printf ("flags\n" ); if (aip->ai_flags == 0 ){ printf (" 0" ); } else { if (aip->ai_flags & AI_PASSIVE) printf (" passive\n" ); if (aip->ai_flags & AI_CANONNAME) printf (" cannon\n" ); if (aip->ai_flags & AI_NUMERICHOST) printf (" numhost\n" ); if (aip->ai_flags & AI_NUMERICSERV) printf (" numserv\n" ); if (aip->ai_flags & AI_V4MAPPED) printf (" v4mapped\n" ); if (aip->ai_flags & AI_ALL) printf (" all\n" ); } }int main (int argc, char *argv[]) { struct addrinfo *ailist , *aip ; struct addrinfo hint ; struct sockaddr_in *sinp ; const char *addr; int err; char abuf[INET_ADDRSTRLEN]; if (argc != 3 ) { fprintf (stderr , "usage: %s nodename service" , argv[0 ]); exit (-1 ); } hint.ai_flags = AI_CANONNAME; hint.ai_family = 0 ; hint.ai_socktype = 0 ; hint.ai_protocol = 0 ; hint.ai_addrlen = 0 ; hint.ai_canonname = NULL ; hint.ai_addr = NULL ; hint.ai_next = NULL ; if ((err = getaddrinfo(argv[1 ], argv[2 ], &hint, &ailist)) != 0 ) { fprintf (stderr , "getaddrinfo erro: %s" , gai_strerror(err)); exit (-1 ); } for (aip = ailist; aip != NULL ; aip = aip->ai_next){ print_flags(aip); print_family(aip); print_type(aip); print_protocol(aip); printf ("\n\thost %s" , aip->ai_canonname ? aip->ai_canonname:"-" ); if (aip->ai_family == AF_INET) { sinp = (struct sockaddr_in *)aip->ai_addr; addr = inet_ntop(AF_INET, &sinp->sin_addr, abuf, INET_ADDRSTRLEN); printf (" address %s" , addr?addr:"unknown" ); printf (" port %d" , ntohs(sinp->sin_port)); } printf ("\n" ); } exit (0 ); }
bind 服务器使用bind
函数关联地址与套接字,服务器通常选择保留一个地址并且注册在/etc/setvices
或者某个名字服务中。
1 2 3 4 #include <sys/socket.h> int bind (int sockfd, const struct sockaddr *addr, socklen_t len) ;
getsockname 调用getsockname
函数来发现绑定到套接字上的地址
1 2 3 4 5 #include <sys/socket.h> int getsockname (int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp) ;
alenp
:指向整数指针,里面存放着缓冲区sockaddr
的长度,若成功执行则将返回长度存放到该整数中
getpeername 若与对等方建立了连接,那么通过getpeername
函数可以获得对方绑定的地址
1 2 3 #include <sys/socket.h> int getpeername (int sockfd, struct sockaddr *restrict addr, socklen_t *restrict alenp) ;
connect connect
函数用于需要建立客户端与服务器之间的连接,例如SOCK_STREAM
或SOCK_SEQPACKET
1 2 3 4 #include <sys/socket.h> int connect (int sockfd, const struct sockaddr *addr, socklen_t len) ;
sockfd
:本地的套接字
addr
:需要建立连接的远程地址
16-10.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #define MAXSLEEP 128 int connect_retry (int sockfd, const struct sockaddr *addr, socklen_t alen) { int numsec; for (numsec = 1 ; numsec <= MAXSLEEP; numsec <<= 1 ){ if (connect(sockfd, addr, alen) == 0 ){ return 0 ; } if (numsec <= MAXSLEEP/2 ) sleep(numsec); } return (-1 ); }
16-11.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #define MAXSLEEP 128 int connect_retry (int domain, int type, int protocol, const struct sockaddr *addr, socklen_t alen) { int numsec, fd; for (numsec = 1 ; numsec <= MAXSLEEP; numsec <<= 1 ) { if ((fd = socket(domain, type, protocol)) < 0 ) return (-1 ); if (connect(fd, addr, alen) == 0 ){ return (fd); } close(fd); if (numsec <= MAXSLEEP/2 ) sleep(numsec); } return (-1 ); }
listen 服务器调用listen
函数表示愿意接受请求
1 2 3 4 #include <sys/socket.h> int listen (int sockfd, int backlog) ;
backlog
:提供提示,用于指示系统该进程所要入队的未完成连接请求数量
accept 服务器用于接收连接
1 2 3 #include <sys/socket.h> int accpet (int sockfd, struct sockaddr *restrict addr, socklen_t *restrict len) ;
addr
:用于存储客户端的地址,不需要时可以设置为NULL
len
:缓冲区的长度
16-12.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include <errno.h> #include <sys/socket.h> int initserver (int type, const struct sockaddr *addr, socklen_t alen, int qlen) { int fd; int err = 0 ; if ((fd = socket(addr->sa_family, type, 0 )) < 0 ) { return (-1 ); } if (bind(fd, addr, alen) < 0 ) goto errout; if (type == SOCK_STREAM || type == SOCK_SEQPACKET) { if (listen(fd, qlen) < 0 ) goto errout; } return (fd); errout: err = errno; close(fd); errno = err; return (-1 ); }
send 用于网络套接字发送数据,才使用这个函数之前,连接已经建立完毕
1 2 3 4 #include <sys/socket.h> ssize_t send (int sockfd, const void *buf, size_t nbytes, int flags) ;
sendto 与send
函数的唯一区别是该函数可以携带地址
1 2 ssize_t sendto (int sockfd, const void *buf, size_t nbytes, int flags, const struct sockaddr *destaddr, socklen_t destlen) ;
sendmsg 与上两个发送函数的区别,信息以msghdr
结构体携带
1 2 3 4 #include <sys/socket.h> ssize_t sendmsg (int sockfd, const struct msghdr *msg, int flags) ;
msghdr 结构体
1 2 3 4 5 6 7 8 9 10 struct msghdr {void *msg_name; socklen_t msg_namelen; struct iovec *msg_iov ; int msg_iovlen; void *msg_control; socklen_t msg_controllen; int msg_flags; ... }
recv 1 2 3 #include <sys/socket.h> ssize_t recv (int sockfd, void *buf, size_t nbytes, int flags) ;
recvfrom recvfrom
函数与recv
函数的区别在于可以接收发送信息方的地址
1 2 3 4 #include <sys/socket.h> ssize_t recvfrom (int sockfd, void *restrict buf,size_t len, int flags, struct sockaddr *restrict addr, socklen_t *restrict addrlen) ;
recvmsg 使用msghdr
结构体接收信息
1 2 3 #include <sys/socket.h> ssize_t recvmsg (int sockfd, struct msghdr *msg, int flags) ;
16-16.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 #include <netdb.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <unistd.h> #include <string.h> #define BUFLEN 128 extern int connect_retry (int , int , int , const struct sockaddr *, socklen_t ) ;void print_uptime (int sockfd) { int n; char buf[BUFLEN]; while ((n = recv(sockfd, buf, BUFLEN, 0 )) > 0 ) { printf ("writing\n" ); write(STDOUT_FILENO, buf, n); } if (n < 0 ) { fprintf (stderr , "recv error\n" ); exit (-1 ); } }int main (int argc, char *argv[]) { struct addrinfo *ailist , *aip ; struct addrinfo hint ; int sockfd, err; if (argc != 2 )OP[E] { fprintf (stderr , "usage: ruptime hostname" ); exit (-1 ); } memset (&hint, 0 , sizeof (hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_canonname = NULL ; hint.ai_addr = NULL ; hint.ai_next = NULL ; if ((err = getaddrinfo(argv[1 ], "ruptime" , &hint, &ailist)) != 0 ) { fprintf (stderr , "getaddrinfo error: %s" , gai_strerror(err)); exit (-1 ); } for (aip = ailist; aip != NULL ; aip = aip->ai_next){ if ((sockfd = connect_retry(aip->ai_family, SOCK_STREAM, 0 , aip->ai_addr, aip->ai_addrlen)) < 0 ){ err = errno; } else { print_uptime(sockfd); exit (0 ); } } fprintf (stderr , "can't connect to %s" ,argv[1 ]); exit (-1 ); }
16-17.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <errno.h> #include <string.h> #include <unistd.h> #include <netdb.h> #define BUFLEN 1024 extern int connect_retry (int , int , int , const struct sockaddr *, socklen_t ) ;void print_uptime (int sockfd) { int n; char buf[BUFLEN]; while ((n = recv(sockfd, buf, BUFLEN, 0 )) > 0 ) { printf ("writing\n" ); write(STDOUT_FILENO, buf, n); } if (n < 0 ) { fprintf (stderr , "recv error\n" ); exit (-1 ); } }int main (int argc, char *argv[]) { struct addrinfo *ailist , *aip ; struct addrinfo hint ; int sockfd, err; memset (&hint, 0 , sizeof (hint)); hint.ai_socktype = SOCK_STREAM; hint.ai_canonname = NULL ; hint.ai_addr = NULL ; hint.ai_next = NULL ; if ((err = getaddrinfo(argv[1 ], "ruptimed" , &hint, &ailist)) != 0 ) { fprintf (stderr , "getaddrinfo error: %s" , gai_strerror(err)); exit (-1 ); } for (aip = ailist; aip != NULL ; aip = aip->ai_next){ fprintf (stderr , "ai_canonname:%s" ,aip->ai_canonname); if ((sockfd = connect_retry(aip->ai_family, SOCK_STREAM, 0 , aip->ai_addr, aip->ai_addrlen)) < 0 ){ err = errno; }else { print_uptime(sockfd); exit (0 ); } } fprintf (stderr ,"can't connect to %s" ,argv[1 ]); exit (-1 ); }
16-18.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 #include <netdb.h> #include <errno.h> #include <syslog.h> #include <fcntl.h> #include <sys/socket.h> #include <sys/wait.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/resource.h> #include <string.h> #define QLEN 10 #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 #endif extern int initserver (int type, const struct sockaddr *addr, socklen_t alen, int qlen) ;int set_cloexec (int fd) { int val; if ((val = fcntl(fd, F_GETFD, 0 )) < 0 ) return (-1 ); val |= FD_CLOEXEC; return (fcntl(fd, F_SETFD,val)); }void daemonize (const char *cmd) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl ; struct sigaction sa ; umask(0 ); if (getrlimit(RLIMIT_NOFILE, &rl) < 0 ) { fprintf (stderr ,"%s: can't get file limit" , cmd); exit (-1 ); } if ((pid = fork()) < 0 ) { fprintf (stderr ,"%s: can't fork" , cmd); exit (-1 ); } else if (pid != 0 ) exit (0 ); setsid(); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0 ; if (sigaction(SIGHUP, &sa, NULL ) < 0 ) { fprintf (stderr ,"%s: can't ignore SIGHUP" , cmd); exit (-1 ); } if ((pid = fork()) < 0 ) { fprintf (stderr ,"%s: can't fork" , cmd); exit (-1 ); } else if (pid != 0 ) exit (0 ); if (chdir("/" ) < 0 ) { fprintf (stderr ,"%s: can't change directory to /" , cmd); exit (-1 ); } if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024 ; for (i = 0 ; i < rl.rlim_max; i++) close(i); fd0 = open("/dev/null" , O_RDWR); fd1 = dup(0 ); fd2 = dup(0 ); openlog(cmd, LOG_CONS, LOG_DAEMON); if (fd0 != 0 || fd1 != 1 || fd2 != 2 ) { syslog(LOG_ERR, "unexpected file descriptors %d %d %d" , fd0, fd1, fd2); exit (1 ); } }void serve (int sockfd) { int clfd, status; pid_t pid; set_cloexec(sockfd); for (;;) { if ((clfd = accept(sockfd, NULL , NULL )) < 0 ){ syslog(LOG_ERR, "ruptimed: accept error: %s" , strerror(errno)); exit (-1 ); } if ((pid = fork()) < 0 ){ syslog(LOG_ERR, "ruptimed: fork error: %s" ,strerror(errno)); exit (-1 ); } else if (pid == 0 ) { if (dup2(clfd, STDOUT_FILENO) != STDOUT_FILENO || dup2(clfd, STDERR_FILENO) != STDERR_FILENO) { syslog(LOG_ERR, "ruptimed: unexpected error" ); exit (1 ); } close(clfd); execl("/usr/bin/uptime" , "uptime" , (char *)0 ); syslog(LOG_ERR, "ruptimed: uunexpecred return from exec: %s" , strerror(errno)); } else { close(clfd); waitpid(pid, &status, 0 ); } } }int main (int argc, char *argv[]) { struct addrinfo *ailist , *aip ; struct addrinfo hint ; int sockfd, err, n; char *host; if (argc != 1 ) { fprintf (stderr , "usgae: ruptimed" ); exit (-1 ); } if ((n = sysconf(_SC_HOST_NAME_MAX)) < 0 ) n = HOST_NAME_MAX; if ((host = malloc (n)) == NULL ) { fprintf (stderr , "malloc error" ); exit (-1 ); } if (gethostname(host, n) < 0 ){ fprintf (stderr , "gethostname error" ); exit (-1 ); } daemonize("ruptimed" ); memset (&hint, 0 , sizeof (hint)); hint.ai_flags = AI_CANONNAME; hint.ai_socktype = SOCK_STREAM; hint.ai_canonname = NULL ; hint.ai_addr = NULL ; hint.ai_next = NULL ; if ((err = getaddrinfo(host, "ruptime" , &hint, &ailist)) != 0 ) { syslog(LOG_ERR, "ruptimed: getaddrinfo error: %s" ,gai_strerror(err)); exit (1 ); } for (aip = ailist; aip != NULL ; aip = aip->ai_next) { if ((sockfd = initserver(SOCK_STREAM, aip->ai_addr, aip->ai_addrlen, QLEN)) >= 0 ) { serve(sockfd); exit (0 ); } } }
16-19.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include <netdb.h> #include <errno.h> #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define BUFLEN 128 #define TIMEOUT 20 void sigalrm (int signo) { }void print_uptime (int sockfd, struct addrinfo *aip) { int n;; char buf[BUFLEN]; buf[0 ] = 0 ; if (sendto(sockfd, buf, 1 , 0 , aip->ai_addr, aip->ai_addrlen) < 0 ) { fprintf (stderr , "sento error" ); exit (-1 ); } alarm(TIMEOUT); if ((n = recvfrom(sockfd, buf, BUFLEN, 0 , NULL , NULL )) <0 ){ if (errno != EINTR) alarm(0 ); fprintf (stderr , "recv error" ); exit (-1 ); } alarm(0 ); write(STDOUT_FILENO, buf, n); }int main (int argc, char *argv[]) { struct addrinfo *ailist , *aip ; struct addrinfo hint ; int sockfd, err; struct sigaction sa ; if (argc != 2 ) { fprintf (stderr , "usage: ruptime hostname" ); exit (-1 ); } sa.sa_handler = sigalrm; sa.sa_flags = 0 ; sigemptyset(&sa.sa_mask); if (sigaction(SIGALRM, &sa, NULL ) < 0 ) { fprintf (stderr , "sigaction error" ); exit (-1 ); } memset (&hint, 0 , sizeof (hint)); hint.ai_socktype = SOCK_DGRAM; hint.ai_canonname = NULL ; hint.ai_addr = NULL ; hint.ai_next = NULL ; if ((err = getaddrinfo(argv[1 ], "ruptime" , &hint, &ailist)) != 0 ) { fprintf (stderr , "getaddrinfo error :%s" ,gai_strerror(err)); exit (-1 ); } for (aip = ailist; aip != NULL ; aip = aip->ai_next) { if ((sockfd = socket(aip->ai_family, SOCK_DGRAM, 0 )) < 0 ) { err = errno; } else { print_uptime(sockfd, aip); exit (0 ); } } fprintf (stderr , "can't contact %s: %s\n" , argv[1 ], strerror(err)); exit (1 ); }
16-20.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 #include <netdb.h> #include <errno.h> #include <syslog.h> #include <sys/socket.h> #include <stdio.h> #include <string.h> #include <stdlib.h> #include <sys/resource.h> #define BUFLEN 128 #define MAXADDRLEN 256 #ifndef HOST_NAME_MAX #define HOST_NAME_MAX 256 #endif extern int initserver (int , const struct sockaddr *, socklen_t , int ) ;void daemonize (const char *cmd) { int i, fd0, fd1, fd2; pid_t pid; struct rlimit rl ; struct sigaction sa ; umask(0 ); if (getrlimit(RLIMIT_NOFILE, &rl) < 0 ) { fprintf (stderr ,"%s: can't get file limit" , cmd); exit (-1 ); } if ((pid = fork()) < 0 ) { fprintf (stderr ,"%s: can't fork" , cmd); exit (-1 ); } else if (pid != 0 ) exit (0 ); setsid(); sa.sa_handler = SIG_IGN; sigemptyset(&sa.sa_mask); sa.sa_flags = 0 ; if (sigaction(SIGHUP, &sa, NULL ) < 0 ) { fprintf (stderr ,"%s: can't ignore SIGHUP" , cmd); exit (-1 ); } if ((pid = fork()) < 0 ) { fprintf (stderr ,"%s: can't fork" , cmd); exit (-1 ); } else if (pid != 0 ) exit (0 ); if (chdir("/" ) < 0 ) { fprintf (stderr ,"%s: can't change directory to /" , cmd); exit (-1 ); } if (rl.rlim_max == RLIM_INFINITY) rl.rlim_max = 1024 ; for (i = 0 ; i < rl.rlim_max; i++) close(i); fd0 = open("/dev/null" , O_RDWR); fd1 = dup(0 ); fd2 = dup(0 ); openlog(cmd, LOG_CONS, LOG_DAEMON); if (fd0 != 0 || fd1 != 1 || fd2 != 2 ) { syslog(LOG_ERR, "unexpected file descriptors %d %d %d" , fd0, fd1, fd2); exit (1 ); } }void serve (int sockfd) { int n; socklen_t alen; FILE *fp; char buf[BUFLEN]; char abuf[MAXADDRLEN]; struct sockaddr *addr = (struct sockaddr *)abuf; set_cloexec(sockfd); for (;;) { alen = MAXADDRLEN; if ((n = recvfrom(sockfd, buf, BUFLEN, 0 , addr, &alen)) < 0 ) { syslog(LOG_ERR, "ruptimed: recvfrom error :%s" , strerror(errno)); exit (1 ); } if ((fp = popen("/usr/bin/uptime" , "r" )) == NULL ) { sprintf (buf, "error: %s\n" , strerror(errno)); sendto(sockfd, buf, strlen (buf), 0 , addr, alen); } else { if (fgets(buf, BUFLEN, fp) != NULL ) sendto(sockfd, buf, strlen (buf), 0 , addr, alen); pclose(fp); } } }int main (int argc, char *argv[]) { struct addrinfo *ailist , *aip ; struct addrinfo hint ; int sockfd, err, n; char *host; if (argc != 1 ) { fprintf (stderr , "usage: ruptimed" ); exit (-1 ); } if ((n = sysconf(_SC_HOST_NAME_MAX)) < 0 ) n = HOST_NAME_MAX; if ((host = malloc (n)) == NULL ) { fprintf (stderr , "malloc error" ); exit (-1 ); } if (gethostname(host, n) < 0 ) { fprintf (stderr , "gethostname error" ); exit (-1 ); } daemonize("ruptimed" ); memset (&hint, 0 , sizeof (hint)); hint.ai_flags = AI_CANONNAME; hint.ai_socktype = SOCK_DGRAM; hint.ai_canonname = NULL ; hint.ai_addr = NULL ; hint.ai_next = NULL ; if ((err = getaddrinfo(host, "ruptime" , &hint, &ailist)) != 0 ){ syslog(LOG_ERR, "ruptimed: getaddrinfo error:%s" , gai_strerror(err)); exit (1 ); } for (aip = ailist; aip != NULL ; aip = aip->ai_next) { if ((sockfd = initserver(SOCK_DGRAM, aip->ai_addr, aip->ai_addrlen, 0 )) >= 0 ){ serve(sockfd); exit (0 ); } } exit (1 ); }
套接字选项
通用选项
在套接字层次管理的选项,但是依赖于下层协议的支持
特定于某协议的选项,每个协议独有的
setsockopt 1 2 3 #include <sys/socket.h> int setsockopt (int sockfd, int level, int option, const void *val, socklen_t len) ;
level
:标识了选项应用的协议
通用套接字层则为SOL_SOCKET
TCP
为IPPRTO_TCP
IP
为IPPROTO_IP
val
:根据选项的不同指向一个数据结构或者一个整数。
len
:指定val
指向的对象的大小
getsockopt 1 2 3 4 #include <sys/socket.h> int getsockopt (int sockfd, int level, int option, void *restrict val, socklen_t *restrict lenp) ;
sockatmark 判断是否到达紧急标志
1 2 3 #include <sys/socket.h> int sockatmark (int sockfd) ;
高级进程间通信 UNIX域套接字 1 2 3 #include <sys/socket.h> int socketpair (int domain, int type, int protocol, int sockfd[2 ]) ;
17-2.c
1 2 3 4 5 6 #include <sys/socket.h> int fd_pipe (int fd[2 ]) { return (socketpair(AF_UNIX, SOCK_STREAM, 0 , fd)); }
借助UNIX域套接字轮询XSI消息队列
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 #include <poll.h> #include <pthread.h> #include <sys/msg.h> #include <sys/socket.h> #include <string.h> #include <stdio.h> #define NQ 3 #define MAXMSZ 512 #define KEY 0x123 struct threadinfo { int qid; int fd; };struct mymesg { long mtype; char mtext[MAXMSZ]; };void *helper (void *arg) { int n; struct mymesg m ; struct threadinfo *tip = arg; for (;;){ memset (&m, 0 , sizeof (m)); if ((n = msgrcv(tip->qid, &m, MAXMSZ, 0 , MSG_NOERROR)) < 0 ) { fprintf (stderr , "msgrcv error!\n" ); exit (-1 ); } if (write(tip->fd, m.mtext, n) < 0 ) { fprintf (stderr , "write error!\n" ); exit (-1 ); } } }int main () { int i, n, err; int fd[2 ]; int qid[NQ]; struct pollfd pfd [NQ ]; struct threadinfo ti [NQ ]; pthread_t tid[NQ]; char buf[MAXMSZ]; for (i = 0 ; i < NQ; i++){ if ((qid[i] = msgget((KEY+i), IPC_CREAT | 0666 )) < 0 ) { fprintf (stderr , "msgget error\n" ); exit (-1 ); } printf ("queue ID %d is %d\n" , i, qid[i]); if (socketpair(AF_UNIX, SOCK_DGRAM, 0 , fd) < 0 ) { fprintf (stderr , "pthread_create error" ); exit (-1 ); } pfd[i].fd = fd[0 ]; pfd[i].events = POLLIN; ti[i].qid = qid[i]; ti[i].fd = fd[1 ]; if ((err = pthread_create(&tid[i], NULL , helper, &ti[i])) != 0 ) { fprintf (stderr , "pthread create error!" ); exit (-1 ); } } for (;;) { if (poll(pfd, NQ, -1 ) < 0 ) { fprintf (stderr , "poll error" ); exit (-1 ); } for (i = 0 ; i < NQ; i++) { if (pfd[i].revents & POLLIN) { if ((n = read(pfd[i].fd, buf, sizeof (buf))) < 0 ) { fprintf (stderr , "read error" ); exit (-1 ); } buf[n] = 0 ; printf ("queue id %d, message %s\n" , qid[i], buf); } } } exit (0 ); }
17-4.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <sys/msg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define MAXMSZ 512 struct mymesg { long mtype; char mtext[MAXMSZ]; };int main (int argc, char *argv[]) { key_t key; long qid; size_t nbytes; struct mymesg m ; if (argc != 3 ) { fprintf (stderr ,"usage: sendmsg KEY message\n" ); exit (-1 ); } key = strtol(argv[1 ], NULL , 0 ); if ((qid = msgget(key, 0 )) < 0 ) { fprintf (stderr , "can't open queue key %s\n" , argv[1 ]); exit (-1 ); } memset (&m, 0 , sizeof (m)); strncpy (m.mtext, argv[2 ], MAXMSZ-1 ); nbytes = strlen (m.mtext); m.mtype = 1 ; if (msgsnd(qid, &m, nbytes, 0 ) < 0 ) { fprintf (stderr , "can't send message\n" ); exit (-1 ); } exit (0 ); }
17-5.c 命名UNIX域套接字
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 #include <stdio.h> #include <stdlib.h> #include <sys/socket.h> #include <sys/un.h> #include <string.h> #include <stddef.h> int main (int argc, char *argv[]) { int fd, size; struct sockaddr_un un ; un.sun_family = AF_UNIX; strcpy (un.sun_path, "foo.socket" ); if ((fd = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0 ) { fprintf (stderr , "socket failed" ); exit (-1 ); } size = offsetof(struct sockaddr_un, sun_path) + strlen (un.sun_path); if (bind(fd, (struct sockaddr *)&un, size) < 0 ) { fprintf (stderr , "bind failed" ); exit (-1 ); } printf ("UNIX domain socket bound\n" ); exit (0 ); }
唯一连接 1 2 3 4 5 6 7 8 9 10 #include "apue.h" int serv_listen (const char *name) ;int serv_acceptt (int listenfd, uid_t *uidptr) ;int cli_conn (const char *name) ;
serv_listen 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <stddef.h> #define QLEN 10 int serv_listern (const char *name) { int fd, len, err, rval; struct sockaddr_un un ; if (strlen (name) >= sizeof (un.sun_path)) { errno = ENAMETOOLONG; return (-1 ); } if ((fd = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0 ) return (-2 ); unlink(name); memset (&un, 0 , sizeof (un)); un.sun_family = AF_UNIX; strcpy (un.sun_path, name); len = offsetof(struct sockaddr_un, sun_path) + strlen (name); if (bind(fd, (struct sockaddr *)&un, len) < 0 ) { rval = -3 ; goto errout; } if (listen(fd, QLEN) < 0 ) { rval = -4 ; goto errout; } return (fd); errout: err = errno; close(fd); errno = err; return (rval); }
serv_accept 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 #include <sys/socket.h> #include <sys/un.h> #include <time.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #include <sys/stat.h> #include <stddef.h> #define STALE 30 int serve_accept (int listenfd, uid_t *uidptr) { int clifd, err, rval; socklen_t len; time_t staletime; struct stat statbuf ; struct sockaddr_un un ; char *name; if ((name = malloc (sizeof (un.sun_path) + 1 )) == NULL ) return (-1 ); len = sizeof (un); if ((clifd = accept(listenfd, (struct sockaddr *)&un, &len)) < 0 ){ free (name); return (-2 ); } len -= offsetof(struct sockaddr_un, sun_path); memcpy (name, un.sun_path, len); name[len] = 0 ; if (stat(name, &statbuf) < 0 ) { rval = -3 ; goto errout; }#ifdef S_ISSOCK if (S_ISSOCK(statbuf.stmode) == 0 ) { rval = -4 ; goto errout; }#endif if ((statbuf.st_mode & (S_IRWXG | S_IRWXO)) || (statbuf.st_mode & S_IRWXU) != S_IRWXU) { rval = -5 ; goto errout; } staletime = time(NULL ) - STALE; if (statbuf.st_atime < staletime || statbuf.st_ctime < staletime || statbuf.st_mtime < staletime) { rval = -6 ; goto errout; } if (uidptr != NULL ) *uidptr = statbuf.st_uid; unlink(name); free (name); return (clifd); errout: err = errno; close(clifd); free (name); errno = err; return (rval); }
cli_conn 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 #include <sys/socket.h> #include <sys/un.h> #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <stddef.h> #include <sys/stat.h> #define CLI_PATH "/var/tmp" #define CLI_PERM S_IRWXU int cli_conn (const char *name) { int fd, len, err, rval; struct sockaddr_un un , sun ; int do_unlink = 0 ; if (strlen (name) >= sizeof (un.sun_path)) { errno = ENAMETOOLONG; return (-1 ); } if ((fd = socket(AF_UNIX, SOCK_STREAM, 0 )) < 0 ) return (-1 ); memset (&un, 0 , sizeof (un)); un.sun_family = AF_UNIX; sprintf (un.sun_path, "%s%05ld" , CLI_PATH, (long )getpid()); len = offsetof(struct sockaddr_un, sun_path) + strlen (un.sun_path); unlink(un.sun_path); if (bind(fd, (struct sockaddr *)&un, len) < 0 ) { rval = -2 ; goto errout; } if (chmod(un.sun_path, CLI_PERM) < 0 ) { rval = -3 ; do_unlink = 1 ; goto errout; } memset (&sun, 0 , sizeof (sun)); sun.sun_family = AF_UNIX; strcpy (sun.sun_path, name); len = offsetof(struct sockaddr_un, sun_path) + strlen (name); if (connect(fd, (struct sockaddr *)&sun, len) < 0 ) { rval = -4 ; do_unlink = 1 ; goto errout; } return (fd); errout: err = errno; close(fd); if (do_unlink) unlink(un.sun_path); errno = err; return (rval); }
传送文件描述符 1 2 3 4 5 6 7 #include "apue.h" int send_fd (int fd, int fd_to_send) ;int send_err (int fd, int status, char *errmsg) ;int recv_fd (int fd, ssize_t (*userfunc)(int , const void *, size_t )) ;
msghdr结构体
1 2 3 4 5 6 7 8 9 struct msghdr { void *msg_name; socklen_t msg_namelen; struct iovec *msg_iov ; int msg_iovlen; void *msg_control; socklen_t msg_controllen; int msg_flags; };
msg_control字段指向cmsghdr
1 2 3 4 5 6 struct cmsghdr { socklen_t cmsg_len; int cmsg_level; int cmsg_type; }
访问控制数据 1 2 3 4 5 6 7 8 9 10 #include <sys/socket.h> unsigned char *CMSG_DATA (struct cmsghdr *cp) ;struct cmsghdr *CMSG_FIRSTHDR (struct msghdr *mp) ;struct cmsghdr *CMSG_NXTHDR (struct msghdr *mp, struct cmsghdr *cp) ;unsigned int CMSG_LEN (unsigned int nbytes) ;
17-13.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #if defined(SCM_CREDS) #define CREDSTRUCT cmsgcred #define SCM_CREDTYPE SCM_CREDS #elif defined(SCM_CREDENTIALS) #define CREDSTRUCT ucred #define SCM_CREDTYPE SCM_CREDENTIALS #else #endif #define RIGHTSLEN CMSG_LEN(sizeof(int)) #define CREDSLEN CMSG_LEN(sizeof(struct CREDSTRUCT)) #define CONTROLLEN (RIGHTSLEN + CREDSLEN) static struct cmsghdr *cmptr = NULL ;int send_fd (int fd, int fd_to_send) { struct iovec iov [1]; struct msghdr msg ; char buf[2 ]; iov[0 ].iov_base = buf; iov[0 ].iov_len = 2 ; msg.msg_iov = iov; msg.msg_name = NULL ; msg.msg_namelen = 0 ; if (fd_to_send < 0 ) { msg.msg_control = NULL ; msg.msg_controllen = 0 ; buf[1 ] = -fd_to_send; if (buf[1 ] == 0 ) buf[1 ] = 1 ; } else { if (cmptr = NULL && (cmptr = malloc (CONTROLLEN)) == NULL ) return (-1 ); cmptr->cmsg_level = SOL_SOCKET; cmptr->cmsg_type = SCM_RIGHTS; cmptr->cmsg_len = CONTROLLEN; msg.msg_control = cmptr; msg.msg_controllen = CONTROLLEN; *(int *)CMSG_DATA(comptr) = fd_to_send; buf[1 ] = 0 ; } buf[0 ] = 0 ; if (sendmsg(fd, &msg, 0 ) != 2 ) reurn(-1 ); return (0 ); }
17-14.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #define CONTROLLEN CMSCG_LEN(sizeof(int)) #define MAXLINE 256 static struct cmsghdr *cmptr = NULL ;int recv_fd (int fd, ssize_t (*userfunc)(int , const void *, size_t )) { int newfd, nr, status; char *ptr; char buf[MAXLINE]; struct iovec iov [1]; struct msghdr msg ; status = -1 ; for (;;) { iov[0 ].iov_base = buf; iov[0 ].iov_len = sizeof (buf); msg.msg_iov = iov; msg.msg_iovlen = 1 ; msg.msg_name = NULL ; msg.msg_namelen = 0 ; if (cmptr == NULL && (cmptr = malloc (CONTROLLEN)) == NULL ) return (-1 ); msg.msg_control = cmptr; msg.msg_controllen = CONTROLLEN; if ((nr = recvmsg(fd, &msg, 0 )) < 0 ) { fprintf (stderr , "recvmsg error\n" ); return (-1 ); } else if (nr == 0 ) { fprintf (stderr , "connection closed by servver" ); exit (-1 ); } for (ptr = buf; ptr < &buf[nr];) { if (*ptr++ == 0 ) { if (ptr != &buf[nr-1 ]) { fprintf (stderr , "message format error" ); exit (-1 ); } status = *ptr & 0xFF ; if (status == 0 ){ if (msg.msg_controllen < CONTROLLEN) { fprintf (stderr , "status = 0 but no fd" ); exit (-1 ); } newfd = *(int *)CMSG_DATA(cmptr); } else { newfd = -status; } nr -= 2 ; } } } if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr) return (-1 ); if (status >= 0 ) return (newfd); }
在FreeBSD
中,将证书作为cmsgcred
结构传送
1 2 3 4 5 6 7 8 9 10 11 #define CMGROUP_MAX 16 struct cmsgcred { pid_t cmcred_pid; uid_t cmcred_uid; uid_t cmcred_euid; gid_t cmcred_gid; short cmcred_ngroups; gid_t cmcred_groups[CMGROUP_MAX]; short cmcred_ngroups; gid_t cmcred_groups[CMGROUP_MAX]; };
在Linux
中,将证书作为ucred
结构传送
1 2 3 4 5 struct ucred { pid_t pid; uid_t uid; gid_t gid; };
17-15.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <sys/socket.h> #include <stdio.h> #include <stdlib.h> #if defined(SCM_CREDS) #define CREDSTRUCT cmsgcred #define SCM_CREDTYPE SCM_CREDS #elif defined(SCM_CREDENTIALS) #define CREDSTRUCT ucred #define SCM_CREDTYPE SCM_CREDENTIALS #endif #define RIGHTSLEN CMSG_LEN(sizeof(int)) #define CREDSLEN CMSG_LEN(sizeof(struct CREDSTRUCT)) #define CONTROLLEN (RIGHTSLEN + CREDSLEN) static struct cmsghdr *cmptr = NULL ;int send_fd (int fd, int fd_to_send) { struct CREDSTRUCT *credp ; struct cmsghdr *cmp ; struct iovec iov [1]; struct msghdr msg ; char buf[2 ]; iov[0 ].iov_base = buf; iov[0 ].iov_len = 2 ; msg.msg_iov = iov; msg.msg_controllen = 1 ; msg.msg_name = NULL ; msg.msg_namelen = 0 ; msg.msg_flags = 0 ; if (fd_to_send < 0 ) { msg.msg_control = NULL ; msg.msg_controllen = 0 ; buf[1 ] = -fd_to_send; if (buf[1 ] == 0 ) buf[1 ] = 1 ; } else { if (cmptr == NULL && (cmptr = malloc (CONTROLLEN)) == NULL ) return (-1 ); msg.msg_control = cmptr; msg.msg_controllen = CONTROLLEN; cmp = cmptr; cmp->cmsg_level = SOL_SOCKET; cmp->cmsg_type = SCM_RIGHTS; cmp->cmsg_len = RIGHTSLEN; *(int *)CMSG_DATA(cmp) = fd_to_send; cmp = CMSG_NXTHDR(&msg, cmp); cmp->cmsg_level = SOL_SOCKET; cmp->cmsg_type = SCM_CREDTYPE; cmp->cmsg_len = CREDSLEN; credp = (struct CREDSTRUCT *)CMSG_DATA(cmp); #if defined(SCM_CREDENTIALS) credp->uid = geteuid(); credp->gid = getegid(); credp->pid = getpid();#endif buf[1 ] = 0 ; } buf[0 ] = 0 ; if (sendmsg(fd, &msg, 0 ) != 2 ) return (-1 ); return (0 ); }
17-16.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 #include <sys/socket.h> #include <sys/un.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #if defined(SCM_CREDS) #define CREDSTRUCT cmsgcred #define CR_UID #define SCM_CREDTYPE SCM_CREDS #elif defined(SCM_CREDENTIALS) #define CREDSTRUCT ucred #define CR_UID uid #define CREDOPT SO_PASSCRED #define SCM_CREDTYPE SCM_CREDENTIALS #endif #define RIGHTSLEN CMSG_LEN(sizeof(int)) #define CREDSLEN CMSG_LEN(sizoef(struct CREDSTRUCT)) #define CONTROLLEN (RIGHTSLEN + CREDSLEN) #define MAXLINE 256 static struct cmsghdr *cmptr = NULL ;int recv_ufd (int fd, uid_t *uidptr, ssize_t (*userfunc)(int , const void *, size_t )) { struct cmsghdr *cmp ; struct CREDSTRUCT *credp ; char *ptr; char buf[MAXLINE]; struct iovec iov [1]; struct msghdr msg ; int nr; int newfd = -1 ; int status = -1 ;#if defined(CREDOPT) const int on = 1 ; if (setsockopt(fd, SOL_SOCKET, CREDOPT, &on, sizeof (int )) < 0 ) { fprintf (stderr , "setsockopt error" ); exit (-1 ); }#endif for (;;) { iov[0 ].iov_base = buf; iov[0 ].iov_len = sizeof (buf); msg.msg_iov = iov; msg.msg_iovlen = 1 ; msg.msg_name = NULL ; msg.msg_namelen = 0 ; if (cmptr == NULL && (cmptr = malloc (CONTROLLEN)) == NULL ) return (-1 ) msg.msg_control = cmptr; msg.msg_controllen = CONTROLLEN; if ((nr = recvmsg(fd, &msg, 0 )) < 0 ) { fprintf (stderr , "recvmsg error" ); return (-1 ); } else if (nr == 0 ) { fprintf (stderr , "connection closed by server" ); return (-1 ); } for (ptr = buf; ptr < &buf[nr]; ) { if (*ptr++ == 0 ) { if (ptr != &buf[nr-1 ]) { fprintf (stderr , "message format error" ); exit (-1 ); } } status = *ptr & 0xFF ; if (status == 0 ) { if (msg.msg_controllen != CONTROLLEN) { fprintf (stderr , "status = 0 but no fd" ); exit (-1 ); } for (cmp = CMSG_FIRSTHDR(&msg); cmp != NULL ; cmp = CMSG_NXTHDR(&msg, cmp)) { if (cmp->cmsg_level != SOL_SOCKET) continue ; switch (cmp->cmsg_type) { case SCM_RIGHTS: newfd = *(int *)CMSG_DATA(cmp); break ; case SCM_CREDTYPE: credp = (struct CREDSTRUCT *)CMSG_DATA(cmp); *uidptr = credp->CR_UID; break ; } } } else { newfd = -status; } nr -= 2 ; } } if (nr > 0 && (*userfunc)(STDERR_FILENO, buf, nr) != nr) return (-1 ); if (status >= 0 ) return (newfd); }
17-21.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 #include <errno.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <fcntl.h> #include <sys/uio.h> #include "include/apue.h" #define CL_OPEN "open" #define MAXLINE 256 #define MAXARG 50 #define WHITE "\t\n" char errmsg[MAXLINE];int oflag;char *pathname;int cli_args (int , char **) ;void handle_request (char *, int , int ) ;int buf_args (char *buf, int (*optfunc)(int , char **)) { char *ptr, *argv[MAXARG]; int argc; if (strtok(buf, WHITE) == NULL ) return (-1 ); argv[argc = 0 ] = buf; while ((ptr = strtok(NULL , WHITE)) != NULL ) { if (++argc >= MAXARG - 1 ); return (-1 ); argv[argc] = ptr; } argv[++argc] = NULL ; return ((*optfunc)(argc, argv)); }int cli_args (int argc, char **argv) { if (argc != 3 || strcmp (argv[0 ], CL_OPEN) !=0 ) { strcpy (errmsg, "usage: <pathname> <oflag> \n" ); return (-1 ); } pathname = argv[1 ]; oflag = atoi(argv[2 ]); return (0 ); }void handle_request (char *buf, int nread, int fd) { int newfd; if (buf[nread-1 ] != 0 ) { snprintf (errmsg, MAXLINE-1 , "request not null terminated: %*.*s\n" , nread, nread, buf); send_err(fd, -1 , errmsg); return ; } if (buf_args(buf, cli_args) < 0 ) { send_err(fd, -1 , errmsg); return ; } if ((newfd = open(pathname, oflag)) < 0 ) { snprintf (errmsg, MAXLINE - 1 , "can't open %s: %s\n" , pathname, strerror(errno)); send_err(fd, -1 , errmsg); return ; } if (send_fd(fd, newfd) < 0 ) err_sys("send_fd error" ); close(newfd); }int main (void ) { int nread; char buf[MAXLINE]; for (;;) { if ((nread = read(STDIN_FILENO, buf, MAXLINE)) < 0 ) { fprintf (stderr , "read error on stream pipe" ); exit (-1 ); } else if (nread == 0 ) break ; handle_request(buf, nread, STDOUT_FILENO); } exit (0 ); }
opend.h
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 #include "include/apue.h" #include <errno.h> #define CS_OPEN "/tmp/opend.socket" #define CL_OPEN "open" extern int debug;extern char errmsg[];extern int oflag;extern char *pathname;typedef struct { int fd; uid_t uid; } Client;extern Client *client;extern int client_size;int cli_args (int , char **) ;int client_add (int , uid_t ) ;void client_del (int ) ;void loop (void ) ;void handle_request (char *, int , int , uid_t ) ;
17-27.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 #include "opend.h" #include <stdio.h> #include <stdlib.h> #include <fcntl.h> #define NALLOC 10 static void client_alloc (void ) { int i; if (client == NULL ) client = malloc (NALLOC * sizeof (Client)); else client = realloc (client, (client_size+NALLOC)*sizeof (Client)); if (client == NULL ) { fprintf (stderr , "can't alloc for client array" ); exit (-1 ); } for (i = client_size; i < client_size + NALLOC; i++) client[i].fd = -1 ; client_size += NALLOC; }int client_add (int fd, uid_t uid) { int i; if (client == NULL ) client_alloc(); again: for (i = 0 ; i < client_size; i++) { if (client[i].fd == -1 ) { client[i].fd = fd; client[i].uid = uid; return (i); } } client_alloc(); goto again; }void client_del (int fd) { int i; for (i = 0 ; i < client_size; i++) { if (client[i].fd == fd) { client[i].fd = -1 ; return ; } } log_quit("can't find client entry for fd %d" , fd); }
命令处理 1 2 3 4 5 6 7 #include <unistd.h> int getopt (int argc, char *const argv[], const char *options) ;extern int optind, opterr, optopt;extern char *optarg;
17-28.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 #include "opend.h" #include <syslog.h> #include <sys/select.h> int debug, oflag, client_size, log_to_stderr;char errmsg[MAXLINE];char *pathname; Client *clinet = NULL ;void loop (void ) { int i, n, maxfd, maxi, listenfd, clifd, nread; char buf[MAXLINE]; uid_t uid; fd_set rset, allset; FD_ZERO(&allset); if ((listenfd = serv_listen(CS_OPEN)) < 0 ) log_sys("serv_listen error" ); FD_SET(listenfd, &allset); maxfd = listenfd; maxi = -1 ; for ( ; ; ){ rset = allset; if ((n = select(maxfd + 1 , &rset, NULL , NULL , NULL )) < 0 ) log_sys("select error" ); if (FD_ISSET(listenfd, &rset)) { if ((clifd = serv_accept(listenfd, &uid)) < 0 ) log_sys("serv_accept error: %d" , clifd); i = client_add(clifd, uid); FD_SET(clifd, &allset); if (clifd < maxfd) maxfd = clifd; if (i > maxi) maxi = i; log_msg("new connection: uid %d, fd %d" , uid, clifd); continue ; } for (i = 0 ; i <=maxi; i++) { if ((clifd = client[i].fd) < 0 ) continue ; if (FD_ISSET(clifd, &rset)) { if ((nread = read(clifd, buf, MAXLINE)) < 0 ){ log_sys("read error on fd %d" , clifd); }else if (nread == 0 ){ log_msg("closed: uid %d, fd %d" , client[i].uid, clifd); client_del(clifd); FD_CLR(clifd, &allset); close(clifd); }else { handle_request(buf, nread, clifd, client[i].uid); } } } } }int main (int argc, char *argv[]) { int c; log_open("open.serv" , LOG_PID, LOG_USER); opterr = 0 ; while ((c = getopt(argc, argv, "d" )) != EOF) { switch (c) { case 'd' : debug = log_to_stderr = 1 ; break ; case '?' : err_quit("unrecognized option: -%c" , optopt); } } if (debug == 0 ) daemonize("opend" ); loop(); }
17-30.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 #include "opend.h" #include <poll.h> #include <stdio.h> #include <stdlib.h> #include <string.h> #define NALLOC 10 static struct pollfd *grow_pollfd (struct pollfd *pfd, int *maxfd) { int i; int oldmax = *maxfd; int newmax = oldmax + NALLOC; if ((pfd = realloc (pfd, newmax * sizeof (struct pollfd))) == NULL ) { fprintf (stderr , "realloc error" ); exit (-1 ); } for (i = oldmax; i < newmax; i++) { pfd[i].fd = -1 ; pfd[i].events = POLLIN; pfd[i].revents = 0 ; } *maxfd = newmax; return (pfd); }void loop (void ) { int i, listenfd, clifd, nread; char buf[MAXLINE]; uid_t uid; struct pollfd *pollfd ; int numfd = 1 ; int maxfd = NALLOC; if ((pollfd = malloc (NALLOC * sizeof (struct pollfd))) == NULL ) err_sys("malloc error" ); for (i = 0 ; i < NALLOC; i++) { pollfd[i].fd = -1 ; pollfd[i].events = POLLIN; pollfd[i].revents = 0 ; } if ((listenfd = serv_listen(CS_OPEN)) < 0 ) log_sys("serv_listen error" ); client_add(listenfd, 0 ); pollfd[0 ].fd = listenfd; for (;;) { if (poll(pollfd, numfd, -1 ) < 0 ) log_sys("poll error" ); if (pollfd[0 ].revents & POLLIN) { if ((clifd = serv_accept(listenfd, &uid)) < 0 ) log_sys("serv_accept" ); client_add(clifd, uid); if (numfd == maxfd) pollfd = grow_pollfd(pollfd, &maxfd); pollfd[numfd].fd = clifd; pollfd[numfd].events = POLLIN; pollfd[numfd].revents = 0 ; numfd++; log_msg("new connection: uid %d, fd %d" , uid, clifd); } for (i = 1 ; i < numfd; i++) { if (pollfd[i].revents & POLLHUP) { goto hungup; } else if (pollfd[i].revents & POLLIN) { if ((nread = read(pollfd[i].fd, buf, MAXLINE)) < 0 ) { log_sys("read error on fd %d" , pollfd[i].fd); } else if (nread == 0 ) { hungup: log_msg("closed: uid %d, fd %d" , client[i].uid, pollfd[i].fd); client_del(pollfd[i].fd); close(pollfd[i].fd); if (i < (numfd - 1 )) { pollfd[i].fd = pollfd[numfd-1 ].fd; pollfd[i].events = pollfd[numfd-1 ].events; pollfd[i].revents = pollfd[numfd-1 ].revents; i--; } numfd--; } else { handle_request(buf, nread, pollfd[i].fd, client[i].uid); } } } } }
终端I/O 程序禁用中断字符,并将文件结束符设置为Ctrl+B
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <termios.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> isatty(int fd) { struct termios ts ; return (tcgetattr(fd, &ts) != -1 ); }int main (void ) { struct termios term ; long vdisable; if (isatty(STDIN_FILENO) == 0 ) { fprintf (stderr , "standard input is not a terminal device" ); exit (-1 ); } if ((vdisable = fpathconf(STDIN_FILENO, _PC_VDISABLE)) < 0 ) { fprintf (stderr , "fpathconf error or _POSIX_VDISABLE not in effect" ); exit (-1 ); } if (tcgetattr(STDIN_FILENO, &term) < 0 ) { fprintf (stderr , "tcgetattr error" ); exit (-1 ); } term.c_cc[VINTR] = vdisable; term.c_cc[VEOF] = 2 ; if (tcsetattr(STDIN_FILENO, TCSAFLUSH, &term) < 0 ) { fprintf (stderr , "tcsetattr error" ); exit (-1 ); } exit (0 ); }
获得和设置终端属性 获得和设置termios
结构,调用tcgetattr
和tcsetattr
函数
1 2 3 4 5 6 #include <termios.h> int tcgetattr (int fd, struct termios *termptr) ;int tcsetattr (int fd, int opt, const struct termios *termptr) ;
fd
:引用终端设备
opt
参数
TCSANOW
:更改立即发生
TCSADRAIN
:发送了所有输出后更改才发生。若更改输出参数则应使用此选项。
TCSAFLUSH
:发送了所有输出后更改才发生。更进一步,在更改发生时未读的所有输入数据都被丢弃(冲洗)。
终端选项标志 18-11.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <termio.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> int main () { struct termios term ; if (tcgetattr(STDIN_FILENO, &term) < 0 ) { fprintf (stderr , "tcgetattr error" ); exit (-1 ); } switch (term.c_cflag & CSIZE) { case CS5: printf ("5 bits/byte\n" ); break ; case CS6: printf ("6 bits/byte\n" ); break ; case CS7: printf ("7 bits/byte\n" ); break ; case CS8: printf ("8 bits/byte\n" ); break ; default : printf ("unknown bits/byte\n" ); } term.c_cflag &= ~CSIZE; term.c_cflag |= CS8; if (tcsetattr(STDIN_FILENO, TCSANOW, &term) < 0 ) { fprintf (stderr , "tcsetattr error" ); exit (-1 ); } exit (0 ); }
stty命令 在程序中使用tcgetattr
和tcsetattr
函数进行检查和更改。在命令行中用stty
命令进行检查和更改。
波特率函数 波特率表示为位/秒
1 2 3 4 5 6 7 8 9 #include <termios.h> speed_t cfgetispeed (const struct termios *temptr) ;speed_t cfgetospeed (const struct termios *termptr) ;int cfsetispeed (struct termios *termptr, speed_t speed) ;int cfsetospeed (struct termios *termptr speed_t speed) ;
行控制函数 1 2 3 4 5 6 7 8 #include <termios.h> int tcdrain (int fd) ;int tcflow (int fd, int action) ;int tcflush (int fd, int queue ) ;int tcsendbreak (int fd, int duration) ;
tcdrain
函数等待所有输出都被传递
tcflow
函数用于对输入和输出流控制进行控制,action
参数
TCOOFF
:输出被挂起
TCOON
:重新启动以前被挂起的输出。
TCIOFF
:系统发送一个STOP
字符,这将使终端设备停止发送数据。
TCION
:系统发送一个START
字符,这将使终端设备恢复发送数据
tcflush
函数冲洗输入缓冲区或输出缓冲区。queue
参数必定是下列3个常量之一
TCIFLUSH
冲洗输入队列
TCOFLUSH
冲洗输出队列
TCIOFLUSH
冲洗输入队列和输出队列
tcsendbreak
函数在一个指定的时间区间内发送连续的0值位流。若duration
参数为0,则此种传递延续0.25~0.5。POSIX.1
说明若duration
非0,则传递时间依赖于实现。
终端标识 1 2 3 4 #include <stdio.h> char *ctermid (char *ptr) ;
ctermid函数的实现
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdio.h> #include <string.h> static char ctermid_name[L_ctermid];char *ctermid (char *str) { if (str == NULL ) str = ctermid_name; return (strcpy (str, "/dev/tty" )); }
若文件描述符引用一个终端设备则isatty
返回真。ttyname
返回的是在该文件描述符上打开的终端设备的路径名。
1 2 3 4 5 6 #include <unistd.h> int isatty (int fd) ;char *ttyname (int fd) ;
isatty函数的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 #include <termios.h> #include <stdio.h> int isatty (int fd) { struct termios ts ; return (tcgetattr(fd, &ts) != -1 ); }int main (void ) { printf ("fd 0: %s\n" , isatty(0 ) ? "tty" : "not a tty" ); printf ("fd 1: %s\n" , isatty(1 ) ? "tty" : "not a tty" ); printf ("fd 2: %s\n" , isatty(2 ) ? "tty" : "not a tty" ); }
ttyname函数的实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 #include <sys/stat.h> #include <dirent.h> #include <limits.h> #include <string.h> #include <termio.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> struct devdir { struct devdir *d_next ; char *d_name; };static struct devdir *head ;static struct devdir *tail ;static char pathname[_PC_PATH_MAX + 1 ];static void add (char *dirname) { struct devdir *ddp ; int len; len = strlen (dirname); if ((dirname[len - 1 ] == '.' ) && (dirname[len - 2 ] == '/' || (dirname[len - 2 ] == '.' && dirname[len-3 ] == '/' ))) return ; if (strcmp (dirname, "/dev/fd" ) == 0 ) return ; if ((ddp = malloc (sizeof (struct devdir))) == NULL ) return ; if ((ddp->d_name = strdup(dirname)) == NULL ) { free (ddp); return ; } ddp->d_next = NULL ; if (tail == NULL ) { head = ddp; tail = ddp; } else { tail->d_next = ddp; tail = ddp; } }static void cleanup (void ) { struct devdir *ddp , *nddp ; ddp = head; while (ddp != NULL ) { nddp = ddp->d_next; free (ddp->d_name); free (ddp); ddp = nddp; } head = NULL ; tail = NULL ; }static char *searchidr (char *dirname, struct stat *fdstatp) { struct stat devstat ; DIR *dp; int devlen; struct dirent *dirp ; strcpy (pathname, dirname); if ((dp = opendir(dirname)) == NULL ) return (NULL ); strcat (pathname, "/" ); devlen = strlen (pathname); while ((dirp = readdir(dp)) != NULL ) { strcpy (pathname + devlen, dirp->d_name); if (strcmp (pathname, "/dev/stdin" ) == 0 || strcmp (pathname, "/dev/stdout" ) == 0 || strcmp (pathname, "/dev/stderr" ) == 0 ) continue ; if (stat(pathname, &devstat) < 0 ) continue ; if (S_ISDIR(devstat.st_mode)) { add(pathname); continue ; } if (devstat.st_ino == fdstatp->st_ino && devstat.st_dev == fdstatp->st_dev) { closedir(dp); return (pathname); } } closedir(dp); return (NULL ); }char *ttyname (int fd) { struct stat fdstat ; struct devdir *ddp ; char *rval; if (isatty(fd) == 0 ) return (NULL ); if (fstat(fd, &fdstat) < 0 ) return (NULL ); if (S_ISCHR(fdstat.st_mode) == 0 ) return (NULL ); rval = searchidr("/dev" , &fdstat); if (rval == NULL ) { for (ddp = head; ddp != NULL ; ddp = ddp->d_next) if ((rval = searchidr(ddp->d_name, &fdstat)) != NULL ) break ; } cleanup(); return (rval); }int main (void ) { char *name; if (isatty(0 )) { name = ttyname(0 ); if (name == NULL ) name = "undefined" ; } else { name = "not a tty" ; } printf ("fd 0: %s\n" , name); if (isatty(1 )) { name = ttyname(1 ); if (name == NULL ) name = "undefined" ; } else { name = "not a tty" ; } printf ("fd 1: %s\n" , name); if (isatty(2 )) { name = ttyname(2 ); if (name == NULL ) name = "undefined" ; } else { name = "not a tty" ; } printf ("fd 2: %s\n" , name); exit (0 ); }#include <sys/stat.h> #include <dirent.h> #include <limits.h> #include <string.h> #include <termio.h> #include <unistd.h> #include <stdlib.h> #include <stdio.h> struct devdir { struct devdir *d_next ; char *d_name; };static struct devdir *head ;static struct devdir *tail ;static char pathname[_PC_PATH_MAX + 1 ];static void add (char *dirname) { struct devdir *ddp ; int len; len = strlen (dirname); if ((dirname[len - 1 ] == '.' ) && (dirname[len - 2 ] == '/' || (dirname[len - 2 ] == '.' && dirname[len-3 ] == '/' ))) return ; if (strcmp (dirname, "/dev/fd" ) == 0 ) return ; if ((ddp = malloc (sizeof (struct devdir))) == NULL ) return ; if ((ddp->d_name = strdup(dirname)) == NULL ) { free (ddp); return ; } ddp->d_next = NULL ; if (tail == NULL ) { head = ddp; tail = ddp; } else { tail->d_next = ddp; tail = ddp; } }static void cleanup (void ) { struct devdir *ddp , *nddp ; ddp = head; while (ddp != NULL ) { nddp = ddp->d_next; free (ddp->d_name); free (ddp); ddp = nddp; } head = NULL ; tail = NULL ; }static char *searchidr (char *dirname, struct stat *fdstatp) { struct stat devstat ; DIR *dp; int devlen; struct dirent *dirp ; strcpy (pathname, dirname); if ((dp = opendir(dirname)) == NULL ) return (NULL ); strcat (pathname, "/" ); devlen = strlen (pathname); while ((dirp = readdir(dp)) != NULL ) { strcpy (pathname + devlen, dirp->d_name); if (strcmp (pathname, "/dev/stdin" ) == 0 || strcmp (pathname, "/dev/stdout" ) == 0 || strcmp (pathname, "/dev/stderr" ) == 0 ) continue ; if (stat(pathname, &devstat) < 0 ) continue ; if (S_ISDIR(devstat.st_mode)) { add(pathname); continue ; } if (devstat.st_ino == fdstatp->st_ino && devstat.st_dev == fdstatp->st_dev) { closedir(dp); return (pathname); } } closedir(dp); return (NULL ); }char *ttyname (int fd) { struct stat fdstat ; struct devdir *ddp ; char *rval; if (isatty(fd) == 0 ) return (NULL ); if (fstat(fd, &fdstat) < 0 ) return (NULL ); if (S_ISCHR(fdstat.st_mode) == 0 ) return (NULL ); rval = searchidr("/dev" , &fdstat); if (rval == NULL ) { for (ddp = head; ddp != NULL ; ddp = ddp->d_next) if ((rval = searchidr(ddp->d_name, &fdstat)) != NULL ) break ; } cleanup(); return (rval); }int main (void ) { char *name; if (isatty(0 )) { name = ttyname(0 ); if (name == NULL ) name = "undefined" ; } else { name = "not a tty" ; } printf ("fd 0: %s\n" , name); if (isatty(1 )) { name = ttyname(1 ); if (name == NULL ) name = "undefined" ; } else { name = "not a tty" ; } printf ("fd 1: %s\n" , name); if (isatty(2 )) { name = ttyname(2 ); if (name == NULL ) name = "undefined" ; } else { name = "not a tty" ; } printf ("fd 2: %s\n" , name); exit (0 ); }
规范模式 getpass函数实现
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <signal.h> #include <stdio.h> #include <termio.h> #include <unistd.h> #include <stdlib.h> #define MAX_PASS_LEN 8 char *getpass (const char *prompt) { static char buf[MAX_PASS_LEN + 1 ]; char *ptr; sigset_t sig, osig; struct termios ts , ots ; FILE *fp; int c; if ((fp = fopen(ctermid(NULL ), "r+" )) == NULL ) return (NULL ); setbuf(fp, NULL ); sigemptyset(&sig); sigaddset(&sig, SIGINT); sigaddset(&sig, SIGTSTP); sigprocmask(SIG_BLOCK, &sig, &osig); tcgetattr(fileno(fp), &ts); ots = ts; ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); tcsetattr(fileno(fp), TCSAFLUSH, &ts); fputs (prompt, fp); ptr = buf; while ((c = getc(fp)) != EOF && c != '\n' ) if (ptr < &buf[MAX_PASS_LEN]) *ptr++ = c; *ptr = 0 ; putc('\n' , fp); tcsetattr(fileno(fp), TCSAFLUSH, &ots); sigprocmask(SIG_SETMASK, &osig, NULL ); fclose(fp); return (buf); }int main (void ) { char *ptr; if ((ptr = getpass("Enter password:" )) == NULL ) { fprintf (stderr , "getpass error\n" ); exit (-1 ); } printf ("password: %s\n" , ptr); while (*ptr != 0 ) *ptr++ = 0 ; exit (0 ); }
非规范模式 18-20.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 #include <signal.h> #include <stdio.h> #include <termio.h> #include <unistd.h> #include <stdlib.h> #define MAX_PASS_LEN 8 char *getpass (const char *prompt) { static char buf[MAX_PASS_LEN + 1 ]; char *ptr; sigset_t sig, osig; struct termios ts , ots ; FILE *fp; int c; if ((fp = fopen(ctermid(NULL ), "r+" )) == NULL ) return (NULL ); setbuf(fp, NULL ); sigemptyset(&sig); sigaddset(&sig, SIGINT); sigaddset(&sig, SIGTSTP); sigprocmask(SIG_BLOCK, &sig, &osig); tcgetattr(fileno(fp), &ts); ots = ts; ts.c_lflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); tcsetattr(fileno(fp), TCSAFLUSH, &ts); fputs (prompt, fp); ptr = buf; while ((c = getc(fp)) != EOF && c != '\n' ) if (ptr < &buf[MAX_PASS_LEN]) *ptr++ = c; *ptr = 0 ; putc('\n' , fp); tcsetattr(fileno(fp), TCSAFLUSH, &ots); sigprocmask(SIG_SETMASK, &osig, NULL ); fclose(fp); return (buf); }int main (void ) { char *ptr; if ((ptr = getpass("Enter password:" )) == NULL ) { fprintf (stderr , "getpass error\n" ); exit (-1 ); } printf ("password: %s\n" , ptr); while (*ptr != 0 ) *ptr++ = 0 ; exit (0 ); }
终端窗口大小 1 2 3 4 5 6 struct winsize { unsigned short ws_row; unsigned short ws_col; unsigned short ws_xpixel; unsigned short ws_ypixel; };
18-22.c获取窗口大小
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <termio.h> #ifndef TIOCGWINSZ #include <sys/ioctl.h> #endif #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> static void pr_winsize (int fd) { struct winsize size ; if (ioctl(fd, TIOCGWINSZ, (char *)&size)); { fprintf (stderr , "TIOCGWINSZ error\n" ); exit (-1 ); } printf ("%d rows, %d columns\n" , size.ws_row, size.ws_col); }static void sig_winch (int signo) { printf ("SIGWINCH received\n" ); pr_winsize(STDIN_FILENO); }int main (void ) { if (isatty(STDIN_FILENO) == 0 ) exit (1 ); if (signal(SIGWINCH, sig_winch) == SIG_ERR) { fprintf (stderr , "signal error" ); exit (-1 ); } pr_winsize(STDIN_FILENO); for ( ; ; ) pause(); }
打开伪终端设备 打开主设备
1 2 3 4 5 #include <stdlib.h> #include <fcntl.h> int posix_openpt (int oflag) ;
grantpt
获取访问从设备的权限,unlockpt
解除限制
1 2 3 4 5 #include <stdlib.h> int grantpt (int fd) ;int unlockpt (int fd) ;
获取从设备的路径
1 2 3 4 #include <stdlib.h> char *ptsname (int fd) ;
ptym_open
打开PTY
主设备文件描述符,ptys_open
打开PTY
从设备文件描述符
1 2 3 4 5 6 #include "apue.h" int ptym_open (char *pts_name, int pts_namesz) ;int ptys_open (char *pts_name) ;
19-9
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <errno.h> #include <fcntl.h> #if defined(SOLARIS) #include <stropt.h> #endif int ptym_open (char *pts_name, int pts_namesz) { char *ptr; int fdm, err; if ((fdm = posix_openpt(O_RDWR)) < 0 ) return (-1 ); if (grantpt(fdm) < 0 ) goto errout; if (unlockpt(fdm) < 0 ) goto errout; if ((ptr = ptsname(fdm)) == NULL ) goto errout; strncpy (pts_name, ptr, pts_namesz); pts_name[pts_namesz - 1 ] = '\0' ; return (fdm); errout: err = errno; close(fdm); errno = err; return (-1 ); }int ptys_open (char *pts_name) { int fds;#if defined(SOLARIS) int err, setup;#endif if ((fds = open(pts_name, O_RDWR)) < 0 ) return (-1 );#if defined(SOLARIS) if ((setup = ioctl(fds, I_FIND, "ldterm" )) < 0 ) goto errout; if (setup == 0 ) { if (ioctl(fds, I_PUSH, "pterm" ) < 0 ) goto errout; if (ioctl(fds, I_PUSH, "ldterm" ) < 0 ) goto errout; if (ioctl(fds, I_PUSH, "ttcompat" ) < 0 ) { errout: err = errno; close(fds); errno = err; return (-1 ); } }#endif return (fds); }
pty_fork 1 2 3 4 5 6 7 8 #include "apue.h" #include <termios.h> pid_t pty_fork (int *ptrfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize) ;
slave_name
: 存放从设备名
slave_termios
:若不为空,则初始化为设备的终端行规程,若为空,则把从设备termios
结构设置成实现定义的初始状态
slave_winsize
:若不为空,则根据指针指向的结构体初始化设备的窗口大小。若为空,winsize
结构体被初始化为0
19-10.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 #include <termio.h> #include <errno.h> #include <fcntl.h> #include <stdlib.h> #include <stdio.h> #include <unistd.h> #if defined(SOLARIS) #include <stropt.h> #endif int ptym_open (char *pts_name, int pts_namesz) { char *ptr; int fdm, err; if ((fdm = posix_openpt(O_RDWR)) < 0 ) return (-1 ); if (grantpt(fdm) < 0 ) goto errout; if (unlockpt(fdm) < 0 ) goto errout; if ((ptr = ptsname(fdm)) == NULL ) goto errout; strncpy (pts_name, ptr, pts_namesz); pts_name[pts_namesz - 1 ] = '\0' ; return (fdm); errout: err = errno; close(fdm); errno = err; return (-1 ); }int ptys_open (char *pts_name) { int fds;#if defined(SOLARIS) int err, setup;#endif if ((fds = open(pts_name, O_RDWR)) < 0 ) return (-1 );#if defined(SOLARIS) if ((setup = ioctl(fds, I_FIND, "ldterm" )) < 0 ) goto errout; if (setup == 0 ) { if (ioctl(fds, I_PUSH, "pterm" ) < 0 ) goto errout; if (ioctl(fds, I_PUSH, "ldterm" ) < 0 ) goto errout; if (ioctl(fds, I_PUSH, "ttcompat" ) < 0 ) { errout: err = errno; close(fds); errno = err; return (-1 ); } }#endif return (fds); }pid_t pty_fork (int *ptrfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize) { int fdm, fds; pid_t pid; char pts_name[20 ]; if ((fdm = ptym_open(pts_name, sizeof (pts_name))) < 0 ) { fprintf (stderr , "can't open master pty: %s, error %d" , pts_name, fdm); exit (-1 ); } if (slave_name != NULL ) { strncpy (slave_name, pts_name, slave_namesz); slave_name[slave_namesz - 1 ] = '\0' ; } if ((pid = fork()) < 0 ) { return (-1 ); } else if (pid == 0 ) { if (setsid() < 0 ) { fprintf (stderr , "setsid error" ); exit (-1 ); } if ((fds = ptys_open(pts_name)) < 0 ) { fprintf (stderr , "can't open slave pty" ); exit (-1 ); } close(fdm);#if defined(BSD) if (ioctl(fds, TIOCSCTTY, (char *)0 ) < 0 ) { fprintf (stderr , "TIOCSCTTY error" ); exit (-1 ); }#endif if (slave_termios != NULL ) { if (tcsetattr(fds, TCSANOW, slave_termios) < 0 ) { fprintf (stderr , "tcsetattr error on slave pty" ); exit (-1 ); } if (slave_winsize != NULL ) { if (ioctl(fds, TIOCGWINSZ, slave_winsize) < 0 ) { fprintf (stderr , "TIOCSWINSZ error on slave pty" ); exit (-1 ); } } if (dup2(fds, STDIN_FILENO) != STDIN_FILENO) { fprintf (stderr , "dup2 error to stdin" ); exit (-1 ); } if (dup2(fds, STDOUT_FILENO) != STDOUT_FILENO) { fprintf (stderr , "dup2 error to stdout" ); exit (-1 ); } if (dup2(fds, STDERR_FILENO) != STDERR_FILENO) { fprintf (stderr , "error to stderr" ); exit (-1 ); } if (fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO) close(fds); returnr(0 ); } else { *ptrfdm = fdm; return (pid); } } }
19-11.c
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 264 265 266 267 268 269 270 271 272 273 274 275 276 277 278 279 280 281 282 283 284 285 286 287 288 289 290 291 292 293 294 295 296 297 298 299 300 301 302 303 304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 329 330 331 332 333 334 335 336 337 338 339 340 341 342 343 344 345 346 347 348 349 350 351 352 353 354 355 356 357 358 359 360 361 362 363 364 365 366 367 368 369 370 371 372 373 374 375 376 377 378 379 380 381 382 383 384 385 386 387 388 389 390 391 392 393 394 395 396 397 398 399 400 401 402 403 404 405 406 407 408 409 410 411 412 413 414 415 416 417 418 419 420 421 422 423 424 425 426 427 428 429 430 431 432 433 434 435 436 437 438 439 440 441 442 443 444 445 446 447 448 449 450 451 452 453 454 455 456 457 458 459 460 461 462 463 464 465 466 467 468 469 470 471 472 473 474 475 476 477 478 479 480 481 482 483 484 485 #include <termio.h> #include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <fcntl.h> #ifdef LINUX #define OPTSTR "+d:eiv" #else #define OPTSTR "d:einv" #endif static void set_noecho (int ) ;void do_driver (char *) ;void loop (int , int ) ;extern int optind, opterr, optopt;extern char *optarg;int ptym_open (char *pts_name, int pts_namesz) { char *ptr; int fdm, err; if ((fdm = posix_openpt(O_RDWR)) < 0 ) return (-1 ); if (grantpt(fdm) < 0 ) goto errout; if (unlockpt(fdm) < 0 ) goto errout; if ((ptr = ptsname(fdm)) == NULL ) goto errout; strncpy (pts_name, ptr, pts_namesz); pts_name[pts_namesz - 1 ] = '\0' ; return (fdm); errout: err = errno; close(fdm); errno = err; return (-1 ); }int ptys_open (char *pts_name) { int fds;#if defined(SOLARIS) int err, setup;#endif if ((fds = open(pts_name, O_RDWR)) < 0 ) return (-1 );#if defined(SOLARIS) if ((setup = ioctl(fds, I_FIND, "ldterm" )) < 0 ) goto errout; if (setup == 0 ) { if (ioctl(fds, I_PUSH, "pterm" ) < 0 ) goto errout; if (ioctl(fds, I_PUSH, "ldterm" ) < 0 ) goto errout; if (ioctl(fds, I_PUSH, "ttcompat" ) < 0 ) { errout: err = errno; close(fds); errno = err; return (-1 ); } }#endif return (fds); }static struct termios save_termios ;static int ttysavefd = -1 ;static enum { RESET, RAW, CBREAK} ttystate = RESET;int tty_cbreak (int fd) { int err; struct termios buf ; if (ttystate != RESET) { errno = EINVAL; return (-1 ); } if (tcgetattr(fd, &buf) < 0 ) return (-1 ); save_termios = buf; buf.c_lflag &= ~(ECHO | ICANON); buf.c_cc[VMIN] = 1 ; buf.c_cc[VTIME] = 0 ; if (tcsetattr(fd, TCSAFLUSH, &buf) < 0 ) return (-1 ); if (tcgetattr(fd, &buf) < 0 ) { err = errno; tcsetattr(fd, TCSAFLUSH, &save_termios); errno = err; return (-1 ); } if ((buf.c_cflag & (ECHO | ICANON)) || buf.c_cc[VMIN] != 1 || buf.c_cc[VTIME] != 0 ) { tcsetattr(fd, TCSAFLUSH, &save_termios); errno = EINVAL; return (-1 ); } ttystate = CBREAK; ttysavefd = fd; return (0 ); }int tty_raw (int fd) { int err; struct termios buf ; if (ttystate != RESET) { errno = EINVAL; return (-1 ); } if (tcgetattr(fd, &buf) < 0 ) return (-1 ); save_termios = buf; buf.c_lflag &= ~(ECHO | ICANON | IEXTEN | ISIG); buf.c_iflag &= ~(BRKINT | ICRNL | INPCK | ISTRIP | IXON); buf.c_cflag &= ~(CSIZE | PARENB); buf.c_cflag |= CS8; buf.c_oflag &= ~(OPOST); buf.c_cc[VMIN] = 1 ; buf.c_cc[VTIME] = 0 ; if (tcsetattr(fd, TCSAFLUSH, &buf) < 0 ) return (-1 ); if (tcgetattr(fd, &buf) < 0 ) { err = errno; tcsetattr(fd, TCSAFLUSH, &save_termios); errno = err; return (-1 ); } if ((buf.c_lflag & (ECHO | ICANON | IEXTEN | ISIG)) || (buf.c_iflag & (BRKINT | ICRNL | INPCK | ISTRIP | IXON)) || (buf.c_cflag & (CSIZE | PARENB | CS8)) != CS8 || (buf.c_oflag & OPOST) || buf.c_cc[VMIN] != 1 || buf.c_cc[VTIME] != 0 ) { tcsetattr(fd, TCSAFLUSH, &save_termios); errno = EINVAL; return (-1 ); } ttystate = RAW; ttysavefd = fd; return (0 ); }int tty_reset (int fd) { if (ttystate == RESET) return (0 ); if (tcsetattr(fd, TCSAFLUSH, &save_termios) < 0 ) return (-1 ); ttystate = RESET; return (0 ); }void tty_atexit (void ) { if (ttysavefd >= 0 ) tty_reset(ttysavefd); }struct termios *tty_termios (void ) { return (&save_termios); }pid_t pty_fork (int *ptrfdm, char *slave_name, int slave_namesz, const struct termios *slave_termios, const struct winsize *slave_winsize) { int fdm, fds; pid_t pid; char pts_name[20 ]; if ((fdm = ptym_open(pts_name, sizeof (pts_name))) < 0 ) { fprintf (stderr , "can't open master pty: %s, error %d" , pts_name, fdm); exit (-1 ); } if (slave_name != NULL ) { strncpy (slave_name, pts_name, slave_namesz); slave_name[slave_namesz - 1 ] = '\0' ; } if ((pid = fork()) < 0 ) { return (-1 ); } else if (pid == 0 ) { if (setsid() < 0 ) { fprintf (stderr , "setsid error" ); exit (-1 ); } if ((fds = ptys_open(pts_name)) < 0 ) { fprintf (stderr , "can't open slave pty" ); exit (-1 ); } close(fdm);#if defined(BSD) if (ioctl(fds, TIOCSCTTY, (char *)0 ) < 0 ) { fprintf (stderr , "TIOCSCTTY error" ); exit (-1 ); }#endif if (slave_termios != NULL ) { if (tcsetattr(fds, TCSANOW, slave_termios) < 0 ) { fprintf (stderr , "tcsetattr error on slave pty" ); exit (-1 ); } if (slave_winsize != NULL ) { if (ioctl(fds, TIOCGWINSZ, slave_winsize) < 0 ) { fprintf (stderr , "TIOCSWINSZ error on slave pty" ); exit (-1 ); } } if (dup2(fds, STDIN_FILENO) != STDIN_FILENO) { fprintf (stderr , "dup2 error to stdin" ); exit (-1 ); } if (dup2(fds, STDOUT_FILENO) != STDOUT_FILENO) { fprintf (stderr , "dup2 error to stdout" ); exit (-1 ); } if (dup2(fds, STDERR_FILENO) != STDERR_FILENO) { fprintf (stderr , "error to stderr" ); exit (-1 ); } if (fds != STDIN_FILENO && fds != STDOUT_FILENO && fds != STDERR_FILENO) close(fds); returnr(0 ); } else { *ptrfdm = fdm; return (pid); } } }#define BUFFSIZE 512 static void sig_term (int ) ;static volatile sig_atomic_t sigcaught;void loop (int ptym, int ignoreeof) { pid_t child; int nread; char buf[BUFFSIZE]; if ((child = fork()) < 0 ) { fprintf (stderr , "fork error" ); exit (-1 ); } else if (child == 0 ) { for ( ; ; ) { if ((nread = read(STDIN_FILENO, buf, BUFFSIZE)) < 0 ) { fprintf (stderr , "read error from stdin" ); exit (-1 ); } else if (nread == 0 ) break ; if (writen(ptym, buf, nread) != nread) { fprintf (stderr , "write error to master pty" ); exit (-1 ); } if (ignoreeof == 0 ) kill(getppid(), SIGTERM); exit (0 ); } if (signal_intr(SIGTERM, sig_term) == SIG_ERR) { fprintf (stderr , "signal_intr error for SIGTERM" ); exit (-1 ); } for ( ; ;) { if ((nread = read(ptym, buf, BUFFSIZE)) <= 0 ) break ; if (writen(STDOUT_FILENO, buf, nread) != nread) { fprintf (stderr , "writen error to stdout" ); exit (-1 ); } } if (sigcaught == 0 ) kill(child, SIGTERM); } }static void sig_term (int signo) { sigcaught = 1 ; }int main (int argc, char *argv[]) { int fdm, c, ignoreeof, interactive, noecho, verbose; pid_t pid; char *driver; char slave_name[20 ]; struct termios orig_termios ; struct winsize size ; interactive = isatty(STDIN_FILENO); ignoreeof = 0 ; noecho = 0 ; verbose = 0 ; driver = NULL ; opterr = 0 ; while ((c = getopt(argc, argv, OPTSTR)) != EOF) { switch (c) { case 'd' : driver = optarg; break ; case 'e' : noecho = 1 ; break ; case 'i' : ignoreeof = 1 ; break ; case 'n' : interactive = 0 ; break ; case 'v' : verbose = 1 ; break ; case '?' : fprintf (stderr , "unrecognized option: -%c" , optopt); exit (-1 ); } } if (optind >= argc) { fprintf (stderr , "usage: pty [ -d driver -einv ] program [ arg ... ]" ); exit (-1 ); } if (interactive) { if (tcgetattr(STDIN_FILENO, &orig_termios) < 0 ) { fprintf (stderr , "tcgetattr error on stdin" ); exit (-1 ); } if (ioctl(STDIN_FILENO, TIOCGWINSZ, (char *)&size) < 0 ) { fprintf (stderr , "TIOCGWINSZ error" ); exit (-1 ); } pid = pty_fork(&fdm, slave_name, sizeof (slave_name), &orig_termios, &size); } else { pid = pty_fork(&fdm, slave_name, sizeof (slave_name), NULL , NULL ); } if (pid < 0 ) { fprintf (stderr , "fork error" ); exit (-1 ); } else if (pid == 0 ) { if (noecho) set_noecho(STDIN_FILENO); if (execvp(argv[optind], &argv[optind]) < 0 ) { fprintf (stderr , "can't execute: %s" , argv[optind]); exit (-1 ); } } if (verbose) { fprintf (stderr , "slave name = %s\n" , slave_name); if (driver != NULL ) fprintf (stderr , "driver = %s\n" , driver); } if (interactive && driver == NULL ) { if (tty_raw(STDIN_FILENO) < 0 ) { fprintf (stderr , "tty_raw error" ); exit (-1 ); } if (atexit(tty_atexit) < 0 ) { fprintf (stderr , "atexit error" ); exit (-1 ); } } if (driver) do_driver(driver); loop(fdm, ignoreeof); exit (0 ); }static void set_noecho (int fd) { struct termios stermios ; if (tcgetattr(fd, &stermios) < 0 ) { fprintf (stderr , "tcgetaattr error" ); exit (-1 ); } stermios.c_cflag &= ~(ECHO | ECHOE | ECHOK | ECHONL); stermios.c_oflag &= ~(ONLCR); if (tcsetattr(fd, TCSANOW, &save_termios) < 0 ) { fprintf (stderr , "tcsetattr error" ); exix(-1 ); } }
数据库函数库 打开数据库
1 2 3 4 5 6 #include "apue_db.h" DBHANDLE db_open (const char *pathname, int oflag, ...) ;void db_close (DBHANDLE db) ;
数据插入
1 2 3 4 #include "apue_db.h" int db_store (DBHANDLE db, const char *key, const char *data, int flag) ;
flag
为插入选项
DB_INSERT
:插入一条新纪录
DB_REPLACE
:替换一条已有的记录
DB_STORE
:插入一条新记录或替换一条已有的记录
指定键从数据库中获取一条记录
1 2 3 4 5 #include "apue_db.h" char *db_fetch (DBHANDLE db, const char *key) ;
删除一条记录
1 2 3 4 #include "apue_db.h" int db_delete (DBHANDLE db, const char *key) ;
回滚数据库以及逐条读每条记录
1 2 3 4 5 6 #include "apue_db.h" void db_rewind (DBHANDLE db) ;char *db_nextrec (DBHANDLE db, char *key) ;