UNIX环境高级编程-第三版-程序员宅基地

技术标签: 嵌入式  

Unix环境高级编程-第三版

之前学习了《Linux系统编程》对于常见的概念和函数都有了基础的认知,这里准备通过这本书,深入学习系统API相关内容。笔记内容会有所倾向,不会严格反应书本内容。

基础概念

+----------+
|应用程序   |
+------+   |
| Shell|   |
+------+   |
| 内核  |   |
+------+   |
| Libs |   |
+------+---+

内核的接口称为系统调用(system call)。公用函数库(libs)建立在系统调用基础上,应用程序既可以使用公共函数库,也可以系统调用。Shell是比较特殊的应用程序,为其他程序提供一个接口。Linux是GNU操作系统使用的内核,一般称为GNU/Linux操作系统,更常见的叫法为Linux。

登陆

用户在登陆Unix系统时,输入用户名和口令,系统在口令文件/etc/passwd中查看。口令文件是由7个冒号分割的字段组成,依次是:登录名、加密口令、用户ID、数字组ID、注释字段、起始目录、Shell程序。
如:sar:x:205:105:Stephen Rago:/home/sar:/bin/ksh.目前,所有系统的加密口令移动到单独文件中了。

Shell是一个命令行解释权,它读取用户输入,然后执行命令。sh是Unix默认shell, csh是BSD默认shell, bash是sh的改进,支持csh的特色,所有Linux都有。

文件和目录

文件名的最大长度是255个字符(characters),文件路径的最大长度是4096字符(characters), 即可以包含16级的最大文件长度的路径。

API说明

#include <sys/types.h>
#include <dirent.h>

DIR *opendir(const char *name);
DIR *fdopendir(int fd);

struct dirent {
    
    ino_t          d_ino;       /* Inode number */
    off_t          d_off;       /* Not an offset; see below */
    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]; /* Null-terminated filename */
};
struct dirent *readdir(DIR *dirp);

#include <unistd.h>

pid_t getpid(void);
pid_t getppid(void);
pid_t fork(void);

extern char **environ;

int execl(const char *path, const char *arg, ...
                /* (char  *) NULL */);
int execlp(const char *file, const char *arg, ...
                /* (char  *) NULL */);
