3atv精品不卡视频,97人人超碰国产精品最新,中文字幕av一区二区三区人妻少妇,久久久精品波多野结衣,日韩一区二区三区精品

歡迎訪問 生活随笔!

生活随笔

當前位置: 首頁 > 编程资源 > 编程问答 >内容正文

编程问答

《APUE》中的函数整理

發(fā)布時間:2025/4/16 编程问答 16 豆豆
生活随笔 收集整理的這篇文章主要介紹了 《APUE》中的函数整理 小編覺得挺不錯的,現在分享給大家,幫大家做個參考.

第1章 unix基礎知識

1. char *strerror(int errnum)

該函數將errnum(就是errno值)映射為一個出錯信息字符串,返回該字符串指針。聲明在string.h文件中。

2.void perror(const char *s)

該函數基于當前的errno值,在標準出錯文件中輸出一條出錯消息,然后返回。聲明在stdio.h文件中。它首先輸出由s指向的字符串,然后是一個冒號,一個空格,接著是errno值對應的出錯信息,最后是一個換行符。

?

第2章 UNIX標準化及實現

1.long sysconf(int name)

?? long fpathconf(int fd, int name)

?? long pathconf(char *path, int name)

第一個函數用來獲取系統運行時的配置信息。后兩個函數用來獲取文件的name選項的配置信息,它們的區(qū)別在于一個提供文件描述符,一個提供文件路徑。聲明在unistd.h文件中。這些name參數的常量值參考《apue》。

2.系統基本數據類型。這些類型在不同系統上被聲明為不同的c基本類型。因此使用它們可以增強代碼的可移植性。

caddr_t? ? ? 核心地址 ??????????? ? ? ? ? ? ?? clock_t?? 時鐘滴答計數器(進程時間) ?????????? comp_t??? 壓縮的時鐘時間

dev_t??????? 設備號(主和次) ??????????? fd_set?? 文件描述符集 ???????????????????????????????????? fpos_t?? 文件位置

gid_t ? ? ? ? 用戶組ID ??????????????? ? ? ? ??? ino_t??? i節(jié)點編號 ????????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? mode_t??? 文件類型,文件創(chuàng)建模式

nlink_t?????? 目錄項的鏈接技術 ??????????? off_t??? 文件大小和偏移量 ???????????? ? ? ? ? ? ? ?? ??? pid_t??? 進程ID和進程組ID

ptrdiff_t???? 兩個指針相減的結果 ???????? rlim_t??? 資源限制 ???????????? ? ? ? ? ? ? ? ? ? ? ? ? ? ???? sig_atomic_t??? 能原子訪問的數據類型

sigset_t???? 信號集 ????????????????????????????? size_t??? 對象大小(例如字符串),無符號 ???? ssize_t??? 返回的字節(jié)計數,帶符號的(如read,write返回值,需要錯誤值)

time_t??????? 日歷時間的秒計數器 ??????? uid_t???? 用戶ID ??????????????????????????????????????????????? wchar_t??? 能表示所有不同的字符碼

?

第3章 文件IO

1.int open(const char *pathname, int flags)

?? int open(const char *pathname, int flags, node_t mode)

該函數用來打開文件,聲明在fcntl.h中。flags標志有如下:O_RDONLY,O_WRONLY,O_RDWR(這三個標志必須要包含其中之一),還有一些文件創(chuàng)建標志和狀態(tài)標志,可以包含0個或多個,O_CLOEXEC,O_CREAT,O_DIRECTORY,O_EXEL,O_NOCITY,O_NOFOLLOW,O_TRUNC,O_TTY_INIT,O_NONBLOCK,O_SYNC。(這些都是打開的時候才設置,屬于某個打開的文件,inode中并不存在)

另外,當使用到O_CREAT標志來創(chuàng)建文件時,mode中需要包含下列符號:S_IRWXU,S_IRUSR,S_IWUSR,S_IXUSR,S_IRWXG,S_IRGRP,S_IWGRP,S_IXGRP,

S_IRWXO,S_IROTH,S_IWOTH,S_IXOTH。

2.int creat(const char *pathname, mode_t mode)

該函數用來創(chuàng)建一個文件,聲明在fcntl.h中。mode中的值同上。當open中的flags包含O_CREAT時,open和creat功能是一樣的。

3.int close(int fd)

該函數用來關閉有open/creat打開的文件描述符,聲明在unistd.h中。

4.off_t lseek(int fd, off_t offset, int whence)

該函數用來對已打開文件fd進行定位,聲明在unistd.h中。whence中可以包含如下值:SEEK_SET,SEEK_CUR,SEEK_END,分別表示從文件開頭,文件當前位置,文件結尾作為偏移量的開始。

5.ssize_t read(int fd, void *buf, size_t count)

該函數用來從文件中讀數據,聲明在unistd.h中。

6.ssize_t write(int fd, const void *buf, size_t count)

該函數用來向文件中寫數據,聲明在unistd.h中。

7.ssize_t pread(int fd, void *buf, size_t count, off_t offset)

該函數類似于read函數,區(qū)別在于該函數是原子執(zhí)行,并且不更新文件指針位置,offset用來設置在文件中讀取的位置。聲明在unistd.h中。

?8.ssize_t pwrite(int fd, const void *buf, size_t count, off_t offset)

該函數也類似于pread和write。聲明在unistd.h中。

注意:7,8兩個函數主要用在多線程對同一文件進行讀寫的場合。

9.int dup(int oldfd)

?? int dup2(int oldfd, int newfd)

這兩個函數用來復制文件描述符,聲明在unistd.h中。dup使用最小的空閑描述符作為新描述符,dup2的newfd提供了新描述符。

10.void sync(void)

? ?? int fsync(int fd)

  int fdatasync(int fd)

這三個函數用來將文件緩沖區(qū)中的數據寫入磁盤文件中。聲明在unistd.h中。區(qū)別在于,sync將塊緩沖區(qū)放入寫隊列就返回,并不等待實際寫入磁盤中;fsync將fd文件緩沖塊

立即寫入磁盤中,并等待寫入后才返回,同時更新文件屬性;fdatasync類似于syncfs,不過只影響文件的部分數據。

11.?int fcntl(int fd, int cmd, ... /* arg */ )

該函數用來更改已打開文件fd的屬性,聲明在fcntl.h中。該函數實現了5中功能,體現在不同的cmd所包含的標志中:

a.復制一個現有描述符(cmd=F_DUPFD)

b.獲得/設置文件描述符標志(cmd=F_GETFD/F_SETFD)

c.獲得/設置文件狀態(tài)標志(cmd=F_GETFL/F_SETFL)

d.獲得/設置異步I/O所有權(cmd=F_GETOWN/cmd=F_SETOWN)

e.獲得/設置記錄鎖(cmd=G_GETLK/G_SETLK/F_SETLKW)

當cmd為F_DUPFD,fcntl函數相當于dup/dup2函數,返回復制后的新描述符,該描述符是空閑描述符中大于或等于第三個參數的值;當cmd為F_GETFD,用來獲取文件描述符的所有標志(當前只有一個標志FD_CLOEXEC),當cmd為F_SETFD,用來設置文件描述父的標志(由第三個參數提供);當cmd為F_GETFL,用來獲取文件的狀態(tài)標志,文件狀態(tài)標志如下所示。當cmd為F_SETFL,用來設置文件的狀態(tài)標志。當cmd為F_GETOWN,用來取當前接收SIGIO和SIGURG信號的進程ID和進程組ID;當cmd為F_SETOWN,設置接收SIGIO和SIGURG信號的進程ID和進程組ID。

文件狀態(tài)標志:

O_RDONLY?????????????? 只讀打開

O_WRONLY????????????? 只寫打開

O_RDWR?????????????????? 讀寫打開

O_APPEND?????????????? 追加打開

O_NONBLOCK????????? 非阻塞模式

O_SYNC?????????????????? 等待寫完成(數據和屬性)?

O_DSYNC???????????????? 等待寫完成(僅數據)

O_RSYNC???????????????? 同步讀寫

O_FSYNC???????????????? 等待寫完成(僅FreeBSD和Mac OS X)

O_ASYNC???????????????? 異步I/O(僅FreeBSD和Mac OS X)

12.int ioctl(int d, int request, ...)

該函數進行I/O設置,不能用本章中其他函數設置的I/O操作都可用該函數完成。比如終端I/O等等。聲明在sys/ioctl.h中。

?

第4章 文件和目錄

1.int stat(const char *path, struct stat *buf)

?? int fstat(int fd, struct stat *buf)

? ?int lstat(const char *path, struct stat *buf)

這幾個函數用來獲取文件狀態(tài)(從inode節(jié)點中取出,除了st_ino之外),將文件狀態(tài)保存在buf指向的struct stat類型結構體中。聲明在sys/stat.h中。區(qū)別在于,stat和lstat中文件以路徑名形式提供,fstat中文件以文件描述符形式提供,另外,lstat用來獲得軟連接的文件信息,而不是軟連接所指向文件的信息。struct stat結構體信息如下:

struct stat {
?????????????? dev_t???? st_dev;???? /* ID of device containing file */
?????????????? ino_t???? st_ino;???? /* inode number */
?????????????? mode_t??? st_mode;??? /* protection */? //該域中保存了文件的訪問權限以及文件類型的信息,還包括了設置用戶ID位和設置組ID位
?????????????? nlink_t?? st_nlink;?? /* number of hard links */
?????????????? uid_t???? st_uid;???? /* user ID of owner */
?????????????? gid_t???? st_gid;???? /* group ID of owner */
?????????????? dev_t???? st_rdev;??? /* device ID (if special file) */
?????????????? off_t???? st_size;??? /* total size, in bytes */
?????????????? blksize_t st_blksize; /* blocksize for filesystem I/O */
?????????????? blkcnt_t? st_blocks;? /* number of 512B blocks allocated */
?????????????? time_t??? st_atime;?? /* time of last access */
?????????????? time_t??? st_mtime;?? /* time of last modification */
?????????????? time_t??? st_ctime;?? /* time of last status change */
?????????? };

2.S_ISDIR

?? S_ISREG

?? S_ISCHR

?? S_ISBLK

?? S_ISFIFO

?? S_ISLNK

?? S_ISSOCK

這幾個都是宏定義,依據struct stat中st_mode成員的值進行判斷(eg: S_ISDIR(buf.st_mode)),當前文件是否是目錄文件,普通文件,字符設備,塊設備,FIFO,軟連接,套接字。聲明在sys/stat.h中。

3.S_ISUID

?? S_ISGID

這兩個宏依據struct stat中st_mode成員的值,來測試該文件是否設置了“設置用戶ID位”和“設置組ID位”。

4.int access(const char *pathname, int mode)

該函數用來檢測當前進程的實際用戶和實際組是否有訪問由pathname所指示的文件。聲明在unistd.h文件中。mode可取以下值:

R_OK????? 測試讀權限:

W_OK????? 測試寫權限

X_OK?????? 測試執(zhí)行權限

F_OK?????? 測試文件是否存在

5.mode_t umask(mode_t mask)

該函數用來設置當前進程的文件模式創(chuàng)建屏蔽字,這會屏蔽掉當前進程中創(chuàng)建的文件的那些權限。聲明在sys/stat.h文件中。該函數不改變shell的屏蔽字。mask取值集合和open函數的mode參數一致。注意:umask中mask包含的權限會被屏蔽掉,這和open用法相反。

6.int chmod(const char *path, mode_t mode)

?? int fchmod(int fd, mode_t mode)

這兩個函數用來改變文件的訪問權限。聲明在sys/stat.h文件中。區(qū)別在于一個提供了文件的路徑,另一個提供了文件描述符。此外,該函數的mode中除了能包含open的mode取值集合,還可以包含如下三個:S_ISUID,S_ISGID,S_ISVTX,其中前兩個分別是設置用戶ID位和設置組ID位,第三個是粘住位。

7.int chown(const char *path, uid_t owner, gid_t group)

?? int fchown(int fd, uid_t owner, gid_t group)

? ?int lchown(const char *path, uid_t owner, gid_t group)

這幾個函數用來設置文件的用戶ID和組ID。聲明在unistd.h文件中。第一三個通過文件路徑來提供文件,第二個用fd提供文件。第三個函數只是修改符號鏈接的用戶ID和組ID,沒有修改符號鏈接指向的文件。只有調用這些函數的進程的有效用戶為文件的擁有者時才能完成修改,超級用戶可以修改任何文件。

8.int truncate(const char *path, off_t length)

? ?int ftruncate(int fd, off_t length)

這兩個函數用來將文件截短為length字節(jié)(即將length字節(jié)以后的部分去掉)。聲明在unistd.h文件中。

9.int link(const char *oldpath, const char *newpath)

該函數為oldpath文件創(chuàng)建一個硬鏈接newpath(實際上就是個目錄項)。聲明在unistd.h文件中。最后oldpath和newpath指向了同一個inode節(jié)點。inode中的鏈接計數加1。

創(chuàng)建硬鏈接一般需要:1.超級用戶權限才能創(chuàng)建目錄的硬鏈接,2.硬鏈接和文件位于同一文件系統中。

10.int unlink(const char *pathname)

該函數刪除一個目錄項(也就是刪除一個硬鏈接),該目錄項指向的inode中的鏈接計數減1。聲明在unistd.h文件中。

11.int remove(const char *pathname)

這是c的標準庫函數,聲明在stdio.h中。用來刪除文件或目錄的鏈接。刪除文件時相當于unlink,刪除目錄時相當于rmdir。

12.int rename(const char *oldpath, const char *newpath)

該函數是c的標準庫函數,聲明在stdio.h中。用來為文件或者目錄重命名。

13.int symlink(const char *oldpath, const char *newpath)

該函數用來為oldpath文件創(chuàng)建一個符號鏈接newpath。聲明在unistd.h文件中。

14.ssize_t readlink(const char *path, char *buf, size_t bufsiz)

該函數用來讀一個符號鏈接文件中的值(而不是所指向的文件)。聲明在unistd.h文件中。雖然open函數可以打開文件,但是由于其跟隨符號鏈接(打開符號鏈接所指向的文件),故不能打開符號鏈接文件本身。

15.int utime(const char *filename, const struct utimbuf *times)

該函數用來更改一個文件的訪問和修改時間。聲明在utime.h文件中。struct utimbuf結構體如下:

struct utimbuf {
?????????????? time_t actime;?????? /* access time */
?????????????? time_t modtime;????? /* modification time */
?????????? };

16.int mkdir(const char *pathname, mode_t mode)

該函數用來創(chuàng)建一個新目錄。聲明在sys/stat.h文件中。創(chuàng)建目錄時,至少需要設置一個執(zhí)行權限位,以允許訪問該目錄的文件名。

17.int rmdir(const char *pathname)

該函數用來刪除一個空目錄。聲明在unistd.h文件中。

18.DIR *opendir(const char *name)

???? DIR *fdopendir(int fd)

這兩個函數用來打開目錄文件。聲明在dirent.h文件中。返回一個指向目錄流的指針,目錄流不用關心。

19.struct dirent *readdir(DIR *dirp)

該函數從打開的目錄文件中讀取目錄項,保存到struct dirent結構中,并返回指向該結構的指針。聲明在dirent.h文件中。該結構體如下:

struct dirent {
?????????????? ino_t????????? d_ino;?????? /* inode number */
?????????????? off_t????????? d_off;?????? /* not an offset; see NOTES */
?????????????? unsigned short d_reclen;??? /* length of this record */
?????????????? unsigned char? d_type;????? /* type of file; not supported
????????????????????????????????????????????? by all filesystem types */
?????????????? char?????????? d_name[256]; /* filename */
?????????? };

20.void rewinddir(DIR *dirp)

該函數用來重置目錄流內的指針位置,使之指向目錄的開頭。聲明在dirent.h文件中。

21.int closedir(DIR *dirp)

該函數關閉dirp所指向的目錄流。聲明在dirent.h文件中。

22.long telldir(DIR *dirp)

該函數返回目錄流內指針的當前位置。聲明在dirent.h文件中。

23.void seekdir(DIR *dirp, long loc)

該函數設置目錄流內的指針位置,下一次readdir將從設置好的位置上讀取。聲明在dirent.h文件中。

24.int chdir(const char *path)

???? int fchdir(int fd)

這兩個函數更改當前進程的當前工作目錄。聲明在unistd.h文件中。

25.char *getcwd(char *buf, size_t size)

該函數獲取當前進程的當前工作目錄。聲明在unistd.h文件中。

?

第5章 標準I/O庫

1.int fwide(FILE *stream, int mode)

該函數設置文件流stream的定向(字節(jié)定向或寬定向)。但是不設置已經定向的流的定向。該函數無出錯返回(返回負值說明是字節(jié)定向,返回正值是寬定向,返回0無定向)。聲明在wchar.h文件中。根據mode取值不同,函數功能不同:

mode值為負,將流設置為字節(jié)定向。

mode值為正,將流設置為寬定向。

mode值為0,不設置流的定向,但返回該流定向值。

2.void setbuf(FILE *stream, char *buf)

? ?int setvbuf(FILE *stream, char *buf, int mode, size_t size)

這兩個函數改變給定流stream的緩沖類型。聲明在stdio.h文件中。對于setbuf而言,當buf為NULL,則stream流被設置為無緩沖;否則,buf的長度應該定義為BUFSIZE(在stdio.h中定義),將根據stream流是磁盤文件或者終端設備而將其設置為全緩沖或者行緩沖。對setvbuf而言,根據mode取值不同,設置的緩沖區(qū)類型也不同:

mode取值_IOFBF,設置為全緩沖,buf為非空,全緩沖為用戶的buf,大小為size;buf為NULL,系統自動分配合適大小的全緩沖區(qū)。

mode取值_IOLBF,設置為行緩沖,同上,全緩沖改為行緩沖即可。

mode取值_IONBF,設置為無緩沖。(忽略buf和size)

此外,setvbuf函數要在所有對流進行操作的函數之前被調用(打開流以后就立即調用該函數)。

3.?int fflush(FILE *stream)

該函數強制沖洗一個流,將stream流的所有未寫數據傳送到內核(然后寫入磁盤/終端設備中)。若stream為NULL,強制沖洗所有輸出流。

4.FILE *fopen(const char *path, const char *mode)?

?? FILE *freopen(const char *path, const char *mode, FILE *stream)

?? FILE *fdopen(int fd, const char *mode)

這幾個函數打開一個標準I/O流。聲明在stdio.h文件中。fopen打開指定的文件;freopen在指定的流上打開一個指定的文件,如果指定流已打開,那么先關閉該流,如果指定流已定向,那么清除該定向,該函數一般用來將一個指定的文件打開到標準輸入/輸出/出錯流上;fdopen為一個打開的文件描述符關聯一個標準I/O流。這幾個函數的mode取值如下:

r/rb??????????????????????????? 為讀而打開

w/wb????????????????????????? 為寫而打開或為寫而創(chuàng)建,并將文件截短為0

a/ab?????????????????????????? 為追加而打開或為追加而創(chuàng)建

r+/r+b/rb+????????????????? 為讀寫而打開

w+/w+b/wb+?????????????? 為讀寫而打開,并將文件截短為0

a+/a+b/ab+??????????????? 為讀或追加打開或創(chuàng)建

(b僅代表二進制文件,不影響其他)

?fdopen的mode取值有些特殊,取各種“寫”的時候,并不截短文件(因為fd表明該文件已經打開,而不是由標準I/O打開的,所以標準I/O沒資格截短)。而且取各種“寫”的時候不能沒有創(chuàng)建功能,因為fd文件已存在,無須創(chuàng)建。

另外,如果多個進程使用標準I/O打開一個文件,那么并發(fā)去寫文件都可以正確將數據寫入文件,標準I/O流的強大之處。。還有,如果同時以讀和寫打開一個文件,那么:1.如果中間沒有fflush,fseek,fsetpos或rewind,則在輸出后不能直接跟隨輸入;2.如果中間沒有fseek,fsetpos或rewind,或者一個輸入操作沒有達到文件尾部,則在輸入操作之后不能直接跟隨輸出。

最后,用標準I/O創(chuàng)建的文件無法說明文件的訪問權限。

5.int fclose(FILE *fp)

該函數關閉標準I/O流fp。聲明在stdio.h文件中。關閉之前沖洗緩沖區(qū)的輸出數據,丟棄緩沖區(qū)中的輸入數據。

6.int getc(FILE *stream)

? ?int fgetc(FILE *stream)

?? int getchar(void)

這些函數從標準I/O流stream中每次讀一個字符。聲明在stdio.h文件中。其中getc是宏函數,getchar相當于fgetc(stdin)。返回值為讀到的字符,字符類型本為unsigned char,在這里強制轉化成int類型,是為了當出錯或者到達文件末尾的時候能夠返回-1。在stdio.h中,EOF定義為-1,因此將這些函數返回值和EOF比較就可知道是否出錯或者是否到達文件末尾。

7.int ferror(FILE *stream)

?? int feof(FILE *stream)

?? void clearerr(FILE *stream)

前兩個函數是為了判斷流是否出錯。每個流在FILE對象中維持了兩個標志:出錯標志和文件結束標志(用以區(qū)分出錯和到達文件末尾)。它們都聲明在stdio.h文件中。第三個函數清除這兩個標志。

8.int ungetc(int c, FILE *stream)

該函數將字符c回送到stream流中,而不是文件中。下次從流中讀取時,將讀到該字符。聲明在stdio.h文件中。

9.int putc(int c, FILE *stream)

?? int fputc(int c, FILE *stream)

?? int putchar(int c)

這三個函數將字符c輸出到輸出流中。和6中的三個函數類似,putc是宏函數,putchar相當于fputc(c,stdout)。聲明在stdio.h文件中。

10.char *gets(char *s)

???? char *fgets(char *s, int size, FILE *stream)

gets函數從stdin中讀取一行字符存入s中(在stdin中遇到換行符或者EOF結束標志就算一行),不推薦使用該函數,容易造成緩沖區(qū)溢出。fgets從指定的流stream中讀取,并且指定了緩沖區(qū)大小為size,也是每次讀取一行,讀取的字符數最多為size-1,最后加‘\0’。對于這兩個函數而言,讀取到一行后,會將'\n'或者EOF替換為‘\0’。它們都聲明在stdio.h文件中。

11.int fputs(const char *s, FILE *stream)

???? int puts(const char *s)

fputs函數將以‘\0’結尾的串s輸出到stream流中。puts函數將以‘\0’結尾的串s輸出到stdout中。‘\0’都不寫入流中。puts函數最后會將一個‘\n’輸出到stdout流中。它們都聲明在stdio.h文件中。

12.size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream)

? ? ?size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream) ???

這兩個函數是二進制I/O函數,可以進行塊的讀寫操作。聲明在stdio.h文件中。

13.long ftell(FILE *stream)

???? int fseek(FILE *stream, long offset, int whence)

? ? ?void rewind(FILE *stream)

這幾個函數用來對標準I/O流進行定位(偏移量都以字節(jié)為單位)。ftell獲取流內指針的當前位置,? fseek將流內指針偏移量設為offset,whence提供了偏移量的相對起始位置,這和第三章的第4個函數lseek的該參數取值相同。rewind函數將流內指針設置到文件的開頭。對于fseek函數而言,定位文本文件時,whence只能取SEEK_SET,offset只能取兩種值:0或ftell的返回值。它們都聲明在stdio.h文件中。

14.int fseeko(FILE *stream, off_t offset, int whence)

? ? ?off_t ftello(FILE *stream)

這兩個函數和13中前兩個函數用法相同,區(qū)別是這兩個函數的偏移類型為off_t。聲明在stdio.h文件中。 ?

15.int fgetpos(FILE *stream, fpos_t *pos)

? ? ?int fsetpos(FILE *stream, fpos_t *pos)

第一個函數將定位的流內指針位置保存到pos中,第二個函數可以使用pos將流內指針設置為該位置。聲明在stdio.h文件中。

16.int printf(const char *format, ...)

? ? ?int fprintf(FILE *stream, const char *format, ...)

???? int sprintf(char *str, const char *format, ...)

???? int snprintf(char *str, size_t size, const char *format, ...)

這幾個函數用于格式化輸出。聲明在stdio.h文件中。第一個函數輸出到標準輸出流stdout。第二個函數輸出到stream流中。后兩個函數輸出到str緩沖區(qū)中。sprintf可能會造成緩沖區(qū)溢出,因此不建議使用。snprintf規(guī)定了緩沖區(qū)大小為size,因此比較安全。

17.int vprintf(const char *format, va_list ap)

? ? ?int vfprintf(FILE *stream, const char *format, va_list ap)

? ? ?int vsprintf(char *str, const char *format, va_list ap)

???? int vsnprintf(char *str, size_t size, const char *format, va_list ap)

這四個函數和16中的函數類似,區(qū)別是將可變參數替換成了ap參數。聲明在stdarg.h文件中。

18.int scanf(const char *format, ...)

???? int fscanf(FILE *stream, const char *format, ...)

???? int sscanf(const char *str, const char *format, ...)

這幾個函數用于格式化輸入。聲明在stdio.h文件中。第一個函數從標準輸入流stdin中讀取,第二個函數從stream流中讀取,第三個函數從str緩沖區(qū)中讀取。

19.int vscanf(const char *format, va_list ap)

???? int vsscanf(const char *str, const char *format, va_list ap)

???? int vfscanf(FILE *stream, const char *format, va_list ap)

這幾個函數類似于18中的函數,也是將可變參數替換成了ap參數。聲明在stdarg.h文件中。

20.int fileno(FILE *stream)

該函數將stream流所關聯的文件描述符返回。聲明在stdio.h文件中。

21.char *tmpnam(char *s)??

第一個函數用來產生一個與現有文件名不同的有效路徑名字符串。如果s為NULL,函數自動申請空間來存放有效路徑名字符串,并返回空間地址,如果s不為NULL,則將有效路徑名字符串存放于s中,并將s地址返回,然后就可用該路徑名在程序中手動創(chuàng)建文件了。聲明在stdio.h文件中。該函數有個問題,就是在產生出一個有效文件名和使用該文件名創(chuàng)建文件中間一般會有時間差,在該時間差內可能別的進程會使用該文件名創(chuàng)建文件,這樣就會出現問題。

22.FILE *tmpfile(void)

???? char *tempnam(const char *dir, const char *pfx)

第一個函數創(chuàng)建一個臨時二進制文件,在關閉該文件或程序結束時將自動刪除該文件。第二個函數也是用來創(chuàng)建一個臨時文件。關閉該文件或者程序運行完后文件會被自動刪除。聲明在stdio.h文件中。其中,dir規(guī)定了創(chuàng)建臨時文件所在的目錄,pfx如果非NULL的話,是一個最多包含5個字符的字符串,作為文件名的頭幾個字符。第二個函數也存在21函數的時間差問題。dir有如下要求:

