1 string 字符串操作 2 3 4 操作数的都是 ( char * )型,操作数必须是指向字符串的指针("a"),不能是字符('a'),操作时不考虑末尾的'\0'。 5 6 size_t strlen( const char *s ); 字符串求长,返回字符串或指针的实际大小,和 sizeof() 不同,比如创建字符数组 strlen 只返回其中字符串的大小,不包括 '\0' 7 char *strrev( char *s ); 字符串反转,不包括 NULL 8 char *strcpy( char *dest, const char *src ); 字符串复制, dest 必须有足够的空间来容纳 src 的字符串, dest 空间不够时结果是为定义的(结果显示增大了空间去容纳 src 的字符),dest 不能是字符指针 9 int atoi( const char *nptr ); 字符串转化为整数 10 char *strcat( char *dest, const char *src ); 字符串连接 11 int strcmp( const char *s1, const char *s2 ); 字符串比较,区分大小写 12 int strcasecmp(const char *s1, const char *s2); 不区分大小写比较,在头文件 strings.h 中,可在字符串指针和字符数组之间相互比较,注意地址范围 13 int strncasecmp(const char *s1, const char *s2, size_t n); 不区分大小写比较,只比较 s1 前 n 个字符 14 char *strchr( const char *s, int c ); 查找字符,返回首次出现 c 的位置的指针,如果 s 中不存在 c 则返回 NULL 15 void bzero(void *s, size_t n); 将字符串 s 中前 n 个( 0 - n-1 )字符设为 '\0', strings.h 中 16 17 18 19 void perror(const char *s); 20 sprintf( buf, "hello world! %s\n", string ); 将字符串写到 buf 中,而不是标准输出 21 int scanf(const char *format, ...); 成功返回读取到是字符数,失败返回 EOF,目标是指针(数组指针)时不用 "&" 22 int fscanf( FILE *stream, const char *format, ... ); 从文件流中读取一个单词,不包含空格,读取的内容存到字符数组里,以 '\0' 结尾 23 int sscanf(const char *str, const char *format, ...); 24 int vscanf(const char *format, va_list ap); 25 int getc(FILE *stream); 从文件流中读取一个字符 26 int getchar(void); 从标准输入中读取一个字符,用 ASCII 码将字符转换为 int 27 char *gets(char *s); 从 stdin 中读取一行放入 s 中,只到遇到 EOF 或者 newline 28 int fgetc(FILE *stream); 29 char *fgets(char *buffer, int size, FILE * stream); 从文件中读取一行或指定个数的字符; buffer 为缓冲区,用来保存读取到的数据; size 为要读取的字符的个数。如果该行字符数大于size-1,则读到 size-1 个字符时结束,并在最后补充' \0';如果该行字符数小于等于 size-1,则读取所有字符,并在最后补充 '\0'。即,每次最多读取 size-1 个字符。读取的字符包括换行符; stream 为文件指针 30 31 32 33 文件 I/O 编程 34 35 36 int open( const char *pathname, int oflag [, mode_t mode ] ); pathname 是打开或创建文件的全路径名; oflag 由 O_RDONLY O_WRONLY O_ROWR 中的一个和 O_APPEND(每次写都追加到文件尾) O_CREAT(文件不存在就创建他,须说明第三个参数) O_EXCL(如果指定了 O_CREAT 而文件已存在则出错) O_TRUNC(文件存在且由写权限,则将其长度截为 0) O_NOCTTY(如果 pathname 指的是终端设备,则不将此设备分配作为此进程的控制终端) O_NONBLOCK(如果打开的是一个 fifo 、块特殊文件或字符特殊文件则本次打开和后续 I/O 操作设为非阻塞) O_SYNC(使每次 write 都等到物理 I/O 操作完成) 中的一个或多个进行“ | ”运算构成; mode 只有创建文件时才使用,权限设置;失败返回 -1 37 int creat( const char *pathname, mode_t mode ); 创建文件,以只写方式打开,失败返回 -1 38 ssize_t read( int fd, void *buf,size_t count ); buf 不能是字符指针,一般为字符数组名; count 是 期望读取的最大字节数,一个汉字占 3 个字节(linux);返回 0 时标是读到文件尾; 39 ssize_t write( int fd, const void *buf, size_t count ); 如果读写的大小 count 超出了给出字符串的大小不会出错,读文件后不会删除内容 40 int close( int filedes ); 进程结束内核会自动关闭打开的文件 41 off_t lseek (int filesdes, off_t offset, int whence ); 设置文件读写位置;文件偏移量,默认为 0(除非指定 O_APPEND); off_t 相当于 signed int(typedef),可正可负; whence 为 SEEK_SET 距文件开始处 offset 个字节, SEEK_CUR 当前值加 offset 字节, SEEK_END 是文件尾 ;成功返回新的文件位移,失败返回 -1 42 43 FILE *fopen(const char *path, const char *mode); mode 为 "r" 表示以只读放是打开文件, r 只读, w 只写, a 追加, t 文本文件(可省略不写), b 二进制文件,+ 读和写;凡用“r”打开一个文件时,该文件必须已经存在,有 + 时可以读写;用“w”打开的文件只能向该文件写入,若打开的文件不存在,则以指定的文件名建立该文件,若打开的文件已经存在,则将该文件删去,重建一个新文件;若要向一个已存在的文件追加新的信息,只能用“a”方式打开文件,如果指定文件不存在则尝试创建该文件;把一个文本文件读入内存时,要将ASCII码转换成二进制码,而把文件以文本方式写入磁盘时,也要把二进制码转换成ASCII码,因此文本文件的读写要花费较多的转换时间,对二进制文件的读写不存在这种转换;标准输入文件(键盘),标准输出文件(显示器),标准出错输出(出错信息)是由系统打开的,可直接使用;出错返回 NULL 44 FILE *fdopen(int fildes,const char * mode); fdopen()会将参数fildes 的文件描述词,转换为对应的文件指针后返回 45 size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 46 size_t fwrite(const void *ptr, size_t size, size_t nmemb, FILE *stream); 47 int fclose(FILE * stream); 48 49 int fcntl( int fd, int cmd, .../* arg */); 操作文件描述符。 cmd 为 F_DUPFD,复制一个存在的文件描述符, F_GETFD/F_SETFD,获得/设置文件描述符标志, F_GETFL/F_SETFL,获得/设置文件描述符属性(第三个参数为新设置的文件属性,只能改变文件的 O_APPEND, O_NONBLOCK, O_SYNC, O_ASYNC 如 flag = flag &~O_APPEND ), cmd = F_GETOWN/F_SETOWN,获得/设置异步 I/O 所有者, F_GETLK/F_SETLK/F_SETLKW,获得/设置文件记录锁;失败返回 -1 50 int ioctl( int fd, int cmd, ...); 串口的波特率等设置,光盘的操作...; ioctl( fd, CDROMEJECT, NULL ) 弹出光驱,用 O_NONBLOCK 打开光驱,因为光驱中可能没有光盘 51 int select( int maxfd, fd_set *readset, fd_set *writeset, fd_set *exceptset, const struct timeval *timeout ); 监控多个文件描述符上是否由输入输出或错误,处理阻塞方式打开的文件和非阻塞方式打开的文件 select 都是以阻塞方式对待,而不会立即返回; maxfd 是检测描述符的个数,值应该是最大文件描述符的值 +1; readset 是被监控是否有输入的文件描述符集,不监控时设为 NULL,writeset 被监控是否可以输出的文件描述符集; exceptset 被监控是否由错误产生的文件描述符集; timeval 监控超时时间,设为 NULL 表示一直阻塞到有文件描述符被监控到有变化;成功返回 3 个集合中有指定变化的文件描述符数目,超时返回 0,失败返回 -1 52 FD_ZERO( &rset ); 将文件描述符集 rset 的所有位清 0 53 FD_SET( 4, &rset ); 设置文件描述符集 set 的 bit 4 ,监控文件描述符为 4 的文件( fd_set rfds; FD_SET( 4, rfds ) 54 FD_CLR( fileno( stdin ), &rset ); 将文件描述符集 rset 的 bit 0 置 0 55 FD_ISSET( socketfd, &rset ); 若文件描述符集 rset 的 socketfd 位置 1,返回真,有输入时就被置 1 56 int poll( struct pollfd fds[], nfds_t nfds, int timeout ); 监控多个文件描述符; nfds 是 fds[] 大小; timeout 是超时时间(毫秒),负值表示没有超时时间; events 和 revents 中的事件使用 poll.h 中定义的宏来表示, POLLIN 有普通数据可读, POLLPRI 有高优先级数据可读, POLLOUT 无阻塞的写, POLLRDUP TCP 套结字的对端关闭了写,即不能再读, POLLERR 出现了错误, POLLNVAL 描述符 fd 没有打开, POLLHUP 挂断;成功返回 fds 数组中就绪元素个数(该元素的 revents 不为 0),超时而描述符没有就绪则返回 0,失败返回 -1(例 fds[0].fd = 0; fds[0].events = POLLIN; 设置事件 -> fds[0].revents & POLLIN 判断事件是否发生) 57 int stat( const char *path, struct stat *buf ); 查看文件或目录属性,复制到 buf 中;成功返回 0,失败返回 -1 58 int lstat( const char *path, struct stat *buf ); 和 stat 区别在于如果文件是软链接,会获取软链接本身的属性 59 int fstat( const int filedes, struct stat *buf); 和 stat 区别在于他使用文件描述符 60 61 int S_ISREG( buf.st_mode ); 普通文件 62 int S_ISDIR( buf.st_mode ); 目录 63 int S_ISCHR( buf.st_mode ); 字符设备文件 64 int S_ISBLK( buf.st_mode ); 块设备 65 int S_ISFIFO( buf.st_mode ); 有名管道 66 int S_ISLNK( buf.st_mode ); 软链接 67 int S_ISSOCK( buf.st_mode ); 套结字文件 68 69 int S_ISUID( buf.st_mode ); 执行时设置用户 ID 70 int S_ISGID( buf.st_mode ); 执行时设置组 ID 71 int S_ISVTX( buf.st_mode ); 保存正文 72 int S_IRWXU( buf.st_mode ); 用户(所有者)读、写、执行 73 int S_IRUSR( buf.st_mode ); 用户读 74 int S_IWUSR( buf.st_mode ); 用户写 75 int S_IXUSR( buf.st_mode ); 用户执行 76 int S_IRWXG( buf.st_mode ); 组读、写、执行 77 int S_IRGRP( buf.st_mode ); 组读 78 int S_IWGRP( buf.st_mode ); 组写 79 int S_IXGRP( buf.st_mode ); 组执行 80 int S_IRWXO( buf.st_mode ); 其他读、写、执行 81 int S_IROTH( buf.st_mode ); 其他读 82 int S_IWOTH( buf.st_mode ); 其他写 83 int S_IXOTH( buf.st_mode ); 其他执行 84 85 int MAJOR( buf->st_rdev ); 获取主设备号 86 int MINOR( buf->st_rdev ); 获取次设备号 87 88 time_t time( time_t *tp ); 返回系统时间给 tp ,返回自 epoch 到现在的秒数;成功返回值也是 tp,失败返回 -1 89 int gettimeofday( struct timeval *tv, struct timezone *tz ); 得到当前时间和时区,在 sys/time.h 中,分别写到 tv 和 tz 中, tz 常为空,如果 tz 为 NULL 则不向 tz 写入;成功返回 0,失败返回 -1;使用时不要使用结构体指针 90 char *ctime( time_t *tp ); 将日历时间转换为字符串返回,必须包含 time.h 头文件,不然会出现段错误 91 struct tm *gmtime( time_t ); 92 sturct tm *localtime( time_t ); 将日历时间转换为结构体记录的时间 93 char *asctime( const struct *tm ); 将结构体时间转换为字符串返回 94 size_t strftime( char *s, size_t max, const char *format, const struct tm *tm ); 将结构体记录时间转换为自定义格式的字符串 95 time_t mktime( struct tm *tm ); 将结构体记录的时间转换为日历时间 96 clock_t clock(void); 成功返回程序开始到调用此函数时使用 CPU 的时间,不考虑子进程等,标准 POSIX 精确到10ms 97 98 DIR *opendir( const char *name ); 成功返回 DIR* 形态的目录流,失败返回 NULL 99 struct dirent *readdir( DIR *dir ); 每次读取一条目录结构;成功返回 dirent 指针,失败返回 NULL 100 int closedir( DIR *dir ); 失败返回 -1;错误原因存在 errno 中 101 void rewinddir( DIR *dirp ); 回绕目录,回到目录流开始处,不用关闭目录 102 103 ssize_t readlink( const char *path, char *buf, size_t bufsize ); 返回软链接指向文件的文件名存放在 buf 中,不包括字符串结束符'\0',需要自己添加; buf 是字符数组; bufsize 是 buf 大小;成功返回是软链接指向文件文件名的长度,失败返回 -1 104 105 struct passwd *getpwuid( uid_t uid ); 在 pwd.h 中,获得 uid 对应用户的属性信息,失败返回 NULL 106 struct group *getgrgid( gid_t gid ); 在 grp.h 中,失败返回 NULL 107 108 109 多进程编程 110 111 pid_t getpid( void ); 获得调用进程的 id 112 pid_t getppid( void ); 113 char *getenv( const char *name ); 获取环境变量 114 int putenv( const char *str ); 新建环境变量( str like " name = string " ) 115 int setenv( const char *name, const char *value, int rewrite ); 设置环境变量,若已存在,则参考 rewrite 参数 116 int fork(); 成功子进程返回值为 0 ,父进程返回子进程 pid,失败返回 -1 给父进程 117 int dup( fildes ); 复制已存在的文件描述符 fildes(默认为 0 ), 成功返回新的文件描述符 118 int dup2( int fildes, int fildes2 ); 进程内部复制文件描述符,如果 fildes2 已经是一个文件描述符,则先关闭,成功则返回 fildes2 119 pid_t wait( int *status ); 父子进程同步(阻塞父进程),等待任何子进程,子进程变成僵尸进程也可以销调; status 用于存放子进程结束状态;成功则返回子进程 id 号,失败返回 -1 120 pid_t waitpid( pid_t pid, int *status, int options ); 等待子进程结束; pid == -1 等待任何子进程, > 0 等待进程 id 和 pid 相等的子进程, == 0 等待其组 id 等于调用进程的组 id 的任一子进程, < -1 等待其组 id 等于 pid 的绝对值的任一子进程; options 设置等待方式, 0 不设置, WNOHANG 如果没有任何已结束的进程则立即返回, WUNTRACED 如果子进程进入暂停状态则立即返回 121 122 int execl( const char *pathname, const char *arg0, ..., (char *)0 ); 成功不返回任何值,失败返回-1;pathname 是执行文件的全路径名,arg0 是文件执行的第一个参数,以 ( char * )0 表示命令行参数的结束 123 int execv( const char *pathname, char *const argv[] ); 124 int execle( const char *pathname, const char *arg0, (char *)0, char *const envp[] ); envp 是新程序的环境变量 125 int execve( const char *pathname, char *const argv[], char *const envp[] ); 系统调用,其他 5 个是封装的 execve 126 int execlp( const char *filename, const char *arg0, ..., (char *)0 ); 127 int execvp( const char *filename, char *const argv[] ); 128 E 传递环境变量表,L 单独的参数传递(最后需要一个 NULL),V 传一个指针数组作为命令行参数,P 按照 PATH 查找新程序 129 130 void fflush( stdout ); 刷新输出缓冲区(强制 printf 提交内容) 131 void fgets( buf, 100, stdin ); 输入字符串 132 133 void exit( int status ); stdlib.h, 返回内核前做些清理工作,包括调用 Exit handler, 彻底关闭 I/O 流(将缓冲区数据都提交给内核 134 void _exit( int status ); unistd.h, 立即返回内核,不执行 exit handler, 不刷新 I/O 流 135 int atexit( void ( *func )( void )); 注册 exit handler,函数无参数,无返回值,在 ANSI C 中,最多注册 32 个 exit handler,按注册顺序逆序调用;成功返回 0 136 int on_exit( void ( *func )( int, void * ),void *arg ); 注册 exit handler, 函数有两个参数,一个 int 参数(是系统回调函数时传给函数进程的退出值),另一个参数是指针 arg(任何指针都可做参数),参数的值为程序执行完后的值,这时缓冲区内容被删除了,堆栈区内容没有被删除 137 138 void getpgrp() 返回调用进程的进程组 id (进程组组长的 id ) 139 void setpgid() 参加一个现有进程组或创建一个新进程组,创建守护进程 daemon ,创建时最好关闭所有文件描述符等 140 int setsid(); 如果调用此函数的进程不是一个进程组组长就创建一个新对话期( session )(多个进程组集合),此进程变为新对话期的首进程,成为新进程组的组长,此进程没有控制终端;是进程组组长则返回出错 141 void chdir("/tmp"); 改变工作目录 142 void umask(0); 将 umask 改为 0 143 pid_t sysconf( _SC_OPEN_MAX ); 返回系统允许打开的文件描述符最大数目 144 pause(); 暂停程序 145 146 void openlog( const char *indent, int option, int facility ); 使用 syslog 可以没有这个函数; indent 指向的字符串将出现在每条消息的最前面; option 为 LOG_CONS 表示当向 syslog 守护进程传送消息失败时把消息写入系统控制台, LOG_PID 表示守护进程的 pid 将出现在每条消息中, LOG_NDELAY LOG_NOWAIT LOG_ODELAY LOG_PERROR; facility 为 LOG_DAEMON 表示消息由系统守护进程发送, LOG_AUTH LOG_AUTHPRIV LOG_CRON LOG_FTP LOG_KERN LOG_NEWS LOG_SYSLOG LOG_USER LOG_UUCP LOG_LOCAL0~LOG_LOCAL7 OG_LPR OG_MAIL 147 void syslog( int priority, const char *format,... ); 向 syslogd 守护进程送出消息(写入 /var/log/messages 中); priority 表示消息紧急程度,可以是 LOG_EMERG LOG_ALERT LOG_CRIT LOG_ERR LOG_WARNING LOG_NOTICE LOG_INFO LOG_DEBUG; format 是写入消息的字符串,可以和 printf 一样使用格式化串 148 void closelog( void ); 使用 syslog 可以没有这个函数; 149 150 151 进程间通信 152 153 void ( *signal( int signum, void (*handler)(int)) )(int); 可靠性低,在一个信号处理程序运行时接收到其他信号可能打断当前处理程序运行,可用 sigaction() 代替;成功返回可用信号处理函数( void (*)(int) ),失败返回 SIG_ERR; handler 时信号处理函数的函数指针,有个 int 参数(为系统传给处理函数的信号值),还可以是 SIG_IGN(忽略该信号)、 SIG_DFL(采用默认行为) 154 int kill( pid_t pid, int sig ); 发送信号到进程,成功返回 0,失败返回 -1 155 int alarm( unsigned int seconds ); 在 seconds 秒后发送 SIGALRM 信号,seconds 为 0 则取消设置的闹钟;成功则返回上一个闹钟还剩多少时间,失败返回 -1 156 void abort(void); 结束进程,并向父进程发送 SIGABRT 信号 157 158 int WIFEXINTED( int status ); 判断子进程的返回状态是不是正常,正常退出返回真 159 int WEXITSTATUS( int status ); 返回子进程正常退出的状态值 160 int WIFSIGNALED( int status ); 判断子进程退出状态是否是非正常退出,非正常退出返回真 161 int WTERMSIG( int status ); 返回非正常退出状态的信号 162 163 int sigemptyset( sigset_t *set ); 清空信号集合,全 0 ,不阻塞任何信号 164 int sigfillset( sigset_t *set ); 将信号集合置位,阻塞相应的信号 165 int sigaddset( sigset_t *set, int signum ); 对应位置 1 166 int sigdelset( sigset_t *set, int signum ); 对应位置 0 167 int sigpending( sigset_t *set ); 将正在被阻塞的信号记录到集合中,获取阻塞了的信号集合 168 int sigismember( const sigset_t *set, int signum ); 集合中对应信号位为 1 则返回真,用来判断集合中是否由此信号 169 int sigprocmask( int how, const sigset_t *set, sigset_t *oldset ); 用来获得和改变调用进程的信号掩码,当前信号集合会赋值给 oldset;使用时在关键代码前设置掩码,然后执行关键代码,之后获取被阻塞的信号,逐步处理信号; how = SIG_BLOCK 信号集合掩码设置为当前信号集合与 set 的并集, how = SIG_UNBLOCK 设置为当前信号集合减去 set 中的信号, how =SIG_SETMASK 设置为 set 170 171 int sigaction( int signum, const struct sigaction *act, struct sigaction *oldact ); signum 不能是 SIGSTOP 或者 SIG_KILL, oldact 返回以前的 act,可以指定为 NULL 172 173 int pipe( int fd[2] ); 无名管道,写入 fd[1], 从 fd[0] 读取,只能在有亲缘关系的进程间通信;成功返回 0,失败返回 -1 174 175 int mkfifo( char *pathname, mode_t mode ); 有名管道,打开管道文件进行读写,如果管道为空,进程将阻塞,管道关闭写端再读时将立即返回,返回值为 0,读端关闭写端再写将产生 sigpipe 信号,默认结束进程 176 177 int shmget( key_t key, size_t size, int shmflg ); key 值唯一标志共享内存( Shared memory );size 是建立内存大小;shmflg = IPC_CREAT 如果共享内存不存在则创建一个,存在则获得该内存;shmflg = IPC_EXCL 只有共享内存不存在时才创建,不然就产生错误;成功则返回共享内存标志码,其唯一标志一块共享内存,错误返回 -1 178 void *shmat( int shmid, const void *shmaddr, int shmflg ); 允许进程访问一块共享内存;shmid 是 shmget 的返回值;shmaddr 是共享内存在本进程中的虚拟地址起始地址,通常程序员不知道就填 0;shmflg 是进程对该共享内存的操作模式,SHM_RDONLY 表示只读模式,0 表示可读可写;成功返回共享内存的虚拟地址起始地址(数组名),失败返回 -1 179 int shmdt( const void *shmaddr ); 断开共享内存区的映射,成功返回 0,失败返回 -1 180 int shmctl( int shmid, int cmd, struct shmid_ds *buf ); cmd = IPC_RMID 删除共享内存;buf 是保存内存模式状态和访问权限的数据结构,常为 NULL;成功返回 0,失败返回 -1 181 182 int semget( key_t key, int nsems, int semflg ); 获取信号量数组; key 唯一标志信号量( Semaphore );nsems 是信号量个数,一般为 1(若干个信号量组合在一起形成信号量数组,其实 nsems 就是指定数组元素个数); semflg 标志相当于信号量的访问权限;成功返回信号量标志码,失败返回 -1 183 int semctl( int semid, int semnum, int cmd , ...); 控制信号量,设初值或是删除信号量集、查看信号量信息等; semnum 信号量编号,用于指定要操作的信号量是信号量数组中的哪一个; cmd 为 IPC_STAT 读取一个信号量集的数据结构 semid_ds ,并将其存放在 union semun 类型的第四个参数的 buf 中; IPC_SET 设置信号量集的数据结构 semid_ds 中的元素 ipc_perm,其值取自 union 类型的第 4 个参数 buf; IPC_RMID 将信号量集从内存中删除; GETALL 读取信号量集中的所有信号量值; GETNCNT 返回正在等待资源的进程数目; GETPID 返回最后一个执行 semop 操作的进程 pid; GETVAL 返回信号量集中单个的信号量的值; GETZCNT 返回正在等待玩空空闲的资源的进程数目; SETALL 设置信号量集中的所有信号量的值; SETVAL 设置信号量集中的一个单独信号量的值,被设置的值在 union semun 类型的第 4 个参数的 val 中;最后的省略号表示可能有第 4 个参数,如果 cmd 要设置初值就是第 4 个参数 184 int semop(int semid, struct sembuf *sops, size_t nsops ); semid 指明操作的信号量数组; sops 包含了具体的操作(P/V),详见结构体部分;nsops 说明 sops 指向的结构体数量,通常为 1 185 186 int msgget( key_t key, int flag ); 打开现存队列或创建消息队列,成功返回消息队列 msgid ,失败返回 -1 187 int msgsnd( int msgid, const void *ptr, size_t nbytes, int flag ) ptr 指向一个长整形数,包含了正长整形的消息类型,在其后立即跟随了消息数据,nbytes 是消息长度(为 0 则无数据),若发送的最长消息是 512,则定义结构体:struct mymesg{ long mtype;/*positive message type*/ char mtext[512];};, ptr 即指向这个结构体,接受这可以使用消息类型以非先进先出的次序; flag 为 IPC_NOWAIT 时,当消息队列满会使得 msgsnd 立即出错返回,errno 被置为 EAGAIN ,若不设为 IPC_NOWAIT,进程会一直阻塞直到有空闲空间或系统删除了此队列( errno 被置为 EIDRM )或捕捉到信号从信号处理程序返回( errno 被置为 EINTR );成功返回 0,失败返回 -1 188 int msgrcv( int msgid, void *ptr, size_t nbytes, long type, int flag ); ptr 和 msgsnd 一样;nbytes 说明缓冲区长度,若返回消息大于 nbytes,如果在 flag 中设置了 MSG_NOERROR, 则截短消息(这种情况下出错回返回信息),如果没有设置此标志,则出错返回 E2BIG (消息仍留在队列中); type == 0 返回队列中的第一个消息, type > 0 返回队列中消息类型为 type 的第一个消息, type < 0 返回队列中消息类型小于或等于 type 绝对值,且其类型值又最小的消息(设置优先级); flag 值为 IPC_NOWAIT 使操作不阻塞,这是如果没有指定消息,则出错返回 ENOMSG,不为 IPC_NOWAIT 时处理方式和 msgsnd 一样;成功返回数据长度,失败返回 -1 189 190 191 多线程编程初步 192 193 int pthread_create( pthread_t *thread, pthread_attr_t *attr, void *(*func)(void*), void *arg); thread 是被创建线程的标识; attr 是线程的属性的指针,NULL 为使用默认属性; func 被创建线程的程序代码( (void*) funcname ),无返回值; arg 是 func 的参数,是一个指针,可为 NULL。 194 pthread_exit( void *reval ); 线程自行结束,retval 存放线程退出状态 195 int pthread_join( pthread_t th, void **thread_return ); 等待 th 线程结束,指针 thread_return 指向的位置存放的是终止线程的返回状态, void 类型,可强制转换为 int 型,调用时使用 &thread_return,可设为 NULL;成功返回 0,失败 EINVAL、 ESRCH 或其他(失败原因可能是这个线程是分离的) 196 197 pthread_mutex_t mutex; 198 pthread_mutex_init( &mutex, NULL ); 在主线程中初始化锁为解锁状态 199 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; 在编译时初始化为解锁状态,不用上面的步骤咯 200 pthread_mutex_lock( &mutex ); 在访问对象前加锁( P 操作 ),如果已被加锁,则阻塞直到锁打开 201 pthread_mutex_unlock( &mutex ); 访问对象后解锁( V 操作 ) 202 pthread_mutex_trylock( &mutex ); 加锁对象已被加锁,立即返回,返回值为 EBUSY 203 204 int sem_init( sem_t *sem, int pshared, unsigned int value ); 初始化信号量,sem 为指向信号量结构的一个指针; pshared 不为 0 时此信号在进程间共享,否则只能在当前进程的所有线程共享; value 给出信号量初值;成功返回 0,失败返回 -1 205 int sem_wait( sem_t *sem ); 信号量减 1;当 sem 为 0 时阻塞;成功返回 0,失败返回 -1 206 int sem_post( sem_t *sem ); 信号量加 1;当有线程阻塞时调用这个函数使得其中一个进程不再阻塞,选择机制由线程调度策略决定;成功返回 0 ;失败返回 -1 207 int sem_destroy( sem_t *sem ); 刪除信号量,成功返回 0,失败返回 -1 208 209 int pthread_attr_init( pthread_attr *attr ); 初始化线程属性结构,设置好 attr 后才能创建线程,主要包括是否绑定,是否分离,堆栈地址,堆栈大小,优先级,默认非绑定、非分离、1 MB 的堆栈,与进程相同优先级;绑定是将线程“绑”在一个轻进程上,被绑定的进程具有较高的响应速度;分离状态决定了线程以什么方式终结自己,非分离线程只有等待创建的线程结束才能释放自己占用的资源( pthread_join 返回时创建的线程擦才结束),而分离线程不会被其他线程所等待,运行结束就释放资源 210 int pthread_attr_destroy( pthread_attr_t *attr ); 211 int pthread_attr_setscope( pthread_attr *attr, PTHREAD_SCOPE_SYSTEM ); 设置绑定状态,PTHREAD_SCOPE_SYSTEM 为绑定,PTHREAD_SCOPE_PROCESS 为非绑定 212 int pthread_attr_setdetachstate( pthread_attr_t *attr, int detachstate ); 第二个参数选择 PTHREAD_CREATE_DETACHED 为分离, PTHREAD_CREATE_JOINABLE 为不分离 213 int pthread_cond_timewait(); 留出足够时间让函数 pthread_create 执行完,避免错误;设置一个分离线程,如果他运行很快,他可能在函数 pthread_create 返回之前就终止,这样函数就可能返回错误的线程号 214 int pthread_attr_getschedparam( pthread_attr_t *attr, struct sched_param *param ); 得到线程优先级,存放在结构体 sched_param 中,param.sched_priority = 10 215 int pthread_attr_setschedparam( pthread_attr_t *attr, const struct sched_param *param ); 设置线程优先级 216 217 int pthread_cancel( pthread_t tid ); 一个线程杀死另一个线程时调用, tid 是线程标识 218 int pthread_setcancelstate( PTHREAD_CANCEL_DISABLE, NULL ); 设置线程属性拒绝被杀死, PTHREAD_CANCEL_ENABLE 表示允许被杀死(缺省) 219 int pthread_setcanceltype( PTHREAD_CANCEL_DEFDERRED, NULL ); 设置延后被杀死(缺省),PTHREAD_CANCEL_ASYNCHRONOUS 立即被杀死 220 void pthread_testcancel( void ); 用于设置杀死点,在延迟杀死的情况下,线程会在收到杀死请求后延迟至第一个杀死点时被杀死;杀死点是在被杀死的进程中,杀死点有调用 testcancel 的点和引起进程阻塞的系统调用( sleep )处, 221 int pthread_detach( pthread_t tid ); 设置线程不需要被 pthread_join, 因为已经结束的线程没有被 pthread_join 将会在系统中累积僵尸进程 222 223 224 网络编程 225 226 int socket( int family, int type, int protocol ); 返回套结字的文件描述符; family 是协议或地址族,TCP/IP 为 PF_INET 或 AF_INET; type 是服务类型,TCP 是 SOCK_STREAM, UDP 是 SOCK_DGRAM; protocol 是使用的协议号,用 0 指定 family 和 type 的默认协议号;成功则返回 sockfd,失败返回 -1 227 int bing( int sockfd, const struct sockaddr *localaddr, socklen_t addrlen ); 为一个套结字指明一个本地 IP 和协议端口号;sockfd 是 socket 创建的套结字描述符; localaddr 是一个地址结构,指定 IP 地址和协议端口号; addrlen 是地址结构的字节数大小;成功返回 0,失败返回 -1 228 int listen( int sockfd, int queuelen ); 使套结字处于监听状态; queuelen 是请求队列大小,并不是一个准确的连接数;成功返回 0 ,失败返回 -1 229 int accept( int sockfd, struct sockaddr *addr, socklen_t *addrlen ); 只用于流套结字,从监听队列中取走下一个连接请求,创建一个新套结字,并返回新套结字描述符; addr 是一个地址结构的指针,调用 accept 成功后在该结构中填入远程机器的 IP 地址和协议端口号; addrlen 初始指定为 struct sockaddr 结构体大小的存放地址,调用 accept 后在其中填入远程机器 socket 地址的实际大小;成功返回一个非负套结字描述符,失败返回 -1 230 int connet(int sockfd, const struct sockaddr * addr, socklen_t addrlen ); 允许调用者为先前创建的套结字指明远程端点的地址,如果套结字使用了 TCP ,他就使用 3 次握手建立一个连接;成功返回 0 231 232 ssize_t sendto( int sockfd, const void *buf, size_t len, int flags, const struct sockaddr *dest_addr, socklen_t addrlen ); 向指定数据接收端发送指定数据;dest_addr 为 NULL ,addrlen 为 0 时等于 send; flags 发送标识,一般为 0; dest_addr 数据接收端地址结构体的指针;addrlen 数据接收端地址结构体大小;成功返回实际发送的数据,失败返回 -1 233 ssize_t recvfrom( int sockfd, void *buf, size_t len, int flags, struct sockaddr *src_addr, socklen_t *addrlen ); src_addr 存放数据发送端的地址; addrlen 是 src_addr 的大小,不需要 src_addr 和 addrlen 置为 NULL;成功返回实际接收到的字节数,失败返回 -1 234 235 ssize_t send( int sockfd, const void *buf, size_t len, int flags ); 返回时发送出去的字节(同步)或发送到 socket 缓冲区的字节(异步),系统默认发送和接受一次为 8688 B( 8.5 K ) 236 ssize_t recv(int sockfd, void *buf, size_t len, int flags); flags 为 0 时等于 write/read, flags 参数为 MSG_DONTROUTE send 不查找路由表(目标在本地网络),MSG_OOB send/recieve 带外数据, MSG_PEEK recieve 查看数据并不从系统缓冲区移走数据, MSG_WAITALL recieve 阻塞,等待所有数据,返回值等于 len 237 ssize_t sendmsg( int sockfd, const struct msghdr *msg, int flags ); 238 ssize_t recvmsg( int sockfd, const struct msghdr *msg, int flags ); 239 240 int shutdown( int fd, int how ); TCP 连接是双向的,使用 close 会把读写通道都关闭, shutdown 只关闭一个方向; how 为 SHUT_WR 不再发送, SHUT_RD 不再读,接收到的对端数据都将被确认,然后丢弃, SHUT_RDWR 与 close 不同, colse 把描述字的引用计数减 1,当计数变为 0 时才关闭该套结字,而 shutdown 直接就发送 FIN 分节(父进程的关闭操作将导致客户端的网络连接被关闭) 241 242 uint16_t htons( uint16_t hostshort ); 将 unsigned short 类型的数据的主机字节顺序转换为网络字节顺序 243 unsigned long int htonl( unsigned long int hostlong ); 将本机上的 long 转换为网络上的 long 244 unsigned short int htons( unsigned short int hostshort ); 245 unsigned long int ntohl( unsigned long int netlong ); 246 unsigned short int ntohs( unsigned short int netshort ); 247 248 int inet_pton( int af, const char *src, void *dst ); 将点分格式的地址字符串转换为网络字节序整型数;af 为 AF_INET 或 AF_INET6;src 点分格式的地址; dst 转换后的地址;成功返回 1,错误返回 -1 249 const char *inet_ntop( int af, const void *src, char *dst, socklen_t cnt ); 将网络字节序整型转换为点分格式的 ip 地址; src 整形变量地址; des 用来存储转换后的数据的地址; cnt 存储空间大小 常为 16;成功后返回转换后的地址,失败返回 NULL 250 251 int inet_aton(const char *cp, struct in_addr *inp); 将点分十进制 IP 地址 cp 转换为二进制数 inp;成功返回非 0,失败返回 0 252 in_addr_t inet_addr(const char *cp); 将点分十进制 IP 地址 cp 转换为二进制数;成功返回二进制地址,失败返回 -1 253 char *inet_ntoa(struct in_addr in); 将二进制地址转换为点分十进制 254 255 struct hostent *gethostbyname( const char *hostname ); 将机器名(如 linux.yu.net )转换为一个结构体指针,指针里存放了域名和 IP 地址等信息 256 struct hostent *gethostbyaddr( const void *addr, int len, int type ); 将一个 socket 地址结构体里面的 IP 地址转换为一个结构体指针;失败返回 NULL 且设置 h_errno 错误变量 257 char *h_strerror(int errnum); 打印详细的错误信息( h_errno ) 258 259 int getsockname( int sockfd, struct sockaddr *localaddr, int *addrlen ); 获取套结口 sockfd (已绑定的)的本端 socket 地址,存放到 localaddr 中,其长度放到 addrlen 中;成功返回 0,失败返回 -1 260 int getpeername( int sockfd, struct sockaddr *peeraddr, int *addrlen ); 获取套结口 sockfd 的远端 socket 地址 261 struct servent *getservbyname( const char *servname, const char *protoname ); 通过服务得到端口号 262 struct servent *getservbyport( int port, const char *protoname ); 得到指定端口号的服务(知名服务和端口号对应) 263 264 int getsockopt( int sockfd, int level, int optname, void *optval, socklen_t *optlen ); 获得 socket 属性 265 int setsockopt( int sockfd, int level, int optname, const void *optval, socklen_t optlen ); 设置 socket 属性 266 int setsockopt( sockfd, SOL_SOCKET, SO_REUSEADDR, &on, sizeof(on) ); 在 socket 和 bind 之间设置允许地址重用套结字选项(解决不能绑定到指定地址);设置套结口属性 SO_REUSEADDR,在一台机器上运行多个服务通配地址被占用时要设置这个属性;on 为整数 1,表示 true; 267 int setsockopt( s, SOL_SOCKET, SO_REUSEADDR, ( const char* )&bReuseaddr, sizeof(bReuseaddr) ); 关闭 socket 后想继续重用该 socket;bReuseaddr = 1 268 int setsockopt( s, SOL_SOCKET, SO_DONTLINGER, ( const char* )&bDontLinger, sizeof(bDontLinger) ); 处于连接状态的 socket 在调用 close 后立即关闭,不经历 TIME_WAIT 269 int setsockopt( socket, SOL_SOCKET, SO_SNDTIMEO, ( char * )&nNetTimeout, sizeof(int) ); 设置发送时限为 nNetTimeout ,单位是毫秒 270 int setsockopt( socket, SOL_SOCKET, SO_RCVTIMEO, ( char * )&nNetTimeout, sizeof(int) ); 设置接收时限为 nNetTimeout 271 int setsockopt( s, SOL_SOCKET, SO_RCVBUF, ( const char* )&nRecvBuf, sizeof(int) ); 设置接收缓冲大小 32 * 1024 B,避免 send() recv() 不断循环收发 272 int setsockopt( s, SOL_SOCKET, SO_SNDBUF, ( const char* )&nSendBuf, sizeof(int) ); 设置发送缓冲区大小 273 int setsockopt( s, SOL_SOCKET, SO_SNDBUF, ( char* )&nZero, sizeof(int) ); nZero = 0, 在发送数据时不经历由系统缓冲区复制到 socket 缓冲区而影响程序性能 274 int setsockopt( s, SOL_SOCKET, SO_RCVBUF, ( char* )&nZero, sizeof(int) ); nZero = 0, 在接收数据时不经历由 socket 缓冲区复制到系统缓冲区而影响程序性能 275 int setsockopt( s, SOL_SOCKET, SO_BROADCAST, ( const char* )&bBroadcast, sizeof(int) ); bBroadcast = 1, 发送的数据具有广播特性 276 int setsockopt( s, SOL_SOCKET, SO_CONDITIONAL_ACCEPT, ( const char* )&bConditionalAccept, sizeof(int) ); bConditionalAccept = 1, client 连接服务器过程中,在非阻塞模式(阻塞模式下没用)下 connect 过程中设置 connect 延时到 accept 被呼叫 277 int setsockopt( s, SOL_SOCKET, SO_LINGER, ( const char* )&m_sLinger, sizeof(linger) ); 在关闭 socket 时允许数据发送完再关闭 socket;使用结构体 linger( 实例 m_sLinger ),设置 m_sLinger.l_onoff = 1;在 close 过程中数据还没发送完毕时允许逗留 m_sLinger.l_linger = 5;允许逗留 5 s; 278 279 280 281 头文件 282 283 284 stdlib.h atexit, on_exit, pipe, malloc, abort 285 stdio.h printf, perror 286 unistd.h STDIN_FIFLNO(0), STDOUT_FIFLNO, STDERR_FIFLNO, dup2, _exit, alarm 287 fcntl.h O_CREAT, O_APPEND, O_EXCL, O_RDONLY 288 poll.h 289 curses.h 编译时要指定库 ncurses,缺少时安装 kernel-devel ncurses-devel 290 errno.h EXIT_SUCCESS, EXIT_FAILED, perror 291 limits.h PIPE_BUF( 4096 ), 292 netdb.h 网络, struct hostent, 293 time.h 294 dirent.h 目录 295 pwd.h 296 grp.h 297 pthread.h 线程 298 semaphore.h 信号量 299 signal.h 300 string.h 301 strings.h strcasecmp, bzero 302 syslog.h 303 304 arpa/inet.h 网络 305 306 sys/ioctl.h 307 sys/ipc.h 308 sys/msg.h 消息队列 309 sys/select.h 310 sys/sem.h 信号量 311 sys/shm.h 共享内存 312 sys/socket.h 313 sys/stat.h 314 sys/time.h gettimeofday 315 sys/types.h 316 sys/wait.h 317 318 319 320 自定义类型 321 322 323 FILE 324 DIR 325 atomic_t typedef struct { volatile int counter; } atomic_t; 326 bool typedef _Bool bool; 327 clockid_t typedef __kernel_clockid_t clockid_t; 328 daddr_t typedef __kernel_daddr_t daddr_t; 329 dev_t typedef __kernel_dev_t dev_t; 330 gid16_t typedef __kernel_gid16_t gid16_t; 331 gid_t typedef __kernel_gid32_t gid_t; 332 id_t 可以涵盖任务 pid_t,uid_t 或者 gid_t 的整数类型,不能分割来使用 333 ino_t typedef __kernel_ino_t ino_t; 334 in_addr_t 335 in_port_t typedef uint16_t in_port_t; 336 key_t typedef __kernel_key_t key_t; 337 loff_t typedef long long __kernel_loff_t; typedef __kernel_loff_t loff_t; 338 mode_t typedef unsigned int __kernel_mode_t; typedef __kernel_mode_t mode_t; 339 mqd_t typedef __kernel_mqd_t mqd_t; 340 nlink_t typedef __kernel_nlink_t nlink_t; 341 off_t typedef __kernel_off_t off_t; 342 old_gid_t typedef __kernel_old_gid_t old_gid_t; 343 old_uid_t typedef __kernel_old_uid_t old_uid_t; 344 pid_t typedef __kernel_pid_t pid_t; 345 sa_family_t typedef unsigned short int sa_family_t; 346 sem_t typedef long sem_t; 347 size_t typedef __kernel_size_t size_t; 348 ssize_t typedef __kernel_ssize_t ssize_t; 349 suseconds_t typedef long __kernel_suseconds_t; typedef __kernel_suseconds_t suseconds_t 350 timer_t typedef __kernel_timer_t timer_t; 351 time_t typedef long time_t; 352 uid16_t typedef __kernel_uid16_t uid16_t; 353 uid_t typedef __kernel_uid32_t uid_t; 354 uintptr_t typedef unsigned long uintptr_t; 355 __kernel_clock_t typedef long __kernel_clock_t; 356 __kernel_gid_t typedef unsigned short __kernel_gid_t; 357 __kernel_ino_t typedef unsigned long __kernel_ino_t; 358 __kernel_ipc_pid_t typedef unsigned short __kernel_ipc_pid_t; 359 __kernel_mode_t typedef unsigned short __kernel_mode_t; 360 __kernel_nlink_t typedef unsigned short __kernel_nlink_t; 361 __kernel_off_t typedef long __kernel_off_t; 362 __kernel_pid_t typedef int __kernel_pid_t; 363 __kernel_ptrdiff_t typedef int __kernel_ptrdiff_t; 364 __kernel_size_t typedef unsigned int __kernel_size_t; 365 __kernel_ssize_t typedef int __kernel_ssize_t; 366 __kernel_suseconds_t typedef long __kernel_suseconds_t; 367 __kernel_timer_t typedef int __kernel_timer_t; 368 __kernel_time_t typedef long __kernel_time_t; 369 __kernel_uid_t typedef unsigned short __kernel_uid_t; 370 371 POSIX-Portable Operating System Interface for Unix,要求C语言库的 <.../types.h> 头文件为每个实现 POSIX 的机器定义一个一致的类型集。其中的每一个类型定义很容易与本地C语言区分开来,因为它们都以_t结尾。 372 373 374 375 结构体 376 377 struct timeval //使用范围: gettimeofday,设置时间间隔,没有定义在 time.h 中 378 { 379 time_t tv_sec; //用 gettimeofday 创建结构体时得到到 Epoch 时的秒数 380 suseconds_t tv_usec; //微秒数,六位;在实际中,该函数以及 Linux 内核返回的 timeval 类型的时间值, tv_usec 代表的是微秒精度(10的-6次方秒) 381 }; 382 383 struct timezone { 384 int tz_minuteswest; /* minutes west of Greenwich */ 385 int tz_dsttime; /* type of DST correction */ 386 }; 387 388 struct pollfd{ 389 int fd; //文件描述符 390 short events; // requested events 用于输入 391 short revents; // returned events 用于输出,可以是 events 中指定已发生的事件,也可以是 POLLERR POLLHUP POLLNVAL 3 个错误中的一个 392 } 393 394 struct stat{ 395 dev_t st_dev; //id of device containing file 396 ino_t st_ino; //inode number 397 mode_t st_mode; //protection 398 nlink_t st_nlink; //hard link number 399 uid_t st_uid; 400 gid_t st_gid; 401 dev_t st_rdev; //device id( if special device ) 主次设备号 402 off_t st_size; //total size, in bytes 403 blksize_t st_blksize; //blocksize for filesystem I/O 404 blkcnt_t st_blocks; //number of blocks allocated 405 time_t st_atime; //access 406 time_t st_mtime; //modification 407 time_t st_ctime; //status change 408 } 409 410 struct tm{ 411 int tm_sec; 412 int tm_min; 413 int tm_hour; 414 int tm_mday; 415 int tm_mon; 416 int tm_year; 417 int tm_wday; 418 int tm_yday; 419 int tm_isdst; 420 } 421 422 struct dirent{ 423 ino_t d_ino; //inode number 424 off_t d_off; //offset to the next dirent 425 unsigned short d_reclen; //length of this record 426 unsigned char d_type; //type of file 427 char d_name[256]; //filename 428 } 429 430 struct passwd{ 431 char *pw_name; //用户名 432 char *pw_passwd; //用户密码 433 uid_t pw_uid; 434 gid_t pw_gid; 435 char *pw_gecos; //用户真实姓名 436 char *pw_dir; //用户主目录 437 char *pw_shell; //用户主 shell 程序 438 } 439 440 struct sigaction{ 441 void ( *sa_handler )( int ); //信号处理程序,有一个 int 参数 442 void ( *sa_sigaction )( int, siginfo_t *, void * ); //信号处理程序,第一个参数是信号,第二个参数是指向 siginfo_t 的指针,第三个参数是指向 ucontext_t 的指针 443 sigset_t sa_mask; //信号掩码集合,该集合中屏蔽的信号将在信号处理程序结束之后再被处理,默认屏蔽同类信号 444 int sa_flags; //信号处理程序行为标志,SA_SIGINFO 表示使用 sa_sigaction 代替 sa_handler; SA_NODEFER 表示不屏蔽同类信号,SA_NOCLDSTOP 若 signum 是 SIGCHLD,当一个子进程停止时,不产生此信号; SA_RESTART 由此信号中断的系统调用自动重启; SA_ONSTACK 若用 sigaltstack 已说明了一替换栈,则此信号递送给替换栈上的进程; SA_NOCLDWAIT 若 signum 是 SIGCHLD,则当调用进程的子进程终止时,不创建僵尸进程,若调用进程在后面调用 wait,则阻塞到它所有子进程都终止,此时返回 -1; SA_RESETHAND 对此信号的处理方式在此信号捕捉函数的入口处复原为 SIG_DFL 445 void ( *sa_restorer )( void ); //不再使用 446 }; 447 448 union semun{ 449 int val; //SETVAL 450 struct semid_ds *buf; //IPC_STAT IPC_SET 451 unsigned short *array; //GETALL SETALL 452 struct seminfo *__buf; //IPC_INFO 453 }; 454 455 struct sembuf{ 456 unsigned short sem_num; // 操作信号在信号量集中的编号,第一个编号为 0 457 short sem_op; // 如果值为正数,加到现有信号量内含值中,常用于释放资源;为负数,其绝对值大于信号的现值,操作阻塞,直到信号值大于或等于 sem_op 的绝对值,常用于资源的使用权 458 short sem_flg; // 信号操作标志, IPC_NOWAIT 对信号的操作不能满足时 semop 不会阻塞,并立即返回,同时设定错误信息; C_UNDO 程序结束时,保证信号值被重设为 semop 调用前的值,避免程序异常情况下将资源锁定 459 }; 460 461 struct hostent{ 462 char *h_name; // 主机的正式名称 463 char **h_aliases; // 主机的别名 464 int h_addrtype; // 主机的地址类型 AF_INET 465 int h_length; // 主机的地址长度(对于 ip4 是 32 位) 466 char **h_addr_list; // 主机的 ip 地址列表 467 }; 468 469 struct servent{ 470 char *s_name; // 正式服务名 471 char **s_aliases; // 别名列表 472 int s_port; // 端口号 473 char *s_proto; // 使用的协议 474 }; 475 476 struct sockaddr{ // /usr/include/sys/socket.h,一般的编程中并不直接对此数据结构进行操作,而使用另一个与之等价的数据结构sockaddr_in 477 sa_family_t sa_family; // 2字节的地址家族,如 AF_INET 478 char sa_data[14]; // protocol address 479 }; 480 481 struct sockaddr_un{ // 长度为 110B 482 sa_family_t sun_family; 483 char sun_path[108]; 484 }; 485 486 struct sockaddr_in{ // 长度为 16B 487 sa_family_t sin_family; // 在 socket 编程中只能是 AF_INET 488 in_port_t sin_port; 489 struct in_addr sin_addr; 490 unsigned char sin_zero[sizeof(struct sockaddr) - (sizeof(unsigned short int)) - sizeof(in_port_t) - sizeof(struct in_addr)]; // 长度为 8B,暂时未用,应初始化为 0,为了让 sockaddr 与 sockaddr_in 两个数据结构保持大小相同而保留的空字节。 491 }; 492 493 struct in_addr{ // <arpa/inet.h> 494 in_addr_t s_addr; // 点分十进制转换为的二进制数,32 位的 IPv4 地址 495 }; 496 497 struct linger{ // /usr/include/sys/socket.h 498 unsigned short l_onoff; 499 unsigned short l_linger; 500 }; 501 502 struct msghdr{ 503 void *msgname; 504 int msg_name_len; 505 struct iovec *msg_iov; 506 int msg_iovlen; 507 void *msg_control; 508 int msg_controllen; 509 int msg_flags; 510 }; 511 512 struct iovec{ 513 void *iov_base; // 缓冲区开始地址 514 size_t iov_len; // 缓冲区长度 515 }; 516 517 struct file { 518 /* 519 * fu_list becomes invalid after file_free is called and queued via 520 * fu_rcuhead for RCU freeing 521 */ 522 union { 523 struct list_head fu_list; 524 struct rcu_head fu_rcuhead; 525 } f_u; 526 struct path f_path; 527 #define f_dentry f_path.dentry 528 #define f_vfsmnt f_path.mnt 529 const struct file_operations *f_op; 530 atomic_t f_count; 531 unsigned int f_flags; 532 mode_t f_mode; 533 loff_t f_pos; 534 struct fown_struct f_owner; 535 unsigned int f_uid, f_gid; 536 struct file_ra_state f_ra; 537 unsigned long f_version; 538 #ifdef CONFIG_SECURITY 539 void *f_security; 540 #endif 541 /* needed for tty driver, and maybe others */ 542 void *private_data; 543 #ifdef CONFIG_EPOLL 544 /* Used by fs/eventpoll.c to link all the hooks to this file */ 545 struct list_head f_ep_links; 546 spinlock_t f_ep_lock; 547 #endif /* #ifdef CONFIG_EPOLL */ 548 struct address_space *f_mapping; 549 }; //Linux 2.6.11内核,文件结构体代表一个打开的文件,系统中的每个打开的文件在内核空间都有一个关联的struct file。它由内核在打开文件时创建,并传递给在文件上进行操作的任何函数。在文件的所有实例都关闭后,内核释放这个数据结构。 550 551 552 553 基础 554 555 556 文件描述符范围是 0~OPEN——MAX 早期为 19(每个进程打开20个文件),现在多为256。 557 558 POSIX 标只能是 Unix 中最流行的应用编程界面标准。 559 560 文件结束符 ctrl d , EOF is -1(int),不在任何头文件中定义,应该是 liunx 默认的宏。 561 562 563 文件描述符本质是数组下标,如果子进程不打算使用父进程打开的文件,应该在调用 fork 后关闭该文件;如果父子进程同时打开一个文件,父进程读取文件后,子进程将读取同一文件的后续内容。而两个进程独立读取同一文件时,不会相互干扰。 564 565 内核支持文件描述符的数据结构中,最右边的是 i 节点表,整个系统只有一张,可视为结构体数组,每个元素对应一个物理文件;中间的是文件表,整个系统一张,可视为结构体数组,元素中有 3 个重要的字段,指定文件被打开时采用的选项,文件当前读写位置,指向 i 节点表的指针(关联物理文件);左侧的表是文件描述符表,每个进程一张,可视为指针数组,指向文件表中的一个元素,记录打开的文件。 566 567 extern char **environ; 指向系统环境变量的指针 568 569 刷新 I/O 缓存条件:用户空间的 I/O 库缓存被填满;I/O 缓存库遇到换行符,并且目标是行缓冲设备(屏幕);I/O 流被关闭。调用系统调用(如 write )可以将 I/O 缓存库内容提交给内核。 570 571 572 进程内存布局图:初始数据段data包含已初始化的全局变量和局部静态变量;bbs存放的是未初始化全局变量和未初始化的static局部变量,他们初始值都为0;stack存放的是动态局部变量,heap是malloc分配的空间;之所以子函数不能相互访问对方动态局部变量是因为 main 函数调用 A 函数后再调用 B 函数将覆盖 A 函数的动态局部变量存储空间。 573 574 进程组、对话期、控制终端:对话期是一个或多个进程组的集合,建立与控制终端连接的对话期首进程叫作控制进程;如果对话期首进程调用 open 打开第一个尚未与一个对话期相关联的终端设备时没有指定 O_NOCTTY ,此终端设备则分配给此对话期,如打开 /dev/tty 。 575 576 一般来说,收到 SIGCHLD 后,父进程会使用 wait 系统调用以取得子进程的退出状态,然后内核就可以从内存中释放已结束的子进程的 PCB;而如若父进程没有这么做的话,子进程的 PCB 就会一直驻留在内存中,也即成为僵尸进程。 577 孤儿进程则是指父进程结束后仍在运行的子进程。在类UNIX系统中,孤儿进程一般会被 init 进程所“收养”,成为 init 的子进程。 578 为避免产生僵尸进程,实际应用中一般采取的方式是: 579 1.将父进程中对 SIGCHLD 信号的处理函数设为 SIG_IGN (忽略信号); 580 2. fork 两次并杀死一级子进程,令二级子进程成为孤儿进程而被 init 所“收养”、清理。 581 582 583 进程间通信包括信号、无名管道、有名管道、共享内存、信号量、消息队列。 584 585 操作共享内存类似于操作字符指针,用下标来读写。 586 587 信号量和互斥锁,信号量为 1 时就是互斥锁。 588 589 每个进程都有一个信号掩码,用来定义被阻塞递送的信号集合,阻塞信号在关键代码执行完后才处理信号。 590 591 592 gcc 编译线程时使用 -l pthread ,在 /usr/lib/libpthread.so 中。 593 594 轻进程( LWP )即内核线程,CPU 时间片调度是面向轻进程的,位于系统层和用户层之间,系统通过轻进程实现对线程资源的分配、对线程的控制,一个轻进程控制一个或多个线程。 595 596 597 网络中的 3 次握手和 4 分节终止序列。 598 599 小端系统( little endian )中变量的高位存放在高地址,大端系统中变量的高位存放在低地址,网络中传送的是大端字节。 600 601 流式套结字采用 TCP 协议,数据报套结字采用 UDP 协议,原始套结字允许对底层协议(如 ip 或 icmp )直接访问,常用于新的网络协议的测试等。 602 603 socket 组成为:协议,本地地址,本地端口,远程地址,远程端口。 604 605 TCP 网络编程模型 606 服务器编程模型: socket -> bind -> listen -> accept -> read -> write -> close 607 客户端编程模型: socket -> connet -> write -> read -> close 608 服务器编程模型: socket 建立无任何网络连接的 socket ,返回值是一个文件描述符 -> bind 将 socket 和本机 ip 地址和端口号绑定( 3 元组) -> listen 将 socket 指定为一个监听套结字,用于监听客户端到达的网络连接请求 -> accept 使 socket 进入网络连接状态,服务器能接收客户端连接,没有连接则阻塞,其返回另一个 socket ,被称为连接套结字,是一个 5 元组 -> read 读客户端发送的数据,否则阻塞 -> write -> close 609 客户端编程模型: socket -> connet 将服务器 IP 和端口号作为参数传入,本机选择一个未使用且大于 1024 的端口号作为本地端口,发起连接,建立连接 -> write -> read -> close 610 611 迭代服务器一次只能处理一个客户请求,并发服务器使用多个进程(线程)处理客户连接。并发服务器的一个问题是子进程结束了而父进程还在,会累积僵尸进程,解决方法是子进程结束后给父进程发送 SIGCHLD 信号,父进程在信号处理程序中调用 waitpid 销调子进程。 612 613 UDP 编程模型 614 服务器: socket -> bind -> recvfrom -> sendto -> close 615 客户端: socket -> sendto -> recvfrom -> close 616 617 618 619 实践 620 621 622 判断条件时“if(( flag = open( "./hel.c", O_RDONLY ))< 0);” 等价于 “if( open( "./hel.c", O_RDONLY )< 0);”, 623 但是 if( flag = open( "./hel.c", O_RDONLY )< 0); is wrong. 624 625 626 程序打开的文件的读写权限要基于文件本身的读写权限。 627 628 629 time()函数的使用; 630 631 632 633