int execle(const char *path, const char *arg, ...
                /*, (char *) NULL, 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[]);

#include <stdio.h>
int fgetc(FILE *stream);
char *fgets(char *s, int size, FILE *stream);
int getc(FILE *stream);
int getchar(void);
int ungetc(int c, FILE *stream);

#include <sys/wait.h>
// 这一类的api都是为了让父进程等待子进程,并且获取子进程状态变化
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);

char* strerror(int errnum); // 映射消息的文本信息
void perror(const char* msg); // 输出一条出错消息,然后返回

模仿的Demo:

/* 模拟ls */
#include "apue.h"
#include <dirent.h>
int main(int argc, char *argv[]) {
    
    DIR *dp;
    struct dirent *dirp;
    if( argc != 2 ){
    
        err_quit("usage: ls <path>");
    }
    if( (dp = opendir(argv[1])) == NULL ){
    
        err_sys("can't open %s", argv[1]);
    }
    while( (dirp = readdir(dp))!=NULL ){
    
        printf("%s -> %d %d\n", dirp->d_name, dirp->d_reclen, dirp->d_type);
    }
    closedir(dp);
    return 0;
}
// xls ./

/* 无缓冲IO */
#include "apue.h"
#define BUFFSIZE 4096

int main(){
    
    int n;
    char buf[BUFFSIZE];
    while( (n=read(STDIN_FILENO, buf, BUFFSIZE)) >0 ){
    
        if( write(STDOUT_FILENO, buf, n)!=n ){
    
            err_sys("write error!");
        }
    }
    if(n<0)
        err_sys("read error!");
    return 0;
}

// xcp < in.txt > out.txt

/* 标准IO */
// 库提供缓冲管理
#include "apue.h"

int main(){
    
    int c;
    while( (c=getc(stdin))!=EOF ){
    
        if( putc(c, stdout) == EOF ){
    
            err_sys("output error");
        }
    }
    if( ferror(stdin) ){
    
        err_sys("input error");
    }
    return 0;
}
// xcp2 < in.txt > out.txt

/* 类似shell功能 */
#include "apue.h"
#include <sys/wait.h>

int main(){
    
    char buf[MAXLINE];
    pid_t pid;
    int status;
    printf("%% ");
    while(fgets(buf, MAXLINE, stdin) != NULL ){
    
        if( buf[strlen(buf)-1] == '\n' ){
    
            buf[strlen(buf)-1] = 0;
        }
        if( (pid=fork()) < 0 ){
    
            err_sys("fork_error");
        }else if( pid == 0 ){
    
            // child
            execlp(buf, buf, (char*)0); // 这个实现不是很完美
            err_ret("couldn't execute: %s", buf);
            exit(127);
        }
        // parent
        if( (pid = waitpid(pid, &status, 0)) < 0 ){
    
            err_sys("waitpid error");
        }  
        printf("%% ");
    } 
    return 0;
}

用户组

除了用户ID和组ID,大多数Unix还允许用户属于另外其他的组,这个功能从4.2BSD开始,最多允许用户属于16个其他组。
查询用户属于的组cat /etc/group | grep root, 分别为用户组name,用户组口令,用户组ID,用户。使用指令groups可获取到当前用户所在的组。

头文件标准

ISO C标准定义

头文件 说明
<assert.h> 验证程序断言
<complex.h> 复数算术运算支持
<ctype.h> 字符分类和映射支持
<errno.h> 出错码
<fenv.h> 浮点环境
<float.h> 浮点常量及特性
<inttypes.h> 整形格式变换
<iso646.h> 赋值、关系、一元操作宏
<limits.h> 实现常量
<locale.h> 本地化类别及定义
<math.h> 数学函数、类型、常量
<setjmp.h> 非局部goto
<signal.h> 信号
<stdarg.h> 可变长度参数表
<stdbool.h> 布尔类型和值
<stddef.h> 标准定义
<stdint.h> 整形
<stdio.h> 标准IO库
<stdlib.h> 实用函数
<string.h> 字符串操作
<tgmath.h> 通用类型数学宏
<time.h> 时间和日期
<wchar.h> 扩充的多字符和宽字符支持
<wctype.h> 宽字符分类和映射支持

POSIX.1标准 包含了iso c标准库

头文件 说明
<aio.h> 异步IO
<cpio.h> cpio归档值
<dirent.h> 目录项
<dlfcn.h> 动态链接库
<fcntl.h> 文件控制
<fnmatch.h> 文件名匹配类型
<glob.h> 路径名模式匹配与生成
<grp.h> 组文件
<iconv.h> 代码集变换实用程序
<langinfo.h> 语言信息常量
<monetary.h> 货币类型与函数
<netdb.h> 网络数据库操作
<nl_types.h> 消息类
<poll.h> 投票函数
<pthread.h> 线程
<pwd.h> 口令文件
<regex.h> 正则表达式
<sched.h> 执行调度
<semaphore.h> 信号量
<strings.h> 字符串操作
<tar.h> tar归档
<termios.h> 终端IO
<unistd.h> 符号常量
<wordexp.h> 字扩充类型
<arpa/inet.h> 因特网定义
<net/if.h> 套接字本地接口
<netinet/in.h> 因特网地址族
<netinet/tcp.h> 传输协议定义
<sys/mman.h> 存储管理
<sys/select.h> select函数
<sys/socket.h> 套接字接口
<sys/stat.h> 文件状态
<sys/statvfs.h> 文件系统信息
<sys/times.h> 进程时间
<sys/types.h> 基本系统数据类型
<sys/un.h> UNIX套接字定义
<sys/utsname.h> 系统名
<sys/wait.h> 进程控制

POSIX.1 2008 可选头文件

头文件 说明
<fmtmsg.h> 消息显示结构
<ftw.h> 文件树漫游
<libgen.h> 路径名管理函数
<ndbm.h> 数据库操作
<search.h> 搜索表
<syslog.h> 系统出错日志记录
<utmpx.h> 用户账户数据库
<sys/ipc.h> IPC
<sys/msg.h> XSI消息队列
<sys/resource.h> 资源操作
<sys/sem.h> XSI信号量
<sys/shm.h> XSI共享存储
<sys/time.h> 时间类型
<sys/uio.h> 矢量IO操作
<mqueue.h> 消息队列
<spawn.h> 实时spawn接口

IO

IO操作是所有操作中最常用的一个,一般只需要5个函数open/read/write/lseek/close,这些函数都是不带缓冲的IO。实际使用时,更多时候需要使用dup/fcntl/sync/fsync/ioctl配合使用。

文件IO基础操作比较简单,不做说明。

特殊的,当fork等手段产生多个进程读写同一文件时,会导致数据丢失或者异常,为此,设计了原子性操作函数:pread/pwrite,作用等同lseek然后read/write,但是这个过程不会被打断。

#include <unistd.h>

int dup(int oldfd);
int dup2(int oldfd, int newfd);
// 等效于 dcntl(fd, F_DUPFD, 0), dup是原子操作

// sync/fsync/fdatasync 

#include <fcntl.h>
int fcntl(int fd, int cmd, ... /* arg */ ); // 改变fd的属性

#include <sys/ioctl.h>
int ioctl(int fd, unsigned long request, ...); // IO杂物箱,其他函数不能做的都要它来做

因为大部分情况下,针对串口、摄像头等设备,都需要使用ioctl设置参数,所以,这里需要注意ioctl的request参数,它根据设备不同,参数也不同。

类别 常量名 头文件 ioctl数
盘标号 DIOxxx <sys/disklabel.h> 4
文件IO FIOxxx <sys/fileio.h> 14
磁带IO MTIOxxx <sys/mtio.h> 11
套接字IO SIOxxx <sys/sockio.h> 73
终端IO TIOxxx <sys/ttycom.h> 43

/dev/fd/x等价与复制描述符n。

文件和目录

之前了解了基础的IO操作,这里主要关注文件系统和文件的性质。

#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);

#include <fcntl.h>           /* Definition of AT_* constants */
#include <sys/stat.h>
int fstatat(int dirfd, const char *pathname, struct stat *statbuf, int flags);

struct stat {
    
    dev_t     st_dev;         /* ID of device containing file */
    ino_t     st_ino;         /* Inode number */
    mode_t    st_mode;        /* File type and mode */
    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;     /* Block size for filesystem I/O */
    blkcnt_t  st_blocks;      /* Number of 512B blocks allocated */

    /* Since Linux 2.6, the kernel supports nanosecond
        precision for the following timestamp fields.
        For the details before Linux 2.6, see NOTES. */

    struct timespec st_atim;  /* Time of last access */
    struct timespec st_mtim;  /* Time of last modification */
    struct timespec st_ctim;  /* Time of last status change */

#define st_atime st_atim.tv_sec      /* Backward compatibility */
#define st_mtime st_mtim.tv_sec
#define st_ctime st_ctim.tv_sec
};

这类函数都返回文件的信息结构。其中,文件类型可以通过对st_mode宏S_ISREG/S_ISDIR/S_ISCHR/S_ISBLK/S_ISFIFO/S_ISLNK/S_ISSOCK 判断文件类型。在IPC中对整个结构体判断IPC对象类型S_TYPEISMQ/S_TYPEISSEM/S_TYPEISSHM.

st_mode中也保存了对文件的访问权限位。除了常见的权限,其实每个文件都有9个访问权限。

st_mode屏蔽 含义
S_IRUSR 用户读
S_IWUSR 用户写
S_IXUSR 用户执行
S_IRGRP 组读
S_IWGRP 组写
S_IXGRP 组执行
S_IROTH 其他读
S_IWOTH 其他写
S_IXOTH 其他执行

当create文件或者目录时,默认使用进程的有效用户ID和组ID,如果想要测试是否有权限,如下:

#include <unistd.h>
int access(const char *pathname, int mode);

// 如果需要改变权限,如下
#include <sys/stat.h>
int chmod(const char *pathname, mode_t mode);
int fchmod(int fd, mode_t mode);

// 改变用户ID或者组ID
#include <unistd.h>
int chown(const char *pathname, uid_t owner, gid_t group);
int fchown(int fd, uid_t owner, gid_t group);
int lchown(const char *pathname, uid_t owner, gid_t group);

// 文件截断
#include <unistd.h>
#include <sys/types.h>
int truncate(const char *path, off_t length);
int ftruncate(int fd, off_t length);

// 链接
int link(const char *oldpath, const char *newpath);
int unlink(const char *pathname);

int remove(const char *pathname); // 通用文件与文件夹

int rename(const char *oldpath, const char *newpath);

int symlink(const char *target, const char *linkpath);

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

// 文件
// st_atim 访问时间/st_mtim 内容修改时间/st_ctim 属性更改时间
int futimens(int fd, const struct timespec times[2]);
int utimensat(int dirfd, const char *pathname, const struct timespec times[2], int flags);

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

#include <dirent.h>
DIR *opendir(const char *name);
DIR *fdopendir(int fd);
struct dirent *readdir(DIR *dirp);
void rewinddir(DIR *dirp);
int closedir(DIR *dirp);
long telldir(DIR *dirp);
void seekdir(DIR *dirp, long loc);

int chdir(const char *path);
int fchdir(int fd);

char *getcwd(char *buf, size_t size);
char *getwd(char *buf);
char *get_current_dir_name(void);

流和FILE对象

#include <stdio.h>
#include <wchar.h>

int fwide(FILE *stream, int mode); // 设置流的定向,决定是单字符还是宽字符模式
void setbuf(FILE *restrict stream, char *restrict buf); // 修改缓冲机制
int setvbuf(FILE *restrict stream, char *restrict buf, int type, size_t size);
int fflush(FILE *stream); // 强制冲洗一个流
fopen/freopen/fdopen // 打开、重新打开、获取描述符
int fclose(FILE* fp); // 关闭一个流

getc/fgetc/getchar // 一次读取一个字节
ferror/feof // 获取流错误码
clearerr // 清除错误码
ungetc // 将字符压回流
putc/fputc/putchar // 输出一个字符

fgets/gets // 读取一行
fputs/puts // 写入一串数据,不一定是一行

fread/fwrite // 读写二进制数据

ftell/fseek/rwind/fgetpos/fsetpos // 读、写指针位置

printf/sprintf/fprintf/dpintf/snprintf // 格式化io
vprintf/vfprintf/vdprintf/vsprintf/vsnprintf // 可变参数的
// %[flags][fldwidth][precision][lenmodifier]convtype

// 百分号后跟随字符意义 flags
// , 整数按照千分位分组
// - 左对齐
// + 总是显示正负号
//   如果第一个字符不是正负号,则添加一个空格
// # 指定另一种转换形式,比如16进制加0x
// 0 添加前导0填充,不使用空格

// lenmodifier 参数长度
// hh (u)char 类型长度
// h  (u)short
// l  (u)long
// ll  (u)long long
// j  (u)intmax_t
// z  size_t
// t  ptrdiff_t
// L  long double

// convtype 如何解释参数
// d,i  有符号10进制
// o  无符号八进制
// u  无符号十进制
// x,X  无符号16进制
// f,F  双精度浮点
// e,E  指数格式双精度
// g,G  解释为f/F/e/E
// a,A  16进制指数格式双精度
// c  字符,lc是宽字符
// s  字符串,ls是宽字符串
// p  void指针
// n  printf输出字符数目写入到带符号整数中
// %  %自身
// C  宽字符,=lc
// S  宽字符串,=ls

// convtype 还存在显式指定第n个参数的格式化,%n$,与上面的不能同时使用

scanf/fscanf/sscanf // 格式化输入
// %[*][fldwidth][m][lenmodifier]convtype
// d  有符号10进制
// i  有符号10进制,基数由输入格式确定
// o  无符号八进制
// u  有符号十进制,基数10
// x,X  无符号16进制
// f,F  双精度浮点
// e,E  指数格式双精度
// g,G  解释为f/F/e/E
// a,A  16进制指数格式双精度
// c  字符,lc是宽字符
// s  字符串,ls是宽字符串
// p  void指针
// n  printf输出字符数目写入到带符号整数中
// %  %自身
// C  宽字符,=lc
// S  宽字符串,=ls
// [ 匹配列出的字符序列,以]结束
// [^ 匹配列出字符以外的序列,以]终止

#include <stdlib.h>
#include <stdio.h>

int main(int argc, char *argv[]) {
    
	printf("Hello!\n");
	unsigned int n = 0;
	printf("%2$.2f %1$4.2d -%3$n\n", -12, -3.2567,&n);
	printf("%*s%d\n",n,"",n);
	return 0;
}
// %n$ 从1开始,指明第几个参数
// %n 指定一个uint地址,存储%n所在位置的序号
// %*s 需要2个参数,第一个是类似fildwidth,第二个是%s的值,效果与%.2f类似

int fileno(FILE *stream); 
char *tmpnam(char *s); // 产生一个与现有文件名不同的有效路径名字符串,最多TMP_MAX
FILE *tmpfile(void); // 产生一个临时二进制文件(wb+),关闭或程序结束是自动删除
char *mkdtemp(char *template);

// 与上述不同的是,产生的临时文件不会主动删除,需要自己unlink
int mkstemp(char *template);
int mkostemp(char *template, int flags);
int mkstemps(char *template, int suffixlen);
int mkostemps(char *template, int suffixlen, int flags);

// 内存流
FILE *fmemopen(void *buf, size_t size, const char *mode);

FILE *open_memstream(char **ptr, size_t *sizeloc);
#include <wchar.h>
FILE *open_wmemstream(wchar_t **ptr, size_t *sizeloc);

系统数据文件

// /etc/passwd
#include <pwd.h>
struct passwd {
    
    char   *pw_name;       /* username */
    char   *pw_passwd;     /* user password */
    uid_t   pw_uid;        /* user ID */
    gid_t   pw_gid;        /* group ID */
    char   *pw_gecos;      /* user information */
    char   *pw_dir;        /* home directory */
    char   *pw_shell;      /* shell program */
};
struct passwd *getpwnam(const char *name);
struct passwd *getpwuid(uid_t uid);
struct passwd *getpwent(void);
void setpwent(void);
void endpwent(void);

// demo
#include <stdlib.h>
#include <stdio.h>
#include <pwd.h>

int main(int argc, char *argv[]) {
    
	struct passwd* ptr = NULL;
	while ( (ptr = getpwent())!= NULL ) {
    
		printf("U/P:%s %s \nID:%d %d \nI:%s \nDIR:%s \nSHELL:%s\n",
			ptr->pw_name, ptr->pw_passwd, ptr->pw_uid, ptr->pw_gid, ptr->pw_gecos,
			ptr->pw_dir, ptr->pw_shell);
			printf("-----------------\n");
	}
	endpwent();
	return 0;
}

// 在.etc/passwd 中,用户密码都是x,因为它被移到单独的文件/etc/shadow中,并且加密了,阴影口令文件
#include <shadow.h>
struct spwd {
                                                      
    char *sp_namp;     /* Login name */                        
    char *sp_pwdp;     /* Encrypted password */                
    long  sp_lstchg;   /* Date of last change                  
                          (measured in days since              
                          1970-01-01 00:00:00 +0000 (UTC)) */  
    long  sp_min;      /* Min # of days between changes */     
    long  sp_max;      /* Max # of days between changes */     
    long  sp_warn;     /* # of days before password expires    
                          to warn user to change it */         
    long  sp_inact;    /* # of days after password expires     
                          until account is disabled */         
    long  sp_expire;   /* Date when account expires            
                          (measured in days since              
                          1970-01-01 00:00:00 +0000 (UTC)) */  
    unsigned long sp_flag;  /* Reserved */                     
};                                                             
struct spwd *getspnam(const char *name);
struct spwd *getspent(void);
void setspent(void);
void endspent(void);
struct spwd *fgetspent(FILE *stream);
struct spwd *sgetspent(const char *s);
int putspent(const struct spwd *p, FILE *stream);
int lckpwdf(void);
int ulckpwdf(void);

char *crypt(const char *key, const char *salt); // 密码hash, -lcrypt
// demo, 可以验证自己的密码
#define _GNU_SOURCE
#include <crypt.h>

int main(int argc, char *argv[]) {
    
    char* pT1 = crypt("my_pwd", "$6$tFoDYuJAMie9hU/s");
    printf("%s\n", pT1);
    return 0;
}

// 组文件 /etc/group
#include <sys/types.h>
#include <grp.h>
struct group {
    
    char   *gr_name;        /* group name */
    char   *gr_passwd;      /* group password */
    gid_t   gr_gid;         /* group ID */
    char  **gr_mem;         /* NULL-terminated array of pointers
                                to names of group members */
};
struct group *getgrnam(const char *name);
struct group *getgrgid(gid_t gid);
struct group *getgrent(void);
void setgrent(void);
void endgrent(void);

// 4.2BSD 开始,用户有附属组ID
#include <sys/types.h>
#include <unistd.h>
#include <grp.h>
int getgroups(int size, gid_t list[]);
int setgroups(size_t size, const gid_t *list);
int initgroups(const char *user, gid_t group);

其他数据文件:
BSD各网络服务器提供服务的数据文件/etc/services,记录协议信息的/etc/protocols,记录网络信息的/etc/networks

系统标志:

#include <sys/utsname.h>
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 */
};
int uname(struct utsname *buf);
// cat /etc/lsb-release
/*
Linux 
xinvm
4.15.0-39-generic
#42-Ubuntu SMP Tue Oct 23 15:48:01 UTC 2018
x86_64
*/
int gethostname(char *name, size_t len);
int sethostname(const char *name, size_t len);