a.如果定義了環(huán)境變量TMPDIR,則用其作為目錄。

b.如果dir非空,則用dir作為目錄。

c.將stdio.h中的字符串P_tmpdir用作目錄。

d.將本地目錄(通常是/tmp)用作目錄。

該函數自動分配空間來存放臨時的文件名,不接受用戶自己的buf。

23.? int mkstemp(char *template)

該函數也是用來創(chuàng)建一個臨時文件,返回文件描述符。與上邊的函數不同的是,它所創(chuàng)建的文件不會被自動刪除。臨時文件名由template參數提供,該串的最后6個字符為XXXXXX,然后該函數用不同字符代替XXXXXX,以創(chuàng)建唯一路徑名。聲明在stdio.h文件中。

?

第6章 系統數據文件和信息

1.struct passwd *getpwuid(uid_t uid)

?? struct passwd *getpwnam(const char *name)

這兩個函數通過口令文件(/etc/passwd,該文件中存放了用戶的所有信息除了用戶密碼),將inode中的用戶ID或者登錄時輸入的登錄名在口令文件中的項找出來,填入struct passwd結構中,并返回該結構指針,該結構是函數中定義的靜態(tài)結構,以后的函數調用會不斷覆蓋該結構的內容。該結構中包含了用戶的各種信息。聲明在pwd.h文件中。

2.struct passwd *getpwent(void)

?? void setpwent(void)

?? void endpwent(void)

第一個函數在循環(huán)中,可以逐個取出口令文件中的項。第二個函數用于返繞口令文件,即設置口令文件指針到文件的開頭。第三個函數用于關閉由第一個函數打開的口令文件。它們都聲明在pwd.h文件中。

3.struct spwd *getspnam(const char *name)

?? struct spwd *getspent(void)

?? void setspent(void)

?? void endspent(void)

這幾個函數用來讀取陰影文件(/etc/shadow,保存著用戶名和用戶密碼)的項。聲明在shadow.h文件中。這些函數用法和1,2函數類似。第一個函數根據用戶名name獲得對應的項,第二個函數在循環(huán)中可以逐個取出陰影文件中的項,最后兩個函數用來返繞和關閉陰影文件。

4.struct group *getgrgid(gid_t gid)

?? struct group *getgrnam(const char *name)

這兩個函數通過組文件(/etc/group,該文件中存放了所有用戶組的信息),將組ID或者組名對應的項讀取出來,寫入struct group結構,并返回該結構的指針。類似于1中的函數。聲明在grp.h文件中。struct group結構體如下:

?struct group {
?????????????? char?? *gr_name;?????? /* group name */
?????????????? char?? *gr_passwd;???? /* group password */
?????????????? gid_t?? gr_gid;??????? /* group ID */
?????????????? char? **gr_mem;??????? /* group members */? //該組的所有用戶名構成的數組
?????????? };

5.struct group *getgrent(void)

?? void setgrent(void)

?? void endgrent(void)

這些函數用來讀取組文件中的項,類似于2中的函數。聲明在grp.h文件中。

6.int getgroups(int size, gid_t list[])

該函數用來獲取當前用戶的附加組ID。聲明在unistd.h文件中。每個用戶除了可以加入口令文件中顯示的那個組外,同時可以加入若干個附加組。對于該函數,list數組用來存放所有的附加組ID數值,size說明了數組的元素個數。當size為0是,該函數返回當前用戶的附加組數(可以用來分配list的長度)。

7.int setgroups(size_t size, const gid_t *list)

該函數為當前用戶設置附加組ID表。聲明在grp.h文件中。該函數一般只在initgroups函數中進行調用。

8.int initgroups(const char *user, gid_t group)

該函數用來為當前用戶初始化附加組ID表。聲明在grp.h文件中。該函數會讀整個組文件,找到user所在的所有組,構成list數組后,調用setgroups函數來設置。并將當前用戶所在組的ID號group也存入附加組ID表。

9.struct utmp

1 struct utmp { 2 short ut_type; /* Type of record */ 3 pid_t ut_pid; /* PID of login process */ 4 char ut_line[UT_LINESIZE]; /* Device name of tty - "/dev/" */ 5 char ut_id[4]; /* Terminal name suffix, 6 or inittab(5) ID */ 7 char ut_user[UT_NAMESIZE]; /* Username */ 8 char ut_host[UT_HOSTSIZE]; /* Hostname for remote login, or 9 kernel version for run-level 10 messages */ 11 struct exit_status ut_exit; /* Exit status of a process 12 marked as DEAD_PROCESS; not 13 used by Linux init(8) */ 14 /* The ut_session and ut_tv fields must be the same size when 15 compiled 32- and 64-bit. This allows data files and shared 16 memory to be shared between 32- and 64-bit applications. */ 17 #if __WORDSIZE == 64 && defined __WORDSIZE_COMPAT32 18 int32_t ut_session; /* Session ID (getsid(2)), 19 used for windowing */ 20 struct { 21 int32_t tv_sec; /* Seconds */ 22 int32_t tv_usec; /* Microseconds */ 23 } ut_tv; /* Time entry was made */ 24 #else 25 long ut_session; /* Session ID */ 26 struct timeval ut_tv; /* Time entry was made */ 27 #endif 28 29 int32_t ut_addr_v6[4]; /* Internet address of remote 30 host; IPv4 address uses 31 just ut_addr_v6[0] */ 32 char __unused[20]; /* Reserved for future use */ 33 }; struct utmp

/run/utmp文件中保存了當前所有登錄到系統中的用戶。通過該結構體可以對文件中的項進行讀取和寫入。

10.int uname(struct utsname *buf)

該函數可以獲取當前主機和操作系統的有關信息。聲明在sys/utsname.h文件中。struct utsname結構體如下:

struct utsname {
?????????????? char sysname[];??? /* Operating system name (e.g., "Linux") */
?????????????? char nodename[];?? /* Name within "some implementation-defined
???????????????????????????????????? network" */
?????????????? char release[];??? /* Operating system release (e.g., "2.6.28") */
?????????????? char version[];??? /* Operating system version */
?????????????? char machine[];??? /* Hardware identifier */
?????????? #ifdef _GNU_SOURCE
?????????????? char domainname[]; /* NIS or YP domain name */
?????????? #endif
?????????? };
11.int gethostname(char *name, size_t len)

該函數獲取主機的名字存入name數組中,len指定了數組長度。聲明在unistd.h文件中。

12.time_t time(time_t *t)

該函數返回當前時間和日期(從1970.1.1 0:0:0開始至今的秒數),存入t所指向的變量中。聲明在time.h文件中。

13.int gettimeofday(struct timeval *tv, struct timezone *tz)

該函數也是返回當前時間,將時間保存在struct timeval結構體中。聲明在sys/time.h文件中。該函數比time函數有更高分辨率(微秒級),tz一般為NULL。struct timeval結構體如下:

struct timeval {
?????????????? time_t????? tv_sec;???? /* seconds */?? //秒,也是從1970.1.1 0:0:0開始至今的秒數,和time函數獲取數值的相同
?????????????? suseconds_t tv_usec;??? /* microseconds */??? //微秒
?????????? };

14.struct tm *gmtime(const time_t *timep)

???? struct tm *localtime(const time_t *timep)

第一個函數將timep的秒數轉換成國際標準時間(年月日時分秒星期),第二個函數將timep的秒數轉換成本地時間。轉換后的時間保存在struct tm靜態(tài)結構體中,然后返回結構體指針。聲明在time.h文件中。

15.time_t mktime(struct tm *tm)

該函數將本地時間轉換成time_t類型的值(秒數)。聲明在time.h文件中。

16.char *asctime(const struct tm *tm)

???? char *ctime(const time_t *timep)

這兩個函數分別將struct tm類型的時間和time_t類型的秒數轉換成26字節(jié)的字符串。eg:Sun Sep? 7 22:27:44 CST 2014。聲明在time.h文件中。

17.size_t strftime(char *s, size_t max, const char *format,? const struct tm *tm)

該函數將tm中的時間格式化輸出到長度為max的s數組中。若max大小不夠,則函數返回0。否則返回存入的字符數。聲明在time.h文件中。format格式和printf的format類似,但有一些不同,相見《APUE》p145頁。

?

第7章 進程環(huán)境

18.void exit(int status)

???? void _Exit(int status)

這兩個函數用來正常終止一個進程。第二個函數立即進入內核,第一個函數執(zhí)行完清理工作(關閉標準IO)然后進入內核。status是進程的終止狀態(tài)。聲明在stdlib.h文件中。

在main函數中調用exit等價于return語句。

19.void _exit(int status)

該函數也是用來正常終止一個進程。并且直接進入內核。聲明在unistd.h文件中。status是進程的終止狀態(tài)。

?注:在我的ubuntu14.04上測試,main函數的結束處未顯式的寫出返回語句(18,19函數或return),或者返回語句中沒有明確的返回狀態(tài)(數字),則進程終止狀態(tài)均為6,而不是《apue》中提到的0。

在這里總結下進程的5種正常終止方式和3種異常終止方式:

a.在main函數中執(zhí)行return。(等價于執(zhí)行exit,也就是在主線程中結束進程,如果僅想結束主線程而不是進程,則在main最后調用pthread_exit即可)

b.調用exit函數(主線程中或者任意子線程中調用)。該函數將調用由atexit登記過的終止處理函數,并關閉所有的標準I/O流。

c.調用_exit或_Exit函數。這兩個函數直接陷入內核,因此就不會執(zhí)行終止處理程序或者信號處理程序。

d.進程的最后一個線程從其啟動例程(就是線程的執(zhí)行函數)中返回。線程的返回值不會做為進程的返回值,進程將以終止狀態(tài)0返回。

e.進程的最后一個線程調用pthread_exit函數。這種情況下,進程的終止狀態(tài)為0,而不是pthread_exit的參數。

3種異常終止方式:

a.調用abort。

b.當進程接收到某些信號。

c.最后一個線程對“取消”請求做出響應。

無論進程以何種方式結束,最終會執(zhí)行內核中的同一段代碼,來關閉所有打開的文件描述符,釋放它所使用的存儲器等。如果子進程希望父進程知道它是如何結束的,通過調用exit,_exit,_Exit函數,并把退出狀態(tài)作為參數傳遞給它們,而在異常終止情況下,內核會產生一個指示其異常終止原因的終止狀態(tài),最終父進程通過調用wait或waitpid函數就可以獲取到終止狀態(tài)。exit和_Exit最終還是調用_exit函數,將退出狀態(tài)轉換成進程的終止狀態(tài)。exit,_exit,_Exit三個函數的參數(退出狀態(tài))必然等于進程的終止狀態(tài),但是線程的結束函數pthread_exit的參數(退出狀態(tài))不會等于進程的終止狀態(tài)。

20.int atexit(void (*function)(void))

由該函數登記的function函數,會被exit(或者主線程的return/最后一個線程的return)自動調用,也就說只有在進程正常結束時才調用,線程結束不會調用,就算是在某個非主線程中用atexit設置的清理函數,也得等到整個進程結束時才調用。聲明在stdlib.h文件中。

21.void *malloc(size_t size)

???? void *calloc(size_t nmemb, size_t size)

???? void *realloc(void *ptr, size_t size)

???? void free(void *ptr)

前三個函數在堆中為進程分配空間,最后一個函數釋放這些空間。其中,malloc函數不對分配的空間進行初始化,calloc函數將分配的空間初始化為 0,realloc用來調整(變大或減小)由前兩個函數非配空間的大小,其中size參數是新存儲區(qū)的大小,不是新舊存儲區(qū)大小之差,當擴大空間時,如果原存儲區(qū)周圍的空閑空間不足,則在其他地方重新開辟空間,再將 原存儲區(qū)中的值拷貝到新空間;如果原存儲區(qū)周圍空間足夠,則在原存儲區(qū)位置上向高地址方向擴充。聲明在stdlib.h文件中。

22.char *getenv(const char *name)

該函數在進程的環(huán)境表(環(huán)境表是一個指針數組,每個元素指向了“name=value”形式的串)中查找name的環(huán)境變量值。返回一個字符串指針。聲明在stdlib.h文件中。

23.int putenv(char *string)

???? int setenv(const char *name, const char *value, int overwrite)

???? int unsetenv(const char *name)

這幾個函數用來設置進程環(huán)境表中的環(huán)境變量值。聲明在stdlib.h文件中。對于putenv函數,會將string指向的串(形式為“name=value”)放到環(huán)境表name的項中,如果name已存在則刪除name的定義。對于setenv函數,name和value值分別由兩個參數提供,如果overwrite非0,則會刪除環(huán)境表中已存在的name定義,如果overwrite為0,則不會刪除刪除環(huán)境表中已存在的name定義,也不設置新的value值,也不出錯。對于unsetenv函數,將刪除環(huán)境表中已存在的name定義,若環(huán)境表中不存在name的定義也不出錯。

在改變或者增加環(huán)境變量的時候,有如下規(guī)定:

a.修改一個現有name,如果新value長度小于或等于現有value長度,則就在原空間中寫入新字符串。

b.修改一個現有name,如果新value長度大于現有value長度,則用malloc在堆中為新value分配空間,然后將該空間地址放入環(huán)境表中原來的name元素中。

c.新增一個name,如果是第一次新增,調用malloc分配一個新的環(huán)境表,并將原來環(huán)境表中的值復制過來,將新的value串的指針放在環(huán)境表的表尾,再在其后放入一個空指針。此時環(huán)境表中的其他大多數指針仍指向了棧頂之上的各個value串。

d.新增一個name,如果這不是第一次新增,說明之前調用過malloc,則使用realloc改變環(huán)境表的大小,之后操作類似于c。

24.int setjmp(jmp_buf env)

? ? ?void longjmp(jmp_buf env, int val)

第一個函數將調用時的堆棧內容保存在env中,以供第二個函數使用。第二個函數使用env值,會跳到設置env的函數中(即setjmp),由于setjmp可以對應多個longjmp函數,因此val變量傳遞的數字會對多個longjmp函數加以區(qū)分。

《apue》上說,對于無優(yōu)化的編譯,使用longjmp函數跳回后,全局變量,靜態(tài)局部變量,自動變量,寄存器變量,volatile聲明的變量都不會回滾到以前的值。對于優(yōu)化后的編譯,自動變量和寄存器變量會回滾到以前的值。筆者未進行過測試。

25.int getrlimit(int resource, struct rlimit *rlim)

???? int setrlimit(int resource, const struct rlimit *rlim)

每個進程都有一組資源限制,這兩個函數可以獲取和設置每個資源限制。聲明在sys/resource.h文件中。resource代表資源限制的類型,struct rlimit結構體中裝有該資源的軟限制值和硬限制值。該結構體如下:

struct rlimit {
?????????????? rlim_t rlim_cur;? /* Soft limit */
?????????????? rlim_t rlim_max;? /* Hard limit (ceiling for rlim_cur) */
?????????? };
a.任何一個進程都可將一個軟限制值更改為小于或等于其硬限制值。

b.任何一個進程都可降低其硬限制值,但它必須大于或等于其軟限制值,這種降低對普通用戶而言是不可逆的。

c.只有超級用戶進程可以提高硬限制值。

?

第8章 進程控制

1.pid_t getpid(void)

? ?pid_t getppid(void) ?

?? uid_t getuid(void)

?? uid_t geteuid(void)

?? gid_t getgid(void)

?? gid_t getegid(void)

這些函數用來返回當前進程的進程ID,父進程ID,實際用戶ID,有效用戶ID,實際組ID,有效組ID。它們均無出錯返回。聲明在unistd.h文件中。注意:當前進程的保存的設置用戶ID無法返回。

2.pid_t fork(void)

該函數為調用進程創(chuàng)建一個子進程。聲明在unistd.h文件中。由該函數創(chuàng)建的子進程和其父進程使用了”寫時復制“技術。在父進程中,該函數返回一個正數;在子進程中,該函數返回0;出錯的話該函數返回-1。

子進程會繼承父進程的以下屬性或資源:

a.實際用戶ID,實際組ID,有效用戶ID,有效組ID。

b.附加組ID。? c.進程組ID。?? d.會話ID。? e.控制終端

f.設置用戶ID標志和設置組ID標志。?? g.當前共組目錄。???

h.根目錄???? i.文件模式創(chuàng)建屏蔽字。

j.信號屏蔽和安排。

k.針對任一打開文件描述符的在執(zhí)行時關閉(close-on-exec)標志。

l.環(huán)境。? m.鏈接的共享存儲段。? n.存儲映射??? o.資源限制。

p.父進程所打開的文件描述符的文件表項(父子進程共享由父進程打開的文件)。

父子進程有如下區(qū)別:

a.fork返回值不同。? b.進程ID不同。?

c.兩個進程具有不同的父進程ID。

d.子進程的tms_utime,tms_stime,tms_cutime,tms_ustime均被設置為0。

e.父進程設置的文件鎖不會被子進程繼承。

f.子進程未處理的鬧鐘(alarm)被清除。

g.子進程的未處理信號集設置為空集。

fork失敗的兩個主要原因:

a.系統中的進程數太多。

b.該實際用戶擁有的進程總數超過了系統限制。

fork有下面兩種用法:

a.父進程希望子進程復制父進程的代碼,使得父,子進程執(zhí)行不同的代碼段。網絡服務器進程一般這么用。

b.子進程執(zhí)行其他的程序。子進程的fork返回后立即調用exec。

3.pid_t vfork(void)

該函數和fork類似,也是用來為當前進程創(chuàng)建一個子進程。區(qū)別是:該函數不使用“寫時復制”技術。也就是說,該函數創(chuàng)建的子進程目的就是要用exec執(zhí)行一個新程序。子進程如果不執(zhí)行exec函數,就永遠運行在父進程的地址空間中,就算執(zhí)行寫操作,也不會復制父進程的地址空間到子進程。此外,vfork會保證子進程先運行,子進程調用exec或exit之后父進程才可能被調度。(如果在調用者兩個函數之前子進程依賴于父進程的進一步動作,則會導致死鎖)

4.pid_t wait(int *status)

?? pid_t waitpid(pid_t pid, int *status, int options)

在父進程中調用這兩個函數來等待子進程的結束并收回子進程所占資源。聲明在sys/wait.h文件中。二者區(qū)別是:wait函數等待第一個結束的子進程,如果還沒有子進程結束的話父進程會阻塞;waitpid進程則可以等待由pid指定的進程,并且option提供了若干選項,可以選擇該函數非阻塞。有若干個宏可以用來檢測這兩個函數返回的子進程狀態(tài)status,詳見man手冊。下面說下waitpid函數的pid和options參數的用法:

pid == -1 ? ? ? ? ? ? 等待任一子進程,相當于wait函數

waitpid > 0 ? ? ? ?? 等待進程ID為pid的子進程

pid == 0 ? ? ? ? ? ?? 等待進程組ID等于調用進程組ID的任一子進程

pid < -1 ? ? ? ? ? ? ? 等待進程組ID等于pid絕對值的任一子進程

options選項:

WCONTINUED????????? 若系統支持作業(yè)控制,那么由pid指定的任一子進程在暫停后已經繼續(xù),但其狀態(tài)尚未報告,返回其狀態(tài)
WNOHANG??????? ? ? ?? 若由pid指定的子進程還沒有結束,則waitpid函數不阻塞,立即返回
WUNTRACED ? ??????? 若系統支持作業(yè)控制,那么由pid指定的任一子進程處于暫停狀態(tài),并且自暫停狀態(tài)以來還未報告過,返回其狀態(tài)

5. int waitid(idtype_t idtype, id_t id, siginfo_t *infop, int options)

該函數和waitpid函數類似,也是等待指定pid的子進程返回。聲明在sys/wait.h文件中。但區(qū)別是:該函數把要等待的子進程ID和子進程所在組ID區(qū)分開了,分別表示。infop中包含了引起子進程狀態(tài)改變的信號的詳細信息。idtype提供了要等待的ID類型:

P_PID??????? 等待進程號為id的子進程

P_PGID???? 等待進程組號為id的子進程

P_ALL?????? 等待任一子進程,忽略id

options選項:

WCONTINUED???? 同上

WEXITED???????????? 等待已退出的進程

WNOHANG????????? 同上

WNOWAIT  ???? 不破壞子進程的退出狀態(tài),該退出狀態(tài)可有后續(xù)的wait,waitid,waitpid調用取得

WSTOPPED  ? 等待一個已暫停但是狀態(tài)還未報告的子進程

6.pid_t wait3(int *status, int options, struct rusage *rusage);

?? pid_t wait4(pid_t pid, int *status, int options, struct rusage *rusage)

這兩個函數和上面函數類似,只是多了一個功能:rusage可以收集到子進程的資源統計信息(用戶CPU時間總量,系統CPU時間總量,頁面出錯次數,接收到信號的次數等)。聲明在sys/wait.h文件中。

7.int execl(const char *path, const char *arg, ...)

?? int execlp(const char *file, const char *arg, ...)

?? int execle(const char *path, const char *arg,? ..., char * const envp[])

?? int execv(const char *path, char *const argv[])

?? int execvp(const char *file, char *const argv[])

?? int execvpe(const char *file, char *const argv[], char *const envp[])

這些函數將調用進程所執(zhí)行的程序替換為新程序,一般在子進程中使用。聲明在unistd.h文件中。它們之間的區(qū)別:函數名中包含p的,以文件名(file)為參數提供可執(zhí)行文件(若file串中包含/,將它視為路徑名;否則安PATH環(huán)境變量來找),函數名不包含p的,是以路徑名(path)為參數來提供可執(zhí)行文件;函數名中包含l的(前三個函數),命令行參數為可變參數(除了第0個參數,它已經用arg給出來了),其他函數是以指針數組形式提供命令行參數;函數名以e結尾的,最后一個參數是環(huán)境表,其他函數則沒有環(huán)境表參數。

子進程中通過exec函數執(zhí)行新程序之后,2中所列出的a-o那些屬性不會改變,外加tms_utime,tms_stime,tms_cutime,tms_ustime這些值也不會改變,還要注意實際用戶ID和實際組ID是否改變根據新可執(zhí)行文件的設置用戶ID位和設置組ID位是否設置而定;p的話,要根據所打開文件描述符是否設置FD_CLOEXEC標志(執(zhí)行時關閉close-on-exec)有關,若設置了就關閉,否則不關閉。

8.int setuid(uid_t uid)

?? int setgid(gid_t gid)

這兩個函數分別用來設置當前進程的實際用戶ID/有效用戶ID和實際組ID/有效組ID。聲明在unistd.h文件中。有如下設置規(guī)則:

a.如果進程具有超級用戶權限,可將進程的實際用戶ID,有效用戶ID,保存的設置用戶ID設置為uid。

b.如果進程沒有超級用戶權限,但是uid等于進程的實際用戶ID或保存的設置用戶ID,則只將有效用戶ID設置為uid,不改變實際用戶ID和保存的設置用戶ID。

c.如果a和b都不滿足,則將error設置為EPERM,并返回-1。

還要注意以下三點:

a.只有超級用戶進程可以更改實際用戶ID。

b.僅當對程序文件設置了設置用戶ID位時,exec函數才會設置有效用戶ID為程序文件的擁護者ID。否則exec函數不改變有效用戶ID,維持原先值。上邊的b表明了任何時候都可調用setuid將有效用戶ID設置為實際用戶ID或保存的設置用戶ID。

c.保存的設置用戶ID是有exec復制有效用戶ID而得來的。

9.int setreuid(uid_t ruid, uid_t euid)

?? int setregid(gid_t rgid, gid_t egid)

這兩個函數用來交換實際用戶ID和有效用戶ID,實際組ID和有效組ID。聲明在unistd.h文件中。任一參數值為-1,則相應的ID應當保持不變。

10.int seteuid(uid_t euid)

???? int setegid(gid_t egid)

這兩個函數類似于8中的函數,但是只改變當前進程的有效用戶ID,無論是當前是特權用戶還是非特權用戶。聲明在unistd.h文件中。特權用戶進程可將有效用戶ID設置為euid,非特權用戶進程可將有效用戶ID設置為實際用戶ID或保存的設置用戶ID。

注意:上述所有的組ID設置和用戶ID設置類似,不再贅述。附加組ID不受這些函數影響。

?11.解釋器文件:第一行包含“#!pathname”的文件就是解釋器文件。shell會創(chuàng)建一個子進程來執(zhí)行pathname所指示的命令,然后該命令再去解釋執(zhí)行解釋器文件。解釋器文件不一定非得是shell腳本,也可能是其他腳本(比如awk程序腳本)。每一種類型的解釋器文件都會有自己的命令解釋器。內核在執(zhí)行pathname命令時,傳遞給該命令的參數如下:argv[0]:pathname路徑;argv[1]:解釋器文件#!pathname后邊跟的參數;argv[2]:exec的第0個參數(解釋器文件的絕對路徑);argv[3]及以后的參數:exec的第2個及以后的參數。

eg:execl(“home/sar/bin/testinterp”,? "testinterp", "myarg1", "MY ARG2", (char *)0);

其中home/sar/bin/testinterp是解釋器文件。查看該解釋器內容:cat home/sar/bin/testinterp ===>#!/home/sar/bin/echoarg foo。該解釋器文件第一行包含了解釋器命令為/home/sar/bin/echoarg,foo為該命令的參數。execl將執(zhí)行/home/sar/bin/echoarg程序,將“/home/sar/bin/echoarg”,“foo”,“home/sar/bin/testinterp”,"myarg1","MY ARG2"作為參數傳遞進去。

12.int system(const char *command)

該函數用來執(zhí)行一個shell命令。聲明在stdlib.h文件中。在該函數內部調用了fork,exec,waitpid。command字符串中包含了命令以及參數等(包括由管道連接的多個命令,以及輸入輸出重定向),shell會對其進行語法分析,將其分成命令行參數。一般情況下,fork和exec之間會有安全漏洞問題,需要在fork之后,exec之前將子進程的有效用戶改為普通用戶,然后再讓子進程執(zhí)行exec。該函數中fork出的子進程去執(zhí)行/bin/sh程序,然后shell子進程再創(chuàng)建子進程去執(zhí)行command。由于該函數中調用了fork,exec和waitpid,因此該函數的返回值較為特殊,需要注意,有三種返回值:

a.如果fork失敗或者waitpid返回除EINTR之外的出錯,則system返回-1,并且errno中設置了錯誤類型。

b.如果exec失敗(表示不能執(zhí)行shell),則system返回值如同shell執(zhí)行了exit(127)一樣。

c.否則三個函數都執(zhí)行成功,則system返回值是shell的終止狀態(tài)。

注:該函數對信號的處理有要求。該函數在fork子進程前,會先屏蔽掉SIG_INT和SIGQUIT信號,同時屏蔽掉調用進程的SIGCHLD信號(防止用戶在該信號處理函數中調用了wait等函數回收子進程,這樣system函數中的wait函數就獲取不到子進程的終止狀態(tài)),函數返回時會恢復一切。

?

第10章 信號

1.typedef void (*sighandler_t)(int);

? ?sighandler_t signal(int signum, sighandler_t handler)

該函數用來設置進程的信號處理函數。一般不推薦使用該函數,而應該使用sigaction函數替代它。聲明在signal.h文件中。signum參數是要設置的信號名,handler參數是一個函數指針變量,該變量也可以取以下三種值值:SIG_IGN,SIG_DFL,信號處理函數的地址。第一個表示忽略此信號,第二個表示采用系統的默認動作處理此信號,第三個表示此信號發(fā)生時,調用該處理函數來處理此信號。函數返回值是前一個信號處理函數的指針。一般情況下,執(zhí)行一個程序時,如果沒有設置信號的處理函數,則系統會把信號設置為默認的處理動作。當進程執(zhí)行exec函數后,也會把之前設置的信號處理動作更改為信號的默認處理動作。當進程創(chuàng)建了一個子進程時,子進程會繼承父進程的信號處理方式。

注:信號的三種處理方式:忽略,捕獲,默認方式(默認方式一般是終止接收進程)。

2.int kill(pid_t pid, int sig)

?? int raise(int sig)

第一個函數將新號發(fā)送給進程或者進程組。第二個函數向本進程發(fā)送信號。聲明在signal.h文件中。第二個函數等價為kill(getpid(),sig)。kill函數的pid有以下四種情況:

a.pid > 0????????? 將該信號發(fā)送給進程ID為pid的進程

b.pid == 0??????? 將該信號發(fā)送給和發(fā)送進程屬于同一個進程組的所有進程(不包括系統進程),而且發(fā)送進程具有向這些進程發(fā)送信號的權限。

c.pid < 0????????? 將信號發(fā)送給進程組ID等于|pid|的所有進程(不包括系統進程)。并且也要具有發(fā)送權限,類似于b

d.pid == -1?????? 將信號發(fā)送給系統上的所有進程(不包括系統進程)。并且也要具有發(fā)送權限,類似于b和c

上邊提到的發(fā)送權限是這樣的:超級用戶可將信號發(fā)送給任意進程;非超級用戶,是否有權限的依據是發(fā)送者的實際或者有效用戶ID是否等于接收者的實際或者有效用戶ID,相等的話就具有權限,否則不具有權限。另外,普通進程可以將SIGCONT信號發(fā)送給同一個會話中的所有進程,包括系統進程。

如果kill的sig參數為0,用來測試pid號進程是否存在,不存在的話kill返回-1并設置errno為ESRCH,這種測試一般沒有什么價值。另外,kill函數向本進程發(fā)送信號時,會在函數返回前將信號傳遞至該進程。

3.unsigned int alarm(unsigned int seconds)

該函數設置一個鬧鐘(計時器),當鬧鐘時間到之后會投遞一個SIGALRM信號至調用進程。該信號的默認處理是終止調用進程。每個進程只能有一個鬧鐘時間,如果調用alarm函數設置了一個鬧鐘時間,在該時間到之前又調用alarm函數設置了一個鬧鐘時間,則第二次調用alarm函數返回值為第一次alarm調用設置時間的剩余值,然后按照第二次設置的時間重新計時;如果第二次alarm調用參數為0,則取消鬧鐘時間,并將第一次alarm調用設置時間的剩余值返回。聲明在unistd.h文件中。

4.int pause(void)

該函數將進程掛起直到捕捉到一個信號。聲明在unistd.h文件中。當該掛起進程接收到一個信號,執(zhí)行完信號處理函數并從其返回時,pause函數才返回,返回值為-1,并將errno設置為EINTR。

5.int sigemptyset(sigset_t *set)

?? int sigfillset(sigset_t *set)

?? int sigaddset(sigset_t *set, int signum)

?? int sigdelset(sigset_t *set, int signum)

?? int sigismember(const sigset_t *set, int signum)

這五個函數用來初始化信號集。聲明在signal.h文件中。sigemptyset函數會清空set所指向的信號集。sigfillset函數會將所有信號放入set指向的信號集中。所有信號集在使用之前,都要調用這兩個函數的其中之一來初始化信號集。一旦初始化了一個信號集,就可使用其余的函數在信號集中增加或刪除特定的信號。

如果實現的信號數目少于一個整型所包含的位數,則可以用整型變量作為信號集,整型的每一位代表一個信號。此時,sigemptyset函數將整型量設置為0,sigfillset函數將整型量的各個位都設置為1。這兩個函數被實現為宏函數:

#define? sigemptyset(ptr)??? {*(ptr) = 0}

#define? sigfillset(ptr)????????? {*(ptr) = ~(sigset_t)0, 0}

可看出,這兩個宏函數返回值均為0。

sigaddset函數打開一位(將該位置為1),sigdelset函數關閉一位(將該位置為0),sigismember函數測試一個指定位。

6.int sigprocmask(int how, const sigset_t *set, sigset_t *oldset)

該函數設置進程的信號屏蔽字。聲明在signal.h文件中。如果oldset參數非空,則進程的當前信號屏蔽字通過oldset返回;如果set參數非空,這時how指定當前信號屏蔽字的修改方法。how的取值如下:

SIG_BLOCK????????????? 該進程新的信號屏蔽字是其當前的信號屏蔽字和set指向信號集的并集。set包含了我們希望阻塞的附加信號。

SIG_UNBLOCK???????? 該進程新的信號屏蔽字是其當前的信號屏蔽字和set指向信號集的交集。set包含了我們希望解除阻塞的信號。

SIG_SETMASK????????? 該進程新的信號屏蔽字將被set指向的信號集所替代。

如果set為空,則不改變該進程的信號屏蔽字,how值也無意義。另外,調用sigprocmask函數后如果有任何沒被阻塞的未決信號,會在該函數返回前被遞送給該進程。

注意:sigprocmask函數僅能在單線程的進程中使用。多線程中需要使用pthread_sigmask函數。

7.int sigpending(sigset_t *set)

該函數用來查看當前進程的未決信號(實際上就是被阻塞的信號)集。該信號集通過set參數返回。聲明在signal.h文件中。

8. int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact)

