环境变量,PATH环境变量,环境变量表,错误处理 操作系统(OS):管理硬件,软件资源的系统软件
环境变量 形式:键=值,可以此添加环境变量,若存在,则修改(不能在等号左右加空格)
1 2 env //可查看变量列表echo $home //查看环境变量的值
bash环境变量可分为全局环境变量,自定义/私有/局部环境变量 全局:env可看到,可被bash子进程继承 自定义:与全局相反
1 2 export 自定义环境变量 //添加成全局unset xxx //删除全局
PATH环境变量 给bash找命令(可执行程序)用的 故不加./的可执行程序默认查找PATH环境变量
1 $PATH =$PATH :. //添加PATH环境变量(当前路径),当前窗口有效
在 ~/.bashrc 里添加 $PATH=$PATH:.
可永久生效
1 source ~/.bashrc //可使文件对当前bash生效(刷新bash)
环境变量表 进程:正在执行的程序/程序进行的过程
所有程序环境变量由一个字符指针维护环境变量表(数组),最后一个元素为NULL 用二维指针(extern char **environ)来获取数组的首元素的地址(char *),
1 int main (int argc, char * argv[], char * envp[])
可发现用二维指针获取的环境变量和env获取的大致一样,因为environ继承了全局环境变量
其他
该环境变量决定了命令行提示符的内容,可通过修改来精简内容
错误处理 整数类型全局变量errno中存储了最近一次的错误编号(包含errno.h头文件即可运用) /usr/include/errno.h 包含了errno全局变量的外部声明 /usr/include/asm-generic/errno-base.h 包含各种错误号的宏定义
strerror()
函数(包含string.h头文件)char *strerror(int errnum)
参数传错误编号,返回错误信息字符串
perror()
函数(包含stdio.h)void perror(char const*tag)
参数传自定义的提示内容(错误相关信息),在标准出错设备上打印最近一次函数调用的错误信息(会自动在tag后加: )
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 #include <stdio.h> int main (int argc,char * argv[],char * envp[]) { extern char ** environ; for (char ** pp = envp;*pp;pp++){ printf ("%s\n" ,*pp); } printf ("---------------\n" ); printf ("environ = %p\n" ,environ); printf ("envp = %p\n" ,envp); return 0 ; }
库文件,静态库,动态库 静态库static(.a后缀) 本质:将库内容复制步骤 :
实现代码(.c)和接口声明(.h),及接口文件(总的一个.h,包含所有接口声明)
编译成目标文件1 2 gcc -c calc.c //生成.o文件 gcc -c show.c
打包成静态库1 ar -r libmath.a calc.o show.o
用法 :
直接调用,执行时需要同时编译库和文件
用-l指定库名,-L指定路径(静态库名字为libxxx)1 gcc main.c -lmath -L.. //在上层路径找math库
或者用-l制定库名,用LIBRARY_PATH环境变量指定库路径1 2 export LIBRARY_PATH=$LIBRARY_PATH :.. //路径为..,编译期gcc main.c -lmath
动态库shared(.so) 本质:生成指令连接动态库(存在链接器),依赖于动态库步骤 :
实现代码(.c)和接口声明(.h),及接口文件(总的一个.h,包含所有接口声明)
编译成目标文件1 2 gcc -c -fpic calc.c gcc -c -fpic show.c
打包成动态库1 gcc -shared calc.o show.o -o libmath.so
也可一步完成编译链接1 gcc -shared -fpic calc.c show.c -o libmath.so
fPIC:代码大,速度慢,全平台 fpic:上反,上反,部分平台
用法 :
配置环境变量1 export LD_LIBRARY_PATH=$LD_LIBRARY_PATH :.. //运行期
直接使用
动态库的动态加载 什么时候用,什么时候加载,提升内存利用效率 需包含头文件 dlfcn.h 编译时需加 -ldl
载入内存 :
1 void * dlopen (char const * filename, int flag) ;
filename:路径,可文件名,可目录,若只有文件名的话需配置环境变量 flag:
RTLD_LAZY - 延迟加载:真正使用时加载
RTLD_NOW - 立即加载
返回值为句柄,失败NULL
获取函数地址 :
1 void * dlsym (void * handle, char const * symbol) ;
handle:访问句柄 symbol:符号名 返回符号地址
卸载 :
1 int dlclose (void * handle) ;
成功返回0,失败非0
返回错误信息 :
返回错误信息的指针
其他工具 1 2 nm xxx //列出当中的符号,其中T为自己写的,U为调用别的 ldd xxx //查看依赖的库
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <stdio.h> #include <dlfcn.h> int main (void ) { void * handle = dlopen("/home/tarena/2212/day02/shared/libmath.so" ,RTLD_NOW); if (handle == NULL ){ fprintf (stderr ,"dlpen:%s\n" ,dlerror()); return -1 ; } int (*add)(int ,int ) = dlsym(handle,"add" ); if (add == NULL ){ fprintf (stderr ,"dlsym:%s\n" ,dlerror()); return -1 ; } int (*sub)(int ,int ) = dlsym(handle,"sub" ); if (sub == NULL ){ fprintf (stderr ,"dlsym:%s\n" ,dlerror()); return -1 ; } void (*show)(int ,char ,int ,int ) = dlsym(handle,"show" ); if (show == NULL ){ fprintf (stderr ,"dlsym:%s\n" ,dlerror()); return -1 ; } int a = 123 ,b = 456 ; show(a,'+' ,b,add(a,b)); show(a,'-' ,b,sub(a,b)); if (dlclose(handle)){ fprintf (stderr ,"dlclose:%s\n" ,dlerror()); return -1 ; } return 0 ; }
虚拟地址,内存映射,内存管理 虚拟地址 虚拟地址空间(范围): 32位:0x00000000~0xFFFFFFFF 2^32 4G大小 0~3G 用户用 3G~4G 内核用
64位: 0x0000 0000 0000 0000~0x0000 FFFF FFFF FFFF 用户 0xFFFF 0000 0000 0000~0xFFFF FFFF FFFF FFFF 内核
虚拟地址空间布局 参数和环境区:命令行参数和环境变量 栈区(stack):非静态局部变量 堆栈增长的预留空间 堆区(heap):动态内存分配 BSS区(bss):未被初始化的全局和静态局部变量 数据区(data):不具常属性且被初始化的全局和静态局部变量 代码区(text)/只读常量区:可执行指令, 字面值常量,具有常量性且被初始化的全局和静态局部变量 (BSS区和数据区有时也可统称数据区)
内存映射 虚拟地址映射物理地址 需包含sys/mman.h头文件
建立 :
1 void * mmap (void * start, size_t length, int prot, int flag, int fd, off_t offset) ;
start:虚拟地址起始地址,NULL系统自动选定后返回 length:映射字节数,自动按页圆整(一页4096) prot:映射区操作权限
PROT_ERAD 映射区可读
PROT_WRITE 可写
PROT_EXEC 可执行
PROT_NONE 不可访问
flag:
MAP_ANONYMOUS 匿名映射,映射物理地址而非文件
MAP_PRIVATE 映射区写操作只反映到缓冲区
MAP_SHARED 写操作直接反映到文件
MAP_DENYWRITE 拒绝写操作
MAP_FIXED 若在start上无法创建映射, 则失败(无此标志系统自动调整)
fd:文件描述符 offset:文件偏移量(按页圆整) 返回起始地址,失败MAP_FAILED (-1)
解除 :
1 int munmap (void * start, size_t length) ;
允许部分解除,但按页处理
代码 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 #include <stdio.h> #include <string.h> #include <sys/mman.h> int main (void ) {char * start = mmap(NULL ,8192 ,PROT_READ | PROT_WRITE,MAP_PRIVATE | MAP_ANONYMOUS,0 ,0 );if (start == MAP_FAILED){ perror("mmap" ); return -1 ; } strcpy (start,"铁锅炖大鹅" );printf ("%s\n" ,start);if (munmap(start,4096 ) == -1 ){ perror("munmap" ); return -1 ; } char * start2 = start + 4096 ;strcpy (start2,"小鸡炖蘑菇" );printf ("%s\n" ,start2);if (munmap(start2,4096 ) == -1 ){ perror("munmap" ); return -1 ; } return 0 ;}
其他 段错误:一切对虚拟内存的越权访问,都会导致段错误
文件系统 文件的打开与关闭 包含头文件fcntl.h
1 int open (char const * pathname, int flags, mode_t mode) ;
flags:
O_RDONLY 只读
O_WRONLY 只写
O_RDWR 读写
O_APPEND 追加
O_CREAT 不存在即创建,已存在即打开
O_EXCL 不存在即创建,已存在即报错
O_TRUNC 不存在即创建,已存在即清空
mode: 权限模式
用0XXX三位八进制数表示,由高到低分别为拥有者,同组用户,其他
读4,写2,执行1,加法组合
返回文件描述符(整数)
需包含unistd.h头文件
fd:处于打开状态的文件描述符
权限掩码:
影响其他用户权限,例0002,表示禁止写
修改 :
1 2 3 chmod o+w open.txt //对文件修改权限umask 0022 //直接修改权限掩码
文件的内核结构 文件在磁盘中以i节点+数据块形式存储 打开文件,内核会维护两个特殊的结构体,v节点包含v节点,i节点和其他信息;另一个结构体为文件表项,成员有状态标志,读写位置,v节点指针;存储到内存中 每次打开v节点只有一个,结构表项可多个
内核维护程序的一个进程表(进程描述符,进程控制块) 其中有个成员是文件描述符表,为数组,存放指向文件表项的指针,而指针的位置(数组下标)即文件描述符fd
文件描述符是当前未被使用的最小描述符,其中0,1,2默认为标准输入/输出/错误(STDIN/STDOUT/STDERR + _FILENO) perror输出是到标准错误,不经过缓冲区,直接到屏幕
stdin,stdout,stderr为FILE*类型的指针,指向0,1,2;前者是调用标准c库的时候,后者是系统调用
文件读写 包含 unistd.h
1 2 ssize_t write (int fd, void const * buf, size_t count) ;ssize_t read (int fd, void * buf, size_t count) ;
buf:内存缓冲区 count:字节数
当以wq保存的时,会多一个换行符
代码 读 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <stdio.h> #include <unistd.h> #include <fcntl.h> int main (void ) { int fd = open("./shared.txt" ,O_RDONLY); if (fd == -1 ){ perror("open" ); return -1 ; } char buf[32 ] = {}; ssize_t size = read(fd,buf,sizeof (buf)-1 ); if (size == -1 ){ perror("read" ); return -1 ; } printf ("%s\n" ,buf); printf ("实际读取到%ld个字节\n" ,size); close(fd); return 0 ; }
写 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <stdio.h> #include <string.h> #include <fcntl.h> #include <unistd.h> int main (void ) { int fd = open("./shared.txt" ,O_CREAT | O_TRUNC | O_WRONLY,0664 ); if (fd == -1 ){ perror("open" ); return -1 ; } char * buf = "铁锅炖大鹅" ; ssize_t size = write(fd,buf,strlen (buf)); if (size == -1 ){ perror("write" ); return -1 ; } printf ("实际向文件中写入%ld个字节\n" ,size); close(fd); return 0 ; }
顺序读写和随机读写,文件描述符的复制,访问测试,修改文件大小 顺序读写和随机读写 更改读写位置 : 包含unistd.h
1 off_t lseek (int fd, off_t offset, int whence) ;
offset:读写位置偏移量 whence:偏移起点
SEEK_SET 文件头(首字节开始)
SEEK_CUR 当前位置(最后读写字节下一个字节开始)
SEEK_END 文件尾(最后一个字节的下一个字节开始)
返回调整后的位置
超过原本字符串大小后增加内容会出现文件空洞“0”
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> int main (void ) { int fd = open("./lseek.txt" ,O_WRONLY | O_CREAT | O_TRUNC,0664 ); if (fd == -1 ){ perror("open" ); return -1 ; } char * buf = "hello world!" ; if (write(fd,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } off_t len = lseek(fd,0 ,SEEK_CUR); printf ("此时的读写位置是%ld\n" ,len); if (lseek(fd,-6 ,SEEK_END) == -1 ){ perror("lseek" ); return -1 ; } buf = "linux!" ; if (write(fd,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } if (lseek(fd,8 ,SEEK_END) == -1 ){ perror("lseek" ); return -1 ; } buf = "感冒真难受" ; if (write(fd,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } close(fd); return 0 ; }
文件描述符的复制 需包含unistd.h
oldfd:文件描述符 返回复制的新的文件描述符 两者都指向同一个文件表项
1 int dup2 (int oldfd, int newfd) ;
返回newfd,若被占用,则先关闭后复制
访问测试 包含unistd.h判断是否有访问权限
1 int access (char const * pathname, int mode) ;
mode:
R_OK 读
W_OK 写
F_OK 存在
X_OK 执行
成功返回0, 失败-1
修改文件大小 包含unistd.h
1 2 int truncate (char const * path, offset_t length) ;int ftruncate (int fd,off_set length) ;
从尾部开始调整大小
文件锁,文件元数据,内存映射文件 文件锁 包含fcntl.h加解锁 :
1 int fcntl (int fd, int F_SETLK/F_SETLKW, struct flock* lock) ;
第二个参数前者为非阻塞模式加锁,后者为阻塞模式加锁 lock 对文件要加的锁
1 2 3 4 5 6 7 struct flock { short l_type; short l_whence; off_t l_start; off_t l_len; pid_t l_pid; };
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <errno.h> int main (int argc,char * argv[]) { int fd = open("./shared.txt" ,O_WRONLY | O_CREAT | O_APPEND,0664 ); if (fd == -1 ){ perror("open" ); return -1 ; } struct flock lock ; lock.l_type = F_WRLCK; lock.l_whence = SEEK_SET; lock.l_start = 0 ; lock.l_len = 0 ; lock.l_pid = -1 ; while (fcntl(fd,F_SETLK,&lock) == -1 ){ if (errno == EACCES || errno == EAGAIN){ printf ("文件被锁定,干点别的去...\n" ); sleep(1 ); }else { perror("fcntl" ); return -1 ; } } for (int i = 0 ;i < strlen (argv[1 ]);i++){ if (write(fd,&argv[1 ][i],sizeof (argv[1 ][i])) == -1 ){ perror("write" ); return -1 ; } sleep(1 ); } struct flock unlock ; unlock.l_type = F_UNLCK; unlock.l_whence = SEEK_SET; unlock.l_start = 0 ; unlock.l_len = 0 ; unlock.l_pid = -1 ; if (fcntl(fd,F_SETLKW,&unlock) == -1 ){ perror("fcntl" ); return -1 ; } close(fd); return 0 ; }
文件锁的内核结构 以链表形式组织锁,在v节点后跟锁节点,锁节点后再接下一个锁节点。。
文件的元数据 包含sys/stat.h从i节点中提取文件的元数据 ,即文件的属性信息
1 2 3 int stat (char const * path, struct stat* buf) ;int fstat (int fd, struct stat* buf) ;int lstat (char const * path, struct stat* buf) ;
buf:输出型参数,存放提取的元数据
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 struct stat { dev_t st_dev; ino_t st_ino; mode_t st_mode; nlink_t st_nlink; uid_t st_uid; gid_t st_gid; dev_t st_rdev; off_t st_size; blksize_t st_blksize; blkcnt_t st_blocks; time_t st_atime; time_t st_mtime; time_t st_ctime; }
32位不同比特位的排列组合区分功能 辅助分析文件类型的使用宏
1 2 3 4 5 6 7 S_ISREG() S_ISDIR() S_ISSOCK() S_ISCHR() S_ISBLK() S_ISLNK() S_ISFIFO()
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 #include <stdio.h> #include <string.h> #include <sys/stat.h> #include <time.h> char * mtos (mode_t m) { static char s[11 ]; if (S_ISDIR(m)){ strcpy (s,"d" ); }else if (S_ISSOCK(m)){ strcpy (s,"s" ); }else if (S_ISCHR(m)){ strcpy (s,"c" ); }else if (S_ISBLK(m)){ strcpy (s,"b" ); }else if (S_ISLNK(m)){ strcpy (s,"l" ); }else if (S_ISFIFO(m)){ strcpy (s,"p" ); }else { strcpy (s,"-" ); } strcat (s,m & S_IRUSR ? "r" : "-" ); strcat (s,m & S_IWUSR ? "w" : "-" ); strcat (s,m & S_IXUSR ? "x" : "-" ); strcat (s,m & S_IRGRP ? "r" : "-" ); strcat (s,m & S_IWGRP ? "w" : "-" ); strcat (s,m & S_IXGRP ? "x" : "-" ); strcat (s,m & S_IROTH ? "r" : "-" ); strcat (s,m & S_IWOTH ? "w" : "-" ); strcat (s,m & S_IXOTH ? "x" : "-" ); return s; } char * ttos (time_t t) { static char time[20 ]; struct tm * l = localtime(&t); sprintf (time,"%04d-%02d-%02d %02d:%02d:%02d" , l->tm_year + 1900 ,l->tm_mon + 1 ,l->tm_mday,l->tm_hour,l->tm_min,l->tm_sec); return time; } int main (int argc,char * argv[]) { if (argc < 2 ){ fprintf (stderr ,"用法:%s : <文件名>\n" ,argv[0 ]); return -1 ; } struct stat s ; if (stat(argv[1 ],&s) == -1 ){ perror("stat" ); return -1 ; } printf (" 设备ID:%lu\n" ,s.st_dev); printf (" i节点号:%ld\n" ,s.st_ino); printf (" 类型和权限:%s\n" ,mtos(s.st_mode)); printf (" 硬连接数:%lu\n" ,s.st_nlink); printf (" 用户ID:%u\n" ,s.st_uid); printf (" 组ID:%u\n" ,s.st_gid); printf (" 特殊设备ID:%lu\n" ,s.st_rdev); printf (" 总字节数:%ld\n" ,s.st_size); printf (" IO块字节数:%ld\n" ,s.st_size); printf (" 存储块数:%ld\n" ,s.st_blksize); printf (" 最后访问时间:%s\n" ,ttos(s.st_atime)); printf (" 最后修改时间:%s\n" ,ttos(s.st_mtime)); printf (" 最后改变时间:%s\n" ,ttos(s.st_ctime)); return 0 ; }
内存映射文件 实现不同进程间通信 比read,write快
1 2 void * mmap (void * start, size_t length, int prot, int flag, int fd, off_t offset) ;int munmap (void * start, size_t length) ;
具体见3代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 #include <stdio.h> #include <string.h> #include <sys/mman.h> #include <fcntl.h> #include <unistd.h> int main (void ) { int fd = open("./fmap.txt" ,O_RDWR | O_CREAT | O_TRUNC,0664 ); if (fd == -1 ){ perror("open" ); return -1 ; } if (ftruncate(fd,300 ) == -1 ){ perror("ftruncate" ); return -1 ; } char * start = mmap(NULL ,4096 ,PROT_READ | PROT_WRITE,MAP_SHARED,fd,0 ); if (start == MAP_FAILED){ perror("mmap" ); return -1 ; } strcpy (start,"面包牛奶火腿肠" ); printf ("%s\n" ,start); if (munmap(start,4096 ) == -1 ){ perror("munmap" ); return -1 ; } close(fd); return 0 ; }
进程,父子进程 进程 正在执行的程序,程序执行的过程
进程标识: PID一个时刻唯一,可重用,延时重用(所有PID用完再回过头重用)
0号进程:调度进程/交换进程,系统内核一部分,所有进程的根进程 1号进程:init进程,由0号进程创建,读写系统初始化文件,引导系统到一个特定状态,永不终止 父进程PID即子进程的PPID
相关函数: 包含unistd.h
1 2 3 4 5 6 pid_t getpid (void ) ;pid_t getppid (void ) ;uid_t getuid (void ) ; gid_t getgid (void ) ; uid_t geteuid (void ) ; gid_t getegid (void ) ;
创建进程: 包含unistd.h
给父进程返回子进程PID,子进程返回0;可以此区分 子进程是父进程的不完全副本 复制除代码区之外的数据和文件描述符表
区分父子进程代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> #include <unistd.h> int main (void ) { printf ("%d进程:我是父进程,我要创建子进程了\n" ,getpid()); pid_t a = fork(); if (a == -1 ){ perror("fork" ); return -1 ; } if (a == 0 ){ printf ("%d进程:这是子进程执行的代码\n" ,getpid()); return 0 ; } printf ("%d进程:这是父进程执行的代码\n" ,getpid()); return 0 ; }
父子进程操作同一文件代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> int main (void ) { int fd = open("./ftab.txt" ,O_WRONLY | O_CREAT | O_TRUNC,0664 ); if (fd == -1 ){ perror("open" ); return -1 ; } char * buf = "hello world!" ; if (write(fd,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ if (lseek(fd,-6 ,SEEK_END) == -1 ){ perror("lseek" ); return -1 ; } close(fd); return 0 ; } sleep(1 ); buf = "linux!" ; if (write(fd,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } close(fd); return 0 ; }
进程的终止,回收子进程,wait,waitpid 进程终止 正常终止:
return 值(退出码)给main函数,父进程接到返回值,只能拿到最低数位字节
void exit(int status);需包含stdlib.h头文件,status为退出码,习惯上将EXIT_SUCCESS和EXIT_FAILURE作为成功和失败,多数系统定义为0和-1
终止进程前还会做:
a 调用实现通过atexit或on_exit函数注册的退出处理函数
b 冲刷并关闭所有仍处于打开状态的标准I/O流
c 删除所有通过tmpfile函数创建的临时文件
d _exit(status);
注册处理函数: 需stdlib.h,注册顺序和调用顺序相反 1 int atexit (void (*function)(void )) ;
参数为函数指针,指向退出处理函数 1 int on_exit (void (*function)(int , void *), void * arg) ;
参数第一个为函数指针,指向函数的其中的第一个参数是传递给exit函数的status参数或main中return返回值,第二个参数是来自on_exit的arg参数 arg 泛型指针,传递给调用函数的第二个参数
代码 : 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <stdlib.h> #include <unistd.h> void doit (void ) { printf ("遗言也没啥可说的...\n" ); } void doit2 (int status,void * arg) { printf ("status = %d\n" ,status); printf (" arg = %s\n" ,(char *)arg); } int hahaha (void ) { printf ("哈哈哈哈\n" ); _Exit(0 ); return 10 ; } int main (void ) { atexit(doit); on_exit(doit2,"拜拜~~" ); printf ("函数返回%d\n" ,hahaha()); return 0 ; }
调用_exit或_Exit函数(使程序真正结束) 需包含unistd.h
需包含stdlib.h
_exit收尾工作:
a 关闭处于打开状态的文件描述符
b 将调用进程的所有子进程托付给init进程收养
c 向调用进程的父进程发送SIGCHLD(17)信号
d 令调用进程终止运行,将status的低八位作为退出码保存在其终止状态中
异常终止
进程执行危险操作,系统故障
人为触发信号
向进程自己发送信号1 2 #include <stdlib.h> void abort (void ) ;
回收子进程 1 2 #include <sys/wait.h> pid_t wait (int * status) ;
等待回收任意子进程 参数是用于输出子进程的终止状态,可置NULL来不存终止状态 返回回收的子进程PID
1 2 3 4 5 6 7 8 #include <sys/wait.h> WIFEXITED(status) WEXITSTATUS(status) WTERMSIG(status) WIFSIGNALED(status) WTERMSIG(status) WEXITSTATUS(status)
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 #include <stdio.h> #include <unistd.h> #include <sys/wait.h> #include <errno.h> int main (void ) { for (int i = 0 ;i < 5 ;i++){ pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ printf ("%d进程:我是子进程\n" ,getpid()); sleep(i+1 ); return i+1 ; } } for (;;){ int s; pid_t pid = wait(&s); if (pid == -1 ){ if (errno == ECHILD){ printf ("没有子进程了\n" ); break ; }else { perror("wait" ); return -1 ; } } printf ("%d进程:回收了%d进程的僵尸\n" ,getpid(),pid); if (WIFEXITED(s)){ printf ("正常终止:%d\n" ,WEXITSTATUS(s)); }else { printf ("异常终止:%d\n" ,WTERMSIG(s)); } } return 0 ; }
另一个函数
1 pid_t waitpid (pid_t pid, int * status, int options) ;
参数:pid>0可指定具体的子进程PID,-1相当于wait options:
0 阻塞模式
WNOHANG 非阻塞模式,若子进程仍运行,返回0
返回子进程PID或0,失败-1
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 #include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main (void ) { pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ printf ("%d进程:我是子进程,你收不了我!\n" ,getpid()); sleep(10 ); return 0 ; } printf ("%d进程:我要回收那个子进程\n" ,getpid()); for (;;){ pid_t childpid = waitpid(pid,NULL ,WNOHANG); if (childpid == -1 ){ perror("waitpid" ); return -1 ; }else if (childpid == 0 ){ printf ("%d进程:子进程在运行,收不了,干点别的去\n" ,getpid()); sleep(1 ); }else { printf ("%d进程:回收了%d进程的僵尸\n" ,getpid(),childpid); break ; } } return 0 ; }
创建新进程,system 创建新进程
一般将fork和execl相结合使用
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 #include <stdio.h> #include <unistd.h> #include <sys/wait.h> int main (void ) { pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ if (execl("./new" ,"./new" ,"123" ,"hello" ,NULL ) == -1 ){ perror("execl" ); return -1 ; } } int s; if (waitpid(pid,&s,0 ) == -1 ){ perror("waitpid" ); return -1 ; } if (WIFSIGNALED(s)){ printf ("异常终止:%d\n" ,WTERMSIG(s)); }else { printf ("正常终止:%d\n" ,WEXITSTATUS(s)); } pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ if (execl("/bin/ls" ,"ls" ,"-l" ,NULL ) == -1 ){ perror("execl" ); return -1 ; } } if (waitpid(pid,&s,0 ) == -1 ){ perror("waipid" ); return -1 ; } if (WIFEXITED(s)){ printf ("正常终止:%d\n" ,WEXITSTATUS(s)); }else { printf ("异常终止:%d\n" ,WTERMSIG(s)); } return 0 ; }
system 执行终端命令
1 2 #include <stdlib.h> int system (const char * command) ;
参数传终端命令 返回command终止状态,失败返回-1
vfork 与父进程公用一份数据,子进程用时父进程挂起,一般用于搭配exec(免去复制)
信号基础,信号处理,太平间信号,信号发送 信号类似于硬件上的中断 64信号,32,33信号没有,前31不可靠信号
信号处理 1 2 3 #include <signal.h> typedef void (*sighandler_t ) (int ) ;sighandler_t signal (int signum, sighandler_t handler) ;
功能:设置信号的处理方式 参数: signum:信号编号 handler:处理方式:
信号处理函数指针:捕获 成功返回原信号的处理方式,如果之前未处理过则返回NULL,失败返回SIG_ERR
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 #include <stdio.h> #include <unistd.h> #include <signal.h> typedef void (*sighandler_t ) (int ) ;void sigfun (int signum) { printf ("%d进程:捕获到%d号信号\n" ,getpid(),signum); printf ("晚上吃多了,有点撑....\n" ); } int main (void ) { printf ("%d进程:我要开始死循环了\n" ,getpid()); sighandler_t ret = signal(SIGINT,SIG_IGN); if (ret == SIG_ERR){ perror("signal" ); return -1 ; } printf ("ret = %p\n" ,ret); ret = signal(SIGINT,sigfun); if (ret == SIG_ERR){ perror("signal" ); return -1 ; } printf ("ret = %p\n" ,ret); ret = signal(SIGINT,SIG_DFL); if (ret == SIG_ERR){ perror("signal" ); return -1 ; } printf ("ret = %p\n" ,ret); printf ("sigfun = %p\n" ,ret); for (;;); return 0 ; }
太平间信号 17号信号,子进程死亡向父进程发送的信号。用于收尸。 在信号处理函数执行期间,若再有相同的信号多次到来,只保留一个,其余丢弃。
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 #include <stdio.h> #include <unistd.h> #include <signal.h> #include <sys/wait.h> #include <errno.h> void sigchild (int signum) { printf ("%d进程:捕获到%d号信号\n" ,getpid(),signum); sleep(2 ); for (;;){ pid_t pid = waitpid(-1 ,NULL ,WNOHANG); if (pid == -1 ){ if (errno == ECHILD){ printf ("%d进程:没有子进程\n" ,getpid()); break ; }else { perror("waitpid" ); return ; } }else if (pid == 0 ){ printf ("%d进程:子进程在运行,没法收\n" ,getpid()); break ; }else { printf ("%d进程:回收了%d进程的僵尸\n" ,getpid(),pid); } } } int main (void ) { if (signal(SIGCHLD,sigchild) == SIG_ERR){ perror("signal" ); return -1 ; } for (int i = 0 ;i < 5 ;i++){ pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ printf ("%d进程;我是子进程\n" ,getpid()); sleep(1 ); return 0 ; } } for (;;); return 0 ; }
发送信号
不指定信号,缺省发送15终止进程信号 例:
1 2 3 kill -KILL -1 //给所有进程发送9号信号(SIGKILL),不能捕获,忽略的终止信号kill -9 1234 //1234为进程PIDkill -SIGKILL 1234 5678
函数:
1 2 #include <signal.h> int kill (pid_t pid, int signum) ;
pid:
signum:信号编号,取0检查pid进程是否存在,不存在返回-1,errno为ESRCH 成功返回0(至少发送出一个信号),失败-1
向调用进程自己发送信号
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 #include <stdio.h> #include <unistd.h> #include <signal.h> #include <errno.h> #include <sys/wait.h> void sigfun (int signum) { printf ("%d进程:捕获到%d号信号\n" ,getpid(),signum); } int main (void ) { pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ printf ("%d进程:我是子进程\n" ,getpid()); for (;;); return 0 ; } getchar(); if (kill(pid,SIGINT) == -1 ){ perror("kill" ); return -1 ; } getchar(); if (kill(pid,0 ) == -1 ){ if (errno == ESRCH){ printf ("子进程不存在\n" ); }else { perror("kill" ); return -1 ; } }else { printf ("子进程存在\n" ); } getchar(); if (wait(NULL ) == -1 ){ perror("wait" ); return -1 ; } if (kill(pid,0 ) == -1 ){ if (errno == ESRCH){ printf ("子进程不存在\n" ); }else { perror("kill" ); return -1 ; } }else { printf ("子进程存在\n" ); } return 0 ; }
暂停,睡眠与闹钟,信号集,信号屏蔽 暂停 睡眠
1 2 #include <unistd.h> unsigned int sleep (unsigned int seconds) ;
有限睡眠 返回0或剩余秒数
1 int usleep (useconds_t usec) ;
成功返回0,失败-1
暂停
1 2 #include <unsitd.h> int pause (void ) ;
无限睡眠 失败返回-1,信号处理函数结束后返回
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> #include <unistd.h> #include <signal.h> void sigfun (int signum) { printf ("%d号信号处理开始\n" ,signum); sleep(3 ); printf ("%d号信号处理结束\n" ,signum); } int main (void ) { if (signal(SIGINT,sigfun) == SIG_ERR){ perror("signal" ); return -1 ; } printf ("%d进程:我要睡觉了\n" ,getpid()); int ret = pause(); printf ("%d进程:pause函数返回%d\n" ,getpid(),ret); return 0 ; }
信号集 本质:一块128字节连续的存储区 内核用sigset_t类型表示信号集 看1024个比特位的值表明是否有对应信号(像操控寄存器)填满信号集
1 2 #include <signal.h> int sigfillset (sigset_t *sigset) ;
填满信号集(置1),32,33信号没有,故为0
清空信号集
1 int sigemptyset (sigset* sigset)
加入信号
1 int sigaddset (sigset_t * sigset, int signum) ;
sigset:信号集 signum:信号编号
删除信号
1 int sigdelset (sigset_t * sigset, int signum) ;
判断是否有信号
1 int sigismember (const sigset_t * sigset, int signum) ;
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 #include <stdio.h> #include <signal.h> #include <unistd.h> void printb (char byte) { for (int i = 0 ;i < 8 ;i++){ printf ("%d" ,byte & 1 << 7 - i ? 1 : 0 ); } printf (" " ); } void printm (void * buf,size_t size) { for (int i = 0 ;i < size;i++){ printb(((char *)buf)[size-1 -i]); if ((i+1 ) % 8 == 0 ){ printf ("\n" ); } } } int main (void ) { sigset_t s; printf ("填满信号集\n" ); sigfillset(&s); printm(&s,sizeof (s)); printf ("清空信号集\n" ); sigemptyset(&s); printm(&s,sizeof (s)); printf ("添加2号信号\n" ); sigaddset(&s,SIGINT); printm(&s,sizeof (s)); printf ("添加3号信号\n" ); sigaddset(&s,SIGQUIT); printm(&s,sizeof (s)); printf ("删除2号信号\n" ); sigdelset(&s,SIGINT); printm(&s,sizeof (s)); printf ("信号集中%s2号信号\n" ,sigismember(&s,SIGINT) ? "有" : "无" ); printf ("信号集中%s3号信号\n" ,sigismember(&s,SIGQUIT) ? "有" : "无" ); return 0 ; }
信号屏蔽 每个进程都有一个信号掩码(信号集) 有哪个信号,哪个信号无法送达 信号处理函数期间,有哪个信号,信号掩码中就有哪个信号
不可靠信号,屏蔽解除后只接受一个 可靠信号,屏蔽解除后都接收
设置信号掩码
1 2 #include <signal.h> int sigprocmask (int how, const sigset_t * sigset, sigset_t * oldset) ;
how:SIG_BLOCK:将sigset中的信号加入当前信号掩码
SIG_UNBLOCK:从当前信号掩码中删除sigset的信号
SIG_SETMASK:把sigset设置为当前信号掩码
oldset:输出型参数,输出原信号掩码
获取调用进程的未决信号集
1 2 #include <signal.h> int sigpending (sigset_t * sigset) ;
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <stdio.h> #include <unistd.h> #include <signal.h> void updatedb (void ) { for (int i = 0 ;i < 5 ;i++){ printf ("正在更新第%d条数据.....\n" ,i + 1 ); sleep(1 ); } } void sigfun (int signum) { printf ("捕获到%d号信号\n" ,signum); } int main (void ) { int signum = 40 ; printf ("%d进程:捕获%d号信号\n" ,getpid(),signum); if (signal(signum,sigfun) == SIG_ERR){ perror("signal" ); return -1 ; } printf ("%d进程:屏蔽%d号信号\n" ,getpid(),signum); sigset_t sigset; sigemptyset(&sigset); sigaddset(&sigset,signum); sigset_t oldset; if (sigprocmask(SIG_SETMASK,&sigset,&oldset) == -1 ){ perror("sigprocmask" ); return -1 ; } pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ for (int i = 0 ;i < 5 ;i++){ printf ("%d进程:给父进程发送%d号信号\n" ,getpid(),signum); if (kill(getppid(),signum) == -1 ){ perror("kill" ); return -1 ; } } return 0 ; } updatedb(); printf ("%d进程:解除对%d号信号的屏蔽\n" ,getpid(),signum); if (sigprocmask(SIG_SETMASK,&oldset,NULL ) == -1 ){ perror("sigprocmask" ); return -1 ; } for (;;); return 0 ; }
内存壁垒,有名管道,无名管道 进程间通信的种类 内存壁垒:两进程间,有相同的虚拟地址,但没有相同的物理地址
进程间通信种类:
命令行参数execl
环境变量execle
内存映射文件
管道
共享内存
消息队列
信号量
本地套接字
管道:半双工,一页大小 有名管道:任意进程 无名管道:父子,兄弟进程
有名管道 亦称FIFO:特殊文件 在磁盘上只有i节点,没有数据块,不保存数据(0字节) 本质:内核存储区,伪装成特殊的文件供访问
函数:创建有名管道
1 2 #include <sys/stat.h> int mkfifo (char const * pathname, mode_t mode) ;
mode:权限模式
代码 :
写入
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 #include <stdio.h> #include <string.h> #include <unistd.h> #include <fcntl.h> #include <sys/stat.h> int main (void ) { printf ("创建有名管道文件\n" ); if (mkfifo("./fifo" ,0664 ) == -1 ){ perror("mkfifo" ); return -1 ; } printf ("打开有名管道文件\n" ); int fd = open("./fifo" ,O_WRONLY); if (fd == -1 ){ perror("open" ); return -1 ; } printf ("发送数据\n" ); for (;;){ char buf[64 ] = {}; fgets(buf,sizeof (buf),stdin ); if (strcmp (buf,"!\n" ) == 0 ){ break ; } if (write(fd,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } } printf ("关闭有名管道文件\n" ); close(fd); printf ("删除有名管道文件\n" ); unlink("./fifo" ); printf ("大功告成\n" ); return 0 ; }
读取
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 #include <stdio.h> #include <unistd.h> #include <fcntl.h> int main (void ) { printf ("打开有名管道文件\n" ); int fd = open("./fifo" ,O_RDONLY); if (fd == -1 ){ perror("open" ); return -1 ; } printf ("接受数据\n" ); for (;;){ char buf[64 ] = {}; ssize_t size = read(fd,buf,sizeof (buf)-1 ); if (size == -1 ){ perror("read" ); return -1 ; } if (size == 0 ){ printf ("对方关闭管道文件\n" ); break ; } printf ("%s" ,buf); } printf ("关闭有名管道文件\n" ); close(fd); printf ("大功告成\n" ); return 0 ; }
无名管道 借助两个文件描述符控制写端和读端
1 int pipe (int pipefd[2 ]) ;
pipefd[2]:输出型参数,下标0存读端描述符,1存写端描述符
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/wait.h> int main (void ) { printf ("%d进程:创建无名管道\n" ,getpid()); int pipefd[2 ]; if (pipe(pipefd) == -1 ){ perror("pipe" ); return -1 ; } printf ("pipefd[0] = %d\n" ,pipefd[0 ]); printf ("pipefd[1] = %d\n" ,pipefd[1 ]); pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ printf ("%d进程:关闭写端\n" ,getpid()); close(pipefd[1 ]); printf ("%d进程:接受数据\n" ,getpid()); for (;;){ char buf[64 ] = {}; ssize_t size = read(pipefd[0 ],buf,sizeof (buf)-1 ); if (size == -1 ){ perror("read" ); return -1 ; } if (size == 0 ){ printf ("%d进程:写端被关闭\n" ,getpid()); break ; } printf ("%s" ,buf); } printf ("%d进程:关不读端\n" ,getpid()); close(pipefd[0 ]); printf ("%d进程:大功告成\n" ,getpid()); return 0 ; } printf ("%d进程:关闭读端\n" ,getpid()); close(pipefd[0 ]); printf ("%d进程:发送数据\n" ,getpid()); for (;;){ char buf[64 ] = {}; fgets(buf,sizeof (buf),stdin ); if (strcmp (buf,"!\n" ) == 0 ){ break ; } if (write(pipefd[1 ],buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } } printf ("%d进程:关闭写端\n" ,getpid()); close(pipefd[1 ]); if (wait(NULL ) == -1 ){ perror("wait" ); return -1 ; } printf ("%d进程:已收尸\n" ,getpid()); printf ("%d进程:大功告成\n" ,getpid()); return 0 ; }
IPC对象,共享内存, 消息队列 IPC对象 分为共享内存,消息队列,信号量 键(key):IPC对象的外部名,整数
共享内存 创建新的或获取已有的共享内存
1 2 #include <sys/shm.h> int shmget (key_t key, size_t size, int shmflg) ;
key:键 size:字节数,按页圆整 shmflg:创建标志
0:获取,不存在即失败
IPC_CREAT:创建,不存在即创建,已存在即获取
IPC_EXCL:排它,不存在即创建,已存在即失败
成功返回共享内存ID,失败-1
加载共享内存,映射地址
1 void * shmat (int shmid, void const * shmaddr, int shmflg) ;
shmid:共享内存ID shmaddr:虚拟地址起始地址,NULL系统选择 shmflg:加载标志
成功返回起始地址,失败返回(void*)-1
卸载共享内存,为共享内存计数-1
1 int shmdt (void const * shmaddr) ;
销毁共享内存,阻止任何其他进程对此块共享内存进行使用
1 int shmctl (int shmid, IPC_RMID, NULL ) ;
当计数为0时,真正销毁
代码 :写 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/shm.h> int main (void ) { printf ("%d进程:合成键\n" ,getpid()); key_t key = ftok("." ,123 ); if (key == -1 ){ perror("ftok" ); return -1 ; } printf ("%d进程:创建共享内存\n" ,getpid()); int shmid = shmget(key,4096 ,IPC_CREAT | IPC_EXCL | 0664 ); if (shmid == -1 ){ perror("shmget" ); return -1 ; } printf ("%d进程:加载共享内存\n" ,getpid()); char * start = shmat(shmid,NULL ,0 ); if (start == (void *)-1 ){ perror("shmat" ); return -1 ; } sprintf (start,"shmid=%d,key=0x%x,pid=%d\n" ,shmid,key,getpid()); printf ("%d进程:卸载共享内存\n" ,getpid()); getchar(); if (shmdt(start) == -1 ){ perror("shmdt" ); return -1 ; } printf ("%d进程:销毁共享内存\n" ,getpid()); getchar(); if (shmctl(shmid,IPC_RMID,NULL ) == -1 ){ perror("shmctl" ); return -1 ; } printf ("%d进程:大功告成\n" ,getpid()); return 0 ; }
读 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 #include <stdio.h> #include <unistd.h> #include <sys/shm.h> int main (void ) { printf ("%d进程:合成键\n" ,getpid()); key_t key = ftok("." ,123 ); if (key == -1 ){ perror("ftok" ); return -1 ; } printf ("%d进程:获取共享内存\n" ,getpid()); int shmid = shmget(key,0 ,0 ); if (shmid == -1 ){ perror("shmget" ); return -1 ; } printf ("%d进程:加载共享内存\n" ,getpid()); char * start = shmat(shmid,NULL ,0 ); if (start == (void *)-1 ){ perror("start" ); return -1 ; } getchar(); printf ("%s\n" ,start); printf ("%d进程:卸载共享内存\n" ,getpid()); getchar(); if (shmdt(start) == -1 ){ perror("shmdt" ); return -1 ; } printf ("%d进程:大功告成\n" ,getpid()); return 0 ; }
消息队列 向链表插入/摘取节点
可发送消息字节数上限两页(8192) 单条队列总字节数上限16384(16K) 全系统总消息队列数上限16 全系统消息总字节数上限262144(256k)
创建新的或获取已有的消息队列
1 2 #include <sys/msg.h> int msgget (key_t key, int msgflg) ;
发送消息
1 int msgsnd (int msgid, void const * msgp, size_t msgsz, int msgflg) ;
msgid:消息队列的ID msgp:指向一个包含消息类型(4字节)和消息数据的内存块 msgsz:期望发送的消息数据 msgflg:发送标志,一般为0;若包含IPC_NOWAIT,为非阻塞 成功返回0,失败-1
接收消息
1 int msgrcv (int msgid, void * msgp, size_t msgsz, long msgtyp, int msgflg) ;
msgtyp:0为不指定类型,>0表示取特定类型,<0表示小于其绝对值的类型 msgflg:给MSG_EXCEPT,接收除了特定类型的;以及IPC_NOWAIT非阻塞
若数据长度大于msgsz,且msgflg包含MSG_NOERROR,则只截取前msgsz字节返回,剩余丢弃;不包含则返回-1,errno为E2BIG
销毁消息队列
1 int msgctl (int msgid, IPC_RMID, NULL ) ;
代码 :写 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/msg.h> int main (void ) { printf ("%d进程:合成键\n" ,getpid()); key_t key = ftok("." ,123 ); if (key == -1 ){ perror("ftok" ); return -1 ; } printf ("%d进程:创建消息队列\n" ,getpid()); int msgid = msgget(key,IPC_CREAT | IPC_EXCL | 0664 ); if (msgid == -1 ){ perror("msgget" ); return -1 ; } printf ("%d进程:发送消息\n" ,getpid()); for (;;){ struct { long type; char data[64 ]; } buf = {1234 ,"" }; fgets(buf.data,sizeof (buf.data),stdin ); if (strcmp (buf.data,"!\n" ) == 0 ){ break ; } if (msgsnd(msgid,&buf,strlen (buf.data),0 ) == -1 ){ perror("msgsnd" ); return -1 ; } } printf ("%d进程:销毁消息队列\n" ,getpid()); if (msgctl(msgid,IPC_RMID,NULL ) == -1 ){ perror("msgctl" ); return -1 ; } printf ("%d进程:大功告成\n" ,getpid()); return 0 ; }
读 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 #include <stdio.h> #include <unistd.h> #include <sys/msg.h> #include <errno.h> int main (void ) { printf ("%d进程:合成键\n" ,getpid()); key_t key = ftok("." ,123 ); if (key == -1 ){ perror("ftok" ); return -1 ; } printf ("%d进程:获取消息队列\n" ,getpid()); int msgid = msgget(key,0 ); if (msgid == -1 ){ perror("msgget" ); return -1 ; } printf ("%d进程:接受数据\n" ,getpid()); for (;;){ struct { long type; char data[64 ]; } buf = {}; ssize_t size = msgrcv(msgid,&buf,sizeof (buf.data)-1 ,1234 ,0 ); if (size == -1 ){ if (errno == EIDRM){ printf ("%d进程:消息队列被销毁\n" ,getpid()); break ; }else { perror("msgrcv" ); return -1 ; } } printf ("%ld>>%s" ,buf.type,buf.data); } printf ("%d进程:大功告成\n" ,getpid()); return 0 ; }
其他: 手动销毁消息队列 :
1 ipcrm -q xxx //xxx为消息队列id
网络基础,IP地址,套接字,字节序转换 IP地址 IP协议提供的逻辑地址
计算机内部,32为无符号整数表示 人们习惯用点分十进制字符串表示
192.168.182.48 网络地址 + 本地地址 192.168.182.0 (网络地址) 0.0.0.48(本地地址)
IP地址分级(32为无符号整数形式):
地址分级
首
网络地址
本地地址
A级地址:
0为首,
8为网络地址 +
24为本地地址
B
10
16
16
C
110
24
8
D
1110为首的
32位多播地址
IP地址分为公网IP和私网IP
子网掩码可快速区定IP地址的网络地址和本地地址 网络地址 = IP地址 & 子网掩码 本地地址 = IP地址 & ~子网掩码
192.168.222.128/24 (24指子网掩码 255.255.255.0)
套接字 代表计算机网络通讯能力,相当于一个文件描述符,网络就是一种特殊文件
端口号 65536个(16位无符号整数) 对应相应的应用程序,1024以下被其他程序占用
字节序转换 小端/本地字节序:数据低位存放在低地址 大端/网络字节序:数据低位存放在高地址
网络协议栈大端,主机小端
TCP协议,函数,编程模型 函数 创建套接字
1 2 #include <sys/socket.h> int socket (int domain, int type, int protocol) ;
domain:通信域,协议族
PF_LOCAL/PF_UNIX 本地套接字,进程间通信
PF_INET 基于IPv4的网络通信
PF_INET6 基于IPv6的网络通信
PF_PACKET 基于底层包的网络通信
type:套接字类型
SOCK_STREAM 流式套接字,基于TCP协议
SOCK_DGRAM 数据报套接字,基于UDP协议
SOCK_RAW 原始套接字,工作在传输层以下
protocol:特殊协议,对于前两种套接字而言,只能取0
成功返回表示套接字对象的文件描述符,失败-1
基本地址结构 ,本身没有实际意义,仅用于泛型化参数
1 2 3 4 struct sockaddr { sa_family_t sa_family; char sa_data[14 ]; };
本地地址结构 ,用于AF_LOCAL/AF_UNIX的本地通信
1 2 3 4 struct sockaddr_un { sa_family_t sun_family; char sun_path[]; };
网络地址结构 ,用于AF_INET的IPv4网络通信
1 2 3 4 5 struct sockaddr_in { sa_family_t sin_family; in_port_t sin_port; struct in_addr sin_addr ; };
网络地址结构 ,用于AF_INET的IPv4网络通信
1 2 3 4 5 struct in_addr { in_addr_t s_addr; }; typedef uint16_t in_port_t ;typedef uint32_t in_addr_t ;
端口号一般选1024以上的,0到1024已被系统和一些网络服务占据
将套接字和本机的地址结构绑定在一起
1 2 #include <sys/socket.h> int bind (int sockfd, struct sockaddr const * addr, socklen_t addrlen) ;
sockfd:套接字描述符 addr:自己的地址结构 addrlen:地址结构的字节数 成功返回0,失败-1
将套接字和对方的地址结构连接到一起
1 int connect (int sockfd, struct sockaddr const * addr, socklen_t addrlen) ;
addr:对方的地址结构 成功返回0,失败-1
TCP函数 :
1 2 3 4 5 6 7 uint32_t htonl (uint32_t hostlong) ; ntohl netllong uint16_t htons (uint16_t hostshort) ; ntohs netshoort in_addr_t inet_addr (char const * ip) ;int inet_aton (char const * ip, struct in_addr* nip) ;char * inet_ntoa (struct in_addr nip) ;
启动侦听
1 2 #include <sys/socket.h> int listen (int sockfd, int backlog) ;
backlog:未决连接请求队列的最大长度,一般大于1024 成功返回0,失败-1
等待并接受连接请求 ,阻塞
1 int accept (int sockfd, struct sockaddr*addr, socklen_t * addrlen) ;
sockfd:侦听套接字描述符 addr:输出连接请求发起方的地址信息 addrlen:输出连接请求发起方的地址信息字节数 成功返回可用于后续通信的连接套接字描述符,失败-1
接收数据
1 ssize_t recv (int sockfd, void * buf, size_t count, int flags) ;
flags:取0与read函数等价,也可取以下值:
MSG_DONTWAIT:非阻塞接受
MSG_OOB:接收带外数据
MSG_WAITALL:等待所有数据,即不接收到count字节就不返回
成功返回实际接受到的字节数,失败-1
发送数据
1 ssize_t send (int sockfd, void const * buf, size_t count, int flags) ;
flags:取0与write等价
MSG_DONTWAIT
MSG_OOB
MSG_DONTROUTE:不查路由表,直接在本地网络中寻找目的主机
成功返回实际发送的字节数,失败-1
代码 服务端 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 #include <stdio.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> int main (void ) { printf ("服务器:创建套接字\n" ); int sockfd = socket(AF_INET,SOCK_STREAM,0 ); if (sockfd == -1 ){ perror("socket" ); return -1 ; } printf ("服务器:组织地址结构\n" ); struct sockaddr_in ser ; ser.sin_family = AF_INET; ser.sin_port = htons(8888 ); ser.sin_addr.s_addr = INADDR_ANY; printf ("服务器:绑定套接字和地址结构\n" ); if (bind(sockfd,(struct sockaddr*)&ser,sizeof (ser)) == -1 ){ perror("bind" ); return -1 ; } printf ("服务器:启动侦听\n" ); if (listen(sockfd,1024 ) == -1 ){ perror("listen" ); return -1 ; } printf ("服务器:等待客户端的连接请求到来\n" ); struct sockaddr_in cli ; socklen_t len = sizeof (cli); int conn = accept(sockfd,(struct sockaddr*)&cli,&len); if (conn == -1 ){ perror("accept" ); return -1 ; } printf ("服务器:业务处理\n" ); for (;;){ char buf[128 ] = {}; ssize_t size = read(conn,buf,sizeof (buf)-1 ); if (size == -1 ){ perror("read" ); return -1 ; } if (size == 0 ){ break ; } for (int i = 0 ;i < strlen (buf);i++){ buf[i] = toupper (buf[i]); } if (write(conn,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } } printf ("服务器:关闭套接字\n" ); close(conn); close(sockfd); return 0 ; }
客户端 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> int main (void ) { printf ("客户端:创建套接字\n" ); int sockfd = socket(AF_INET,SOCK_STREAM,0 ); if (sockfd == -1 ){ perror("socket" ); return -1 ; } printf ("客户端:组织地址服务器的地址结构\n" ); struct sockaddr_in ser ; ser.sin_family = AF_INET; ser.sin_port = htons(8888 ); ser.sin_addr.s_addr = inet_addr("192.168.222.128" ); printf ("客户端:发起连接请求\n" ); if (connect(sockfd,(struct sockaddr*)&ser,sizeof (ser)) == -1 ){ perror("connect" ); return -1 ; } printf ("客户端:业务处理\n" ); for (;;){ char buf[128 ] = {}; fgets(buf,sizeof (buf),stdin ); if (strcmp (buf,"!\n" ) == 0 ){ break ; } if (send(sockfd,buf,strlen (buf),0 ) == -1 ){ perror("send" ); return -1 ; } if (recv(sockfd,buf,sizeof (buf)-1 ,0 ) == -1 ){ perror("recv" ); return -1 ; } printf (">>> %s" ,buf); } printf ("客户端:关闭套接字\n" ); close(sockfd); return 0 ; }
UDP协议,函数,编程模型,域名解析,http协议 并发服务器:使用子进程负责与客户端通信,使用太平间信号进行收尸
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 #include <stdio.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> #include <signal.h> #include <sys/wait.h> #include <errno.h> void sigchild (int signum) { for (;;){ pid_t pid = waitpid(-1 ,NULL ,WNOHANG); if (pid == -1 ){ if (errno == ECHILD){ printf ("服务器:没有子进程了\n" ); break ; }else { perror("waitpid" ); return ; } }else if (pid == 0 ){ printf ("服务器:子进程在运行,收不了\n" ); break ; }else { printf ("服务器:回收了%d的僵尸\n" ,pid); } } } int main (void ) { if (signal(SIGCHLD,sigchild) == SIG_ERR){ perror("signal" ); return -1 ; } printf ("服务器:创建套接字\n" ); int sockfd = socket(AF_INET,SOCK_STREAM,0 ); if (sockfd == -1 ){ perror("socket" ); return -1 ; } printf ("服务器:组织地址结构\n" ); struct sockaddr_in ser ; ser.sin_family = AF_INET; ser.sin_port = htons(8888 ); ser.sin_addr.s_addr = INADDR_ANY; printf ("服务器:绑定套接字和地址结构\n" ); if (bind(sockfd,(struct sockaddr*)&ser,sizeof (ser)) == -1 ){ perror("bind" ); return -1 ; } printf ("服务器:启动侦听\n" ); if (listen(sockfd,1024 ) == -1 ){ perror("listen" ); return -1 ; } for (;;){ printf ("服务器:等待客户端的连接请求到来\n" ); struct sockaddr_in cli ; socklen_t len = sizeof (cli); int conn = accept(sockfd,(struct sockaddr*)&cli,&len); if (conn == -1 ){ perror("accept" ); return -1 ; } pid_t pid = fork(); if (pid == -1 ){ perror("fork" ); return -1 ; } if (pid == 0 ){ close(sockfd); printf ("服务器:业务处理\n" ); for (;;){ char buf[128 ] = {}; ssize_t size = read(conn,buf,sizeof (buf)-1 ); if (size == -1 ){ perror("read" ); return -1 ; } if (size == 0 ){ break ; } for (int i = 0 ;i < strlen (buf);i++){ buf[i] = toupper (buf[i]); } if (write(conn,buf,strlen (buf)) == -1 ){ perror("write" ); return -1 ; } } printf ("服务器:关闭套接字\n" ); close(conn); return 0 ; } close(conn); } close(sockfd); return 0 ; }
UDP协议 从哪里接受数据
1 2 3 #include <sys/socket.h> ssize_t recvfrom (int sockfd, void * buf, size_t count, int flags, struct sockaddr* src_addr, socklen_t * addrlen) ;
src_addr:输出源主机的地址信息 addrlen:输入输出源主机的地址信息的字节数 成功返回实际接收的字节数,失败返回-1
发送数据到哪里
1 2 ssize_t sendto (int sockfd, void const * buf, size_t count, int flags, struct sockaddr const * dest_addr, socklen_t addrlen) ;
dest_addr:目的主机的地址信息 addrlen:目的主机的地址信息的字节数
代码 :客户端 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> int main (void ) { printf ("客户端:创建套接字\n" ); int sockfd = socket(AF_INET,SOCK_DGRAM,0 ); if (sockfd == -1 ){ perror("socket" ); return -1 ; } printf ("客户端:组织服务器地址结构\n" ); struct sockaddr_in ser ; ser.sin_family = AF_INET; ser.sin_port = htons(9999 ); ser.sin_addr.s_addr = inet_addr("192.168.222.128" ); printf ("客户端:业务处理\n" ); for (;;){ char buf[128 ] = {}; fgets(buf,sizeof (buf),stdin ); if (strcmp (buf,"!\n" ) == 0 ){ break ; } if (sendto(sockfd,buf,strlen (buf),0 ,(struct sockaddr*)&ser,sizeof (ser)) == -1 ){ perror("sendto" ); return -1 ; } if (recv(sockfd,buf,sizeof (buf)-1 ,0 ) == -1 ){ perror("recv" ); return -1 ; } printf ("%s" ,buf); } printf ("客户端:关闭套接字\n" ); close(sockfd); return 0 ; }
服务端 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 #include <stdio.h> #include <string.h> #include <ctype.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> int main (void ) { printf ("服务器:创建套接字\n" ); int sockfd = socket(AF_INET,SOCK_DGRAM,0 ); if (sockfd == -1 ){ perror("socket" ); return -1 ; } printf ("服务器:组织地址结构\n" ); struct sockaddr_in ser ; ser.sin_family = AF_INET; ser.sin_port = htons(9999 ); ser.sin_addr.s_addr = INADDR_ANY; printf ("服务器:绑定套接字和地址结构\n" ); if (bind(sockfd,(struct sockaddr*)&ser,sizeof (ser)) == -1 ){ perror("bind" ); return -1 ; } printf ("服务器:业务处理\n" ); for (;;){ char buf[128 ] = {}; struct sockaddr_in cli ; socklen_t len = sizeof (cli); if (recvfrom(sockfd,buf,sizeof (buf)-1 ,0 ,(struct sockaddr*)&cli,&len) == -1 ){ perror("recvfrom" ); return -1 ; } for (int i = 0 ;i < strlen (buf);i++){ buf[i] = toupper (buf[i]); } if (sendto(sockfd,buf,strlen (buf),0 ,(struct sockaddr*)&cli,sizeof (cli)) == -1 ){ perror("sendto" ); return -1 ; } } printf ("服务器:关闭套接字\n" ); close(sockfd); return 0 ; }
http协议 一种特定的格式要求HTTP的请求 :
POST /form/entry HTTP/1.1 HOST: hackr.jp Connection: keep-alive Content-Type: application/x-www-form-urlencoded Content-Length: 16 name=ueno&age=37
请求行由请求方法字段,URL字段和HTTP协议版本字段3个字段组成,用空格分隔 HTTP1.0定义了三种请求方法:GET,POST,HEAD HTTP1.1新增了五种:OPTIONS,PUT,DELETE,TRACE,CONNECT GET最常用,用来获取服务器的数据
请求头部由关键字/值对组成,通知服务器有关于客户端请求的信息 Accept:告诉服务器自己接受什么类型的介质 Host:客户端指定自己想访问的web服务器的域名 User_Agent:浏览器表名自己的身份 Referer:浏览器web服务器表名自己是从哪个网页URL获得点击当前请求中的网址 Connection:表示是否需要持久连接
HTTP的响应 :
HTTP/1.1 200 OK Date: Tue, 10 Jul 2012 06:50:15 GMT Content-Length: 362 Content-Type: text/html <html> …
HTTP响应也由三部分组成,分别是状态行,响应头,空行,响应正文 状态码:
200 OK:客户端请求成功
400 Bad Request:服务器请求有语法错误,不能被服务器所理解
403 Forbidden:服务器收到请求,但是拒绝提供服务
404 Not Found:请求资源不存在
503 Server Unavailable:服务器当前不能处理客户端的请求,一段时间后可能恢复正常
消息报头一般包括以下内容:
Date:响应时间
Content-Type:响应类型
Content-Length:响应数据大小
Connection:连接状态
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 #include <stdio.h> #include <string.h> #include <unistd.h> #include <sys/socket.h> #include <sys/types.h> #include <arpa/inet.h> int main (int argc,char * argv[]) { if (argc < 3 ){ fprintf (stderr ,"用法:%s <IP地址> <域名> [<资源路径>]\n" ,argv[0 ]); return -1 ; } char * ip = argv[1 ]; char * name = argv[2 ]; char * path = argc < 4 ? "" : argv[3 ]; int sockfd = socket(AF_INET,SOCK_STREAM,0 ); if (sockfd == -1 ){ perror("socket" ); return -1 ; } struct sockaddr_in ser ; ser.sin_family = AF_INET; ser.sin_port = htons(80 ); ser.sin_addr.s_addr = inet_addr(ip); if (connect(sockfd,(struct sockaddr*)&ser,sizeof (ser)) == -1 ){ perror("connect" ); return -1 ; } char request[1024 ] = {}; sprintf (request,"GET /%s HTTP/1.1\r\n" "Host: %s\r\n" "Accept: */*\r\n" "Connection: close\r\n" "User-Agent: Mozilla/5.0\r\n\r\n" ,path,name); if (send(sockfd,request,strlen (request),0 ) == -1 ){ perror("send" ); return -1 ; } for (;;){ char respond[1024 ] = {}; ssize_t size = recv(sockfd,respond,sizeof (respond)-1 ,0 ); if (size == -1 ){ perror("recv" ); return -1 ; } if (size == 0 ){ break ; } printf ("%s" ,respond); } printf ("\n" ); close(sockfd); return 0 ; }
线程 程序执行路线
POSIX线程 创建新线程
1 2 3 #include <pthread.h> int pthread_create (pthread_t * tid, pthread_attr_t const * attr, void * (*start_routine)(void *), void * arg) ;
tid:输出线程ID attr:线程属性,NULL表示缺省属性 start_routine:线程过程函数指针 arg:传递给线程过程函数的参数 成功返回0,失败返回错误码
编译需指定库的名称
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 #include <stdio.h> #include <unistd.h> #include <pthread.h> #define PI 3.14 void * area (void * arg) { double r = *(double *)arg; *(double *)arg = PI * r * r; return NULL ; } int main (void ) { double r = 10 ; pthread_t tid; pthread_create(&tid,NULL ,area,&r); sleep(1 ); printf ("圆的面积是%lg\n" ,r); return 0 ; }
汇合线程 释放线程资源,获得返回值
1 int pthread_join (pthread_t pid, void ** retval) ;
retval:一级指针的地址,用于存放过程函数的返回值
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 #include <stdio.h> #include <unistd.h> #include <pthread.h> #define PI 3.14 void * area (void * arg) { double r = *(double *)arg; static double s ; s = PI * r * r; sleep(5 ); return &s; } int main (void ) { double r = 10 ; pthread_t tid; pthread_create(&tid,NULL ,area,&r); double * area; pthread_join(tid,(void **)&area); printf ("圆的面积是%lg\n" ,*area); return 0 ; }
并发冲突,线程同步,互斥锁,条件变量 分离线程 线程结束,不用主线程回收,内核自动回收,不阻塞,无法汇合
1 void pthread_detach (pthread_t pid) ;
线程同步 互斥锁 初始化互斥体 :
1 int pthread_mutex_init (pthread_mutex_t * mutex, pthread_mutexattr_t const * attr) ;
mutex:互斥体 attr:互斥体属性 成功返回0,失败返回错误码 也可以静态方式初始化互斥锁 :
1 pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
销毁互斥体 :
1 int pthread_mutex_destroy (pthread_mutex_t * mutex) ;
加/解锁 :
1 2 int pthread_mutex_lock (pthread_mutex_t * mutex) ;int pthread_mutex_unlock (pthread_mutex_t * mutex) ;
代码 :
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 #include <stdio.h> #include <unistd.h> #include <pthread.h> int g_cn = 0 ;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;void * pthread_add (void * arg) { for (int i = 0 ;i < 1000000 ;i++){ pthread_mutex_lock(&mutex); g_cn++; pthread_mutex_unlock(&mutex); } return NULL ; } int main (void ) { pthread_t tid1,tid2; pthread_create(&tid1,NULL ,pthread_add,NULL ); pthread_create(&tid2,NULL ,pthread_add,NULL ); pthread_join(tid1,NULL ); pthread_join(tid2,NULL ); printf ("g_cn = %d\n" ,g_cn); return 0 ; }
条件变量 初始化 :
1 int pthread_cond_init (pthread_cond_t * cond, const pthread_condattr_t * attr) ;
cond:条件变量 attr:条件变量属性 也可以静态方式初始化条件变量
1 pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
销毁 :
1 int pthread_cond_destroy (pthread_cond_t * cond) ;
睡入条件变量 :
1 int pthread_cond_wait (pthread_cond_t * cond, pthread_mutex_t * mutex) ;
唤醒条件变量 :
1 int pthread_cond_signal (pthread_cond_t * cond) ;
进程是资源分配的最小单位 线程是调度的最小单位
代码 : 生产者,消费者问题
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> #include <pthread.h> char g_storage[10 ];int g_stock = 0 ;pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;pthread_cond_t pcond = PTHREAD_COND_INITIALIZER;pthread_cond_t ccond = PTHREAD_COND_INITIALIZER;void show (char * who,char * op,char prod) { printf ("%s:" ,who); for (int i = 0 ;i < g_stock;i++){ printf ("%c" ,g_storage[i]); } printf ("%s%c\n" ,op,prod); } void * producer (void * arg) { char * who = (char *)arg; for (;;){ pthread_mutex_lock(&mutex); if (g_stock == 10 ){ printf ("%s:满仓\n" ,who); pthread_cond_wait(&pcond,&mutex); } char prod = 'A' + rand() % 26 ; show(who,"<--" ,prod); g_storage[g_stock] = prod; g_stock++; pthread_cond_signal(&ccond); pthread_mutex_unlock(&mutex); usleep((rand() % 100 ) * 1000 ); } return NULL ; } void * customer (void * arg) { char * who = (char *)arg; for (;;){ pthread_mutex_lock(&mutex); if (g_stock == 0 ){ printf ("%s:空仓\n" ,who); pthread_cond_wait(&ccond,&mutex); } char prod = g_storage[--g_stock]; show(who,"-->" ,prod); pthread_cond_signal(&pcond); pthread_mutex_unlock(&mutex); usleep((rand() % 100 ) * 1000 ); } return NULL ; } int main (void ) { pthread_t t1,t2; pthread_create(&t1,NULL ,producer,"生产者" ); pthread_create(&t2,NULL ,customer,"消费者" ); getchar(); return 0 ; }