时间:

#include <time.h>
struct tm {
    
    int tm_sec;    /* Seconds (0-60) */
    int tm_min;    /* Minutes (0-59) */
    int tm_hour;   /* Hours (0-23) */
    int tm_mday;   /* Day of the month (1-31) */
    int tm_mon;    /* Month (0-11) */
    int tm_year;   /* Year - 1900 */
    int tm_wday;   /* Day of the week (0-6, Sunday = 0) */
    int tm_yday;   /* Day in the year (0-365, 1 Jan = 0) */
    int tm_isdst;  /* Daylight saving time */
};
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);

char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);

struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);

struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);

time_t mktime(struct tm *tm);
// 将时间格式化为字符串,把字符串转化为time_t
size_t strftime(char *s, size_t max, const char *format, const struct tm *tm);
char *strptime(const char *s, const char *format, struct tm *tm);

进程

进程终止有5中正常行为:

  • main返回
  • exit调用
  • _exit/_Exit调用
  • 最后一个线程返回
  • 最后一个线程调用pthread_exit

异常终止有3中方式:

  • abort调用
  • 接到一个信号
  • 最后一个线程读取消请求做出相应

一个进程可以最多登记32个函数,当exit时自动调用,这些函数称之为终止处理程序,他们通过atexit函数注册。调用与登记的顺序相反。