該函數類似于signal函數,用來查看或者設置信號的處理函數。聲明在signal.h文件中。一般推薦使用該函數,去替代signal函數。參數signum是要查看或者修改的信號編號,若參數act非空,則要修改信號處理動作,若參數oldact非空,返回該信號的上一個處理動作。struct sigaction結構體如下:

struct sigaction {
?????????????? void???? (*sa_handler)(int);
?????????????? void???? (*sa_sigaction)(int, siginfo_t *, void *);
?????????????? sigset_t?? sa_mask;
?????????????? int??????? sa_flags;
?????????????? void???? (*sa_restorer)(void);
?????????? };
sa_handler字段指向了信號處理函數的地址;sa_mask字段指定了一個信號集,調用上述信號處理函數之前會屏蔽掉該信號集,從信號處理函數中返回時恢復信號屏蔽字(這樣就保證了在處理一個給定信號時,若這種信號再次發(fā)生,它將被阻塞到對前一個信號的處理結束為止,如果同一種信號發(fā)生多次,通常部將它們排隊,也就是說當解除信號阻塞后,該信號只會被投遞一次)。sa_flags字段指定對信號進行處理的各個選項:

SA_INTERRUPT??????? 由此信號中斷的系統調用不會自動重啟。

SA_NOCLDSTOP?????? 若signum參數是SIGCHLD,當子進程暫停(作業(yè)控制)時或者由暫停繼續(xù)運行時,不產生此信號;當子進程終止時產生此信號。

SA_NOCLDWAIT??????? 若signum參數是SIGCHLD,則當調用進程的子進程終止時,不產生將死進程;若調用進程依然wait子進程,則調用進程阻塞知道所有子進程都終止,才     ? ? ? ? ? ? ? ? ? ? 會返回,返回值為-1,并將errno設置為ECHILD。

SA_NODEFER??????????? 當捕捉到該信號并執(zhí)行其信號處理函數時,系統不自動阻塞此信號(除非sa_mask中包括了此信號)。此種類型操作對應于早期的不可靠信號。

SA_ONSTACK??????????? 若用sigaltstack函數聲明了一個替換棧,則將此信號遞送給替換棧上的進程。

SA_RESETHAND??????? 在此信號函數的入口處,將此信號的處理方式復位為SIG_DFL,并清除SA_SIGINFO標志。此種類型操作對應于早期的不可靠信號。此標志不能自動復           位SIGILL和SIGTRAP這個信號。設置了此標志也會有設置SA_NODEFER標志后的效果。

SA_RESTART???????????? 由此信號中斷的系統調用會自動重啟動。

SA_SIGINFO?????????????? 此選項會用sa_sigaction指向的信號處理函數來處理此信號,而不用sa_handler所指向的信號處理函數。

sa_sigaction指針指向的信號處理函數類型為:void? * function(int, siginfo_t *, void *)。siginfo_t結構體類型如下:

siginfo_t

該結構體將包含信號產生原因的有關信息。

9.int sigsetjmp(sigjmp_buf env, int savesigs)

?? void siglongjmp(sigjmp_buf env, int val)

如果要在信號處理函數中實現遠跳轉,應該使用這兩個函數進行堆棧保存和恢復,而避免使用setjmp和longjmp。因為執(zhí)行信號處理程序時系統自動設置當前信號的屏蔽字,信號處理程序結束時系統自動恢復屏蔽字,而setjmp和longjmp函數不對屏蔽字進行任何操作,因此使用longjmp遠跳轉之后,當前信號的屏蔽字就無法恢復了(以后無法處理當前信號了)。因此需要使用9中這兩個函數。sigsetjmp函數比setjmp多出了一個savesigs參數,該參數非0時,該函數會保存當前進程的信號屏蔽字,然后siglongjmp函數會將保存的信號屏蔽字恢復,這樣當從信號處理函數中遠跳轉后,進程的信號屏蔽字仍然會恢復到以前的狀態(tài)。聲明在setjmp.h文件中。

10.int sigsuspend(const sigset_t *mask)

進程中調用該函數用來等待一個信號。聲明在signal.h文件中。mask參數用來設置進程的信號屏蔽字,也就是說,該函數將等待除了被屏蔽掉的信號以外的任意一個信號到來,在這一信號到來之前當前進程處于阻塞狀態(tài)。信號到來之后并執(zhí)行完信號處理函數后,才從該函數返回。該函數返回時,會自動將信號屏蔽字恢復為該函數被調用之前的狀態(tài)。該函數的返回值總是-1,并將errno設置為EINTR,表示一個被中斷的系統調用。

11.void abort(void)

該函數發(fā)送SIGABRT信號至當前進程,使當前進程非正常終止。無論程序中在調用abort之前,對SIGABRT信號的處理進行何種設置,abort都會終止該進程(abort內部會重新將SIGABRT信號的處理設置為默認方式)。聲明在stdlib.h文件中。

12.unsigned int sleep(unsigned int seconds)

該函數使調用線程睡眠seconds秒。該函數一般使用alarm實現,因此該函數和用戶調用的alarm的之間有交互作用,需要注意。該函數是調用線程睡眠,并不是整個進程睡眠,當然,如果進程只有一個主線程,那么主線程睡眠就相當于進程睡眠。

注意:信號這一章節(jié)有個重要的知識,就是中斷的系統調用。一般情況下,系統調用將進程阻塞后,如果進程收到了任意可以處理的信號,就會激活進程,若設置了該信號的處理函數,則去執(zhí)行該處理函數并且返回后也會從該引起進程阻塞的系統調用處返回。也有一部分系統調用會重新啟動。

13.與作業(yè)控制有關的信號:

SIGCHLD???????? 子進程已暫停或終止

SIGCONT???????? 使暫停的進程繼續(xù)運行

SIGSTOP??????? ? 使進程暫停(不能捕獲或忽略)

SIGTSTP?????????? 交互式暫停信號(用終端命令使進程暫停,比如ctrl+z)

SIGTTIN??????????? 后臺進程組成員讀控制終端

SIGTTOU????????? 后臺進程組成員讀控制終端

除了SIGCHLD以外,其它信號應用程序不去處理。當按ctrl+z時,SIGTSTP信號被送至前臺進程組的所有進程,使之暫停(并轉入后臺)。在命令行下使用fg %n或bg %n時,SIGCONT信號被發(fā)給相應進程,使之轉向前臺繼續(xù)運行或者在后臺繼續(xù)運行。對一個進程產生四種停止信號(SIGTSTP,SIGSTOP,STGTTIN,SIGTTOU)中的任意一種時,對該進程的任一未決SIGCONT信號就會被丟棄。類似,當對一個進程產生SIGCONT信號時,對同一進程的任一未決的停止信號將被丟棄。當對一個暫停的進程產生SIGCONT信號時,無論SIGCONT信號是被阻塞或是忽略,進程都將繼續(xù)運行。

14. void psignal(int sig, const char *s)

該函數將字符串s輸出到標準出錯文件,后接一個冒號和空格,再接著是對sig信號的說明,最后是換行符。聲明在signal.h文件中。類似于perror函數。

15.char *strsignal(int sig)

給出一個信號sig,函數會返回說明該信號的字符串。應用程序可用該字符串打印關于接收到信號的出錯消息。聲明在string.h文件中。類似于strerror函數。

?

第11章 線程概念

1.int pthread_equal(pthread_t t1, pthread_t t2)

該函數用來比較兩個線程IDti和t2是否相等。相等返回非0值,不相等返回0。聲明在pthread.h文件中。由于每個平臺的pthread_t類型不一定相同,因此不能直接比較,只能通過該函數比較。

2.pthread_t pthread_self(void)

該函數獲取當前線程的線程ID。聲明在pthread.h文件中。1,2函數一般可以配合來使用。

3.int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg)

該函數用來創(chuàng)建一個新線程。聲明在pthread.h文件中。thread中包含了新線程的線程ID。attr結構可以為新線程設置線程屬性。start_routine是線程的啟動例程(線程要執(zhí)行的函數)。arg為線程傳遞參數,一般是結構類型指針。線程創(chuàng)建后并不保證哪個線程先運行,所以不能做任何假設。新線程繼承了調用線程的浮點環(huán)境和信號屏蔽字,新線程的未決信號集被清除。并且每個線程都有errno的副本,當線程出錯后,錯誤碼會保存在各自的errno中。新創(chuàng)建的線程一般通過2中函數來獲取自己的線程ID,而不能使用thread參數來獲取,因為新線程可能在創(chuàng)建函數返回前就運行了,而此時也許thread還未設置成線程ID。其他平臺同一個進程中的多個線程,它們的進程號是相同的,線程號是不同的;但是Linux系統,由于它的線程和進程實現機制是一樣的,都用clone系統調用來完成創(chuàng)建,因此Linux系統中的線程實際上是其創(chuàng)建進程的子進程,該子進程可以共享父進程一定數量的執(zhí)行環(huán)境。

4.void pthread_exit(void *retval)

該函數用來結束調用線程。聲明在pthread.h文件中。參數retval是線程的返回碼。其他線程可以通過pthread_join函數獲得該返回碼。線程的三種退出方式如下:

a.線程從啟動例程中返回,返回值是線程的退出碼

b.線程可以被同一進程中的其他線程取消

c.線程調用pthread_exit返回

5.int pthread_join(pthread_t thread, void **retval)

該函數等待一個線程ID號為thread的線程結束。類似于waitpid。該函數會一直阻塞直到等待的線程調用pthread_exit,或從啟動例程返回,或者被其他線程取消。聲明在pthread.h文件中。該函數可以獲得4中函數的返回碼,保存到retval指向的單元,如果對等待線程的返回碼不感興趣,可將retval置為NULL,則該函數不會獲取等待線程的返回碼。如果等待線程是被其他線程取消的,則retval指向的內存單元被置為PTHREAD_CANCELED。

4,5兩個函數的retval參數可以傳遞結構體指針,來包含更多的數值。該結構體一般要用全局結構或者是malloc分配的單元。

6.int pthread_cancel(pthread_t thread)

線程通過調用該函數來取消同一進程中的其他線程。聲明在pthread.h文件中。默認情況下,被取消的線程會表現得跟調用了pthread_exit(PTHREAD_CANCELED)函數一樣。線程可以選擇忽略取消方式或者控制取消方式。此外,pthread_cancel并不等待線程終止,它僅提出請求。

7.void pthread_cleanup_push(void (*routine)(void *)

?? void pthread_cleanup_pop(int execute)

第一個函數用來注冊線程結束時要執(zhí)行的清理函數。類似于進程中的atexit函數。清理函數的執(zhí)行順序與注冊順序相反。這兩個函數需要配對使用,調用了幾次pthread_cleanup_push函數,也需要對應的調用幾次pthread_cleanup_pop函數。它們均聲明在pthread.h文件中。當線程執(zhí)行以下動作時會調用清理函數:

a.調用pthread_exit時

b.響應取消請求時

c.用非0的execute參數賴掉用pthread_cleanup_pop時

當使用參數execute=0來調用pthread_cleanup_pop函數后,無論在上邊那種情況下,清理函數都不會被調用,而會被清理掉。此外,還要注意,當線程從它的啟動例程中返回的話(比如使用return),即使注冊了清理函數,清理函數也不會被調用。

8.int pthread_detach(pthread_t thread)

該函數用來分離一個線程。聲明在pthread.h文件中。一個線程被分離后,它的底層存儲資源在線程終止時立即被收回。而不用等待其他線程調用pthread_join函數或者整個進程結束。注意,一個線程分離后,不能再調用pthread_join函數來等待該分離線程。

9.線程同步

a.互斥量

int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr)

int pthread_mutex_destroy(pthread_mutex_t *mutex)

這兩個函數用來初始化互斥量(動態(tài)初始化)和清理互斥量資源。聲明在pthread.h文件中。如果想靜態(tài)初始化互斥量,定義一個全局的pthread_mutex_t 類型變量,并給其賦值為PTHREAD_MUTEX_INITIALIZER。如果互斥量單元是用malloc等進行分配的,那么在釋放內存之前需要調用pthread_mutex_destroy對資源進行清理。用默認屬性初始化互斥量時只需把attr置為NULL。

int pthread_mutex_lock(pthread_mutex_t *mutex)

int pthread_mutex_trylock(pthread_mutex_t *mutex)

int pthread_mutex_unlock(pthread_mutex_t *mutex)

使用第一個函數對互斥量進行加鎖,使用第三個函數對互斥量解鎖。它們均聲明在pthread.h文件中。如果互斥量已經上鎖,那么調用進程將阻塞直到互斥量被解鎖。第二個函數也是用來對互斥量進行加鎖,如果互斥量已經上鎖,它不會阻塞進程,返回EBUSY,如果成功加鎖則返回0。

b.讀寫鎖

int pthread_rwlock_init(pthread_rwlock_t *restrict rwlock, const pthread_rwlockattr_t *restrict attr)

int pthread_rwlock_destroy(pthread_rwlock_t *rwlock)

這兩個函數用來初始化讀寫鎖和清理讀寫鎖。它們和a中的函數類似。聲明在pthread.h文件中。如果希望讀寫鎖有默認屬性,將attr置為NULL。在釋放動態(tài)分配的內存前,需要先調用第二個函數釋放資源。

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock)

int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock)

int pthread_rwlock_unlock(pthread_rwlock_t *rwlock)

這幾個函數分別在讀模式下或者寫模式下鎖定讀寫鎖,無論哪種模式下鎖定讀寫鎖,都可用第三個函數來解鎖。它們均聲明在pthread.h文件中。讀寫鎖實現的時候可能會對共享模式下可獲取的鎖數量進行限制,所以需要檢查pthread_rwlock_rdlock函數返回值。

int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock)

int pthread_rwlock_trywrlock(pthread_rwlock_t *rwlock)

這兩個函數也類似于a中的函數,獲得鎖時返回0,鎖被占時返回EBUSY,也不會阻塞線程。聲明在pthread.h文件中。

c.條件變量

int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr)?

int pthread_cond_destroy(pthread_cond_t *cond)

這兩個函數和上邊的函數是類似的。聲明在pthread.h文件中。第一個函數用來對條件變量進行初始化。第二個函數用來對條件變量進行資源清理。如果條件變量是由malloc等手動分配的,那么在釋放該空間前,要先調用第二個函數進行資源清理。如果使用默認屬性進行初始化,attr傳遞NULL即可。

int pthread_cond_wait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex)

int pthread_cond_timedwait(pthread_cond_t *restrict cond, pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime)

這兩個函數用來等待一個條件變量cond為真。聲明在pthread.h文件中。如果cond不為真,會將調用線程放到等待條件的線程列表上,并阻塞當前線程,釋放互斥鎖mutex。第二個函數僅僅等待abstime時長,若cond還不為真則返回錯誤碼,同時獲取互斥鎖。這兩個函數的條件變量cond均受到互斥鎖mutex的保護,當函數將線程放入等待條件列表上后,會釋放該互斥鎖。該互斥鎖mutex將會保護兩個資源:一個是消息隊列,另一個是條件變量cond。具體的使用例子參考《apue》。第二個函數的struct timespec參數類型如下:

struct timespec {

  time_t?????? tv_sec,???????? /*seconds*/

  long????????? tm_nsec;???? /*nanoseconds*/

};

第二個函數的等待時間是個絕對數而不是相對數,也就是說,如果需要等待3分鐘,就要在當前時間上加3分鐘然后轉換到struct timespec結構中。

int pthread_cond_signal(pthread_cond_t *cond)

int pthread_cond_broadcast(pthread_cond_t *cond)

這兩個函數用于通知線程條件已經滿足。第一個函數將喚醒等待該條件的某個線程,第二個函數會喚醒等待該條件的所有線程。

?

第12章 線程控制

1.int pthread_attr_init(pthread_attr_t *attr)

?? int pthread_attr_destroy(pthread_attr_t *attr)

這兩個函數用于對線程的屬性結構體進行初始化和清理。聲明在pthread.h文件中。Linux系統支持的線程屬性有下邊四個:

detachstate?????? 線程的分離屬性

guardsize????????? 線程棧末尾的警戒緩沖區(qū)大小(字節(jié)數)

stackaddr????????? 線程棧的最低地址

stacksize?????????? 線程棧的大小(字節(jié)數)

可取消狀態(tài)

可取消類型

并發(fā)度

2.int pthread_attr_getdetachstate(pthread_attr_t *attr, int *detachstate)

?? int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate)

這兩個函數用來獲取和設置線程的分離屬性。聲明在pthread.h文件中。detachstate參數代表了屬性值,有兩個:PTHREAD_CREATE_DETACHED(以分離狀態(tài)啟動線程),PTHREAD_CREATE_JOINABLE(正常啟動線程)。如果一開始就知道不需要獲取線程的返回碼,那就可以分離屬性來創(chuàng)建線程,這樣線程就會以分離狀態(tài)來啟動,線程結束時系統就會收回線程資源。

3.int pthread_attr_getstack(pthread_attr_t *attr, void **stackaddr, size_t *stacksize)

?? int pthread_attr_setstack(pthread_attr_t *attr,? void *stackaddr, size_t stacksize)

這兩個函數用來獲取和設置線程的線程棧地址屬性和線程棧大小屬性。聲明在pthread.h文件中。一個進程中的所有線程共用該進程的棧地址空間,當線程數太多時,可能會消耗完進程的棧地址空間,于是就需要使用malloc或者mmap為線程棧重新分配空間。分配好之后需要用第二個函數來設置棧。stackaddr是線程棧的最低地址,因此它可能是線程的開始或者結尾(棧從高地址向低地址方向伸展)。

4.int pthread_attr_getstacksize(pthread_attr_t *attr, size_t *stacksize)

?? int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize)

這兩個函數用來專門獲取和設置線程棧的大小屬性。聲明在pthread.h文件中。如果希望改變線程棧的默認大小,但又不想自己處理棧的分配問題,就可以用該函數。

5.int pthread_attr_getguardsize(pthread_attr_t *attr, size_t *guardsize)

?? int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize)

這兩個函數用來獲取和設置線程棧的警戒緩沖區(qū)屬性。聲明在pthread.h文件中。參數guardsize控制著線程棧末尾之后用意避免棧溢出的擴展內存(警戒緩沖區(qū))的大小。該緩沖區(qū)默認大小是PAGESIZE字節(jié)。當把guardsize置為0時,則不為線程棧提供警戒緩沖區(qū)。另外,如果對線程屬性stackaddr做了修改,也會使線程棧緩沖區(qū)機制無效,等同于把guardsize置為0。當線程的棧指針溢出到警戒區(qū)域,應用程序就會收到出錯信號。

6.int pthread_getconcurrency(void)

?? int pthread_setconcurrency(int new_level)

這兩個函數用來獲取和設置線程的并發(fā)度。聲明在pthread.h文件中。如果系統當前正控制著并發(fā)度,那么第一個函數會返回0。用第二個設置函數設置并發(fā)度時,只是對系統做一個提示,系統并不保證一定采用new_level參數指定的并發(fā)度。使用new_level參數為0來調用設置函數,會撤消在這之前用非0的new_level參數設置的并發(fā)度。

7.同步屬性

a.互斥量屬性

int pthread_mutexattr_init(pthread_mutexattr_t *attr)

int pthread_mutexattr_destroy(pthread_mutexattr_t *attr)

這兩個函數用來初始化和清理互斥量的屬性。聲明在pthread.h文件中。第一個函數會用默認的互斥量屬性初始化pthread_mutexattr_t結構。有兩個互斥量屬性值得注意:互斥量共享屬性和類型屬性。

int pthread_mutexattr_getpshared(const pthread_mutexattr_t * restrict attr, int *restrict pshared)

int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared)

這兩個函數用來獲取和設置互斥量的共享屬性。在一個進程中,多個線程默認可以訪問同一個同步對象(指互斥量,讀寫變量,條件變量),因此線程共用的同步對象屬性需要設置為PTHREAD_PROCESS_PRIVATE,這也是默認的屬性值;而多個進程可以把同一個內存區(qū)映射到它們各自獨立的地址空間中,多個進程訪問同一個內存區(qū)的數據時也需要同步,那么在共享內存區(qū)域中分配的互斥量就可用于這些進程的同步,這時需要將互斥量的共享屬性設置為PTHREAD_PROCESS_SHARED。

int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type)

int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type)

這兩個函數用來獲取和設置互斥量的類型屬性。互斥量類型屬性值如下:

PTHREAD_MUTEX_NORMAL ? ? ? ? ? ? ? ? ? ? 遞歸加鎖:死鎖 ? ? ? ? 解別人的鎖:未定義 ? ? ? 已解鎖時解鎖:未定義 ?????????