参数表:int main(int argc, char* argv[])(一般第一个是自身,第二个开始正常使用,最后一个是NULL)
环境表:extern char **environ; 最后一个字符串指针是NULL。name=value形式存储.

char *getenv(const char *name);
int putenv(char *string);
int setenv(const char *name, const char *value, int overwrite);
int unsetenv(const char *name);
int clearenv(void);

存储空间分配:

#include <stdlib.h>

void *malloc(size_t size);
void free(void *ptr);
void *calloc(size_t nmemb, size_t size);
void *realloc(void *ptr, size_t size);
void *reallocarray(void *ptr, size_t nmemb, size_t size);

C语言中,goto是不能跨越函数的,所以有了setjmp/longjmp.

#include <setjmp.h>

int setjmp(jmp_buf env); // 设置跳转点环境
int sigsetjmp(sigjmp_buf env, int savesigs);

void longjmp(jmp_buf env, int val); // 跳转,全局、静态、易逝变量保持不变
void siglongjmp(sigjmp_buf env, int val);

资源限制:getrlimit/setrlimit

exec系列函数

解释器文件(interpreter file):
文本文件起始行:#! pathname [optional-args]

int system(const char *command); // 内部实现了fork/exec/waitpid

#include <unistd.h>
char *getlogin(void);
int getlogin_r(char *buf, size_t bufsize);
#include <stdio.h>
char *cuserid(char *string);

#include <unistd.h>
int nice(int inc); // 调整nice值

#include <sys/time.h>
#include <sys/resource.h>
int getpriority(int which, id_t who);
int setpriority(int which, id_t who, int prio);

#include <sys/times.h>
clock_t times(struct tms *buf); // 进程时间

进程组,每个进程除了有进程ID还有一个进程组。同一进程组中的各个进程接收来自同一终端的各种信号。

#include <sys/types.h>
#include <unistd.h>

int setpgid(pid_t pid, pid_t pgid);
pid_t getpgid(pid_t pid);

pid_t getpgrp(void);                 /* POSIX.1 version */
pid_t getpgrp(pid_t pid);            /* BSD version */

int setpgrp(void);                   /* System V version */
int setpgrp(pid_t pid, pid_t pgid);  /* BSD version */

会话是多个进程组的集合。使用setsid创建一个新的会话。

会话和进程组还有一些其他特性:

  • 一个会话可以有一个控制终端,它通常是终端设备或者伪终端设备。
  • 建立与终端连接会话的首进程称为控制进程。
  • 一个会话中的几个进程组可分为一个前台进程组以及多个后台进程组。
  • 如果会话有一个控制终端,则它由一个前台进程组,其他的都为后台进程组。
  • 无论何时键入终端中断键(Delete/Ctrl+C),都会中断信号发送到前台进程组所有进程。
  • 如果终端检测到网络或者连接断开,则挂断信号发送到所有控制进程。
#include <unistd.h>
pid_t tcgetpgrp(int fd);
int tcsetpgrp(int fd, pid_t pgrp);

#define _XOPEN_SOURCE 500        /* See feature_test_macros(7) */
#include <termios.h>
pid_t tcgetsid(int fd);

信号:

#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);

int kill(pid_t pid, int sig); // 发送信号给pid, <0 是发送给进程组
int raise(int sig); // 发送信号给自身
unsigned int alarm(unsigned int seconds); // 发送定时器信息,由内核产生信号
int pause(void); // 进程挂起,直到收到一个信号,并且执行了信号处理函数并返回

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);
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset); 

int sigpending(sigset_t *set);
int sigsuspend(const sigset_t *mask);
int sigaction(int signum, const struct sigaction *act, struct sigaction *oldact);
void abort(void);

#include <time.h>
unsigned int sleep(unsigned int seconds);
int nanosleep(const struct timespec *req, struct timespec *rem);
int clock_nanosleep(clockid_t clock_id, int flags,const struct timespec *request, struct timespec *remain);

void psignal(int sig, const char *s); // print signal info
void psiginfo(const siginfo_t *pinfo, const char *s); // print signal info

extern const char *const sys_siglist[]; // 信号名称与编号的映射数组

线程

线程接口也称之为pthread或者POSIX线程

#include <pthread.h> // link with -pthread

int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine) (void *), void *arg);
pthread_t pthread_self(void);
int pthread_equal(pthread_t t1, pthread_t t2);
void pthread_exit(void *retval);
int pthread_join(pthread_t thread, void **retval);
int pthread_cancel(pthread_t thread); // 发送取消指令,不等待完成
void pthread_cleanup_push(void (*routine)(void *), void *arg); // 退出时清理函数
void pthread_cleanup_pop(int execute);
int pthread_detach(pthread_t thread); // 清理,之后不能使用join

// demo1 
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <pthread.h>
#include <string.h>

pthread_t ntid;

void printids(const char* s){
    
    pid_t pid;
    pthread_t tid;
    pid = getpid();
    tid = pthread_self();
    printf("%s pid %u tid %lu (0x%lx) \n", s, (unsigned long)pid, (unsigned long)tid, (unsigned long)tid);
}

void *thr_fn(void* arg) {
    
    printids("new thread:");
    return (void*)NULL;
}

int main(){
    
    int err;
    err = pthread_create(&ntid, NULL, thr_fn, NULL);
    if( err != NULL ){
    
        perror("can't create thread");
    }
    printids("main thread:");
    sleep(1);
    return 0;
}

// Demo 2
#include <pthread.h>
#include <string.h>
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <errno.h>
#include <ctype.h>

#define handle_error_en(en, msg) \
    do { errno = en; perror(msg); exit(EXIT_FAILURE); } while (0)

#define handle_error(msg) \
    do { perror(msg); exit(EXIT_FAILURE); } while (0)

struct thread_info {
        /* Used as argument to thread_start() */
    pthread_t thread_id;        /* ID returned by pthread_create() */
    int       thread_num;       /* Application-defined thread # */
    char     *argv_string;      /* From command-line argument */
};

/* Thread start function: display address near top of our stack,
  and return upper-cased copy of argv_string */

static void *thread_start(void *arg)
{
    
    struct thread_info *tinfo = arg;
    char *uargv, *p;
    
    printf("Thread %d: top of stack near %p; argv_string=%s\n",
           tinfo->thread_num, &p, tinfo->argv_string);
    
    uargv = strdup(tinfo->argv_string);
    if (uargv == NULL)
        handle_error("strdup");
    
    for (p = uargv; *p != '\0'; p++)
        *p = toupper(*p);
    
    return uargv;
}

int main(int argc, char *argv[])
{
    
    int s, tnum, opt, num_threads;
    struct thread_info *tinfo;
    pthread_attr_t attr;
    int stack_size;
    void *res;
    
    /* The "-s" option specifies a stack size for our threads */
    
    stack_size = -1;
    while ((opt = getopt(argc, argv, "s:")) != -1) {
    
        switch (opt) {
    
        case 's':
            stack_size = strtoul(optarg, NULL, 0);
            break;
            
        default:
            fprintf(stderr, "Usage: %s [-s stack-size] arg...\n",
                    argv[0]);
            exit(EXIT_FAILURE);
        }
    }
    
    num_threads = argc - optind;
    
    /* Initialize thread creation attributes */
    
    s = pthread_attr_init(&attr);
    if (s != 0)
        handle_error_en(s, "pthread_attr_init");
    
    if (stack_size > 0) {
    
        s = pthread_attr_setstacksize(&attr, stack_size);
        if (s != 0)
            handle_error_en(s, "pthread_attr_setstacksize");
    }
    
    /* Allocate memory for pthread_create() arguments */
    
    tinfo = calloc(num_threads, sizeof(struct thread_info));
    if (tinfo == NULL)
        handle_error("calloc");
    
    /* Create one thread for each command-line argument */
    
    for (tnum = 0; tnum < num_threads; tnum++) {
    
        tinfo[tnum].thread_num = tnum + 1;
        tinfo[tnum].argv_string = argv[optind + tnum];
        
        /* The pthread_create() call stores the thread ID into
          corresponding element of tinfo[] */
        
        s = pthread_create(&tinfo[tnum].thread_id, &attr,
                           &thread_start, &tinfo[tnum]);
        if (s != 0)
            handle_error_en(s, "pthread_create");
    }
    
    /* Destroy the thread attributes object, since it is no
      longer needed */
    
    s = pthread_attr_destroy(&attr);
    if (s != 0)
        handle_error_en(s, "pthread_attr_destroy");
    
    /* Now join with each thread, and display its returned value */
    
    for (tnum = 0; tnum < num_threads; tnum++) {
    
        s = pthread_join(tinfo[tnum].thread_id, &res);
        if (s != 0)
            handle_error_en(s, "pthread_join");
        
        printf("Joined with thread %d; returned value was %s\n",
               tinfo[tnum].thread_num, (char *) res);
        free(res);      /* Free memory allocated by thread */
    }
    
    free(tinfo);
    exit(EXIT_SUCCESS);
}

进程原语与线程原语十分相似,这里做一个对比:

进程原语 线程原语 描述
fork pthread_create 创建新的控制流
exit pthread_exit 从现有的控制流退出
waitpid pthread_join 从控制流中得到退出状态
atexit pthread_cleanup_push 注册退出控制流时的函数
getpid pthread_self 获取控制流ID
abort pthread_cancel 请求控制流的非正常退出

线程同步:

// 互斥锁
#include <pthread.h>

int pthread_mutex_destroy(pthread_mutex_t *mutex); // 动态分配必须destroy
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER; // 静态分配

int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);

int pthread_mutex_timedlock(pthread_mutex_t *restrict mutex, const struct timespec *restrict abstime);

// 读写锁
int pthread_rwlock_init(pthread_rwlock_t *rwlock, const pthread_rwlockattr_t *attr);
int pthread_rwlock_destroy(pthread_rwlock_t *rwlock);
pthread_rwlock_t rwlock=PTHREAD_RWLOCK_INITIALIZER;

int pthread_rwlock_rdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_tryrdlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_wrlock(pthread_rwlock_t *rwlock);
int pthread_rwlock_unlock(pthread_rwlock_t *rwlock);

int pthread_rwlock_timedrdlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abstime);
int pthread_rwlock_timedwrlock(pthread_rwlock_t *restrict rwlock, const struct timespec *restrict abstime);

// 条件变量
int pthread_cond_destroy(pthread_cond_t *cond);
int pthread_cond_init(pthread_cond_t *restrict cond, const pthread_condattr_t *restrict attr);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;

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

int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);

// 自旋锁
// 自旋锁与互斥量类似,但是不通过休眠阻塞,而是在获取锁之前一直处于忙等(自旋)阻塞状态,它一般用于:锁持有时间短,而且线程不想再重新调度上花太多成本。
pthread_spin_init/destroy/lock/trylock/unlock

// 屏蔽
// 屏蔽barrier是用户协调多个线程并行工作的同步机制,屏蔽允许每个线程等待,直到所有线程到达某个点。
pthread_barrier_init/destroy/wait

线程中的属性:

// 线程属性
int pthread_attr_init(pthread_attr_t *attr);
int pthread_attr_destroy(pthread_attr_t *attr);
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
int pthread_attr_getdetachstate(const pthread_attr_t *attr, int *detachstate);
int pthread_attr_setstack(pthread_attr_t *attr, void *stackaddr, size_t stacksize);
int pthread_attr_getstack(const pthread_attr_t *attr, void **stackaddr, size_t *stacksize);
int pthread_attr_setstacksize(pthread_attr_t *attr, size_t stacksize);
int pthread_attr_getstacksize(const pthread_attr_t *attr, size_t *stacksize);
int pthread_attr_setguardsize(pthread_attr_t *attr, size_t guardsize);
int pthread_attr_getguardsize(const pthread_attr_t *attr, size_t *guardsize);

// 同步属性
int pthread_mutexattr_destroy(pthread_mutexattr_t *attr);
int pthread_mutexattr_init(pthread_mutexattr_t *attr);
int pthread_mutexattr_getpshared(const pthread_mutexattr_t *attr, int *pshared);
int pthread_mutexattr_setpshared(pthread_mutexattr_t *attr, int pshared);
int pthread_mutexattr_getrobust(const pthread_mutexattr_t *attr, int *robustness);
int pthread_mutexattr_setrobust(const pthread_mutexattr_t *attr, int robustness);

int pthread_mutexattr_gettype(const pthread_mutexattr_t *restrict attr, int *restrict type);
int pthread_mutexattr_settype(pthread_mutexattr_t *attr, int type); // 更改锁状态

// 读写锁属性
pthread_rwlockattr_init/destory/setpshared/getpshared

// 条件变量属性
pthread_condattr_init/destory/setpshared/getpshared/setclock/getclock

// 屏蔽属性
pthread_barrierattr_init/destory/setpshared/getpshared


// 线程安全方式管理FILE对象
void flockfile(FILE *filehandle);
int ftrylockfile(FILE *filehandle);
void funlockfile(FILE *filehandle);

int getc_unlocked(FILE *stream);
int getchar_unlocked(void);
int putc_unlocked(int c, FILE *stream);
int putchar_unlocked(int c);

void clearerr_unlocked(FILE *stream);
int feof_unlocked(FILE *stream);
int ferror_unlocked(FILE *stream);
int fileno_unlocked(FILE *stream);
int fflush_unlocked(FILE *stream);
int fgetc_unlocked(FILE *stream);
int fputc_unlocked(int c, FILE *stream);
size_t fread_unlocked(void *ptr, size_t size, size_t n, FILE *stream);
size_t fwrite_unlocked(const void *ptr, size_t size, size_t n, FILE *stream);
char *fgets_unlocked(char *s, int n, FILE *stream);
int fputs_unlocked(const char *s, FILE *stream);
// wchar版本省略

线程特定数据:
默认情况下,所有的数据是共享的,我们期望没个线程改变自己特定的数据而不影响其他线程。