PTHREAD_MUTEX_ERRORCHECK?????????? 遞歸加鎖:返回錯誤? 解別人的鎖:返回錯誤?? 已解鎖時解鎖:返回錯誤???

PTHREAD_MUTEX_RECURSIVE??????????????? 遞歸加鎖:允許 ? ? ??? 解別人的鎖:返回錯誤?? 已解鎖時解鎖:返回錯誤

PTHREAD_MUTEX_DEFAULT???????????????????? 遞歸加鎖:未定義?? ? 解別人的鎖:未定義 ? ? ? 已解鎖時解鎖:未定義

在一個函數的加鎖,解鎖調用之間需要調用另外的函數,而另外的函數內部也要獲取同一個鎖,這時就需要設置遞歸鎖屬性,否則就會死鎖。此外,使用遞歸鎖時,需要一定的編程技巧。

b.讀寫鎖屬性

int pthread_rwlockattr_init(pthread_rwlockattr_t *attr)

int pthread_rwlockattr_destroy(pthread_rwlockattr_t *attr)

這兩個函數用來初始化和清理讀寫鎖的屬性。聲明在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)

這兩個函數用來獲取和設置讀寫鎖的共享屬性。聲明在pthread.h文件中。類似于互斥量的該函數。讀寫鎖僅支持共享屬性,沒有類型屬性。

c.條件變量屬性

int pthread_condattr_init(pthread_condattr_t *attr)

int pthread_condattr_destroy(pthread_condattr_t *attr)

這兩個函數用來初始化和清理條件變量的屬性。聲明在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)

這兩個函數用來獲取和設置條件變量的共享屬性。聲明在pthread.h文件中。類似于互斥量和讀寫鎖的該函數。讀寫鎖僅支持共享屬性,沒有類型屬性。

8.void flockfile(FILE *filehandle)

?? int ftrylockfile(FILE *filehandle)

?? void funlockfile(FILE *filehandle)

這幾個函數用來為FILE對象加鎖和解鎖。聲明在stdio.h文件中。其中第二個函數不會阻塞當前線程。標準IO函數都是線程可重入的函數(所謂一個函數是可重入的,指的是該函數可以被并發(fā)的調用),因為它們會對標準IO流FILE進行加鎖訪問。并不是說標準IO函數內部調用了這幾個函數,而是表現出來的特性就像是調用了這幾個函數。這幾個函數主要是留給應用程序來調用,對沒有加鎖的FILE流進行加鎖保護。、

注:可重入函數一般分為對線程可重入(線程安全)或者對信號處理程序可重入(異步-信號安全),前者的話就是上邊的定義,后者的話指的是在該函數中發(fā)生信號后函數仍然是安全的(執(zhí)行完信號處理函數,返回到該函數中)。

9.int getc_unlocked(FILE *stream)

?? int getchar_unlocked(void)

?? int putc_unlocked(int c, FILE *stream)

?? int putchar_unlocked(int c)

這幾個函數是標準IO函數的不加鎖版本。聲明在stdio.h文件中。在使用這幾個函數的時候就需要用到8中的函數。

10.int pthread_key_create(pthread_key_t *key, void (*destructor)(void*))

在分配線程的私有數據之前,需要調用該函數創(chuàng)建與該數據關聯的鍵。這個鍵用于獲取對線程私有數據的訪問權。聲明在pthread.h文件中。該函數的一般用法:在主線程中調用完成鍵的創(chuàng)建,然后在其他線程中使用該鍵去綁定私有數據即可(因此鍵的變量要定義為全局變量)。創(chuàng)建好的鍵存放在key所指向的內存單元中,這個鍵可以被進程中的所有線程使用,而每個線程把這個鍵與不同的線程私有數據地址進行關聯。創(chuàng)建新鍵時,每個線程的數據地址設為NULL。destructor參數用來為該鍵關聯析構函數,當線程正常退出時,如果傳遞的destructor參數非NULL,就會調用該析構函數。而當線程調用exit,_exit,_Exit函數或者以非正常方式退出時,就不會調用該析構函數。通常會使用mallocl為線程私有數據分配內存空間,在析構函數中釋放這些空間,否則,就可能使線程所屬的進程出現內存泄漏。線程可以為私有數據分配多個鍵,每個鍵的析構函數可以相同也可以不同。當所有析構函數都調用完后,系統會檢查是否還有非NULL的線程私有數據與鍵關聯,有的話繼續(xù)執(zhí)行相應析構函數,直到所有私有數據為NULL。

11.int pthread_key_delete(pthread_key_t key)

該函數用來取消鍵與私有數據值之間的關聯關系。聲明在pthread.h文件中。該函數在釋放鍵時,并不會激活鍵的析構函數,因此需要在程序中手動釋放由malloc函數分配的私有數據空間。

12.pthread_once_t once_control = PTHREAD_ONCE_INIT;

???? int pthread_once(pthread_once_t *once_control, void (*init_routine)(void))

當多個線程中同時調用10中函數創(chuàng)建鍵的時候,很可能會發(fā)生沖突(其實鍵只需要被創(chuàng)建一次就行)。該函數用來避免此沖突。因為該函數只要在線程中被調用一次后,init_routine參數指向的函數將只會被調用一次,因此在多個線程中都調用一次pthread_once函數,系統就能保證init_routine函數只會被調用一次,然后再在init_routine函數中調用10中的函數來完成鍵的創(chuàng)建,這樣多個線程之間就不會發(fā)生沖突。另外,該函數的once_control參數必須是全局或者靜態(tài)變量,而且被初始化為PTHREAD_ONCE_INIT。具體例子可以參考《apue》。

13.void *pthread_getspecific(pthread_key_t key)

???? int pthread_setspecific(pthread_key_t key, const void *value)

這兩個函數用來用來獲取和設置key鍵所對應的線程私有數據。聲明在pthread.h文件中。如果key還沒有對應的私有數據,第一個函數返回NULL,否則返回私有數據空間地址,第二個函數可以根據第一個函數的返回值進行判斷,進而選擇是否要為鍵設置私有數據空間。另外,只有鍵所對應的私有數據非空時,析構函數才會被調用。

14.int pthread_setcancelstate(int state, int *oldstate)

該函數用來設置當前線程的可取消狀態(tài)。聲明在pthread.h文件中。該函數把當前的可取消狀態(tài)設置為state,把原來的可取消狀態(tài)保存到oldstate指向的單元中。pthread_cancel調用并不等待線程終止,該調用執(zhí)行后,被取消線程繼續(xù)運行,當運行到某個取消點時,才被檢查自己是否要被取消。《apue》書中列舉了POSIX.1所定義的取消點函數。當線程長時間都執(zhí)行不到這些取消點函數時,可以調用15中的函數,它是專門的取消點函數。線程的可取消狀態(tài)state有兩個值:PTHREAD_CANCEL_ENABLE和PTHREAD_CANCEL_DISABLE,線程啟動時默認取值是前者。如果線程的可取消狀態(tài)值設為前者,則線程在取消點可以被取消;否則設為后者的話,在取消點也不會殺死線程,這時,取消請求對該線程而言處于未決狀態(tài),當該線程的可取消狀態(tài)再次被設為PTHREAD_CANCEL_ENABLE時,該線程將在下一個取消點上對所有的未決請求進行處理。

15.void pthread_testcancel(void)

該函數用來為當前線程添加取消點。聲明在pthread.h文件中。如果當前線程的可取消狀態(tài)被設為PTHREAD_CANCEL_DISABLE,則該函數沒有任何效果。

16.int pthread_setcanceltype(int type, int *oldtype)

該函數用來用當前線程設置可取消類型。聲明在pthread.h文件中。type是新類型,oldtype存放的是之前的舊類型。線程的可取消類型值有兩個:PTHREAD_CANCEL_DEFERRED和PTHREAD_CANCEL_ASYNCHRONOUS。默認情況下,線程的可取消類型是前者,即延遲取消(就是執(zhí)行完pthread_cancel調用后,被請求線程并不馬上被取消)。后者是異步取消,也就是說線程可以在任意時間取消,而不是非得遇到取消點才能被取消。

17.int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset)

該信號用來屏蔽當前線程的某些信號。聲明在pthread.h文件中。信號是屬于一個進程的,因此可以被進程的所有線程共享,一般情況下,信號到達進程時,哪個線程正在運行,就在該線程的上下文中執(zhí)行信號處理函數。但是,每個線程都有自己的信號屏蔽字,也就是說每個線程都可以屏蔽自己不想處理的信號,但這不影響其他線程的信號屏蔽字。另外,由于信號是屬于進程的,所以任何線程中設置或修改了信號的處理函數,整個進程的信號處理函數也就隨之改變了。

18.int sigwait(const sigset_t *set, int *sig)

線程通過調用該函數等待一個或多個信號發(fā)生。聲明在pthread.h文件中。函數的返回值存放在sig指向的變量中,表明信號發(fā)送的數量。如果信號集set中的某個信號在sigwait調用的時候處于未決狀態(tài),那么sigwait函數將無阻塞的返回,返回前sigwait將從進程中移除那些處于未決狀態(tài)的信號。線程在調用sigwait函數之時,必須阻塞它所等待的信號,從而避免錯誤發(fā)生(在函數返回之前的時間內,又會有新的等待信號產生)。sigwait函數在返回前會取消信號集的阻塞狀態(tài)(即恢復線程的屏蔽字)。

使用該函數使得線程簡化了信號處理,將異步產生的信號用同步的方式來處理,說白了就是當信號產生時,不去執(zhí)行進程的信號處理函數,而是被等待信號的線程所攔截(通過sigwait函數)而從sigwait函數返回,這樣就相當于將等待信號的線程環(huán)境(線程中sigwait以后的代碼)作為了信號處理程序,而不是進程的信號處理函數。當有多個線程調用sigwait函數來等待同一個信號,信號發(fā)生時只會有一個線程從sigwait返回。這塊討論的信號到底是由線程的sigwait捕獲還是進程的信號處理函數捕獲,是由操作系統實現的,操作系統要么實現前者,要么實現后者,而不會二者皆可。

19.int pthread_kill(pthread_t thread, int sig)

線程通過調用該函數向其他線程發(fā)送信號,thread參數是其他線程的線程id,sig是信號名。聲明在pthread.h文件中。當傳遞sig=0的參數到函數中時,可以用來檢查thread線程是否存在。另外,如果信號的默認處理動作是終止進程,那么把信號傳遞給某個線程也仍然會殺死整個進程。

20.int pthread_atfork(void (*prepare)(void), void (*parent)(void), void (*child)(void))

該函數用于清除子進程中的鎖狀態(tài)。聲明在pthread.h文件中。當在一個線程中使用fork創(chuàng)建子進程時,子進程通過繼承父進程的整個地址空間,從而也繼承了父進程的所有互斥量,讀寫鎖和條件變量狀態(tài)。但是,在子進程中只存在一個線程,那就是父進程中調用fork的那個線程的副本。由于子進程不包含其他線程副本,于是子進程就無法感知到其他線程中的鎖,因此需要借助該函數來清理鎖狀態(tài)。如果子進程創(chuàng)建好后,立即調用了exec函數,就不需要清理那些鎖了,因為整個舊的地址空間會被拋棄。

該函數的作用是來幫助fork后的子進程把其所有繼承而來的鎖打開。該函數的原理:該函數在fork函數之前進行調用,來注冊三個處理函數prepare,parent和child。注冊成功后,prepare函數會在fork函數創(chuàng)建子進程之前調用,作用是獲取父進程定義的所有鎖;parent會在fork創(chuàng)建完子進程,將要返回父進程環(huán)境時調用,作用是打開父進程的所有鎖;child函數會在fork創(chuàng)建完子進程,將要返回子進程環(huán)境時調用,作用是打開子進程的所有鎖。至此,子進程中所有繼承而來的鎖全部被打開了。注意:如果不想用哪個處理函數,注冊時該函數的參數傳遞為NULL。該函數可以在fork之前多次調用,或者在多個模塊中調用,詳細例子參考《apue》。

?

第14章 高級I/O

.......

........

從I/O多路轉接開始。。。。。

1.int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout)

該函數具有進行同步I/O多路轉接功能(確定多個文件描述符fd的狀態(tài))。聲明在sys/select.h文件中。nfns參數告訴內核需要確定的描述符范圍為0~nfds-1,內核將來只會返回該范圍的文件描述符狀態(tài)。readfds,writefds,excpetfds三個參數用來設置需要了解其狀態(tài)的描述符,同時用來返回已準備好的可讀,可寫,有異常消息的描述符。timeout參數用來設置select的阻塞時間。

先來說明最后一個參數,它指定了select函數愿意等待的時間,struct timeval結構如下:

struct timeval {
?????????????? long??? tv_sec;???????? /* seconds */
?????????????? long??? tv_usec;??????? /* microseconds */
?????????? };
當timeout == NULL時,select函數永遠等待。當所指定的文件描述符之一準備好時或者函數捕捉到一個信號時,等待結束。如果捕捉到一個信號函數返回-1并設置errno值。

當timeout->tv_sec == 0 && timeout->tv_usec == 0時,select函數完全不阻塞。測試完所指定的描述符后立即返回。這種方式適合用輪詢來獲得描述符狀態(tài)。

當timeout->tv_sec != 0 || timeout->tv_usec != 0時,select函數等待所指定的秒數或微秒數。當指定的描述符之一準備好時或者指定的時間已超過時立即返回。

中間的三個參數readfds,writefds,excpetfds用下面的函數來設置。(這三個參數類型是fd_set,也就是描述符集,每個描述符在其中占據一位,下面的函數使用描述符fd來指定是哪一位)

void FD_CLR(int fd, fd_set *set)

int? FD_ISSET(int fd, fd_set *set)

void FD_SET(int fd, fd_set *set)

void FD_ZERO(fd_set *set)

第一個函數將fd指定的一位清除。第二個函數測試一個指定位是否設置。第三個函數用來設置一個指定位。最后一個函數將fd_set變量的所有位設置為0。一般在定一個了一個描述符集變量后,必須先用FD_ZERO清除其所有位,然后再設置我們關心的各個位。

select函數的中間三個參數readfds,writefds,excpetfds中的任意一個或者全部都可以為空指針,這表示對相應狀態(tài)并不關心。如果這三個參數都是空指針,select函數就相當于sleep函數,不過睡眠的時間更加精確。select有三個可能的返回值:

a.返回值為-1表示出錯。比如所有指定的描述符都沒有準備好時(select正阻塞著),進程接收到了一個其它的信號。

b.返回值為0表示沒有描述符準備好。這種情況出現在,要么select不阻塞立即返回,要么阻塞的時間已到,描述符都沒有準備好。這是readfds,writefds,excpetfds三個描述符集的所有位均為0。

c.返回值為正值表示已經有描述符準備好了。如果有同一個描述符的讀和寫都準備好了,返回值為2。這時,readfds,writefds,excpetfds三個描述符集中打開的位(為1)對應于準備好的描述符。

另外,對于普通文件描述符,獲取其讀,寫,異常狀態(tài)時總是返回準備好了。如果在一個描述符上碰到了文件結尾處,select指示該描述符是可讀狀態(tài),而不指示其為異常狀態(tài)。

2.int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask)

該函數域select函數類似。聲明在sys/select.h文件中。有以下區(qū)別:

a.select的超時值用timeval結構指定,但pselect使用timespec結構指定。該結構如下所示:

struct timespec {
?????????????? long??? tv_sec;???????? /* seconds */
?????????????? long??? tv_nsec;??????? /* nanoseconds */
?????????? };

b.pselect的超時值被設置為const,這保證了函數調用期間不會改變該該結構值。

c.pselect函數可以設置信號屏蔽字,如果sigmask為null,那么在信號有關方面該函數和select運行狀況相同。該函數以原子操作安裝信號屏蔽字,在函數返回時恢復屏蔽字。

3.int poll(struct pollfd *fds, nfds_t nfds, int timeout)

該函數和select類似,不過接口不同。聲明在poll.h文件中。該函數使用了一個結構體數組fds,為每個要獲取其狀態(tài)的描述符分配了一個結構體(結構體中包含了描述符的各種狀態(tài)),而不是為每個狀態(tài)構造一個描述符集。stuct pollfd結構體如下:

struct pollfd {
?????????????? int?? fd;???????? /* file descriptor */
?????????????? short events;???? /* requested events */
?????????????? short revents;??? /* returned events */
?????????? };
結構體中的events成員用來告訴內核我們關心該描述符的什么狀態(tài)。函數返回時,內核設置revents成員,以說明對于該描述符已經發(fā)生了什么事件。events和revents取值如下:

events:

POLLIN ? ? ? ? ? ? ? ? ?? 不阻塞地可讀除高優(yōu)先級外的數據(等效于POLLRDNORM | POLLRDBAND) ?????????????

POLLRDNORM??????? 不阻塞地可讀普通數據(優(yōu)先級波段為0)

POLLRDBAND????????? 不阻塞地可讀非0優(yōu)先級波段數據

POLLPRI?????????????????? 不阻塞地可讀高優(yōu)先級數據

前四行用來測試可讀性

POLLOUT???????????????? 不阻塞地可寫普通數據

POLLWRNORM???????? 與POLLOUT 相同

POLLWRBAND????????? 不阻塞地可寫非0優(yōu)先級波段數據

這三行用來測試可寫性

revents:

POLLERR????????????????? 已出錯

POLLHUP????????????????? 已掛斷

POLLNVAL???????????????? 描述符不引用一打開文件

當描述符被掛斷后,就不能再寫向該描述符,但仍可能從該描述符中讀到數據。應該區(qū)分文件結束和掛斷之間的區(qū)別:如果正從終端輸入數據,并鍵入了文件結束字符,POLLIN被打開,于是就可讀文件結束標志(read返回0)。如果測試的正在讀調制解調器,并且電話線已掛斷,則在revents中將街道POLLHUP通知(之前POLLHUP在revents中沒有打開)。

4.ssize_t readv(int fd, const struct iovec *iov, int iovcnt)

?? ssize_t writev(int fd, const struct iovec *iov, int iovcnt)

這兩個函數在一次函數調用中讀,寫多個非連續(xù)緩沖區(qū)。聲明在sys/uio.h文件中。第一個函數將fd文件中的數據讀到iov數組中,第二個函數將iov數組中的數據聚集起來寫入fd文件中。struct iovec結構如下:

struct iovec {
?????????????? void? *iov_base;??? /* Starting address */
?????????????? size_t iov_len;???? /* Number of bytes to transfer */
?????????? };
iov_base成員指向緩沖區(qū)的首地址,iov_len成員代表該緩沖區(qū)大小。writev以順序iov[0],iov[1]至iov[iovcnt-1]從緩沖區(qū)中聚集輸出數據。函數返回輸出的字節(jié)總數,通常,它應該等于所有緩沖區(qū)長度之和。readv將讀到的數據按照上述的順序散步到緩沖區(qū)中。readv函數總是填滿一個緩沖區(qū),再填寫下一個緩沖區(qū)。readv返回讀到的字節(jié)數,遇到文件結尾返回0。

5.void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset)

該函數將一個給定文件映射到一個存儲區(qū)域中。聲明在sys/mman.h文件中。addr用于指定映射存儲區(qū)的起始地址,通常將其設置為0,表示由系統選擇該映射區(qū)的起始地址(系統一般選擇堆區(qū)和棧區(qū)中間的共享區(qū))。該函數返回值為映射區(qū)的起始地址。fd參數指定要被映射文件的描述符,在映射之前,先要打開該文件。length參數指示映射區(qū)的長度(字節(jié)為單位)。offset參數指示要映射字節(jié)在文件中的起始偏移量。prot參數說明對映射存儲區(qū)的保護要求:

PROT_READ?????? 映射區(qū)可讀

PROT_WRITE???? 映射區(qū)可寫

PROT_EXEC?????? 映射區(qū)可執(zhí)行

PROT_NONE????? 映射區(qū)不可訪問

對映射存儲區(qū)的保護要求不能超過文件open模式訪問權限。例如,若該文件是只讀打開的,那么對映射存儲區(qū)就不能指定PROT_WRITE。

flags參數影響映射存儲區(qū)的多種屬性:

MAP_FIXED???????? 返回值必須等于addr。不推薦使用該標志,因為不利于可移植性。如果未指定此標志,而且addr非0,則內核只把addr視為一種建議值,并不保證會采用

??????????????????????????? 該地址。將addr指定為0可獲得最大可移植性。

MAP_SHARED???? 該標志說明本進程對該映射區(qū)做的修改對其他進程是可見的。說白了,就是本進程對該映射區(qū)做的修改都會真正修改磁盤上的該文件。這樣,其它使用該

???????????????????????????? 標志映射該文件的進程就會和本進程共享該文件,對該區(qū)域的修改對于這些進程都是可見的,最終也會修改磁盤上的文件。

MAP_PRIVATE???? 使用了該標志,只是將文件的一個副本映射到存儲區(qū)中,進程對存儲區(qū)的修改,只是在副本上進行修改,不會影響到磁盤上的文件,當進程終止后,磁盤

??????????????????????????? 上的該文件沒有變化。當然,對映射區(qū)的修改對于多個進程都是不可見的。

還有許多MAP_類標志,詳情參考man手冊。

需要注意的是,一般映射區(qū)的長度會是內存頁長的整數倍,那么需要映射的文件如果比內存的頁長小,那么系統會把映射區(qū)內除了文件以外的多余部分設置為0,對這部分的操作不會影響到文件。例如,文件長度為12字節(jié),系統頁長為512字節(jié),因此多出的500字節(jié)系統會設置為0,并且操作這500字節(jié)對文件無影響(如果想讓文件映射滿一頁,可以先對打開的文件進行l(wèi)seek操作,將文件指針移動到距離文件開頭一頁大小的位置,再隨意向文件中寫一個字符即可,實際上該操作將文件變大了再映射)。此外,與映射區(qū)相關信號有兩個:SIGSEGV和SIGBUS。第一個信號通常用于指示進程試圖訪問對它不可用的存儲區(qū),如果進程企圖存數據到mmap指定為只讀的映射區(qū),那么也會產生該信號。如果訪問映射區(qū)的一部分,該部分已經不存在了,則會產生第二個信號。例如進程映射了一個文件后,另外一個進程又將該文件截短了,那么該進程如果訪問被截去的部分時就會接收到第二個信號。最后,用fork產生子進程后,子進程會繼承存儲映射區(qū)(因為存儲映射區(qū)也是父進程地址空間的組成部分)。子進程調用exec執(zhí)行新程序后,不再繼承存儲映射區(qū)。

6.int mprotect(void *addr, size_t len, int prot)

該函數用來更改一個已映射存儲區(qū)的權限。聲明在sys/mman.h文件中。prot許可值與mmap函數的該參數一樣。addr地址必須是系統頁邊界對齊。

7.int msync(void *addr, size_t length, int flags)

如果修改了共享映射存儲區(qū)中的頁,就可以使用該函數將頁沖洗到被映射的文件中。該函數類似于fsync,但作用于映射存儲區(qū)。如果映射的存儲區(qū)是私有的,就不會修改被映射的文件。addr地址也必須和頁邊界對其。flags參數用來控制沖洗的方式,必須要指定MS_ASYNC和MS_SYNC二者中的一個。MS_ASYNC表示不等待沖洗完成,MS_SYNC表示等待沖洗完成后函數才返回(沖洗完成之前進程一直阻塞)。MS_INVALIDATE標志是可選標志,用來丟棄與磁盤沒有同步的任何頁(會將函數參數表示的地址范圍的所有頁丟棄),這一般不是期望的操作。聲明在sys/mman.h文件中。

8.int munmap(void *addr, size_t length)

當進程終止時,或者調用了該函數后,映射的存儲區(qū)會被自動解除。關閉文件描述符不會解除映射。聲明在sys/mman.h文件中。該函數調用時,不會將映射區(qū)的內容寫到磁盤文件中。對于MAP_SHARED標志后的映射區(qū),內容同步的工作由內核虛存算法自動進程,和該函數是沒有關系的。對于MAP_PRIVATE標志的映射區(qū),調用該函數解除后將被丟棄。

?

第15章 進程間通信

1.int pipe(int pipefd[2])

該函數用來創(chuàng)建管道。聲明在unistd.h文件中。pipefd數組將返回兩個文件描述符:pipefd[0]為讀而打開,pipefd[1]為寫而打開。向pipefd[1]描述符中寫入的數據,可以從pipefd[0]描述符中讀出。一般不在同一個進程中使用管道,因為沒有意義,而是在父進程和子進程之間使用管道。當用fork創(chuàng)建子進程后,子進程會繼承父進程已打開的文件描述符,因此也就繼承了管道。這時候父進程和子進程中總共有四個管道端口,它們之間都是通的,也就是說,向父進程的pipefd[1]寫入的數據既可以從父進程的pipefd[0]中讀出也可以從子進程的pipefd[0]中讀出;同理,向子進程的pipefd[1]寫入的數據既可以從子進程的pipefd[0]中讀出也可以從父進程的pipefd[0]中讀出。但是管道中的數據被讀一次后,就會被清除,不會保留。向管道中連續(xù)寫入的數據,第二次的數據回追加到第一次數據后面,而不會覆蓋第一次的數據。多個進程對同一個管道進行寫操作時,如果寫入的數據長度大于管道的尺寸PIPE_BUF時,可入的數據可能會穿插,如果寫入的數據小于PIPE_BUF,則沒有穿插問題(可用pathconf/fpathconf函數獲取PIPE_BUF值的大小)。一般在父子進程間使用管道,會關閉父進程的讀(或寫)端描述符,同時關閉子進程的寫(或讀)端描述符,使父子進程半雙工通信。當子進程執(zhí)行exec后,文件描述符會丟失,因此一般將管道端口描述符先重定向到標準輸入輸出描述符中,再使子進程執(zhí)行exec,這樣在子進程中就可繼續(xù)使用管道。當管道的一端被關閉后,下面兩個規(guī)則起作用:

a.當讀一個寫端已被關閉的管道時,在所有數據被讀取后,read返回0,表示到達了文件末尾。

b.如果寫一個讀端已被關閉的管道時,會產生SIGPIPE信號。如果忽略該信號或者捕捉該信號并從信號處理函數返回后,write函數返回-1,errno設置為EPIPE。

最后,a.歷史上的管道是半雙工的,b.管道只能在具有公共祖先的進程之間使用。

2.FILE *popen(const char *command, const char *type)

? ?int pclose(FILE *stream)

第一個函數用來創(chuàng)建一個子進程,同時,創(chuàng)建父子進程之間的管道(這和fork/system是不同的),返回管道的一個端口至父進程。該子進程執(zhí)行shell程序,然后再創(chuàng)建子進程執(zhí)行commad命令(在這點上類似于system函數)。type參數可以是“r”或者“w”,如果是r,管道的寫端將連接到commad進程的標準輸出,此時函數返回管道的讀端;如果w,管道的讀端將連接到commad進程的標準輸入,此時函數返回管道的寫端。