int pthread_key_create(pthread_key_t *key, void (*destructor)(void*));
int pthread_key_delete(pthread_key_t key);

void *pthread_getspecific(pthread_key_t key);
int pthread_setspecific(pthread_key_t key, const void *value);
       
int pthread_once(pthread_once_t *once_control, void (*init_routine)(void));
pthread_once_t once_control = PTHREAD_ONCE_INIT;

创建的键存储在keyp指向的内存中,这个键可以被进程中的所有线程使用,但是每个线程把这个键与不同的线程特定数据地址关联。通过specific函数设置关联内容。
如果每个线程都调用pthread_once,系统就能保证对init_routine只调用一次。

线程与信号
每个线程都有自己的信号屏蔽字,但是信号的处理是进程中所有线程共享的。进程的信号是投递到单个线程的,如果一个信号与硬件故障有关,则该信号会被发送到引起该事件的线程中,而其他信号被发送到任意线程中。

int pthread_sigmask(int how, const sigset_t *set, sigset_t *oldset);
int sigwait(const sigset_t *set, int *sig); // 多个线程同时等待则由操作系统决定
int pthread_kill(pthread_t thread, int sig);

线程与Fork
当线程调用fork,则为子进程创建了整个进程地址空间的副本,在子进程内部只存在一个线程,它是由父进程中调用fork的线程副本构成的。
fork后的子线程与父线程共享锁等内容,为避免死锁等,子进程fork后可以立即调用exec函数中一个,这样旧的地址空间会被丢弃。
在多线程中,为避免不一致的问题,POSIX声明,在fork返回和子进程调用exec函数之间,子进程只能带哦用一部信号安全的函数。
要清除锁状态,可以通过pthread_atfork函数处理fork后程序。

线程与IO
多线程环境下,pread/pwrite函数是很有用的,因为进程公用相同的文件描述符,多线程同时读写就可以变为原子操作。

守护进程

守护进程是生存周期长的一种进程,常在系统引导时启动,系统关闭时终止,他们没有终端,只在后台运行。
ps -axj

  • 父进程ID为0的是内核进程,系统引导时启动。他们放在方括号名字中。
  • 进程ID=1通常是init,它是系统守护进程,主要负责启动特定的系统服务。
  • rpcbind是提供rpc服务,将程序号映射为网络端口号的服务。
  • inetd侦听系统网络接口,取得来自网络的对各种网络服务进程的请求。
  • nfsd,nfsiod,lockd,rpciod,rpc.idmapd,rpc.statd,rpc.mountd提供NFS文件系统支持。
  • cron是定期安排命令执行。
  • cupsd是打印假脱机服务
  • sshd是远程登陆和执行服务

创建一个守护进程:

#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <syslog.h>
#include <fcntl.h>
#include <sys/resource.h>

int main(){
    
    int i,fd0,fd1,fd2;
    pid_t pid;
    struct rlimit r1;
    struct sigaction sa;
    // 1. clear file creation mask
    umask(0);
    // 2. Get Maximum number of file description
    getrlimit(RLIMIT_NOFILE, &rl);
    // 3. Become a seesion leader to lose tty
    if( (pid=fork()) < 0 ){
    
        perror("fork error");
    }else if( pid != 0 ){
    
        exit(0); // parent process exit
    }
    setsid();

	/* ensure future opens won't allocate controlling ttys */
	
    // 4. change current working directory
    chdir("/");

    // 5. close all opened file description
    if( rl.rlim_max == RLIM_INFINITY ){
    
        rl.rlim_max = 1024;
    }
    for(i=0;i<rl.rlim_max;i++){
    
        close(i);    
    }
    // 6. attach 0,1,2 to /dev/null
    fd0 = open("/dev/null",O_RDWR);
    fd1 = dup(0);
    fd2 = dup(0);

    // 7. init log file
    openlog("xxxx",LOG_CONS, LOG_DAEMON);

    return 0;
}

单例守护进程:
每个守护进程创建一个固定名字的文件,并且在这个文件上加锁,那么只允许创建一个这样的锁。其他进程再尝试都会失败,这就可以保证只有一个副本运行。

一些遵循的惯例:

  • 若使用锁文件,一般在/var/run/xxx.pid
  • 若支持配置文件,一般在/etc/xxx.conf.
  • 守护进程可使用命令行启动,它脚本一般在/etc/rc*或/etc/init.d/*
  • 守护进程启动时读取配置,中途配置改变时,一般不处理。一般使用SIGHUP重读配置。

系统日志:

#include <syslog.h>

void openlog(const char *ident, int option, int facility);
void syslog(int priority, const char *format, ...);
void closelog(void);

高级IO

非阻塞IO、记录锁、IO多路转接(select/poll),异步IO、readv/writev、存储映射IO(mmap)。

非阻塞IO: flags = fcntl(fd,F_GETFL,0); flags |= O_NONBLOCK; fcntl(fd,F_SETFL,flags);
记录锁:当程序使用文件某部分时,阻止其他进程修改同一区域。 fcntl F_RDLCK/F_WRLCLK/F_UNLCK
IO多路转接:

  • select/pselect 告诉内核我们关心的描述符和条件,等待select返回,告诉我们哪个描述符哪个条件好了。
/* According to POSIX.1-2001, POSIX.1-2008 */
#include <sys/select.h>

/* According to earlier standards */
#include <sys/time.h>
#include <sys/types.h>
#include <unistd.h>
int select(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, struct timeval *timeout);
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);

#include <sys/select.h>
int pselect(int nfds, fd_set *readfds, fd_set *writefds, fd_set *exceptfds, const struct timespec *timeout, const sigset_t *sigmask);
  • poll与select类似,但是将条件聚合为数组,每个元素指明我们感兴趣的文件描述符和条件
#include <poll.h>
int poll(struct pollfd *fds, nfds_t nfds, int timeout);

#define _GNU_SOURCE         /* See feature_test_macros(7) */
#include <signal.h>
#include <poll.h>
int ppoll(struct pollfd *fds, nfds_t nfds, const struct timespec *tmo_p, const sigset_t *sigmask);

异步IO:系统不告诉我们描述符的状态,需要我们主动查询。

#include <aio.h> // Link with -lrt
int aio_read(struct aiocb *aiocbp);
int aio_write(struct aiocb *aiocbp);
int aio_fsync(int op, struct aiocb *aiocbp);
int aio_error(const struct aiocb *aiocbp);
aio_return/suspend/cancel/listio

readv/writev: 散步度、聚集写

#include <sys/uio.h>
ssize_t readv(int fd, const struct iovec *iov, int iovcnt);
ssize_t writev(int fd, const struct iovec *iov, int iovcnt);
ssize_t preadv(int fd, const struct iovec *iov, int iovcnt, off_t offset);
ssize_t pwritev(int fd, const struct iovec *iov, int iovcnt, off_t offset);
ssize_t preadv2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags);
ssize_t pwritev2(int fd, const struct iovec *iov, int iovcnt, off_t offset, int flags);

存储映射IO:将磁盘文件映射到缓冲上,读写缓冲就相当于读写文件。

#include <sys/mman.h>
void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
int munmap(void *addr, size_t length);
int mprotect(void *addr, size_t len, int prot); // 更改映射权限
int pkey_mprotect(void *addr, size_t len, int prot, int pkey);
int msync(void *addr, size_t length, int flags); // 刷洗缓冲区到文件

IPC