pclose函數用來關閉標準I/O流,等待命令執(zhí)行結束,然后返回shell的終止狀態(tài)。函數的返回值類似于system函數的返回值。由于pclose中會調用waitpid等待它所創(chuàng)建的所有子進程,因此如果在程序中,用戶自行設置了SIGCHILD的處理函數,并且函數中調用了wait等函數,則可能會影響pclose中的waitpid函數。他們均聲明在stdio.h文件中。

3.協同進程(coprocess):當一個進程產生某個過濾進程的輸入,同時又讀取該過濾進程的輸出,則稱該過濾進程為協同進程。實際上協同進程就是幫其它進程處理數據,當然要從其它進程中獲取要處理的數據,然后再將處理以后的數據返回給其它進程。

4.int mkfifo(const char *pathname, mode_t mode)

該函數用來創(chuàng)建一個FIFO文件。聲明在sys/stat.h文件中。創(chuàng)建一個FIFO類似與創(chuàng)建一個文件,因此該函數類似于open/creat函數。mode參數取值和open/creat的mode參數相同。創(chuàng)建FIFO文件的用戶和組權限規(guī)則同創(chuàng)建普通文件是相同的。創(chuàng)建好的FIFO文件可以使用一般的I/O函數(open,close,read,write,unlink等)來操作它。

當打開一個FIFO時:

a.如果沒有指定O_NONBLOCK,那么只讀open要阻塞到某個其它進程為寫而打開它;類似的,只寫open要阻塞到某個其它進程為讀而打開它。

b.如果指定了O_NONBLOCK,只讀open函數會立即返回(如果沒有其它進程寫FIFO);只寫open將返回-1,并設置errno為ENXIO(如果沒有其它進程為讀而打開FIFO)。

和管道類似,若用write寫一個尚無進程為讀而打開的FIFO,則產生信號SIGPIPE;若某個FIFO的最后一個寫進程關閉了該FIFO,則將為該FIFO的讀進程產生一個文件結束標志(如果以讀寫打開FIFO,最后一個寫進程關閉FIFO時,不會產生文件結束標志)。如果有多個進程寫同一個FIFO,如果不希望數據穿插,則需要考慮原子操作。和管道類似,常量PIPE_BUF說明了可被原子寫入FIFO的最大數據量。

FIFO有兩種用途:

a.FIFO由shell命令使用以便將數據從一條管道線傳送到另一條,為此無需創(chuàng)建中間臨時文件。

b.FIFO用于客戶進程---服務器進程應用程序中,以便在客戶進程和服務器進程之間傳遞數據。

5.key_t ftok(const char *pathname, int proj_id)

該函數用來為XSI IPC創(chuàng)建一個鍵(key)。聲明在sys/msg.h文件中。所謂XSI IPC,指的是消息隊列,信號量,共享存儲這三種進程間通信方式。不包括前邊提到的管道和FIFO。XSI IPC創(chuàng)建的IPC結構,不會因為進程的終止而消失,不像管道和FIFO,進程終止后管道就不存在了,FIFO雖然存在,但其數據已被清空。使用該函數創(chuàng)建鍵時,pathname參數所指定的文件必須存在,該函數將使用指定文件的stat結構中st_dev和st_ino字段和proj_id參數的后8位組合起來生成鍵。生成的鍵可供下邊的函數來創(chuàng)建XSI IPC結構。創(chuàng)建消息隊列,信號量,共享存儲所用的函數分別為msgget,semget,shmget,這三個函數都有兩個類似的參數:一個key(就是由ftok生成的鍵)和一個整型msgflag。可以用這三個函數創(chuàng)建新的IPC結構或者打開已存在的IPC結構。如若要創(chuàng)建新結構,需滿足下面兩個條件之一:

a.key值為IPC_PRIVATE

b.key當前未與特定類型的IPC結構相結合,并且msgflag終止定了IPC_CREAT位

如若需要打開已存在的IPC結構,key值必須等于創(chuàng)建IPC結構時所指定的鍵,并且msgflag中不應指定IPC_CREAT。為了打開已存在的IPC結構,key值決不能為IPC_PRIVATE,該值比較特殊,它總是用來創(chuàng)建一個新IPC結構。如果需要訪問用IPC_PRIVATE鍵創(chuàng)建的IPC結構,一定要知道該結構的標識符,然后用其他IPC調用(msgsnd和msgrcv)使用該標識符,而不能用msgget,semget,shmget三個函數來打開。如果需要創(chuàng)建一個新的IPC結構,而且要確保不是引用具有同一標識符的一個已存在IPC結構,需要在msgflag中同時指定IPC_CREAT和IPC_EXCL位,這樣的話,如果IPC結構已經存在就會造成出錯,返回EEXIST(這與指定了O_CREAT和O_EXCL標志的open函數類似)。消息隊列,信號量,共享存儲三者有各自的IPC結構,后邊會列出。這些IPC結構中包含了一個相同結構struct ipc_perm結構,裝有該IPC結構的權限和所有者。如下所示:

struct ipc_perm
? {
??? __key_t __key;?? ??? ??? ?/* Key.? */
??? __uid_t uid;?? ??? ??? ?/* Owner's user ID.? */
??? __gid_t gid;?? ??? ??? ?/* Owner's group ID.? */
??? __uid_t cuid;?? ??? ??? ?/* Creator's user ID.? */
??? __gid_t cgid;?? ??? ??? ?/* Creator's group ID.? */
??? unsigned short int mode;?? ??? ?/* Read/write permission.? */
?? ............

?? ...........
? };

uid,gid,cuid,cgid分別指定了擁有者ID,擁有者組ID,創(chuàng)建者ID,創(chuàng)建者組ID。mode字段指定了訪問者權限,如下所示:(IPC結構不需要執(zhí)行,因此沒有執(zhí)行權限)

用戶讀??????????????????? 0400

用戶寫(更改)????? 0200

組讀??????????????????????? 0040

組寫(更改)????????? 0020

其他讀??????????????????? 0004

其他寫(更改)????? 0002

6.int msgget(key_t key, int msgflg)

該函數用來創(chuàng)建一個新的消息隊列或者打開一個現存消息隊列,返回消息隊列標識符。后面對消息隊列進行操作的函數可以使用該標識符。該隊列ID可以聲明在sys/msg.h文件中。該函數會創(chuàng)建如下所示的結構體,并初始化一部分成員:msg_perm中的各個值;msg_qnum,msg_lspid,msg_lrpid,msg_stime,msg_ctime都設置為0;msg_ctime設置為當前時間;msg_qbytes設置為系統限制值。

struct msqid_ds
{
? struct ipc_perm msg_perm;?? ?/* structure describing operation permission */
? __time_t msg_stime;?? ??? ?/* time of last msgsnd command */
? .......

? __time_t msg_rtime;

? .......

? __time_t msg_ctime;

? .......

? __syscall_ulong_t __msg_cbytes;
? msgqnum_t msg_qnum;?? ??? ?/* number of messages currently on queue */
? msglen_t msg_qbytes;?? ??? ?/* max number of bytes allowed on queue */
? __pid_t msg_lspid;?? ??? ?/* pid of last msgsnd() */
? __pid_t msg_lrpid;?? ??? ?/* pid of last msgrcv() */
? .......

? .......
};

函數執(zhí)行成功,返回一個非負的隊列ID。該隊列可被用于其他三個消息隊列函數。

7.int msgctl(int msqid, int cmd, struct msqid_ds *buf)

該函數可以對消息隊列進行各種控制操作。聲明在sys/msg.h文件中。該函數是XSI IPC中類似于ioctl的函數(垃圾桶函數)。msqid是消息隊列ID(就是msgget函數返回的隊列描述符),cmd參數指定了隊列要執(zhí)行的命令:

a.IPC_STAT???????? 取此隊列的msqid_ds結構,并將它存放在buf指向的結構中

b.IPC_SET?????????? 按由buf指向結構中的值,設置與此隊列相關結構中的下列四個字段:msg_perm.uid,msg_perm.gid,msg_perm.mode和msg_qbytes。此命令只能由下         面兩種進程執(zhí)行:一種是其有效用戶ID等于msg_perm.cuid或msg_perm.uid,另一種是具有超級用戶權限的進程。此外,只有超級用戶才能增加     ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ?msg_qbytes的值。

c.IPC_RMID???????? 從系統中刪除該消息隊列以及仍在該對列中的所有數據。這種刪除立即生效。仍在使用這一消息隊列的其他進程在它們下一次試圖對此隊列進行操作時,?? ? ? ? ? ? ? ? ? ? ? ? ? ? ? ? 將出錯返回EIDRM。此命令只能由下列兩種進程執(zhí)行:一種是其有效用戶ID等于msg_perm.cuid或msg_perm.uid,另一種是具有超級用戶權限的進程。

8.int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg)

調用該函數將數據放到消息隊列中。聲明在sys/msg.h文件中。每個消息都有三部分組成,它們是:正長整型類型字段,非負長度(msgsz)以及實際數據字節(jié)(對應于長度)。消息總是放在隊列尾端。msgp參數指向一個長整型數,它包含了癥的整型消息類型,緊跟其后的是消息數據。(若msgsz是0,則無消息數據)若發(fā)送的最長消息是512字節(jié),則可定義下列結構:

struct mymesg {

  long mtype;

  char mtext[512];

};

然后,ptr就是一個指向該結構的指針。接收者可以使用消息類型以非先進先出的次序取消息。參數msgflg的值可以指定為IPC_NOWAIT,這類似于文件I/O的非阻塞標志。若消息隊列已滿(或者是隊列中的消息總數等于系統限制值,或對列中的字節(jié)總數等于系統限制值),則指定IPC_NOWAIT的話,msgsnd立即出錯并返回EAGAIN;沒有指定IPC_NOWAIT的話,進程將阻塞到下述情況出現為止:有空間可以容納要發(fā)送的消息;從系統中刪除了此隊列,或捕捉到一個信號,并從信號處理程序返回。第二種情況下,返回EIDRM(“標識符被刪除”)。第三種情況則返回EINTR。

需要注意的是,對刪除消息隊列的處理不是很完善,因為對每個消息隊列并沒有設置一個引用計數器(對打開文件則有這種計數器),所以刪除一個隊列會造成仍在使用這一隊列的進程在下次對隊列進行操作時出錯返回。信號量機制也是如此。而文件刪除就比較完善,當使用某個文件的最后一個進程關閉了它的文件描述符后,才能刪除文件內容。

當msgsnd成功返回后,與消息隊列相關的struct msqid_ds結構將得到更新,以標明發(fā)出該消息的進程ID(msg_lspid),進行該調用的時間(msg_stime),并指示隊列中增加了一條消息(msg_qnum增加1)。

9.ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg)

該函數從隊列中取出消息。聲明在sys/msg.h文件中。該函數的msqid參數和msgp參數和9中的函數類似。msgsz參數表示要接收的長度。如果返回的消息長度大于msgsz,并且msgflg中設置了MSG_NOERROR,則接收到的消息會被截短。(這種情況下,不會通知我們消息被截短了,消息截去的那一部分被丟棄)如果沒有設置MSG_NOERROR標志,而消息有太長,則函數出錯返回E2BIG(消息仍留在隊列中)。type參數用于指定想要取哪一種消息:

type == 0???? 返回隊列中的第一個消息

type > 0?????? 返回隊列中消息類型為type的第一個消息

type < 0?????? 返回隊列中消息類型值小于或等于|type|的消息,如果這種消息有若干個,則取類型值最小的消息。

type值非0用于以非先進先出次序讀消息。例如,若應用程序對消息賦優(yōu)先權,那么type就可以是優(yōu)先權值。如果一個消息隊列由若干個客戶進程和一個服務器進程使用,那么type字段可以用來包含客戶進程的進程ID(只要進程ID可以存放在長整型中)。

可以指定flag值為IPC_NOWAIT,使操作不阻塞。這使得如果沒有指定類型的消息,則msgrcv函數返回-1,errno設置為ENOMSG;如果沒有指定IPC_NOWAIT,則進程阻塞到如下情況出現才終止:有了指定類型的消息;從系統中刪除了此隊列(出錯則返回-1且errno置為EIDRM);或捕捉到一個信號并從信號處理程序返回(msgrcv函數返回-1,errno設置為EINTR)。

msgrcv函數成功執(zhí)行時,內核更新與該消息隊列相關聯的struct msqid_ds結構,已指示調用者的進程ID(msg_lrpid)和調用時間(msg_rtime),并將隊列中的消息數減1(msg_qnum)。

注意:當初實施消息隊列時,是因為其他形式IPC都是半雙工管道。而現在有了全雙工管道。因此,在新的應用程序中不應該再使用消息隊列。

10.

11

12

13.int shmget(key_t key, size_t size, int shmflg)

該函數用來創(chuàng)建或者打開一個共享存儲區(qū),返回存儲區(qū)標識符。聲明在sys/shm.h文件中。后邊對共享區(qū)操作的函數就可以使用該標識符。該函數的創(chuàng)建或者打開規(guī)則和msgget函數相同。當創(chuàng)建一個新段時,會創(chuàng)建一個相關的結構體struct shmid_ds,并初始化下列成員:shm_perm中的各個值;shm_lpid,shm_nattch,shm_atime,shm_dtime成員都設置為0;shm_ctime成員設置為當前時間;shm_segsz設置為請求長度size。

?struct shmid_ds
? {
??? struct ipc_perm shm_perm;?? ??? ?/* operation permission struct */
??? size_t shm_segsz;?? ??? ??? ?/* size of segment in bytes */
??? __time_t shm_atime;?? ??? ??? ?/* time of last shmat() */
 ........
??? __time_t shm_dtime;?? ??? ??? ?/* time of last shmdt() */
?? ........
??? __time_t shm_ctime;?? ??? ??? ?/* time of last change by shmctl() */
?? ........
??? __pid_t shm_cpid;?? ??? ??? ?/* pid of creator */
??? __pid_t shm_lpid;?? ??? ??? ?/* pid of last shmop */
??? shmatt_t shm_nattch;?? ??? ?/* number of current attaches */
?? .........
? };

size參數用來指定該共享存儲段的長度,實現通常將其向上取為系統頁長的整數倍。但是,如果應用指定的size值并非系統頁長的整數倍,則最后一頁的余下部分不可使用。如果正在創(chuàng)建一個新段,那么必須指定size值;如果正在引用一個現存段,則將size指定為0。當創(chuàng)建一個新段時,段內的內容初始化為0。

14.int shmctl(int shmid, int cmd, struct shmid_ds *buf)

該函數用來對指定的共享存儲段進行控制。聲明在sys/shm.h文件中。cmd指定了下面5中命令中的一種:

IPC_STAT????? ? ? 取此段的shmid_ds結構,并將它存放在由buf指向的結構中。?

IPC_SET??????????? 按buf指向結構中的值設置與此段相關結構中的下列三個字段:shm_perm.uid,shm_perm.gid和shm_perm.mode。此命令只能由下           ?       面兩種進程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shm_perm.uid,另一種是具有超級用戶權限的進程。

IPC_RMID????????? 從系統中刪除共享存儲段,因為每個共享存儲段有一個連接計數(shmid_ds結構中的shm_nattch字段),所以除非該段最后一個進程終止或與該段分離,否       ? ? 則不會實際上刪除該存儲段。不管此段是否仍在使用,該段標識符立即被刪除,所以不能再用shmat函數與該段連接。此命令只能由下           ?       面兩種進程執(zhí)行:一種是其有效用戶ID等于shm_perm.cuid或shm_perm.uid,另一種是具有超級用戶權限的進程。

IPC_LOCK???????? 將共享存儲段鎖定在內存中,此命令只能由超級用戶執(zhí)行。

IPC_UNLOCK??? 解鎖共享存儲段。此命令只能由超級用戶執(zhí)行。

15.void *shmat(int shmid, const void *shmaddr, int shmflg)

一旦創(chuàng)建了共享存儲段,就可以使用該函數將其連接到進程的地址空間中。聲明在sys/shm.h文件中。共享存儲段連接到調用進程的哪個地址上與shmaddr參數以及在flag中是否指定SHM_RND位有關:

a.如果shmaddr為0,則此段連接到由內核選擇的第一個可用地址上。推薦使用這種方式。(一般位于堆區(qū)和棧區(qū)中間的共享區(qū))

b.如果shmaddr非0,并且沒有指定SHM_RND,則此段連接到shmaddr所指定的地址上。

c.如果shmaddr非0,并且指定了SHM_RND,則此段連接到shmaddr mod ulus SHMLBA所表示的地址上。

如果在shmflag中指定了SHM_RDONLY位,則以只讀方式連接此段,否則以讀寫方式連接此段。shmat函數的返回值是該段所連接的實際地址,如果函數出錯返回-1。如果執(zhí)行成功那么內核將使該共享存儲段shmid_ds結構中的shm_nattch計數器值加1。

16.int shmdt(const void *shmaddr)

當今成使用完存儲共享區(qū)后,調用該函數是共享區(qū)與進程分離。聲明在sys/shm.h文件中。該操作并不從系統中刪除共享區(qū)標識符及其數據結構。該標志符讓然存在,直至某個進程調用shmctl(帶命令IPC_RMID)刪除它。shmaddr參數是共享區(qū)在進程中的起始地址。如果函數執(zhí)行成功,shmid_ds結構中的shm_nattch計數器值減1。

?小結:掌握管道和FIFO,因為在大量應用程序中仍可有效地使用這兩種基本技術。在新的應用程序中,盡量避免使用消息隊列以及信號量,而用全雙工管道和記錄鎖取代。共享存儲段有其應用場合,而mmap函數也能提供同樣的功能。

?

第16章 網絡IPC:套接字

1.int socket(int domain, int type, int protocol)

該函數用來創(chuàng)建一個套接字。聲明在sys/socket.h文件中。參數domain用來指定套接字使用的地址族,取值如下:

AF_INET??????????? ipv4因特網域

AF_INET6????????? ipv6因特網域

AF_UNIX??????????? unix域

AF_UNSPEC????? 未指定

參數type指定套接字的類型,取值如下:

SOCK_DGRAM??????????????? 長度固定的,無連接的不可靠報文傳遞(UDP)

SOCK_RAW????????????????????? IP協議的數據包接口

SOCK_SEQPACKET???????? 長度固定的,有序,可靠的面向連接報文傳遞

SOCK_STREAM??????????????? 有序,可靠,雙向的面向連接字節(jié)流(TCP)

參數protocol通常為零,表示按給定的域和套接字類型選擇默認的協議。當對同一域和套接字類型支持多個協議時,可以使用protocol參數選擇一個特定協議。在AF_INET通信域中套接字類型SOCK_STREAM的默認協議是TCP,在AF_INET通信域中套接字類型SOCK_DGRAM的默認協議是UDP。

socket函數創(chuàng)建的套接字描述符可用普通文件操作函數來操作,但并適用所有的文件操作函數。

2.int shutdown(int sockfd, int how)

該函數用來關閉套接字的輸入/輸出端口。聲明在sys/socket.h文件中。如果參數how是SHUT_RD(關閉讀端),那么無法從套接字讀取數據(但仍可以讀);如果參數how是SHUT_WR(關閉寫端),那么無法使用套接字發(fā)送數據(但仍可以寫);使用SHUT_RDWR則將無法同時讀取和發(fā)送數據。需要注意的是,close函數雖然可以關閉套接字,但是如果使用dup2復制了套接字描述符,那么直到最后一個套接字描述符被關閉后,套接字才會被釋放;而shutdown函數是一個套接字處于不活動狀態(tài),無論它的描述符有多少。

3.uint32_t htonl(uint32_t hostlong)

?? uint16_t htons(uint16_t hostshort)

?? uint32_t ntohl(uint32_t netlong)

?? uint16_t ntohs(uint16_t netshort)

這四個函數用來完成本地字節(jié)序和網絡字節(jié)序之間的轉換。聲明在arpa/inet.h文件中。n代表network,h代表host,l代表long,s代表short。奔騰處理器上的Linux系統采用小端字節(jié)序。

4.地址格式

為了是不同格式地址能夠被傳入到套接字函數,地址會被強制轉換成通用的地址結構struct sockaddr。

Linux對該通用地址結構的實現如下(/usr/include/i386/bits/socket.h):

struct sockaddr
? {
??? __SOCKADDR_COMMON (sa_);?? ?/* Common data: address family and length.? */?? /* #define __SOCKADDR_COMMON(sa_)? \ sa_family_t sa_prefix##family */
??? char sa_data[14];?? ??? ?/* Address data.? */
? };

Linux中IPV4的套接字地址sockaddr_in格式如下:

struct sockaddr_in
? {
??? __SOCKADDR_COMMON (sin_);
??? in_port_t sin_port;?? ??? ??? ?/* Port number.? */??????? /*?typedef uint16_t in_port_t; */
??? struct in_addr sin_addr;?? ??? ?/* Internet address.? */??????? /*?typedef uint32_t in_addr_t; */

??? /* Pad to size of `struct sockaddr'.? */
??? unsigned char sin_zero[sizeof (struct sockaddr) -
?? ??? ??? ??? __SOCKADDR_COMMON_SIZE -
?? ??? ??? ??? sizeof (in_port_t) -
?? ??? ??? ??? sizeof (struct in_addr)];
? };

struct in_addr
? {
??? in_addr_t s_addr;
? };

Linux中IPV6的套接字地址sockaddr_in6格式如下:

struct sockaddr_in6
? {
??? __SOCKADDR_COMMON (sin6_);
??? in_port_t sin6_port;?? ?/* Transport layer port # */
??? uint32_t sin6_flowinfo;?? ?/* IPv6 flow information */
??? struct in6_addr sin6_addr;?? ?/* IPv6 address */
??? uint32_t sin6_scope_id;?? ?/* IPv6 scope-id */
? };

struct in6_addr
? {
??? union
????? {
?? ?uint8_t?? ?__u6_addr8[16];
#if defined __USE_MISC || defined __USE_GNU
?? ?uint16_t __u6_addr16[8];
?? ?uint32_t __u6_addr32[4];
#endif
????? } __in6_u;
#define s6_addr?? ??? ??? ?__in6_u.__u6_addr8
#if defined __USE_MISC || defined __USE_GNU
# define s6_addr16?? ??? ?__in6_u.__u6_addr16
# define s6_addr32?? ??? ?__in6_u.__u6_addr32
#endif
? };

在使用時,無論是ipv4還是ipv6,均要被強制轉換成struct sockaddr_in結構傳入到套接字例程中。

5.in_addr_t inet_addr(const char *cp)

? ?char *inet_ntoa(struct in_addr in)

?? const char *inet_ntop(int af, const void *src, char *dst, socklen_t size)

?? int inet_pton(int af, const char *src, void *dst)

這些函數用于二進制地址格式和點分十進制字符串格式之間的相互轉換。它們均聲明在arpa/inet.h文件中。前兩個函數只能用于IPV4地址。后兩個新函數支持IPV4和IPV6地址。第一和第三個函數將點分十進制字符串轉換成二進制地址,第二和第四個函數剛好相反。后兩個函數的af參數取值為:AF_INET和AF_INET6,分別代表ipv4域和ipv6域。第三個函數的size參數指定了保存文本字符串緩沖區(qū)(dst)的大小,有兩個常數用于簡化工作:INET_ADDRSTRLEN和INET6_ADDRSTRLEN,前者可放ipv4地址,后者可放ipv6地址。對于第四個函數,dst參數指向的地址空間必須足夠大,以便能夠存入32位地址(當af指定為AF_INET)和128位地址(當af指定為AF_INET6)。

6.struct hostent *gethostent(void)?

?? void sethostent(int stayopen)

?? void endhostent(void)

調用這些函數可以獲取計算機的主機信息。均聲明在netdb.h文件中。第一個函數會打開數據庫文件(如果該文件沒有打開),如果文件是打開的,則該函數返回文件的下一條目;第二個函數也可以打開數據庫文件,如果文件是打開的,則將其回繞。第三個函數關閉數據庫文件。第一個函數返回的struct hostent結構如下:

struct hostent {
?????????????? char? *h_name;??????????? /* official name of host */
?????????????? char **h_aliases;???????? /* alias list */
?????????????? int??? h_addrtype;??????? /* host address type */
?????????????? int??? h_length;????????? /* length of address */
?????????????? char **h_addr_list;?????? /* list of addresses */??????
?????????? }
7.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)

這幾個函數用來獲取網絡名字和網絡號。均聲明在netdb.h文件中。將網絡名字和網絡號包含在struct netent結構體中,該結構體如下:

struct netent {
?????????????? char????? *n_name;???? /* official network name */
?????????????? char???? **n_aliases;? /* alias list */
?????????????? int??????? n_addrtype; /* net address type */
?????????????? uint32_t?? n_net;????? /* network number */??????? //返回的地址采用網絡字節(jié)序。
?????????? }
8.struct protoent *getprotobyname(const char *name)

?? struct protoent *getprotobynumber(int proto)

?? struct protoent *getprotoent(void)

?? void setprotoent(int stayopen)

?? void endprotoent(void)

這幾個函數用來獲取協議名字和協議號。均聲明在netdb.h文件中。將協議名字和協議號包含在struct protoent結構體中,該結構體如下:

struct protoent {
?????????????? char? *p_name;?????? /* official protocol name */
?????????????? char **p_aliases;??? /* alias list */
?????????????? int??? p_proto;????? /* protocol number */
?????????? }
9.struct servent *getservbyname(const char *name, const char *proto)

?? struct servent *getservbyport(int port, const char *proto)

?? struct servent *getservent(void)

?? void setservent(int stayopen)

?? void endservent(void)

這幾個函數用來獲取服務名字和服務的端口號。均聲明在netdb.h文件中。返回的struct servent結構如下:

struct servent {
?????????????? char? *s_name;?????? /* official service name */
?????????????? char **s_aliases;??? /* alias list */
?????????????? int??? s_port;?????? /* port number */
?????????????? char? *s_proto;????? /* protocol to use */
?????????? }
10.int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res)

  const char *gai_strerror(int errcode)

???? void freeaddrinfo(struct addrinfo *res)

第一個函數通過主機名字或服務名字來獲取主機的地址信息。聲明在netdb.h文件中。主機可能有多個地址,因此返回一個節(jié)點類型為struct addrinfo的鏈表res。struct addrinfo結構如下:

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;
?????????? };

hints參數是一個過濾地址的模板,用來選擇特定的地址。它僅使用ai_family,ai_flags,ai_protocol和ai_socktype字段。剩余的整數字段必須為0,指針字段必須為空。ai_flags的使用的標志參考《apue》。如果該函數失敗,不能使用perror或strerror函數來生成錯誤信息,而要使用第二個的函數。第二個函數用來釋放這些鏈表節(jié)點。

11.int getnameinfo(const struct sockaddr *sa, socklen_t salen, char *host, size_t hostlen, char *serv, size_t servlen, int flags)

該函數將套接字地址轉換成主機名或服務器名。聲明在netdb.h文件中。如果host非空,它指向一個長度為hostlen字節(jié)的緩沖區(qū)用于存儲返回的主機名;如果serv非空,它指向一個長度為servlen字節(jié)的緩沖區(qū)用于存儲返回的服務名。flags參數指定一些轉換的控制方式,取值參考《apue》。

12.?int bind(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

該函數將地址綁定到一個套接字上。聲明在sys/socket.h文件中。參數addr有以下規(guī)則:

a.在進程所運行的機器上,指定的地址必須有效,不能指定一個其他機器上的地址。

b.地址必須和創(chuàng)建套接字時的地址族所支持的格式相匹配。

c.端口號必須不小于1024,除非該進程具有超級用戶特權。

對于因特網域(AF_INET/AF_INET6),如果指定IP地址為INADDR_ANY,套接字端點可以被綁定到系統的所有網絡接口上,這意味著可以收到這個系統所安裝的所有網卡的數據。一般情況下,與客戶端套接字關聯的地址沒有太大意義,可以讓系統選擇一個默認地址;服務器端需要綁定地址一個眾所周知的地址,使得客戶端可以進行連接。另外,如果沒有使用bind綁定地址,那么當調用connect或listen時,系統會自動將一個地址綁定到套接字上。

13.int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

調用該函數來獲得綁定到套接字上的一個地址。聲明在sys/socket.h文件中。調用該函數時,addrlen參數指定了addr緩沖區(qū)的大小;函數返回時,該參數被設置成緩沖區(qū)實際被使用的大小。如果提供的緩沖區(qū)和地址的實際大小不匹配,則將其截斷而不報錯。如果套接字當前沒有綁定地址,函數結果沒有意義。

14.int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

如果套接字已經和對方連接,調用該函數可以獲得對方的地址。聲明在sys/socket.h文件中。該函數和13中的函數類似。

15.int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen)

該函數在sockfd套接字和addr指定的服務器之間建立一個連接。聲明在sys/socket.h文件中。使用該函數建立連接時,可能會失敗,因此應用程序必須能夠處理該函數返回的錯誤,這些錯誤可能由一些瞬時變化條件引起(這在一些負載很重的服務器上很可能發(fā)生)。關于函數返回的錯誤值類型和原因,參考man手冊。另外,對于UDP套接字調用該函數的話,所有發(fā)送報文的目標地址被設置為該函數中指定的地址,這樣每次傳送報文的時候就不許要再提供地址,此外,也僅能接收來自指定地址的報文。

16.int listen(int sockfd, int backlog)

服務器端調用該函數監(jiān)聽套接字,來宣告可以接受客戶端的連接請求。聲明在sys/socket.h文件中。backlog參數指定了該套接字能連接的最大數量。一旦超過了最大值,服務器會拒絕多余連接請求。

17.int accept(int sockfd, struct sockaddr *addr, socklen_t *addrlen)

一旦服務器調用了listen,就可調用該函數獲得連接請求并建立連接。聲明在sys/socket.h文件中。該函數返回一個套接字描述符,該套接字連接到了客戶端。返回的套接字和原始套接字sockfd具有相同的套接字類型和地址族。而原始套接字sockfd并沒有關聯到客戶端的套接字,而是繼續(xù)保持可用狀態(tài)并接受其他連接請求。如果不關心客戶端的地址,則可將addr參數和addrlen置為null。否則,addr設為足夠大的緩沖區(qū)來存放客戶端的地址信息,len指定了該緩沖區(qū)的大小。返回時,函數會在緩沖區(qū)填充客戶端地址信息并更新len的大小。

如果沒有連接請求,accept函數會阻塞到直到一個請求到來。如果sockfd設置為非阻塞模式,accept函數會返回-1并將errno設置為EAGAIN或EWOULDBLOCK。此外,服務器端可以使用poll或者select函數來等待一個請求的到來,這時,一個請求套接字會以可讀方式出現。

18.ssize_t send(int sockfd, const void *buf, size_t len, int flags)

???? ssize_t sendto(int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen)

???? ssize_t sendmsg(int sockfd, const struct msghdr *msg, int flags)

這三個函數用來發(fā)送數據。聲明在sys/socket.h文件中。第一個函數的用法和write類似,用于TCP套接字發(fā)送數據(UDP套接字不能使用它來發(fā)送),flags標志取值如下:

MSG_DONTROUTE??????? 勿將數據路由出本地網絡

MSG_DONTWAIT??????????? 允許非阻塞操作(等價于使用了O_NONBLOCK)

MSG_EOR?????????????????? ? ? 如果協議支持,此為記錄結束 ??

MSG_OOB?????????????????????? 如果協議支持,發(fā)送帶外數據

如果send成功返回,并不代表連接的另一端接收到了數據,所保證的僅是當send成功返回時,數據已經無錯誤的發(fā)送到網絡上。

UDP套接字可以使用第二個函數來發(fā)送數據,dest_addr參數指定了目標地址。TCP套接字如果使用第二個函數來發(fā)送數據的話,會忽略目標地址。

第三個函數用來發(fā)送由struct msghdr結構體指定的多重緩沖區(qū)的數據,該函數和writev函數類似。struct msghdr結構體如下:

struct msghdr {
?????????????? void???????? *msg_name;?????? /* optional address */
?????????????? socklen_t???? msg_namelen;??? /* size of address */
?????????????? struct iovec *msg_iov;??????? /* scatter/gather array */
?????????????? size_t??????? msg_iovlen;???? /* # elements in msg_iov */
?????????????? void???????? *msg_control;??? /* ancillary data, see below */
?????????????? size_t??????? msg_controllen; /* ancillary data buffer len */
?????????????? int?????????? msg_flags;????? /* flags on received message */
?????????? };

19.ssize_t recv(int sockfd, void *buf, size_t len, int flags)

???? ssize_t recvfrom(int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen)

???? ssize_t recvmsg(int sockfd, struct msghdr *msg, int flags)

這三個函數用來接收數據。聲明在sys/socket.h文件中。第一個函數的用法和read函數類似,flags標志取值如下:

MSG_OOB?????? ? ? ? ? 如果協議支持,接收帶外數據

MSG_PEEK????????????? 返回報文內容而不真正取走報文

MSG_TRUNC?????????? 即使報文被截斷,要求返回的是報文的實際長度

MSG_WAITALL???????? 等待直到接收完len指定的數據長度(僅SOCK_STREAM)

當指定為MSG_PEEK標志時,可以查看下一個要讀的數據但不會真正取走,當再次調用read或recv函數時會返回剛才查看到的數據。

對于SOCK_STREAM套接字(TCP套接字),默認情況下接收到數據可以比請求的少,但是如果設置了MSG_WAITALL標志,則直到接收完len指定的數據長度,recv函數才返回。對于SOCK_DRAM和SOCK_SEQPACKET套接字(它們是基于報文而不是字節(jié)流的),MSG_WAITALL標志沒有改變什么行為,因為這些套接字本身就是一次取走整個報文。如果發(fā)送者調用shudown來結束傳輸,或者發(fā)送端已經關閉,那么當所有數據接收完畢后,recv返回0。

第二個函數可以獲得發(fā)送者的套接字端點地址(src_addr和addrlen必須非空),該函數通常用于UDP套接字,也可以用于TCP套接字(這時等同于recv)。

第三個函數將接收到的數據送入多個緩沖區(qū)(類似于readv),也可以接收輔助數據。msg參數指定了用于接收數據的緩沖區(qū)。可以設置參數flags來改變recvmsg的默認行為,返回時,msghdr結構中的msg_flags字段被設置為所接收數據的各種特征(進入recvmsg時的msg_flags被忽略),這些返回的msg_flags標志如下:

MSG_CTRUNC???????? 控制數據被截斷

MSG_DONTWAIT???? recvmsg處于非阻塞模式

MSG_EOR??????????????? 接收到記錄結束符

MSG_OOB??????????????? 接收到帶外數據

MSG_TRUNC??????????? 一般數據被截斷

20.int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen)?

該函數用來設置套接字選項。聲明在sys/socket.h文件中。套接字有三個層次的選項(XSI僅定義了前兩個選項):

a.通用選項,工作在所有套接字類型上。

b.在套接字層次管理的選項,但是依賴于下層協議的支持。

c.特定于某協議的選項,為每個協議所獨有。

參數level標識了選項的層次,如果是通用層次的選項,level設置成SOL_SOCKET;否則,level設置成套接字層次的選項,例如,對于TCP選項,level是IPPROTO_TCP,對于IP選項,level是IPPROTO_TCP。參數optname代表通用層次和套接字層次的選項,參考《apue》。參數optval根據選項的不同指向一個數據結構或者一個整數。一些選項是on/off開關,當valopt整數非0,選項被啟用,當optval整數為0,選項被禁止。參數optlen指定了optval指向對象的大小。

21.int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen)

該函數用來獲取套接字當前的選項。聲明在sys/socket.h文件中。該函數調用時,optlen設置成optval緩沖區(qū)大小,函數返回時,optlen被更新為實際尺寸。如果實際尺寸大于optlen初始設置值,選項會被截斷而不報錯。

22.帶外數據:帶外數據又叫做緊急數據,TCP協議支持一個字節(jié)的帶外數據,UDP不支持帶外數據。在三個send函數中指定了標志MSG_OOB,就可以傳輸待外數據,傳送的若干個字節(jié)中最后一個字節(jié)就是帶外數據。此外,接收進程可以調用fcntl(sockfd,F_SETOWN,pid)函數來設置pid進程對套接字信號進行處理。當接收進程接收到帶外數據時,pid指定的進程就會收到一個套接字信號。調用owner = fcntl(sockfd,F_GETOWN,0)可以獲得處理套接字信號的進程ID。

23.int sockatmark(int sockfd)

TCP協議支持緊急標記(在普通數據流中緊急數據所處的位置),如果采用套接字選項SO_OOBINLINE,那么可以在普通數據中接收緊急數據。當系一個要讀的字節(jié)是緊急數據的話,該函數返回1。

此外,當帶外數據出現在套接字讀取隊列時,select函數會返回一個文件描述符并且擁有一個異常狀態(tài)。可以在普通數據流上接受緊急數據,或者在某個recv函數中采用MSG_OOB標志在其他隊列數據之前接收緊急數據。TCP隊列僅有一字節(jié)的緊急數據,如果在接收當前的緊急數據之前又有新的緊急數據到來,那么當前的字節(jié)就成了普通字節(jié)(新的最后一個字節(jié)是緊急數據)。

24.套接字非阻塞和基于套接字的異步I/O

通常,recv函數沒有數據可用時會阻塞等待。同樣地,當套接字輸出隊列沒有足夠空間來發(fā)送消息send函數也會阻塞。在套接字非阻塞模式下,這些函數不會阻塞而是失敗,設置errno為EWOULDBLOCK或者EAGAIN。非阻塞模式下,可以使用poll或select來判斷套接字何時能接收或者傳輸數據。

在基于套接字的異步I/O中,當能夠從套接字中讀取數據,或者套接字寫隊列中的空間變得可用時,可以安排發(fā)送信號SIGIO。通過兩個步驟來使用異步I/O:

a.建立套接字擁有者關系,信號可以被傳送到合適的進程

b.設置套接字選項(使用fcntl),使得套接字可用時(可讀或可寫)發(fā)信號告知

有三種方式完成步驟a:

aa.在fcntl中使用F_SETOWN命令

bb.在ioctl中使用FIOSETOWN命令

cc.在ioctl中使用SIOCSPGRP命令

有兩種方式完成步驟b:

aa.在fcntl中使用F_SETFL命令并且啟用文件標志O_ASYNC。

bb.在ioctl中使用FIOASYNC。

轉載于:https://www.cnblogs.com/liangning/p/3959903.html

總結

以上是生活随笔為你收集整理的《APUE》中的函数整理的全部內容,希望文章能夠幫你解決所遇到的問題。

如果覺得生活随笔網站內容還不錯,歡迎將生活随笔推薦給好友。