管道、FIFO、XSI、消息队列、共享存储、套接字
管道:
历史上是半双工的,只能在由公共父进程的之间使用。

#include <unistd.h>
int pipe(int pipefd[2]);

#define _GNU_SOURCE             /* See feature_test_macros(7) */
#include <fcntl.h>              /* Obtain O_* constant definitions */
#include <unistd.h>
int pipe2(int pipefd[2], int flags);

FILE *popen(const char *command, const char *type); // 创建一个pipe然后fork执行cmdstring
int pclose(FILE *stream);

FIFO:
命名管道,未命名的管道只能在相关进程中使用,但是FIFO可以在不相关的进程中使用。

#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);

XSI: (消息队列、信号量、共享存储)
msgget/msgctl/msgsnd/msgrcv/semget/semctl/semop/shmget/shmctl/shmat/shmdt/sem_open/sem_close/sem_unlink/sem_wait/set_post/sem_init/sem_getvalue/

Socket:
只有socket在进程间通信模型中可以跨计算机和网络。

#include <sys/socket.h>

int socket(int domain, int type, int protocol);
int shutdown(int sockfd, int how);

// Name                Purpose                          Man page
// AF_UNIX, AF_LOCAL   Local communication              unix(7)
// AF_INET             IPv4 Internet protocols          ip(7)
// AF_INET6            IPv6 Internet protocols          ipv6(7)
// AF_IPX              IPX - Novell protocols
// AF_NETLINK          Kernel user interface device     netlink(7)
// AF_X25              ITU-T X.25 / ISO-8208 protocol   x25(7)
// AF_AX25             Amateur radio AX.25 protocol
// AF_ATMPVC           Access to raw ATM PVCs
// AF_APPLETALK        AppleTalk                        ddp(7)
// AF_PACKET           Low level packet interface       packet(7)
// AF_ALG              Interface to kernel crypto API

// SOCK_STREAM     Provides sequenced, reliable, two-way, connection-based byte streams.   An  out-
//                of-band data transmission mechanism may be supported.
// 
// SOCK_DGRAM      Supports  datagrams  (connectionless,  unreliable  messages  of  a fixed maximum
//                length).
// 
// SOCK_SEQPACKET  Provides a sequenced, reliable, two-way connection-based data transmission  path
//                for  datagrams of fixed maximum length; a consumer is required to read an entire
//                packet with each input system call.
// 
// SOCK_RAW        Provides raw network protocol access.
// 
// SOCK_RDM        Provides a reliable datagram layer that does not guarantee ordering.
// 
// SOCK_PACKET     Obsolete and should not be used in new programs; see packet(7).

#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);

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

// 地址查询,主机数据库
#include <netdb.h>
extern int h_errno;
struct hostent *gethostbyname(const char *name);

#include <sys/socket.h>       /* for AF_INET */
struct hostent *gethostbyaddr(const void *addr, socklen_t len, int type);
void sethostent(int stayopen);
void endhostent(void);
void herror(const char *s);
const char *hstrerror(int err);

//host相关函数认为过时了,使用net替代
int getaddrinfo(const char *node, const char *service, const struct addrinfo *hints, struct addrinfo **res);
void freeaddrinfo(struct addrinfo *res);
int getnameinfo(const struct sockaddr *addr, socklen_t addrlen, char *host, socklen_t hostlen, char *serv, socklen_t servlen, int flags);

struct netent *getnetent(void);
struct netent *getnetbyname(const char *name);
struct netent *getnetbyaddr(uint32_t net, int type);
void setnetent(int stayopen);
void endnetent(void);

struct protoent *getprotoent(void);
struct protoent *getprotobyname(const char *name);
struct protoent *getprotobynumber(int proto);
void setprotoent(int stayopen);
void endprotoent(void);

struct servent *getservent(void);
struct servent *getservbyname(const char *name, const char *proto);
struct servent *getservbyport(int port, const char *proto);
void setservent(int stayopen);
void endservent(void);

// 端口号小于1024需要sudo权限
int bind(int sockfd, struct sockaddr *my_addr, socklen_t addrlen);
int getsockname(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 本机地址
int getpeername(int sockfd, struct sockaddr *addr, socklen_t *addrlen); // 对方地址
int connect(int sockfd, const struct sockaddr *addr, socklen_t addrlen);
int listen(int s, int backlog);
int accept(int s, struct sockaddr *addr, socklen_t *addrlen);
int send(int s, const void *msg, size_t len, int flags); // 仅用于tcp
int sendto(int  s, const void *msg, size_t len, int flags, const struct sockaddr *to, socklen_t tolen);
int sendmsg(int s, const struct msghdr *msg, int flags);
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);
int getsockopt(int sockfd, int level, int optname, void *optval, socklen_t *optlen);
int setsockopt(int sockfd, int level, int optname, const void *optval, socklen_t optlen);

UNIX域套接字:
UNIX域套接字用于在同一台计算机上运行的进程间通信。它比Socket效率更高。它提供流和数据报表两种接口,就像是Socket与Pipe的混合。

socket(AF_UNIX, SOCK_STREAM, 0)
int socketpair(int domain, int type, int protocol, int sv[2]);

终端IO(TTY)

termios.h

函数 说明
isatty 是否是TTY
tcgetattr 获取属性
tcsetattr 设置属性
cfgetispeed 获取输入速度(波特率)
cfgetospeed 获取输出速度(波特率)
cfsetispeed 设置输入速度
cfsetospeed 设置输出速度
tcdrain 等待所有输出都被传输
tcflow 挂起传输或者接收
tcflush 冲洗未决输入输出
tcsendbreak 发送break字符
tcgetpgrp 获取前台进程组ID
tcsetpgrp 设置前台进程组ID
tcgetsid 得到控制TTY会话进程组ID
getpass // 读入用户在终端上键入的口令
ioctl(TIOCGWINSZ) // 终端大小, TIOCSWINSZ 设置大小

伪终端
网络登陆服务器、窗口系统终端模拟、script程序、expect程序、运行协同进程…
posix_openpt/grantpt/ptsname/unlockpt
``

数据库

dbm在Unix系统中很流行,BSD扩充为ndbm。4.4BSD提供了一个新的库db。

网络打印机

IPP是网络打印机的通信规则。IPP建立在HTTP上。

版权声明:本文为博主原创文章,遵循 CC 4.0 BY-SA 版权协议,转载请附上原文出处链接和本声明。
本文链接:https://blog.csdn.net/bbdxf/article/details/85211143

智能推荐

C#连接OPC C#上位机链接PLC程序源码 1.该程序是通讯方式是CSharp通过OPC方式连接PLC_c#opc通信-程序员宅基地

文章浏览阅读565次。本文主要介绍如何使用C#通过OPC方式连接PLC,并提供了相应的程序和学习资料,以便读者学习和使用。OPC服务器是一种软件,可以将PLC的数据转换为标准的OPC格式,允许其他软件通过标准接口读取或控制PLC的数据。此外,本文还提供了一些学习资料,包括OPC和PLC的基础知识,C#编程语言的教程和实例代码。这些资料可以帮助读者更好地理解和应用本文介绍的程序。1.该程序是通讯方式是CSharp通过OPC方式连接PLC,用这种方式连PLC不用考虑什么种类PLC,只要OPC服务器里有的PLC都可以连。_c#opc通信

Hyper-V内的虚拟机复制粘贴_win10 hyper-v ubuntu18.04 文件拷贝-程序员宅基地

文章浏览阅读1.6w次,点赞3次,收藏10次。实践环境物理机:Windows10教育版,操作系统版本 17763.914虚拟机:Ubuntu18.04.3桌面版在Hyper-V中的刚安装好Ubuntu虚拟机之后,会发现鼠标滑动很不顺畅,也不能向虚拟机中拖拽文件或者复制内容。在VMware中,可以通过安装VMware tools来使物理机和虚拟机之间达到更好的交互。在Hyper-V中,也有这样的工具。这款工具可以完成更好的鼠标交互,我的..._win10 hyper-v ubuntu18.04 文件拷贝

java静态变量初始化多线程,持续更新中_类初始化一个静态属性 为线程池-程序员宅基地

文章浏览阅读156次。前言互联网时代,瞬息万变。一个小小的走错,就有可能落后于别人。我们没办法去预测任何行业、任何职业未来十年会怎么样,因为未来谁都不能确定。只能说只要有互联网存在,程序员依然是个高薪热门行业。只要跟随着时代的脚步,学习新的知识。程序员是不可能会消失的,或者说不可能会没钱赚的。我们经常可以听到很多人说,程序员是一个吃青春饭的行当。因为大多数人认为这是一个需要高强度脑力劳动的工种,而30岁、40岁,甚至50岁的程序员身体机能逐渐弱化,家庭琐事缠身,已经不能再进行这样高强度的工作了。那么,这样的说法是对的么?_类初始化一个静态属性 为线程池

idea 配置maven,其实不用单独下载Maven的。以及设置新项目配置,省略每次创建新项目都要配置一次Maven_安装idea后是不是不需要安装maven了?-程序员宅基地

文章浏览阅读1w次,点赞13次,收藏43次。说来也是惭愧,一直以来,在装环境的时候都会从官网下载Maven。然后再在idea里配置Maven。以为从官网下载的Maven是必须的步骤,直到今天才得知,idea有捆绑的 Maven 我们只需要搞一个配置文件就行了无需再官网下载Maven包以后再在新电脑装环境的时候,只需要下载idea ,网上找一个Maven的配置文件 放到 默认的 包下面就可以了!也省得每次创建项目都要重新配一次Maven了。如果不想每次新建项目都要重新配置Maven,一种方法就是使用默认的配置,另一种方法就是配置 .._安装idea后是不是不需要安装maven了?

奶爸奶妈必看给宝宝摄影大全-程序员宅基地

文章浏览阅读45次。家是我们一生中最重要的地方,小时候,我们在这里哭、在这里笑、在这里学习走路,在这里有我们最真实的时光,用相机把它记下吧。  很多家庭在拍摄孩子时有一个看法,认为儿童摄影团购必须是在风景秀丽的户外,即便是室内那也是像大酒店一样...

构建Docker镜像指南,含实战案例_rocker/r-base镜像-程序员宅基地

文章浏览阅读429次。Dockerfile介绍Dockerfile是构建镜像的指令文件,由一组指令组成,文件中每条指令对应linux中一条命令,在执行构建Docker镜像时,将读取Dockerfile中的指令,根据指令来操作生成指定Docker镜像。Dockerfile结构:主要由基础镜像信息、维护者信息、镜像操作指令、容器启动时执行指令。每行支持一条指令,每条指令可以携带多个参数。注释可以使用#开头。指令说明FROM 镜像 : 指定新的镜像所基于的镜像MAINTAINER 名字 : 说明新镜像的维护(制作)人,留下_rocker/r-base镜像

随便推点

毕设基于微信小程序的小区管理系统的设计ssm毕业设计_ssm基于微信小程序的公寓生活管理系统-程序员宅基地

文章浏览阅读223次。该系统将提供便捷的信息发布、物业报修、社区互动等功能,为小区居民提供更加便利、高效的服务。引言: 随着城市化进程的加速,小区管理成为一个日益重要的任务。因此,设计一个基于微信小程序的小区管理系统成为了一项具有挑战性和重要性的毕设课题。本文将介绍该小区管理系统的设计思路和功能,以期为小区提供更便捷、高效的管理手段。四、总结与展望: 通过本次毕设项目,我们实现了一个基于微信小程序的小区管理系统,为小区居民提供了更加便捷、高效的服务。通过该系统的设计与实现,能够提高小区管理水平,提供更好的居住环境和服务。_ssm基于微信小程序的公寓生活管理系统

如何正确的使用Ubuntu以及安装常用的渗透工具集.-程序员宅基地

文章浏览阅读635次。文章来源i春秋入坑Ubuntu半年多了记得一开始学的时候基本一星期重装三四次=-= 尴尬了 觉得自己差不多可以的时候 就吧Windows10干掉了 c盘装Ubuntu 专心学习. 这里主要来说一下使用Ubuntu的正确姿势Ubuntu(友帮拓、优般图、乌班图)是一个以桌面应用为主的开源GNU/Linux操作系统,Ubuntu 是基于DebianGNU/Linux,支..._ubuntu安装攻击工具包

JNI参数传递引用_jni引用byte[]-程序员宅基地

文章浏览阅读335次。需求:C++中将BYTE型数组传递给Java中,考虑到内存释放问题,未采用通过返回值进行数据传递。public class demoClass{public native boolean getData(byte[] tempData);}JNIEXPORT jboolean JNICALL Java_com_core_getData(JNIEnv *env, jobject thisObj, jbyteArray tempData){ //resultsize为s..._jni引用byte[]

三维重建工具——pclpy教程之点云分割_pclpy.pcl.pointcloud.pointxyzi转为numpy-程序员宅基地

文章浏览阅读2.1k次,点赞5次,收藏30次。本教程代码开源:GitHub 欢迎star文章目录一、平面模型分割1. 代码2. 说明3. 运行二、圆柱模型分割1. 代码2. 说明3. 运行三、欧几里得聚类提取1. 代码2. 说明3. 运行四、区域生长分割1. 代码2. 说明3. 运行五、基于最小切割的分割1. 代码2. 说明3. 运行六、使用 ProgressiveMorphologicalFilter 分割地面1. 代码2. 说明3. 运行一、平面模型分割在本教程中,我们将学习如何对一组点进行简单的平面分割,即找到支持平面模型的点云中的所有._pclpy.pcl.pointcloud.pointxyzi转为numpy

以NFS启动方式构建arm-linux仿真运行环境-程序员宅基地

文章浏览阅读141次。一 其实在 skyeye 上移植 arm-linux 并非难事,网上也有不少资料, 只是大都遗漏细节, 以致细微之处卡壳,所以本文力求详实清析, 希望能对大家有点用处。本文旨在将 arm-linux 在 skyeye 上搭建起来,并在 arm-linux 上能成功 mount NFS 为目标, 最终我们能在 arm-linux 里运行我们自己的应用程序. 二 安装 Sky..._nfs启动 arm

攻防世界 Pwn 进阶 第二页_pwn snprintf-程序员宅基地

文章浏览阅读598次,点赞2次,收藏5次。00为了形成一个体系,想将前面学过的一些东西都拉来放在一起总结总结,方便学习,方便记忆。攻防世界 Pwn 新手攻防世界 Pwn 进阶 第一页01 4-ReeHY-main-100超详细的wp1超详细的wp203 format2栈迁移的两种作用之一:栈溢出太小,进行栈迁移从而能够写入更多shellcode,进行更多操作。栈迁移一篇搞定有个陌生的函数。C 库函数 void *memcpy(void *str1, const void *str2, size_t n) 从存储区 str2 _pwn snprintf

推荐文章

热门文章

相关标签