熟女俱乐部五十路六十路av | 亚洲色大成网站www国产 | 国产乱人伦av在线无码 | 无码av岛国片在线播放 | 在线成人www免费观看视频 | 粗大的内捧猛烈进出视频 | 97久久精品无码一区二区 | 丝袜 中出 制服 人妻 美腿 | 亚洲国产精品久久久天堂 | 国产精品va在线观看无码 | 日本护士毛茸茸高潮 | 色爱情人网站 | 久久久久se色偷偷亚洲精品av | 亚洲人成影院在线无码按摩店 | 亚洲色大成网站www | 97夜夜澡人人爽人人喊中国片 | 思思久久99热只有频精品66 | 国产性生大片免费观看性 | 久精品国产欧美亚洲色aⅴ大片 | 黑人巨大精品欧美黑寡妇 | 久久人人爽人人爽人人片av高清 | 九月婷婷人人澡人人添人人爽 | 日韩人妻无码一区二区三区久久99 | 76少妇精品导航 | 国产色在线 | 国产 | 日本va欧美va欧美va精品 | 丰腴饱满的极品熟妇 | 久9re热视频这里只有精品 | 精品偷拍一区二区三区在线看 | 亚洲日本一区二区三区在线 | 亚洲а∨天堂久久精品2021 | 亚洲乱码国产乱码精品精 | 色欲av亚洲一区无码少妇 | 国产成人综合在线女婷五月99播放 | 欧美精品国产综合久久 | 亚洲精品国产品国语在线观看 | 久久99精品国产.久久久久 | 丰满肥臀大屁股熟妇激情视频 | 爽爽影院免费观看 | 欧美日本日韩 | 狂野欧美激情性xxxx | 成人三级无码视频在线观看 | 国产精品18久久久久久麻辣 | 蜜桃视频韩日免费播放 | 亚洲国精产品一二二线 | а√天堂www在线天堂小说 | 国产免费久久久久久无码 | 在线 国产 欧美 亚洲 天堂 | 亚洲乱码国产乱码精品精 | 亚洲一区二区三区含羞草 | 中文无码成人免费视频在线观看 | 欧美肥老太牲交大战 | 亚洲精品www久久久 | 乱人伦中文视频在线观看 | 国内少妇偷人精品视频免费 | 激情综合激情五月俺也去 | 成人综合网亚洲伊人 | 久精品国产欧美亚洲色aⅴ大片 | 国产卡一卡二卡三 | 国产精品亚洲专区无码不卡 | 丰满妇女强制高潮18xxxx | 99久久人妻精品免费一区 | 女人被男人爽到呻吟的视频 | 丰满人妻精品国产99aⅴ | 丰满妇女强制高潮18xxxx | 女人和拘做爰正片视频 | 99国产欧美久久久精品 | 中文字幕人成乱码熟女app | 99久久婷婷国产综合精品青草免费 | 国产亚洲日韩欧美另类第八页 | 欧美性黑人极品hd | 丰满护士巨好爽好大乳 | 日韩精品一区二区av在线 | 免费观看又污又黄的网站 | 人人妻人人澡人人爽欧美一区九九 | 久久久久久av无码免费看大片 | 亚洲爆乳大丰满无码专区 | 日本护士xxxxhd少妇 | 99久久婷婷国产综合精品青草免费 | 久久久久av无码免费网 | 亚洲中文字幕av在天堂 | 麻豆果冻传媒2021精品传媒一区下载 | 免费国产黄网站在线观看 | 久久国产精品萌白酱免费 | 久久国产精品_国产精品 | 性色欲网站人妻丰满中文久久不卡 | 久久人人爽人人人人片 | 久久www免费人成人片 | 2020久久超碰国产精品最新 | 国产一区二区三区影院 | 国产两女互慰高潮视频在线观看 | 欧美人与禽zoz0性伦交 | 成熟女人特级毛片www免费 | 日本精品少妇一区二区三区 | 国产婷婷色一区二区三区在线 | 鲁鲁鲁爽爽爽在线视频观看 | 天堂а√在线地址中文在线 | 国产偷自视频区视频 | 丝袜 中出 制服 人妻 美腿 | 国产成人无码a区在线观看视频app | 任你躁在线精品免费 | 国产成人精品优优av | 女人被男人爽到呻吟的视频 | 欧美精品无码一区二区三区 | 成年女人永久免费看片 | 亚欧洲精品在线视频免费观看 | 国产精品毛多多水多 | 国产激情艳情在线看视频 | 国语精品一区二区三区 | 少妇一晚三次一区二区三区 | 午夜精品久久久内射近拍高清 | 爽爽影院免费观看 | 久久人人爽人人人人片 | 亚洲天堂2017无码 | 老子影院午夜伦不卡 | 欧美性猛交内射兽交老熟妇 | 中文字幕中文有码在线 | 国产精品无码mv在线观看 | 日本又色又爽又黄的a片18禁 | 久久 国产 尿 小便 嘘嘘 | 青青青爽视频在线观看 | 久久久久99精品成人片 | 亚洲欧美色中文字幕在线 | 奇米影视888欧美在线观看 | 免费无码一区二区三区蜜桃大 | 欧美freesex黑人又粗又大 | 极品尤物被啪到呻吟喷水 | 香港三级日本三级妇三级 | 撕开奶罩揉吮奶头视频 | 大肉大捧一进一出好爽视频 | 高中生自慰www网站 | 九九热爱视频精品 | 亚洲 高清 成人 动漫 | 国内少妇偷人精品视频 | 午夜熟女插插xx免费视频 | 久久久久国色av免费观看性色 | 国产av久久久久精东av | 成人女人看片免费视频放人 | 成熟人妻av无码专区 | 小泽玛莉亚一区二区视频在线 | 久久精品国产亚洲精品 | 色诱久久久久综合网ywww | 99精品无人区乱码1区2区3区 | 蜜臀aⅴ国产精品久久久国产老师 | 久久久中文字幕日本无吗 | 亚洲日本va中文字幕 | 免费网站看v片在线18禁无码 | 国产精品美女久久久网av | 中文字幕亚洲情99在线 | 亚洲a无码综合a国产av中文 | 中文字幕日产无线码一区 | 久久综合狠狠综合久久综合88 | 中文字幕精品av一区二区五区 | 天天av天天av天天透 | 成人一区二区免费视频 | 欧美激情一区二区三区成人 | 最新国产乱人伦偷精品免费网站 | 丰满少妇人妻久久久久久 | 国产精品毛片一区二区 | 99久久久无码国产aaa精品 | 免费人成在线观看网站 | 国产美女精品一区二区三区 | 未满成年国产在线观看 | 人人妻人人澡人人爽欧美精品 | 精品无码国产一区二区三区av | 国产成人久久精品流白浆 | 日本在线高清不卡免费播放 | 少妇邻居内射在线 | 少妇无码av无码专区在线观看 | 精品国产福利一区二区 | 97精品国产97久久久久久免费 | 97人妻精品一区二区三区 | 少妇性荡欲午夜性开放视频剧场 | 精品久久久中文字幕人妻 | 国产无遮挡又黄又爽免费视频 | 熟妇人妻中文av无码 | 久久熟妇人妻午夜寂寞影院 | 麻豆成人精品国产免费 | 国产一区二区三区四区五区加勒比 | 亚洲成a人片在线观看无码3d | 亚洲中文字幕无码中文字在线 | 性史性农村dvd毛片 | 最近中文2019字幕第二页 | 一本久久伊人热热精品中文字幕 | 人妻少妇被猛烈进入中文字幕 | 精品国产av色一区二区深夜久久 | 亚洲七七久久桃花影院 | 免费视频欧美无人区码 | 人人妻人人澡人人爽人人精品浪潮 | 无码av免费一区二区三区试看 | 国产黄在线观看免费观看不卡 | 人人妻人人澡人人爽人人精品浪潮 | 久久国产自偷自偷免费一区调 | 久久综合九色综合97网 | 1000部夫妻午夜免费 | 在线亚洲高清揄拍自拍一品区 | 国产片av国语在线观看 | 久久精品中文闷骚内射 | 国产做国产爱免费视频 | 国产在线精品一区二区高清不卡 | 成人毛片一区二区 | 丁香啪啪综合成人亚洲 | 少妇被黑人到高潮喷出白浆 | 一本大道久久东京热无码av | 欧美 亚洲 国产 另类 | 妺妺窝人体色www婷婷 | 国产精品久久久久7777 | 国产国产精品人在线视 | 在线а√天堂中文官网 | 精品厕所偷拍各类美女tp嘘嘘 | 亚洲男女内射在线播放 | 国产高清av在线播放 | 亚洲码国产精品高潮在线 | 日本欧美一区二区三区乱码 | 精品偷自拍另类在线观看 | 女人被爽到呻吟gif动态图视看 | 亚洲日韩av一区二区三区中文 | 精品无人国产偷自产在线 | 亚洲日本va午夜在线电影 | 亚洲精品综合五月久久小说 | 在线 国产 欧美 亚洲 天堂 | 99riav国产精品视频 | 纯爱无遮挡h肉动漫在线播放 | 亚洲国产成人a精品不卡在线 | 永久免费精品精品永久-夜色 | 夜先锋av资源网站 | 久久久久99精品成人片 | 亚洲精品无码人妻无码 | 无码吃奶揉捏奶头高潮视频 | 无码av最新清无码专区吞精 | 国产精品第一国产精品 | 日本熟妇人妻xxxxx人hd | 久久国产精品偷任你爽任你 | 国产无av码在线观看 | 亚洲精品久久久久久久久久久 | 成人性做爰aaa片免费看 | 亚洲熟妇色xxxxx亚洲 | 国产精品亚洲综合色区韩国 | 无码人妻丰满熟妇区五十路百度 | 一个人看的视频www在线 | 51国偷自产一区二区三区 | 999久久久国产精品消防器材 | 亚洲日本在线电影 | 丰满诱人的人妻3 | 国产三级久久久精品麻豆三级 | 亚洲国产精品一区二区美利坚 | 97久久国产亚洲精品超碰热 | 中文字幕av无码一区二区三区电影 | 国产亚洲精品精品国产亚洲综合 | 久久精品国产日本波多野结衣 | 国产精品无码mv在线观看 | 日韩人妻少妇一区二区三区 | 欧美老熟妇乱xxxxx | 亚洲 激情 小说 另类 欧美 | 日韩人妻无码中文字幕视频 | 亚洲s码欧洲m码国产av | 中文字幕人妻无码一区二区三区 | 强开小婷嫩苞又嫩又紧视频 | 国产人妻人伦精品1国产丝袜 | 亚洲中文字幕在线无码一区二区 | 国内精品久久久久久中文字幕 | 国产综合色产在线精品 | 国产精品对白交换视频 | 性生交大片免费看l | 在线播放亚洲第一字幕 | 久久 国产 尿 小便 嘘嘘 | 人妻有码中文字幕在线 | 永久免费观看美女裸体的网站 | 国产在热线精品视频 | 国产又爽又黄又刺激的视频 | 亚拍精品一区二区三区探花 | 久久精品一区二区三区四区 | 国色天香社区在线视频 | 美女黄网站人色视频免费国产 | av人摸人人人澡人人超碰下载 | 99re在线播放 | 国产精品资源一区二区 | 亚洲精品午夜国产va久久成人 | 亚洲国产精品一区二区美利坚 | 18精品久久久无码午夜福利 | √8天堂资源地址中文在线 | 国产在线一区二区三区四区五区 | 日本乱人伦片中文三区 | 亚洲人亚洲人成电影网站色 | 超碰97人人射妻 | 婷婷五月综合缴情在线视频 | 亚洲va中文字幕无码久久不卡 | 2020最新国产自产精品 | 少妇高潮喷潮久久久影院 | 国产精品美女久久久网av | 日本一卡2卡3卡4卡无卡免费网站 国产一区二区三区影院 | 精品国产精品久久一区免费式 | 久久综合网欧美色妞网 | 国产精品美女久久久久av爽李琼 | 99麻豆久久久国产精品免费 | 亚洲区小说区激情区图片区 | 国产乱人无码伦av在线a | 极品嫩模高潮叫床 | 伊人久久婷婷五月综合97色 | 亚洲国产精品无码一区二区三区 | 无码免费一区二区三区 | 久久久精品国产sm最大网站 | 久久国产精品_国产精品 | 久久久国产一区二区三区 | 人妻中文无码久热丝袜 | 国产精品人人妻人人爽 | 亚洲综合无码久久精品综合 | 国产偷抇久久精品a片69 | 国产亚洲精品久久久久久久久动漫 | 亚洲 a v无 码免 费 成 人 a v | 中文字幕中文有码在线 | 玩弄中年熟妇正在播放 | 欧美性色19p | 欧美人与善在线com | 天堂а√在线地址中文在线 | 国产激情精品一区二区三区 | 午夜精品久久久久久久久 | 国产成人无码午夜视频在线观看 | 精品久久久无码中文字幕 | 色综合久久久久综合一本到桃花网 | 国产成人人人97超碰超爽8 | 99久久精品午夜一区二区 | 久久综合给久久狠狠97色 | 日韩精品一区二区av在线 | 成人无码视频在线观看网站 | 午夜福利一区二区三区在线观看 | 亚洲国产午夜精品理论片 | 免费无码的av片在线观看 | 蜜桃视频插满18在线观看 | 中文字幕日韩精品一区二区三区 | 一区二区三区乱码在线 | 欧洲 | 国产凸凹视频一区二区 | 日本一区二区三区免费高清 | aⅴ亚洲 日韩 色 图网站 播放 | 特黄特色大片免费播放器图片 | 色一情一乱一伦 | 久久久久成人精品免费播放动漫 | 亚洲精品久久久久avwww潮水 | 无码人妻出轨黑人中文字幕 | 最新国产麻豆aⅴ精品无码 | 成熟妇人a片免费看网站 | 巨爆乳无码视频在线观看 | 色情久久久av熟女人妻网站 | 麻豆精品国产精华精华液好用吗 | 给我免费的视频在线观看 | 97精品国产97久久久久久免费 | 国产超碰人人爽人人做人人添 | 色婷婷香蕉在线一区二区 | 国产极品视觉盛宴 | 国产成人精品无码播放 | 少妇被粗大的猛进出69影院 | 免费观看黄网站 | 亚洲热妇无码av在线播放 | 成年美女黄网站色大免费视频 | 国产精品毛片一区二区 | 无码精品国产va在线观看dvd | 草草网站影院白丝内射 | 蜜桃视频韩日免费播放 | 久久国产劲爆∧v内射 | 欧美日韩人成综合在线播放 | 欧美精品免费观看二区 | 亚洲中文字幕va福利 | 久久精品99久久香蕉国产色戒 | 欧美人妻一区二区三区 | 日韩人妻系列无码专区 | 99精品国产综合久久久久五月天 | 婷婷六月久久综合丁香 | 99久久99久久免费精品蜜桃 | 欧美激情综合亚洲一二区 | 亚洲а∨天堂久久精品2021 | 成人三级无码视频在线观看 | 狠狠色噜噜狠狠狠狠7777米奇 | 日本精品少妇一区二区三区 | 亚洲成色在线综合网站 | 又紧又大又爽精品一区二区 | 午夜福利不卡在线视频 | 国产高清不卡无码视频 | 日产精品高潮呻吟av久久 | 国产精品爱久久久久久久 | 欧美亚洲国产一区二区三区 | 中文久久乱码一区二区 | 老头边吃奶边弄进去呻吟 | 精品一二三区久久aaa片 | 国产成人无码a区在线观看视频app | 女人被男人爽到呻吟的视频 | 国产两女互慰高潮视频在线观看 | 青青久在线视频免费观看 | www国产亚洲精品久久网站 | 亚洲欧美日韩国产精品一区二区 | 国产偷抇久久精品a片69 | 亲嘴扒胸摸屁股激烈网站 | 国产精品毛片一区二区 | 久久综合给久久狠狠97色 | 久久综合给久久狠狠97色 | 亚洲精品综合一区二区三区在线 | 3d动漫精品啪啪一区二区中 | 精品一区二区三区无码免费视频 | 亚洲国产精品无码久久久久高潮 | 熟女体下毛毛黑森林 | 露脸叫床粗话东北少妇 | 香港三级日本三级妇三级 | 国产人妻人伦精品 | 国产成人精品优优av | 日日噜噜噜噜夜夜爽亚洲精品 | 国产色在线 | 国产 | 国产麻豆精品一区二区三区v视界 | 成人免费视频视频在线观看 免费 | 动漫av网站免费观看 | 亚洲欧美精品aaaaaa片 | 国产精品无码永久免费888 | 亚洲爆乳精品无码一区二区三区 | 亚洲日韩av片在线观看 | 亚洲色欲色欲天天天www | 成熟人妻av无码专区 | 俄罗斯老熟妇色xxxx | 99精品视频在线观看免费 | 久久久久99精品国产片 | 日本成熟视频免费视频 | 双乳奶水饱满少妇呻吟 | 强辱丰满人妻hd中文字幕 | 99riav国产精品视频 | 国内综合精品午夜久久资源 | 欧美成人免费全部网站 | 97久久国产亚洲精品超碰热 | 欧美一区二区三区视频在线观看 | 国精产品一区二区三区 | 国内老熟妇对白xxxxhd | 露脸叫床粗话东北少妇 | 丰满岳乱妇在线观看中字无码 | 国产精品久久久久无码av色戒 | 亚洲色偷偷男人的天堂 | 亚洲精品无码国产 | 精品少妇爆乳无码av无码专区 | 影音先锋中文字幕无码 | 免费观看又污又黄的网站 | 亚洲欧美日韩综合久久久 | 又紧又大又爽精品一区二区 | 亚洲码国产精品高潮在线 | 97资源共享在线视频 | 久久综合网欧美色妞网 | 国产绳艺sm调教室论坛 | 亚洲日韩av片在线观看 | 精品一区二区不卡无码av | 国产精品18久久久久久麻辣 | www成人国产高清内射 | 国产麻豆精品一区二区三区v视界 | 精品国产av色一区二区深夜久久 | 亚洲一区av无码专区在线观看 | 精品无人国产偷自产在线 | 亚洲欧美色中文字幕在线 | 欧美亚洲日韩国产人成在线播放 | 好男人社区资源 | 日韩精品久久久肉伦网站 | av香港经典三级级 在线 | 精品人人妻人人澡人人爽人人 | 精品无人区无码乱码毛片国产 | 亚洲乱亚洲乱妇50p | 欧美日韩一区二区三区自拍 | 亚拍精品一区二区三区探花 | 国产精品第一区揄拍无码 | 日欧一片内射va在线影院 | 中文精品久久久久人妻不卡 | 成熟妇人a片免费看网站 | 在线看片无码永久免费视频 | 亚洲精品一区二区三区四区五区 | 亚洲乱亚洲乱妇50p | 久久久久成人片免费观看蜜芽 | 国产婷婷色一区二区三区在线 | 欧美丰满熟妇xxxx性ppx人交 | 国产小呦泬泬99精品 | 精品偷拍一区二区三区在线看 | 久久综合激激的五月天 | a在线亚洲男人的天堂 | а√天堂www在线天堂小说 | 色综合视频一区二区三区 | 免费看男女做好爽好硬视频 | 久热国产vs视频在线观看 | 狠狠cao日日穞夜夜穞av | 免费播放一区二区三区 | 午夜精品久久久久久久 | 人人爽人人爽人人片av亚洲 | 中国大陆精品视频xxxx | 色欲综合久久中文字幕网 | 久久精品中文字幕大胸 | 色综合视频一区二区三区 | 日本一卡二卡不卡视频查询 | 中国女人内谢69xxxxxa片 | 无码av中文字幕免费放 | 亚洲天堂2017无码中文 | 曰韩少妇内射免费播放 | 国产精品毛多多水多 | 久久久久亚洲精品男人的天堂 | 亚洲精品国产a久久久久久 | 国内揄拍国内精品少妇国语 | 日韩少妇内射免费播放 | 色婷婷av一区二区三区之红樱桃 | 国産精品久久久久久久 | 亚洲色www成人永久网址 | aⅴ亚洲 日韩 色 图网站 播放 | 人人妻在人人 | 亚洲日本一区二区三区在线 | 国产精品久久精品三级 | 无码人妻出轨黑人中文字幕 | 精品偷自拍另类在线观看 | 丰满少妇弄高潮了www | 国产黑色丝袜在线播放 | 国产乱人伦app精品久久 国产在线无码精品电影网 国产国产精品人在线视 | 动漫av一区二区在线观看 | 激情内射亚州一区二区三区爱妻 | 青青青爽视频在线观看 | 搡女人真爽免费视频大全 | 粉嫩少妇内射浓精videos | 任你躁国产自任一区二区三区 | 久久这里只有精品视频9 | 熟女少妇在线视频播放 | 天堂久久天堂av色综合 | 成人三级无码视频在线观看 | 欧美老妇交乱视频在线观看 | 亚洲欧美日韩国产精品一区二区 | 久久久久久久女国产乱让韩 | 日韩欧美中文字幕公布 | 男女性色大片免费网站 | 天天摸天天透天天添 | 国产激情艳情在线看视频 | 日韩人妻无码一区二区三区久久99 | 波多野结衣一区二区三区av免费 | 欧美激情综合亚洲一二区 | 欧美日韩综合一区二区三区 | 国产精品人妻一区二区三区四 | 狠狠色噜噜狠狠狠狠7777米奇 | av无码不卡在线观看免费 | 中文字幕无码av激情不卡 | 色综合久久久久综合一本到桃花网 | 亚洲综合色区中文字幕 | 亚洲中文字幕乱码av波多ji | 亚洲 欧美 激情 小说 另类 | 国产明星裸体无码xxxx视频 | 色婷婷香蕉在线一区二区 | 亚洲国产精品一区二区第一页 | 理论片87福利理论电影 | 国产精品美女久久久 | 亚洲大尺度无码无码专区 | 国产小呦泬泬99精品 | 又大又硬又黄的免费视频 | 亚洲中文字幕久久无码 | 麻豆精品国产精华精华液好用吗 | 水蜜桃av无码 | 国产精品丝袜黑色高跟鞋 | 亚洲成av人片在线观看无码不卡 | 婷婷六月久久综合丁香 | 人人妻人人澡人人爽欧美一区九九 | 国产亚洲欧美在线专区 | 亚洲欧洲日本综合aⅴ在线 | 亚洲中文字幕久久无码 | 免费无码av一区二区 | 久久综合给久久狠狠97色 | 小鲜肉自慰网站xnxx | 成人毛片一区二区 | 成人欧美一区二区三区黑人免费 | 夜精品a片一区二区三区无码白浆 | 人妻有码中文字幕在线 | 麻花豆传媒剧国产免费mv在线 | 99久久99久久免费精品蜜桃 | 波多野结衣 黑人 | 老熟妇仑乱视频一区二区 | 亚洲一区二区观看播放 | 动漫av网站免费观看 | 日韩视频 中文字幕 视频一区 | 欧美日韩色另类综合 | 亚洲中文字幕成人无码 | 日韩亚洲欧美精品综合 | 国产精品第一国产精品 | 国产午夜福利100集发布 | 国产精品第一区揄拍无码 | 久久99精品久久久久久动态图 | 国产人成高清在线视频99最全资源 | 国产另类ts人妖一区二区 | 真人与拘做受免费视频一 | 婷婷五月综合激情中文字幕 | 亚洲成熟女人毛毛耸耸多 | 亚洲中文字幕在线观看 | 日本饥渴人妻欲求不满 | 精品久久久久久人妻无码中文字幕 | 国产精品久久久久久亚洲影视内衣 | 亚洲自偷自拍另类第1页 | 露脸叫床粗话东北少妇 | 免费无码的av片在线观看 | 中文字幕无码免费久久9一区9 | 亚洲色成人中文字幕网站 | 久久综合九色综合欧美狠狠 | 少妇性荡欲午夜性开放视频剧场 | 一本色道久久综合亚洲精品不卡 | 久久久精品人妻久久影视 | 激情人妻另类人妻伦 | 久久99久久99精品中文字幕 | 国产xxx69麻豆国语对白 | 午夜无码人妻av大片色欲 | 日韩精品无码一区二区中文字幕 | 性色av无码免费一区二区三区 | 成人免费视频一区二区 | 国产精品久久久久久久9999 | 中文字幕 亚洲精品 第1页 | 久久久国产精品无码免费专区 | 国产精品.xx视频.xxtv | 久久久久久久人妻无码中文字幕爆 | 成人一在线视频日韩国产 | 亚洲欧美综合区丁香五月小说 | 亚洲日本一区二区三区在线 | 久久久精品欧美一区二区免费 | 久久久久99精品国产片 | 99精品无人区乱码1区2区3区 | 免费观看激色视频网站 | 国产真实夫妇视频 | 精品国产精品久久一区免费式 | 成人无码视频在线观看网站 | 国产亚av手机在线观看 | 国内精品人妻无码久久久影院蜜桃 | 奇米综合四色77777久久 东京无码熟妇人妻av在线网址 | 久久久久免费看成人影片 | 男人扒开女人内裤强吻桶进去 | 激情五月综合色婷婷一区二区 | 人妻人人添人妻人人爱 | 丰满人妻翻云覆雨呻吟视频 | 欧美国产亚洲日韩在线二区 | 久久精品国产99精品亚洲 | 亚洲s色大片在线观看 | 熟女少妇在线视频播放 | 精品欧洲av无码一区二区三区 | 丰满少妇高潮惨叫视频 | 麻豆国产97在线 | 欧洲 | 国产舌乚八伦偷品w中 | 亚洲天堂2017无码中文 | 亚洲国产成人a精品不卡在线 | 日日噜噜噜噜夜夜爽亚洲精品 | 久久99精品久久久久久 | 精品水蜜桃久久久久久久 | 少妇人妻大乳在线视频 | 人妻少妇精品久久 | 国产精品成人av在线观看 | 色情久久久av熟女人妻网站 | 骚片av蜜桃精品一区 | 国产又爽又猛又粗的视频a片 | 亲嘴扒胸摸屁股激烈网站 | 国产性生大片免费观看性 | 最近免费中文字幕中文高清百度 | 亚洲精品午夜国产va久久成人 | 亚洲成av人综合在线观看 | 婷婷综合久久中文字幕蜜桃三电影 | 亚洲精品无码国产 | 麻豆av传媒蜜桃天美传媒 | 久久久久se色偷偷亚洲精品av | 男女作爱免费网站 | 美女黄网站人色视频免费国产 | 97久久超碰中文字幕 | 日本一卡二卡不卡视频查询 | 国产精品无码成人午夜电影 | 伊人久久婷婷五月综合97色 | 亚拍精品一区二区三区探花 | 十八禁视频网站在线观看 | 欧美熟妇另类久久久久久多毛 | 国产亚洲精品久久久久久国模美 | 九月婷婷人人澡人人添人人爽 | 精品厕所偷拍各类美女tp嘘嘘 | 风流少妇按摩来高潮 | av在线亚洲欧洲日产一区二区 | 国产免费久久精品国产传媒 | 女高中生第一次破苞av | 日本爽爽爽爽爽爽在线观看免 | 国产精品高潮呻吟av久久4虎 | 中文字幕精品av一区二区五区 | 国产女主播喷水视频在线观看 | 少妇人妻av毛片在线看 | 国产精品久久久久久久9999 | 99在线 | 亚洲 | 粉嫩少妇内射浓精videos | 无码人妻丰满熟妇区五十路百度 | 国产亚洲视频中文字幕97精品 | 亚洲欧美综合区丁香五月小说 | 精品久久综合1区2区3区激情 | 欧美国产亚洲日韩在线二区 | 国产亚洲精品久久久久久 | 亚洲成a人一区二区三区 | 欧美乱妇无乱码大黄a片 | 日韩精品无码免费一区二区三区 | 东京热无码av男人的天堂 | 无遮挡啪啪摇乳动态图 | 又大又硬又黄的免费视频 | 无码人妻精品一区二区三区不卡 | 强辱丰满人妻hd中文字幕 | 免费播放一区二区三区 | 亚洲欧美国产精品专区久久 | 国产av一区二区三区最新精品 | 乱中年女人伦av三区 | 久久亚洲日韩精品一区二区三区 | 中文字幕人妻无码一夲道 | 一本色道婷婷久久欧美 | 国产在线一区二区三区四区五区 | 日日天日日夜日日摸 | 又粗又大又硬又长又爽 | 日产精品高潮呻吟av久久 | 久久综合香蕉国产蜜臀av | 激情内射亚州一区二区三区爱妻 | 成年美女黄网站色大免费全看 | 少妇被黑人到高潮喷出白浆 | 国产黄在线观看免费观看不卡 | 少妇高潮喷潮久久久影院 | 国产乱子伦视频在线播放 | 国产成人无码区免费内射一片色欲 | 国产黑色丝袜在线播放 | 久久精品女人天堂av免费观看 | 性色欲情网站iwww九文堂 | 国产精品毛片一区二区 | 中文字幕乱妇无码av在线 | 蜜桃视频韩日免费播放 | 人妻中文无码久热丝袜 | 国产精品久久国产精品99 | 少妇邻居内射在线 | 99麻豆久久久国产精品免费 | 男人的天堂2018无码 | 国产成人精品无码播放 | 丰满人妻翻云覆雨呻吟视频 | 综合网日日天干夜夜久久 | 性做久久久久久久免费看 | 亚洲成熟女人毛毛耸耸多 | 狂野欧美性猛xxxx乱大交 | 岛国片人妻三上悠亚 | 国産精品久久久久久久 | 欧美日韩一区二区三区自拍 | 97无码免费人妻超级碰碰夜夜 | 日本va欧美va欧美va精品 | 成人无码精品一区二区三区 | 永久黄网站色视频免费直播 | 国产精品二区一区二区aⅴ污介绍 | 日本大乳高潮视频在线观看 | 亚洲精品国偷拍自产在线观看蜜桃 | 国产亚洲tv在线观看 | 97久久精品无码一区二区 | 两性色午夜免费视频 | 内射欧美老妇wbb | 激情爆乳一区二区三区 | 人人妻人人藻人人爽欧美一区 | 国产亚洲欧美日韩亚洲中文色 | 亚洲精品一区国产 | 久久99精品国产.久久久久 | 少妇太爽了在线观看 | 白嫩日本少妇做爰 | 精品久久久久久人妻无码中文字幕 | 亚洲阿v天堂在线 | 中文字幕乱码亚洲无线三区 | 亚洲狠狠婷婷综合久久 | 撕开奶罩揉吮奶头视频 | 欧美人妻一区二区三区 | 我要看www免费看插插视频 | 免费无码av一区二区 | 粉嫩少妇内射浓精videos | 中文字幕无线码 | 亚洲一区二区三区在线观看网站 | 少妇性俱乐部纵欲狂欢电影 | 狠狠色欧美亚洲狠狠色www | 久久人人97超碰a片精品 | 欧美日韩色另类综合 | 成人无码影片精品久久久 | 国产性生交xxxxx无码 | 中文字幕人成乱码熟女app | 亚洲综合另类小说色区 | 又大又硬又爽免费视频 | 欧美 丝袜 自拍 制服 另类 | 久久久久人妻一区精品色欧美 | 麻豆国产丝袜白领秘书在线观看 | 牲欲强的熟妇农村老妇女视频 | 精品国产麻豆免费人成网站 | 帮老师解开蕾丝奶罩吸乳网站 | 国语精品一区二区三区 | 狠狠cao日日穞夜夜穞av | 日本成熟视频免费视频 | 色情久久久av熟女人妻网站 | 欧美老熟妇乱xxxxx | 一本无码人妻在中文字幕免费 | 国产精品久久久一区二区三区 | a在线亚洲男人的天堂 | 青青久在线视频免费观看 | 亚洲娇小与黑人巨大交 | 夜夜影院未满十八勿进 | 日韩视频 中文字幕 视频一区 | 骚片av蜜桃精品一区 | 国产九九九九九九九a片 | 兔费看少妇性l交大片免费 | 欧美日韩一区二区三区自拍 | 亚洲欧美综合区丁香五月小说 | 色偷偷人人澡人人爽人人模 | 欧美人与动性行为视频 | 国产熟女一区二区三区四区五区 | 欧洲精品码一区二区三区免费看 | 丰满少妇熟乱xxxxx视频 | 亚洲午夜久久久影院 | 亚洲精品国产精品乱码不卡 | 午夜精品一区二区三区的区别 | 亚洲中文字幕在线无码一区二区 | 国产精品久久久久久亚洲毛片 | 国产舌乚八伦偷品w中 | 国内精品人妻无码久久久影院蜜桃 | 国产乱码精品一品二品 | 东京一本一道一二三区 | 欧美午夜特黄aaaaaa片 | 精品一区二区三区无码免费视频 | 中文精品无码中文字幕无码专区 | 国产精品无套呻吟在线 | 日韩亚洲欧美精品综合 | 亚洲国产精品美女久久久久 | 国产精品久久国产三级国 | 精品国产一区二区三区四区在线看 | 扒开双腿疯狂进出爽爽爽视频 | 日本高清一区免费中文视频 | 西西人体www44rt大胆高清 | 国产真实伦对白全集 | 中文无码成人免费视频在线观看 | 欧美日韩久久久精品a片 | ass日本丰满熟妇pics | 乌克兰少妇xxxx做受 | 国产在线精品一区二区高清不卡 | 欧美性生交活xxxxxdddd | 人妻少妇精品视频专区 | 国产高清av在线播放 | 成人影院yy111111在线观看 | 久久99久久99精品中文字幕 | 自拍偷自拍亚洲精品被多人伦好爽 | 无码人妻出轨黑人中文字幕 | 18无码粉嫩小泬无套在线观看 | 狂野欧美性猛xxxx乱大交 | 又大又硬又黄的免费视频 | 国产网红无码精品视频 | 在线а√天堂中文官网 | 夜夜影院未满十八勿进 | 亚洲色无码一区二区三区 | 国产 精品 自在自线 | 老熟女重囗味hdxx69 | 天下第一社区视频www日本 | 伊人久久大香线蕉亚洲 | 久久人妻内射无码一区三区 | 综合网日日天干夜夜久久 | 欧美成人家庭影院 | 人妻插b视频一区二区三区 | 水蜜桃亚洲一二三四在线 | 爆乳一区二区三区无码 | 国产精品99爱免费视频 | 久久精品女人的天堂av | 久久99精品国产.久久久久 | 美女黄网站人色视频免费国产 | 日韩精品无码一区二区中文字幕 | 亚洲欧洲日本无在线码 | 伊人久久大香线蕉av一区二区 | 丁香啪啪综合成人亚洲 | 熟妇人妻中文av无码 | 夜先锋av资源网站 | 国产无遮挡吃胸膜奶免费看 | 在线天堂新版最新版在线8 | 欧美日韩亚洲国产精品 | 99久久久无码国产aaa精品 | 成人aaa片一区国产精品 | 欧洲极品少妇 | 亚洲人交乣女bbw | 国产小呦泬泬99精品 | 成人欧美一区二区三区黑人免费 | 老子影院午夜精品无码 | 无遮无挡爽爽免费视频 | 亚洲人成网站色7799 | 一二三四社区在线中文视频 | 亚洲乱亚洲乱妇50p | 亚洲男人av天堂午夜在 | 国产成人久久精品流白浆 | 捆绑白丝粉色jk震动捧喷白浆 | 67194成是人免费无码 | 国产精品久久久久久无码 | 人人澡人人透人人爽 | 国产乱人无码伦av在线a | 中文精品久久久久人妻不卡 | 成人精品视频一区二区 | 国产成人午夜福利在线播放 | 久久久久av无码免费网 | 成人无码视频在线观看网站 | 国产偷国产偷精品高清尤物 | 强伦人妻一区二区三区视频18 | 日本一区二区更新不卡 | 国产精品99爱免费视频 | 国产超碰人人爽人人做人人添 | 超碰97人人做人人爱少妇 | 成人亚洲精品久久久久软件 | 色婷婷综合中文久久一本 | 国产无遮挡又黄又爽又色 | 国产精品多人p群无码 | 蜜桃av蜜臀av色欲av麻 999久久久国产精品消防器材 | 国产肉丝袜在线观看 | 樱花草在线社区www | 亚洲精品久久久久中文第一幕 | 欧美freesex黑人又粗又大 | 国产精品香蕉在线观看 | 日日躁夜夜躁狠狠躁 | 国产真实伦对白全集 | 久久久久亚洲精品中文字幕 | 国产精品久久久久久亚洲毛片 | 蜜桃无码一区二区三区 | 麻豆av传媒蜜桃天美传媒 | 亚洲精品一区二区三区婷婷月 | 国产精品高潮呻吟av久久4虎 | 人妻体内射精一区二区三四 | 国产精品久久久久久亚洲毛片 | 亚洲国产欧美在线成人 | 大乳丰满人妻中文字幕日本 | 成人av无码一区二区三区 | 无码人妻久久一区二区三区不卡 | 成人一在线视频日韩国产 | 欧美成人免费全部网站 | 玩弄少妇高潮ⅹxxxyw | 激情内射亚州一区二区三区爱妻 | 高中生自慰www网站 | 51国偷自产一区二区三区 | 少妇一晚三次一区二区三区 | 波多野42部无码喷潮在线 | 成人无码精品一区二区三区 | 久久这里只有精品视频9 | 水蜜桃亚洲一二三四在线 | 激情内射亚州一区二区三区爱妻 | 少妇无码一区二区二三区 | 精品水蜜桃久久久久久久 | 精品aⅴ一区二区三区 | 男女猛烈xx00免费视频试看 | 国产精品二区一区二区aⅴ污介绍 | 欧美国产日韩亚洲中文 | 伊人久久大香线焦av综合影院 | 少妇无套内谢久久久久 | √8天堂资源地址中文在线 | 亚洲娇小与黑人巨大交 | 乱人伦中文视频在线观看 | 无码人妻出轨黑人中文字幕 | 天下第一社区视频www日本 | 午夜精品久久久久久久久 | 婷婷五月综合激情中文字幕 | 国产亚洲欧美日韩亚洲中文色 | 亚洲精品久久久久久一区二区 | 国产无套粉嫩白浆在线 | 最近免费中文字幕中文高清百度 | 国产精品第一区揄拍无码 | 国产av剧情md精品麻豆 | 国产激情综合五月久久 | 人妻少妇精品视频专区 | 久久成人a毛片免费观看网站 | 女人高潮内射99精品 | 性做久久久久久久免费看 | 精品人妻人人做人人爽 | 九九久久精品国产免费看小说 | 亚洲精品一区二区三区在线观看 | 丰满人妻精品国产99aⅴ | 国产精品久久国产精品99 | 自拍偷自拍亚洲精品10p | 国产亚洲人成a在线v网站 | 亚洲男女内射在线播放 | 99精品无人区乱码1区2区3区 | 无码一区二区三区在线 | 国产又爽又猛又粗的视频a片 | 国产成人综合在线女婷五月99播放 | 男女超爽视频免费播放 | 国产美女极度色诱视频www | 中文字幕久久久久人妻 | 少妇久久久久久人妻无码 | 色欲综合久久中文字幕网 | 亚洲精品国产品国语在线观看 | 欧美激情综合亚洲一二区 | 成人动漫在线观看 | 欧美放荡的少妇 | 国产亚洲精品久久久久久久 | 天天躁日日躁狠狠躁免费麻豆 | 中文字幕 亚洲精品 第1页 | 青青青爽视频在线观看 | 377p欧洲日本亚洲大胆 | 精品一区二区不卡无码av | 99久久久无码国产精品免费 | 亚洲自偷自偷在线制服 | 内射巨臀欧美在线视频 | 377p欧洲日本亚洲大胆 | 亚洲小说图区综合在线 | 中文字幕无码免费久久9一区9 | 无码纯肉视频在线观看 | 日韩 欧美 动漫 国产 制服 | 色一情一乱一伦一视频免费看 | 精品亚洲成av人在线观看 | 国内精品久久毛片一区二区 | 亚洲一区二区三区香蕉 | 欧美日本免费一区二区三区 | 国产精品久久久久久亚洲影视内衣 | 国产又爽又黄又刺激的视频 | 99久久亚洲精品无码毛片 | 内射后入在线观看一区 | 99久久精品日本一区二区免费 | 久久精品国产精品国产精品污 | 六十路熟妇乱子伦 | 无码人妻黑人中文字幕 | 国产成人精品三级麻豆 | 激情内射亚州一区二区三区爱妻 | 国产精品资源一区二区 | yw尤物av无码国产在线观看 | 免费人成在线观看网站 | 久久久亚洲欧洲日产国码αv | 国产农村妇女aaaaa视频 撕开奶罩揉吮奶头视频 | 人人澡人人透人人爽 | а√天堂www在线天堂小说 | 国产真实夫妇视频 | 国产精品美女久久久久av爽李琼 | 成人欧美一区二区三区黑人免费 | 中文字幕无线码免费人妻 | 成在人线av无码免观看麻豆 | 鲁大师影院在线观看 | 内射老妇bbwx0c0ck | 国产欧美熟妇另类久久久 | 久久99国产综合精品 | 欧美日韩视频无码一区二区三 | 青青青爽视频在线观看 | 久久亚洲精品中文字幕无男同 | 欧美熟妇另类久久久久久不卡 | 欧美激情内射喷水高潮 | 欧美乱妇无乱码大黄a片 | аⅴ资源天堂资源库在线 | 亚洲va中文字幕无码久久不卡 | 宝宝好涨水快流出来免费视频 | 久久亚洲中文字幕无码 | 久久国产精品萌白酱免费 | 国产亚洲精品久久久久久国模美 | 国产人妻精品午夜福利免费 | 免费无码午夜福利片69 | 天天躁日日躁狠狠躁免费麻豆 | 国产小呦泬泬99精品 | 18禁黄网站男男禁片免费观看 | 国产精品久久久久影院嫩草 | 亚洲精品无码国产 | 少妇无套内谢久久久久 | 欧洲vodafone精品性 | 野外少妇愉情中文字幕 | 国语精品一区二区三区 | 在线观看欧美一区二区三区 | 欧美精品一区二区精品久久 | 丝袜美腿亚洲一区二区 | 免费播放一区二区三区 | 精品无码一区二区三区的天堂 | 亚洲综合精品香蕉久久网 | 无码国产乱人伦偷精品视频 | 亚洲精品国产a久久久久久 | 国产极品美女高潮无套在线观看 | 国内少妇偷人精品视频 | 亚洲欧洲日本综合aⅴ在线 | 伊人久久大香线蕉亚洲 | 97夜夜澡人人双人人人喊 | 日本一卡二卡不卡视频查询 | 久久久国产一区二区三区 | 国产无遮挡吃胸膜奶免费看 | 全球成人中文在线 | 国产婷婷色一区二区三区在线 | 国产精品福利视频导航 | 青青草原综合久久大伊人精品 | 国产午夜手机精彩视频 | 国产亚洲日韩欧美另类第八页 | 色综合久久久久综合一本到桃花网 | 日韩人妻系列无码专区 | 妺妺窝人体色www婷婷 | 中文字幕乱码亚洲无线三区 | 亚洲综合色区中文字幕 | 欧美亚洲国产一区二区三区 | 色诱久久久久综合网ywww | 欧美日韩精品 | 乱码午夜-极国产极内射 | 夜夜高潮次次欢爽av女 | 无码任你躁久久久久久久 | 中文字幕乱码亚洲无线三区 | 日本在线高清不卡免费播放 | 国产卡一卡二卡三 | 未满成年国产在线观看 | 日本精品少妇一区二区三区 | 麻豆md0077饥渴少妇 | 高清国产亚洲精品自在久久 | 日日碰狠狠丁香久燥 | 宝宝好涨水快流出来免费视频 | 精品国产av色一区二区深夜久久 | 国产精品高潮呻吟av久久 | 中文亚洲成a人片在线观看 | 亚洲精品一区三区三区在线观看 | 久久久av男人的天堂 | 男女下面进入的视频免费午夜 | 大乳丰满人妻中文字幕日本 | 男人的天堂2018无码 | 色五月五月丁香亚洲综合网 | 综合网日日天干夜夜久久 | 1000部啪啪未满十八勿入下载 | 无码一区二区三区在线观看 | 日本xxxx色视频在线观看免费 | 免费人成在线观看网站 | 无码人妻出轨黑人中文字幕 | 国内揄拍国内精品人妻 | 亚洲呦女专区 | 沈阳熟女露脸对白视频 | 久久久久久国产精品无码下载 | 日韩人妻系列无码专区 | 中国女人内谢69xxxxxa片 | 久在线观看福利视频 | 牲欲强的熟妇农村老妇女视频 | 国产成人av免费观看 | 无码人妻精品一区二区三区不卡 | 鲁大师影院在线观看 | 国产精品久久久一区二区三区 | 狠狠色噜噜狠狠狠狠7777米奇 | 精品人妻av区 | 欧美xxxx黑人又粗又长 | 一个人免费观看的www视频 | 亚洲精品国产a久久久久久 | 国产精品丝袜黑色高跟鞋 | 精品国产一区二区三区四区 | 亚洲精品中文字幕 | 亚洲中文字幕无码中字 | 亚洲自偷精品视频自拍 | 国产绳艺sm调教室论坛 | 欧美熟妇另类久久久久久多毛 | 久久综合久久自在自线精品自 | 国精产品一品二品国精品69xx | 欧美 日韩 人妻 高清 中文 | 日本成熟视频免费视频 | 精品日本一区二区三区在线观看 | 色婷婷综合激情综在线播放 | 免费无码一区二区三区蜜桃大 | 少妇厨房愉情理9仑片视频 | 国产人成高清在线视频99最全资源 | 激情综合激情五月俺也去 | 国产真人无遮挡作爱免费视频 | 久久99精品久久久久久动态图 | 天天躁日日躁狠狠躁免费麻豆 | 久久精品一区二区三区四区 | 亲嘴扒胸摸屁股激烈网站 | 天堂在线观看www | 成人欧美一区二区三区黑人免费 | 中文亚洲成a人片在线观看 | 亚洲色无码一区二区三区 | 无码帝国www无码专区色综合 | 中文字幕人妻丝袜二区 | 蜜桃臀无码内射一区二区三区 | 色婷婷综合激情综在线播放 | 久久久久人妻一区精品色欧美 | 啦啦啦www在线观看免费视频 | 乱码av麻豆丝袜熟女系列 | 亚洲乱码国产乱码精品精 | aa片在线观看视频在线播放 | 午夜理论片yy44880影院 | 无码精品人妻一区二区三区av | 精品久久久久久亚洲精品 | 国产熟女一区二区三区四区五区 | 欧美丰满熟妇xxxx | 国产成人人人97超碰超爽8 | 国产香蕉尹人视频在线 | 欧美成人午夜精品久久久 | 97久久国产亚洲精品超碰热 | 国产精品久久精品三级 | 国产综合久久久久鬼色 | 无码一区二区三区在线观看 | 少妇性荡欲午夜性开放视频剧场 | 天天躁日日躁狠狠躁免费麻豆 | 亚洲国产日韩a在线播放 | 久久久久久a亚洲欧洲av冫 | 亚洲精品中文字幕 | 亚洲精品无码人妻无码 | 亚洲娇小与黑人巨大交 | 日韩人妻无码一区二区三区久久99 | 免费人成网站视频在线观看 | 久久精品成人欧美大片 | av无码久久久久不卡免费网站 | 日韩人妻无码一区二区三区久久99 | 波多野结衣av在线观看 | 色欲久久久天天天综合网精品 | 蜜桃臀无码内射一区二区三区 | 国产在线精品一区二区三区直播 | 激情内射日本一区二区三区 | 久久午夜无码鲁丝片秋霞 | 红桃av一区二区三区在线无码av | 国产精品沙发午睡系列 | 亚洲一区二区三区无码久久 | 成人免费无码大片a毛片 | 色婷婷香蕉在线一区二区 | 性色欲网站人妻丰满中文久久不卡 | 免费无码肉片在线观看 | 日本高清一区免费中文视频 | 久久国语露脸国产精品电影 | 国产情侣作爱视频免费观看 | 天堂在线观看www | 成人女人看片免费视频放人 | 中文字幕av日韩精品一区二区 | 在线看片无码永久免费视频 | 欧美一区二区三区 | 日本成熟视频免费视频 | 亚洲国产精品无码一区二区三区 | 亚洲精品鲁一鲁一区二区三区 | 性啪啪chinese东北女人 | 亚洲国产欧美国产综合一区 | 99精品国产综合久久久久五月天 | 国产高清不卡无码视频 | 日本一卡二卡不卡视频查询 | 国产三级久久久精品麻豆三级 | 欧美 亚洲 国产 另类 | 日本又色又爽又黄的a片18禁 | 蜜臀av无码人妻精品 | 国产精品亚洲专区无码不卡 | 丰满人妻一区二区三区免费视频 | 成熟女人特级毛片www免费 | 亚洲日本一区二区三区在线 | 日本护士毛茸茸高潮 | 亚洲精品国产精品乱码视色 | 亚洲精品中文字幕久久久久 | 无码一区二区三区在线观看 | 国产成人精品视频ⅴa片软件竹菊 | 综合网日日天干夜夜久久 | 夜先锋av资源网站 | 欧美 亚洲 国产 另类 | 久久精品国产日本波多野结衣 | 一本久道高清无码视频 | 99在线 | 亚洲 | 国产在线一区二区三区四区五区 | 亚洲精品无码国产 | 国产午夜亚洲精品不卡 | 日日噜噜噜噜夜夜爽亚洲精品 | 奇米影视7777久久精品人人爽 | 国产人妻人伦精品1国产丝袜 | 美女黄网站人色视频免费国产 | 国精产品一品二品国精品69xx | 99久久精品无码一区二区毛片 | 久久久久人妻一区精品色欧美 | 国产极品美女高潮无套在线观看 | 国产成人一区二区三区别 | 爱做久久久久久 | 在线观看国产一区二区三区 | 亚洲第一无码av无码专区 | 岛国片人妻三上悠亚 | 精品无人区无码乱码毛片国产 | 亚洲成av人片在线观看无码不卡 | 男女超爽视频免费播放 | 老熟妇仑乱视频一区二区 | 亚洲无人区午夜福利码高清完整版 | 国产激情精品一区二区三区 | 亚洲一区二区三区国产精华液 | 帮老师解开蕾丝奶罩吸乳网站 | 日日天干夜夜狠狠爱 | 久久99精品久久久久久动态图 | 麻豆md0077饥渴少妇 | 亚洲の无码国产の无码影院 | 狠狠cao日日穞夜夜穞av | 天天av天天av天天透 | 啦啦啦www在线观看免费视频 | 荡女精品导航 | 欧美国产亚洲日韩在线二区 | 国产做国产爱免费视频 | 久久综合九色综合欧美狠狠 | 黑人巨大精品欧美黑寡妇 | 午夜精品久久久久久久久 | 亚洲爆乳无码专区 | 国内精品人妻无码久久久影院蜜桃 | 亚洲色无码一区二区三区 | 国产精品理论片在线观看 | 久久久久久久久蜜桃 | 亚洲精品美女久久久久久久 | 亚洲精品综合一区二区三区在线 | 国产午夜福利100集发布 | 永久免费观看美女裸体的网站 | 久久亚洲精品中文字幕无男同 | 成在人线av无码免观看麻豆 | 疯狂三人交性欧美 | 成在人线av无码免费 | 人妻插b视频一区二区三区 | 少妇厨房愉情理9仑片视频 | 九月婷婷人人澡人人添人人爽 | 欧美三级a做爰在线观看 | 男女性色大片免费网站 | www国产亚洲精品久久网站 | 久久久国产一区二区三区 | 日本护士xxxxhd少妇 | 又大又紧又粉嫩18p少妇 | 精品无码国产一区二区三区av | 日韩精品一区二区av在线 | 亚洲爆乳大丰满无码专区 | 欧美三级a做爰在线观看 | 六月丁香婷婷色狠狠久久 | 内射老妇bbwx0c0ck | 日本精品人妻无码免费大全 | 风流少妇按摩来高潮 | 国产做国产爱免费视频 | 国产国产精品人在线视 | 国产成人综合色在线观看网站 | 性欧美videos高清精品 | 色一情一乱一伦 | 88国产精品欧美一区二区三区 | 亚洲大尺度无码无码专区 | 亚洲а∨天堂久久精品2021 | 色综合久久久久综合一本到桃花网 | 高潮毛片无遮挡高清免费视频 | 色综合久久中文娱乐网 | 国产极品美女高潮无套在线观看 | 又紧又大又爽精品一区二区 | 亚洲一区二区三区含羞草 | 国产精品亚洲专区无码不卡 | 色妞www精品免费视频 | 少妇无码av无码专区在线观看 | 亚洲国产精品成人久久蜜臀 | 纯爱无遮挡h肉动漫在线播放 | 天天做天天爱天天爽综合网 | 日本饥渴人妻欲求不满 | 国产农村妇女高潮大叫 | 亚洲欧洲日本综合aⅴ在线 | 国产成人精品视频ⅴa片软件竹菊 | 欧美一区二区三区视频在线观看 | 欧美阿v高清资源不卡在线播放 | 国产熟女一区二区三区四区五区 | 午夜不卡av免费 一本久久a久久精品vr综合 | 老熟妇仑乱视频一区二区 | 国产 精品 自在自线 | 高中生自慰www网站 | 欧美人与禽zoz0性伦交 | 国产人成高清在线视频99最全资源 | 国产成人精品视频ⅴa片软件竹菊 | 欧美性生交xxxxx久久久 | 日韩欧美中文字幕公布 | 国产sm调教视频在线观看 | 国产欧美亚洲精品a | 一本久道久久综合婷婷五月 | 国产成人无码av片在线观看不卡 | 国产农村妇女高潮大叫 | 国产成人一区二区三区在线观看 | 亚洲欧洲无卡二区视頻 | 色婷婷欧美在线播放内射 | 久久久久亚洲精品男人的天堂 | 国产日产欧产精品精品app | 无码人妻精品一区二区三区下载 | 国产亚洲精品久久久久久久久动漫 | 日韩精品乱码av一区二区 | 人妻人人添人妻人人爱 | 97资源共享在线视频 | 欧美xxxxx精品 | 欧美国产日产一区二区 | 99精品久久毛片a片 | 久久午夜夜伦鲁鲁片无码免费 | 无码午夜成人1000部免费视频 | 国产精品99久久精品爆乳 | 色 综合 欧美 亚洲 国产 | 综合人妻久久一区二区精品 | 亚洲精品一区三区三区在线观看 | 午夜肉伦伦影院 | 强伦人妻一区二区三区视频18 | 精品国产一区二区三区av 性色 | 亚洲色欲久久久综合网东京热 | 乱码午夜-极国产极内射 | 熟女俱乐部五十路六十路av | 国产精品爱久久久久久久 | 老头边吃奶边弄进去呻吟 | 国产亚洲欧美日韩亚洲中文色 | 久久国产精品_国产精品 | 超碰97人人射妻 | 国产猛烈高潮尖叫视频免费 | 国产精品人人妻人人爽 | 精品无码成人片一区二区98 | 爱做久久久久久 | 少妇无码av无码专区在线观看 | 麻豆国产丝袜白领秘书在线观看 | 亚洲中文字幕av在天堂 | av无码久久久久不卡免费网站 | 377p欧洲日本亚洲大胆 | 98国产精品综合一区二区三区 | 蜜臀aⅴ国产精品久久久国产老师 | 亚洲人成网站在线播放942 | 国产性生大片免费观看性 | 欧美人与禽zoz0性伦交 | 俺去俺来也在线www色官网 | 亚洲一区二区三区偷拍女厕 | 国产午夜精品一区二区三区嫩草 | 久久午夜夜伦鲁鲁片无码免费 | 免费观看又污又黄的网站 | 亚洲国产精品久久人人爱 | 亚拍精品一区二区三区探花 | 久久国产精品二国产精品 | 日日摸日日碰夜夜爽av | 玩弄中年熟妇正在播放 | 久久久无码中文字幕久... | 一区二区传媒有限公司 | 国产成人无码专区 | 亚洲熟妇色xxxxx欧美老妇 | 久久久久久a亚洲欧洲av冫 | 国产精品igao视频网 | 天堂亚洲免费视频 | 亚洲区欧美区综合区自拍区 | 亚洲综合久久一区二区 | 中文字幕无码av激情不卡 | 日本一卡二卡不卡视频查询 | 国产性生大片免费观看性 | 中文久久乱码一区二区 | 黑人粗大猛烈进出高潮视频 | 无码国内精品人妻少妇 | 国产小呦泬泬99精品 | 久久精品国产一区二区三区肥胖 | 亚洲熟妇色xxxxx欧美老妇 | 欧美激情一区二区三区成人 | 久久无码专区国产精品s | 亚洲一区二区三区国产精华液 | 国产午夜无码视频在线观看 | 日本精品高清一区二区 | 天堂无码人妻精品一区二区三区 | 日日摸夜夜摸狠狠摸婷婷 | 在教室伦流澡到高潮hnp视频 | 日产精品高潮呻吟av久久 | 兔费看少妇性l交大片免费 | 国产一区二区三区影院 | 国产欧美熟妇另类久久久 | 国产精品高潮呻吟av久久 | 亚洲区欧美区综合区自拍区 | 午夜精品久久久久久久 | 国产97人人超碰caoprom | 国产精品久久久久久亚洲影视内衣 | 中文字幕av日韩精品一区二区 | 久久久久亚洲精品男人的天堂 | 国产成人无码午夜视频在线观看 | 国产情侣作爱视频免费观看 | 人人妻人人藻人人爽欧美一区 | 巨爆乳无码视频在线观看 | 精品国精品国产自在久国产87 | 免费无码一区二区三区蜜桃大 | 小sao货水好多真紧h无码视频 | 亚洲精品一区三区三区在线观看 | 国产又爽又黄又刺激的视频 | 黑人玩弄人妻中文在线 | 人人超人人超碰超国产 | 国产精品99久久精品爆乳 | 熟妇人妻激情偷爽文 | 在线视频网站www色 | 色偷偷人人澡人人爽人人模 | 欧美日韩综合一区二区三区 | 亚洲国产午夜精品理论片 | 亚洲欧美精品伊人久久 | 少妇邻居内射在线 | 四虎国产精品免费久久 | 欧美日韩综合一区二区三区 | 永久黄网站色视频免费直播 | 亚洲一区二区三区国产精华液 | 日本va欧美va欧美va精品 | 国产偷自视频区视频 | 久久综合九色综合97网 | 沈阳熟女露脸对白视频 | 国产精品.xx视频.xxtv | 人人澡人人透人人爽 | 日韩少妇白浆无码系列 | 在线观看国产一区二区三区 | 亚洲男人av天堂午夜在 | 色综合久久88色综合天天 | 国产精品99久久精品爆乳 | 久久99精品国产.久久久久 | 377p欧洲日本亚洲大胆 | 99精品国产综合久久久久五月天 | 老熟妇乱子伦牲交视频 | 正在播放东北夫妻内射 | 无套内谢的新婚少妇国语播放 | 露脸叫床粗话东北少妇 | 激情爆乳一区二区三区 | 无遮挡国产高潮视频免费观看 | 97精品人妻一区二区三区香蕉 | 国产精品18久久久久久麻辣 | 无码av免费一区二区三区试看 | 人妻体内射精一区二区三四 | 久久精品丝袜高跟鞋 | 黑森林福利视频导航 | 久热国产vs视频在线观看 | 久久aⅴ免费观看 | 久激情内射婷内射蜜桃人妖 | 日本又色又爽又黄的a片18禁 | 人人妻人人澡人人爽欧美一区 | a在线亚洲男人的天堂 | 国产成人午夜福利在线播放 | 国产真实乱对白精彩久久 | 精品厕所偷拍各类美女tp嘘嘘 | 亚洲 激情 小说 另类 欧美 | 人妻少妇精品视频专区 | 国产精华av午夜在线观看 | 国产成人久久精品流白浆 | 领导边摸边吃奶边做爽在线观看 | 日韩亚洲欧美中文高清在线 | 亚洲综合色区中文字幕 | 亚洲天堂2017无码 | 久久99精品国产麻豆蜜芽 | 中国女人内谢69xxxx | 黄网在线观看免费网站 | 九九热爱视频精品 | 日韩av无码一区二区三区 | 亚洲中文字幕va福利 | 国色天香社区在线视频 | 亚洲成熟女人毛毛耸耸多 | 伊人久久大香线蕉午夜 | 色窝窝无码一区二区三区色欲 | 亚洲人成影院在线观看 | 成人动漫在线观看 | 日日摸天天摸爽爽狠狠97 | 伊人久久大香线蕉午夜 | 亚洲精品国产a久久久久久 | 福利一区二区三区视频在线观看 | 国产xxx69麻豆国语对白 | 2020久久香蕉国产线看观看 | 欧美人与动性行为视频 | 国产人成高清在线视频99最全资源 | 国内少妇偷人精品视频 | 一本大道伊人av久久综合 | 久9re热视频这里只有精品 | 夜夜夜高潮夜夜爽夜夜爰爰 | 高潮喷水的毛片 | 成人试看120秒体验区 | 国产精品久久久av久久久 | 国产成人精品必看 | 黄网在线观看免费网站 | 欧美人与动性行为视频 | 亚洲日韩一区二区三区 | 国产猛烈高潮尖叫视频免费 | 99国产精品白浆在线观看免费 | 亚洲国产精品无码一区二区三区 | 久久99精品国产麻豆 | 亚洲中文字幕无码中字 | 搡女人真爽免费视频大全 | 欧美自拍另类欧美综合图片区 | 真人与拘做受免费视频一 | 无码国内精品人妻少妇 | 99久久亚洲精品无码毛片 | 国产网红无码精品视频 | 亚洲精品午夜无码电影网 | 国产福利视频一区二区 | 国产女主播喷水视频在线观看 | 粉嫩少妇内射浓精videos | 久久视频在线观看精品 | 在线观看国产一区二区三区 | 婷婷色婷婷开心五月四房播播 | 日日摸夜夜摸狠狠摸婷婷 | 俺去俺来也在线www色官网 | 美女张开腿让人桶 | 午夜精品久久久内射近拍高清 | 麻豆国产丝袜白领秘书在线观看 | 天堂亚洲免费视频 | 亚洲狠狠婷婷综合久久 | 亚洲一区二区三区无码久久 | 午夜精品一区二区三区的区别 | 欧洲美熟女乱又伦 | 麻豆国产人妻欲求不满 | 野外少妇愉情中文字幕 | 久久精品99久久香蕉国产色戒 | 国产精品无码一区二区桃花视频 | 色窝窝无码一区二区三区色欲 | 在教室伦流澡到高潮hnp视频 | 色欲久久久天天天综合网精品 | 高清不卡一区二区三区 | 精品水蜜桃久久久久久久 | 精品国产成人一区二区三区 | 久久无码中文字幕免费影院蜜桃 | 成人无码精品1区2区3区免费看 | 国产精品人人妻人人爽 | 国产亚洲日韩欧美另类第八页 | 亚洲国产精品无码久久久久高潮 | 国产 浪潮av性色四虎 | 亚洲 a v无 码免 费 成 人 a v | 国产精品自产拍在线观看 | 亚洲国产精华液网站w | 国产av一区二区精品久久凹凸 | 国产欧美精品一区二区三区 | 欧美精品无码一区二区三区 | 国产成人精品久久亚洲高清不卡 | 影音先锋中文字幕无码 | 激情综合激情五月俺也去 | 无码精品国产va在线观看dvd | 亚洲精品午夜国产va久久成人 | 在线 国产 欧美 亚洲 天堂 | 久久久久久九九精品久 | 九月婷婷人人澡人人添人人爽 | 男人的天堂2018无码 | 麻豆国产人妻欲求不满 | 欧美日韩色另类综合 | 国内精品久久久久久中文字幕 | 免费观看又污又黄的网站 | 亚洲综合在线一区二区三区 | 国产成人人人97超碰超爽8 | 精品熟女少妇av免费观看 | 国产精品永久免费视频 | 国产香蕉尹人视频在线 | 成人性做爰aaa片免费看 | 乌克兰少妇xxxx做受 | 四虎永久在线精品免费网址 | 久久国产精品精品国产色婷婷 | 红桃av一区二区三区在线无码av | 亚洲 a v无 码免 费 成 人 a v | 综合激情五月综合激情五月激情1 | 久久人人爽人人人人片 | 思思久久99热只有频精品66 | av人摸人人人澡人人超碰下载 | 真人与拘做受免费视频 | 国产免费久久精品国产传媒 | 亚洲伊人久久精品影院 | 伊人久久大香线焦av综合影院 | 精品人妻中文字幕有码在线 | 牲交欧美兽交欧美 | 久久人人爽人人人人片 | 中文字幕无码av波多野吉衣 | 亚洲欧美综合区丁香五月小说 | 亚洲精品国产品国语在线观看 | 国产精品无码一区二区三区不卡 | 久久久中文久久久无码 | 国产莉萝无码av在线播放 | 最近中文2019字幕第二页 | 国产成人精品久久亚洲高清不卡 | 国产精品久久久一区二区三区 | 无码人妻出轨黑人中文字幕 | 色婷婷香蕉在线一区二区 | 黑人玩弄人妻中文在线 | 97资源共享在线视频 | yw尤物av无码国产在线观看 | 2020久久超碰国产精品最新 | 综合人妻久久一区二区精品 | 色综合久久久无码中文字幕 | 亚洲经典千人经典日产 | 成人无码视频免费播放 | 国产精品无码久久av | 日韩少妇白浆无码系列 | 久热国产vs视频在线观看 | 99久久精品国产一区二区蜜芽 | 亚洲欧洲日本综合aⅴ在线 | 亚洲区欧美区综合区自拍区 | 亚洲欧洲无卡二区视頻 | 午夜成人1000部免费视频 | 国产人成高清在线视频99最全资源 | 欧美国产日产一区二区 | 国产口爆吞精在线视频 | 色偷偷人人澡人人爽人人模 | 最近的中文字幕在线看视频 | 亚洲国精产品一二二线 | 色狠狠av一区二区三区 | 亚洲国产精品美女久久久久 | 国产猛烈高潮尖叫视频免费 | 77777熟女视频在线观看 а天堂中文在线官网 | 97色伦图片97综合影院 | 久久久无码中文字幕久... | 无码人妻丰满熟妇区五十路百度 | 一本无码人妻在中文字幕免费 | 久激情内射婷内射蜜桃人妖 | 色一情一乱一伦一视频免费看 | 300部国产真实乱 | 久久99国产综合精品 | 久久久亚洲欧洲日产国码αv | 国产精品亚洲lv粉色 | 99精品国产综合久久久久五月天 | 波多野结衣av一区二区全免费观看 | 成人三级无码视频在线观看 | 黄网在线观看免费网站 | 精品少妇爆乳无码av无码专区 | 国产av剧情md精品麻豆 | 婷婷五月综合激情中文字幕 | 国产人妻精品一区二区三区 | 亚洲中文字幕av在天堂 | 精品无人国产偷自产在线 | 国产精品亚洲综合色区韩国 | 成 人影片 免费观看 | 又黄又爽又色的视频 | 国产精品久久久久久久影院 | 国产九九九九九九九a片 | 日日摸夜夜摸狠狠摸婷婷 | 亚洲性无码av中文字幕 | 成人av无码一区二区三区 | 久久精品国产亚洲精品 | 97夜夜澡人人双人人人喊 | 双乳奶水饱满少妇呻吟 | 午夜福利试看120秒体验区 | 国产精品人人妻人人爽 | 精品无码一区二区三区爱欲 | 麻豆国产人妻欲求不满 | 婷婷丁香六月激情综合啪 | 亚洲精品www久久久 | 在线看片无码永久免费视频 | 亚欧洲精品在线视频免费观看 | 国产在线精品一区二区高清不卡 |