IO
1.文件的概念:
文件:一组数据的有序集合
文件名:
2.七大文件类型
1 - 普通文件(.c .h .txt) 2 d 目录文件 3 c 字符设备文件 (/dev/video0(摄像头)) 4 b 块设备文件 (磁盘) 5 l 链接文件 (软连接/硬链接) 6 p 管道文件 (进程间的通信 makefifo创建管道文件) 7 s 套接字文件 (用于网络间的通信) 注:不同操作系统支持的文件类型是 不一样的;
3.系统调用和库函数
1.系统调用: 应用系统向操作系统发出请求,操作系统处理这个请求,再把结果返回给应用程序;(普通用户获取内核数据的一些API) 用户空间进程访问内核的接口 把用户从底层的硬件编程中解放出来 极大的提高了系统的安全性 使用户程序具有可移植性 是操作系统的一部分 2.库函数 库函数为了实现某个功能而封装起来的API集合提供统一的编程接口,更加便于应用程序的移植;是语言或者应用的一部分;
标准 IO————————
通过缓冲机制来减少系统调用的次数,提高效率;
标准IO遵循ANSI C标准,实际上标准IO实现是通过调用标准C库中的函数来实现 • 标准IO通过利用缓冲机制减少系统调用的次数,从而提高程序效率 • 我们把文件IO称为低级IO,标准IO称为高级IO • 标准IO是在文件IO的基础上做了一次再封装 • 标准IO一般用于操作普通文件 • 文件IO可以适用任意文件类型
1.缓冲区:
1 缓冲区是内存空间开辟的一部分 作用:在输入输出设备之间缓存数据,使得低速输入输出设备和高速的CPU工作相协调
2.流
定义:如果数据仅是简单地从文件流入或者流出,这种现象就称为流
用一个FILE 结构体来保存一个文件的相关信息,流用 FILE* 来表示
注:搜索流结构体 grep -r "_IO_FILE" /usr/include/
1.文本流
定义:在流中处理的数据是以字符出现 在处理数据时,是将数据转换为对应的ASCII码,最终再以二进制形式存取 "1234" 49 50 51 52 --> 110001 110010 110011 110100
2.二进制流
在处理数据时,将数据转换为对应的二进制数进行存取 • "1234" --> 1234 --> 100 1101 0010
文本流在处理换行符时跟二进制流不同:
文本流: '\n' --> '\r' '\n'
二进制流: '\n' --> '\n'
在linux中,文本流和二进制流不做区分
3.三种特殊流:
三种特殊流:这三个特殊流在一个文件中是默认打开的 • 标准输入流 -- 键盘 -- stdin -- 0 • 标准输出流 -- 终端 -- stdout -- 1 • 标准错误流 -- -- stderr -- 2
4.缓冲区类型:
(1)全缓冲:当用户打开一个普通文件时,默认使用全缓存。 当缓存区空或者满时才会进行实际的IO操作,或者使用 fflush 强制刷新缓冲区也可以进行IO操作 (2)行缓冲:如果IO操作跟终端相关,则使用行缓存 当 缓冲区满 或者遇到 ('\n') 时,进行实际的IO操作 (3)无缓冲:当跟错误输出相关时,使用无缓存
5.流的刷新
fflush()
1. linux下只对输出缓冲区刷新 2. 当缓冲区满的时候或者满足一定条件,流刷新; 3. fflush() 强制刷新流; #include <stdio.h> int fflush(FILE *stream); { 参数: stream : 流 返回值: 成功:0 失败:-1(EOF)并设置错误号 } 4.行缓冲遇到'\n'刷新; 5.当程序正常结束,流就会被正常关闭,那么缓冲区里面的内容自动刷新;
6.流的打开与关闭
6.1 流的打开(fopen)
fopen() 函数原型: FILE *fopen(const char *pathname,const char *mode) 函数参数: const char *pathname 打开文件路劲/文件名 const char *mode 文件打开方式 函数返回值: 成功:返回FILE的指针 失败:返回NULL 创建文件权限是:0664(~umask)
mode 说明:
r 或 r+b 打开只读文件 该文件必须存在; r+或 r+b 打开可读写文件,该文件必须存在; w 或 wb 打开只写文件 若文件存在则文件长度为0 即会擦写文件以 前的内容;若文件不存在则建立文件; w 或 w+b 打开可读写文件 若文件存在则文件长度为0 即会擦写文件以 前的内容;若文件不存在则建立文件; a 或 ab 以追加的方式打开只写文件。若文件不存在,则会建立该文 件,从末尾读写; a+或 a+b 以追加的方式打开可读写文件。若文件不存在,则会创建该文 件,从末尾读写
例子:
#include <stdio.h> #include <stdlib.h> int main() { FILE *fp = fopen("./test.txt","r"); if(fp == NULL) { peintf("error......\n"); return -1; } printf("fopen succes....\n"); printf("fp = %d\n",fp->_fileno); return 0; }
错位信息的出理
#include <stdio.h> int errno:系统定义一个全局变量,保存错误信息的编号 void perror(char *s) { 功能:先打印s 在根据错误进行编号 } 返回值: 无
6.2流的关闭(fclose)
#include <stdio.h> fclose() { 函数将流指针的缓冲区内的数据全部写入文件中,并释放相关资源。程序结束后会自动关闭所打开的流。 } 函数原型: int fclose (FILE *stream) 参数: stream:流指针 返回值: 成功: 0 失败:EOF 注意:当打开的流被正常关闭,那么缓冲区的内容就会自动刷新,
7.流的读和写
7.1按字符输入/输出
字符输入/输出函数一次仅读写一个字符。
fgetc () 和 getc()从指定的流中读取一个字符(节)
字符输入函数 fgetc
头文件: #include <stdio.h> 函数原型: int getc(FILE *stream); int fgetc(FILE *stream); int getchar(void); 函数参数: stream:流指针 返回值: 成功:读取的字符 失败:EOF fgetc(stdin) == getchar()
字符输出函数fputc
#include <stdio.h> int fputc(int c, FILE *stream); 参数: c:写入的字符 stream:文件对应的流指针 返回值: 成功:返回写入的字符 失败:-1
7.2按行输入/输出
输入 fgets
头文件: #include <stdio.h> 函数原型: char *fgets(char *s, int size, FILE *stream); 参数: s:存放读取内容的缓冲区首地址(字符数组的首地址) size:读取字节数的最大个数 stream:流指针 返回值: 成功:返回读取数据的首地址s 失败:或者读到文件末尾返回NULL 注意: 1、fgets读取的最大字节个数为size-1 2、当遇到换行符时,fgets结束 注意:gets不推荐使用,容易造成缓冲区溢出
输出fputs
头文件: #include <stdio.h> 函数原型: int fputs(const char *s, FILE *stream); 参数: s:写入数据的首地址 stream:流指针 返回值: 成功:非负整数 失败:-1;
7.3按对象来输入/输出
输入fread
头文件: #include <stdio.h> 函数原型: size_t fread(void *ptr, size_t size, size_t nmemb, FILE *stream); 参数: ptr:存放读取内容的缓冲区首地址 size:对象字节数大小 nmemb:对象个数 stream:流指针 返回值: 成功:读取的对象个数 失败:EOF
输出fwrite
c头文件: #include <stdio.h> 函数原型: size_t fwrite(void *ptr, size_t size, size_t nmemb, FILE *stream); 参数: ptr:存放写入内容的缓冲区首地址 size:对象字节数大小 nmemb:对象个数 stream:流指针 返回值: 成功:读取的对象个数 失败:EOF
例子:
实现文件的复制:
#include <stdio.h> #include <stdlib.h> int main(int argc, char *argv[]) { if(argv < 3) //命令行参数判断 { printf("please enter %s srcfile destfile\n",argv[0]); return -1; } FILE *fp1 = fopen ("1.c","r+"); //打开文件 if(NULL == fp1) { perror("open"); exit(-1); } FILE *fp2 =fopen("2.c","w+"); if(NULL == fp2) { perror("fopen"); exit(-1); } char buf[64] = {0}; int ret = 0; while(0 != (ret = fread(buf,sizeof(char),sizeof(buf),fp1))){ printf("ret = %d\n",ret); fwrite(buf,sizeof(char),ret,fp2); } fclose(fp1); fclose(fp2); return 0; }
8.流的定位
rewind()
rewind() { 函数原型: void rewind(FILE *stream); 功能:将文件指针指向开头 } 参数: 留指针
ftell()
ftell() #include <stdio.h> { long ftell(FILE *stream); 参数: stream:流指针 返回值: 成功:返回当前读写位置相对于文件开头得偏移量 } 失败:-1;
fseek()
功能:
把文件指针偏移到任意位置;
头文件: #include <stdio.h> 函数原型: int fseek(FILE *stream,long offset,int ) 参数: stream:流指针 offset:偏移量 whence:某准点 EEEK_ SET: 文件开头 SEEK_ CUR: 文件当的读写位置 SEEK_ END:文件末尾 //最终定位位置 = offset+whence 返回值: 成功:0 失败:EOF
例子:
include <stdio.h> long filesize(FILE*stream); int main(void) { FILE *stream; stream=fopen("MYFILE.TXT","wb+"); fprintf(stream,"Thisisatest"); printf("FilesizeofMYFILE.TXTis%ldbytes\n",filesize(stream)); fclose(stream); return 0; } long filesize(FILE*stream) { long curpos,length; curpos=ftell(stream); fseek(stream,0L,SEEK_END); length=ftell(stream); fseek(stream,curpos,SEEK_SET); return length; }
9.判断流是否出错
ferror()
表头文件#inc1ude<stdio. h> 表头文件#包括<stdio。H> 定义函数: int ferro(FILE *stream) ; 定义函数: int ferror(FILE *stream); 函数说明: ferror()用来检查参数流所指定的文件流是否发生了错误情况,如有错误发生则返回非0值。 返回值: 如果文件流有错误发生则返回非0值。
练习:
1.实现文件的复制
7 #include <stdio.h> 8 #include <string.h> 9 10 int main(int argc, char *argv[]) 11 { 12 char ch; 13 char buf[100] = {0}; 14 FILE *fp1; 15 FILE *fp2; 16 17 scanf("%c",&ch); 18 if(fp1 = fopen("1.c","r")) 19 { 20 ch = fgetc(fp1); 21 fp2 = fopen("2.c","w+"); 22 23 while(ch != EOF) 24 { 25 fputc(ch,fp2); 26 ch = fgetc(fp1); 27 } 28 fclose(fp1); 29 fclose(fp2); 30 } 31 return 0; 32 }
2.计算文件的行数
#include <stdio.h> #include <string.h> int main(int argc, char *argv[]) { FILE *fp = fopen("1.c","r+"); if(NULL == fp) { printf("fopen"); return -1; } int count; char buf[128] = {0}; while(fgets(buf,128,fp) != NULL) { if(buf[strlen(buf)-1] == '\n') //计算字符串的长度 count++; } printf("count=%d\n",count); return 0; }
作业:
编程读写一个文件test.txt,每隔1秒向文件或者终端中写入一行 数据,类似这样: 1, 2007-7-30 15:16:42 2, 2007-7-30 15:16:43 该程序应该无限循环,直到按Ctrl-C中断程序。 再次启动程序写文件时可以追加到原文件之后,并且序号能够接续上次的 序号,比如:
1, 2007-7-30 15:16:42
2, 2007-7-30 15:16:43
3, 2007-7-30 15:19:02
4, 2007-7-30 15:19:03
5, 2007-7-30 15:19:04
time(); localtime(); fprintf();
#include <stdio.h> #include <time.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { FILE *fp = fopen("time.txt","w+"); if(NULL == fp) { perror("fopen"); exit(-1); } int count=0; time_t rawtime; struct tm *info; char buf[64]={0}; while(1){ count++; time(&rawtime); info = localtime(&rawtime); localtime(&rawtime); fflush(fp); fprintf(stdout, "%d %d-%d-%d %d:%d:%0d\n",count,info->tm_year+1900,info->tm_mon+1,info->tm_mday,info->tm_hour,info->tm_min,info->tm_sec); sleep(1); } fclose(fp); return 0; }
文件IO————————
概念:
POSIX表示可移植操作系统接口规范
为了提高UNIX环境下应用程序的可移植性
文件描述符
文件描述符是非负整数;
当打开一个现存的文件或创建一个新文件时,内核向进程返回一个文件描述符;
读写文件时,需要把文件描述符作为参数传递给相应的函数
标准IO和文件IO的区别:
标准IO: ANSIC 有缓冲区 高级IO 流 适用于普通文件 文件IO: POSIX 无缓冲 低级IO 文件描述符 适用于特殊文件(设备文件)
1.打开文件open()
用于创建或打开文件,在打开或创建文件时可以指定文件打开方式及文件的访问权限。
open() 头文件: #include <stdio.h> #include <fcntl.h> 函数原型: int open(const char pathname, int flags, mode_ t mode); 参数: pathname:打开文件的文件名 flags:打开文件的方式 0_RDONLY:以只读方式打开文件 0_WRONLY:以只写方式打开文件 0_RDWR:以读写方式打开文件 mode: 新建文件的初始参数 当open函数新建文件时候,需要用该函数初始权限 --0664 返回值: 成功:返回文件描述符 失败:-1
2.文件的关闭close()
用于关闭一个被打开的文件。
close(){ 头文件: #include <unistd.h> 函数原型: int close(int fd); 参数: fd:文件描述符 返回值: 成功:0 出错:-1 }
3.文件的读写
read()
头文件: #include <unistd. h> 函数原型: ssize_ _t read(int fd, void *buf, size_ .t count); 参数: fd:文件描述符 buf:用户自定义的缓冲区首地址,用于存放读取的内容 count:请求读收的字节数 返回值: 成功:返回真正读取的字节数 0 :表示读到文件末尾 失败:-1
练习:
读取一个文件的前十个字节,并输出到终端;
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <fcntl.h> #include <sys/stat.h> int main(int argc, char *argv[]) { int fd = open("1.c",O_RDWR,0664); //打开原文件,需要错误判断 if(fd < 0) { perror("open"); exit(-1); } char buf[32] = {0}; int ret = read(fd,buf,10); //读取buf内容保存到ret中 if(ret < 0) { perror("read"); exit(-1); } printf("buf: %s\n",buf); close(fd); return 0; }
write()
头文件: #include <unistd. h> 函数原型: ssize_ _t write(int fd, void *buf, size_ .t count); 参数: fd:文件描述符 buf:用户自定义的缓冲区首地址,用于写入文件的内容 count:请求读收的字节数 返回值: 成功:返回真正读取的字节数 0:表示读到文件末尾 失败:返回-1
4.文件的定位
lseek()
头文件: #include <sys/types.h> #inc1ude <unistd. h> 函数原型: off _t 1seek(int fd, off_t offset, int whence); 参数: fd:文件描述符 offset:偏移量,可正可负 whence:基准点 SEEK_SET:文件开头 SEEK_CUR: 当前位置 SEEK_END: 文件末尾 返回值: 成功:返回当前读写的位置(偏移量) 失败:返回-1: 并设置错误号
练习:
1.实现cp命令
#include <stdio.h> #include <fcntl.h> #include <string.h> #include <sys/stat.h> #include <unistd.h> #include <stdlib.h> int main(int argc, char *argv[]) { int fd1,fd2; if(argv < 3) { perror("错误\n"); return -1; } fd1 = open("1.c",O_RDONLY,0664); if(fd1 < 0) { perror("open"); exit(-1); } fd2 = open("2.c",O_RDWR | O_TRUNC,0664); if(fd2 < 0) { perror("open"); exit(-1); } char buf[32] ={0}; memset(buf,0,32); while(read(fd1,buf,32) != 0){ write(fd2,buf,strlen(buf)); memset(buf,0,32); } printf("成功"); return 0; }
2.对图片进行加密
/*=============================================== * 文件名称:text.c * 创 建 者: 任梦飞 * 创建日期:2022年09月23日 * 描 述: ================================================*/ #include <stdio.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <sys/stat.h> #include <fcntl.h> #include <unistd.h> int main(int argc, char *argv[]) { if(argc!=2) { printf("Please enter a file name\n"); } int fd = open(argv[1],O_RDWR); if(fd < 0) { perror("open erro"); return -1; } while(1) { int buf1; scanf("%d",&buf1); if(1 == buf1) { while(1) { char ch[12]; int ret = read(fd,ch,10); if(ret<0) { perror("read erro"); return -1; } if(ret == 0) break; lseek(fd,-10,SEEK_CUR); for(int i = 0;i<10;i++) { ch[i]+=1; } write(fd,ch,10); } close(fd); return 0; } int buf2; scanf("%d",&buf2); if(2 == buf1) { while(1) { char ch[12]; int ret = read(fd,ch,10); if(ret<0) { perror("read erro"); return -1; } if(ret == 0) break; lseek(fd,-10,SEEK_CUR); for(int i = 0;i<10;i++) { ch[i]-=1; } write(fd,ch,10); } close(fd); return 0; } } }
5.目录流
5.1 打开目录opendir()
头文件: #include <sys/types.h> #include <dirent. hs> 函数原型: DIR *opendir(const char *name); 参数: name:打开的目录名 返回值: 成功:返回目录流指针 失败:NULL;
5.2 读取目录readdir()
头文件: #include <dirent.h> 函数原型: struct dirent * readdir(DIR *dirp); 参数: dirp:目录流指针 返回值: 成功:返回一个目录项的相关结构体指针 失败:NULL;
5.3 关闭目录流closedir()
头文件: #include <sys/types.h> #include <dirent.h> 函数原型: int closedir(DIR *dirp); 返回值: 成功:返回0 失败:返回-1,并设置错误号
6.修改文件权限
chmod()
头文件: #include <stdio.h> #include <sys/stat.h> #include <stdlib.h> 函数原型: int chmod(const char *pathname,mode_t mode); int fchmod(int fd,mode_t mode) 返回值: 成功:0 失败:-1并设置错误号
例子:
#include <stdio.h> #include <sys/stat.h> #include <stdlib.h> int main(int argc, char *argv[]) { if(0 != chmod("./1.c",0664)){ perror("chmod"); exit(-1); } printf("success\n"); return 0; }
7.格式的输出
printf() fprintf() int fprintf(FILE *stream, const char *format, ...); 功能:向一个流格式输出 sprintf(); int sprintf(char *str, const char *format, ...); 功能:格式输出到一个str指向的空间中; sscanf(); int sscanf(const char *str, const char *format, ...); 地址表 功能:以某种格式从str指向的空间读取内容
例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> int main(int argc, char *argv[]) { int a =20,b = 10, c = 30; char buf[32] = {0}; sprintf(buf,"%d-%d-%d",a,b,c); printf("buf: %s\n",buf); printf("strlen =%ld\n",strlen(buf)); int var1,var2,var3; sscanf(buf,"%d-%d-%d",&var1,&var2,&var3); printf("var1 = %d,var2 = %d,var3 = %d\n",var1,var2,var3); return 0; }
8.获取文件的属性
stat/lstat/fstat
stat/lstat/fstat函数用来获取文件属性: #include <sys/stat.h> #include<sys/stat.h> int stat(const char *path, struct stat *buf); int lstat(const char *path, struct stat *buf); -- int fstat(int fd, struct stat *buf); --以文件描述符的方式来获取文件的属性 ➢成功时返回0;出错时返回EOF ➢如果path是符号链接stat获取的是目标文件的属性;而Istat获取的是链接文件的属性
例子:
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <sys/stat.h> #include <sys/types.h> #include <string.h> int main(int argc, char *argv[]) { if(argc < 2){ printf("please enter %s filename\n",argv[0]); return -1; } struct stat statbuf; if(-1 ==(lstat(argv[1],&statbuf))) perror("lstat"); return -1; // printf("size: %ld\n",statbuf.st_size); switch(statbuf.st_mode &S_IFMT){ case S_IFSOCK: printf("s"); break; case S_IFLNK: printf("l"); break; case S_IFREG: printf("-"); break; case S_IFBLK: printf("b"); break; case S_IFDIR: printf("d"); break; case S_IFCHR: printf("c"); break; case S_IFIFO: printf("p"); break; } return 0; }
9.文件权限
S_IRUSR 00400 owner has read permission 8 S_IWUSR 00200 owner has write permission 7 S_IXUSR 00100 owner has execute permission 6 S_IRGRP 00040 group has read permission 5 S_IWGRP 00020 group has write permission 4 S_IXGRP 00010 group has execute permission 3 S_IROTH 00004 others have read permission 2 S_IWOTH 00002 others have write permission 1 S_IXOTH 00001 others have execute permission 0
10.用户名称和组的名称
用户名称: struct passwd *getpwuid(uid_t uid); 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 group *getgrgid(gid_t gid);
库——————————–
概念:
库的本质是一个二进制文件 库的类型分为:静态库,动态库(共享库) 静态库和动态库加载代码的时机不同
库的分类:
1.静态库
在程序编译阶段,将静态库中的代码,以复制拷贝的方式取一份加载到原程序中
1.1特点:
浪费资源空间 在运行程序时,不需要静态库存在 方便移植 优化升级不方便,需要重新编译和链接库 静态库制作流程:
1.2优缺点:
优点:程序运行时与函数库再无瓜葛,移植方便
缺点:
1.3静态库的创建:
静态库的命名: lib 开头--->库名--->.a(文件扩展名) 1.创建文件func1.c func2.c 2.gcc -c func1.c func2.c --->func1.o func2.o 3.ar crs -o libmylib.a func1.o func2.o 4.静态库的使用: gcc main.c -L ./ -lmylib -L 指定库的路径 -l 后面跟库名 -I 链接头文件
2.动态库
在编译(链接)阶段,仅是记录动态库用到的一些符号(函数名)在程序运行时,再根据记录的符号加载对应代码
2.1特点:
占用空间资源小 在运行程序时,动态库必循存在 运行速度相对较慢 优化升级相对方便,不需要重新链接动态库;
2.2动态库的创建
动态库的命名: lib开头-->库名-->.a(文件拓展名) gcc -c fPIC fun1.c func2.c -->funcl.o func2.o -fPIC 生成位置无关代码 gcc -shared -o libmylib.so func1.o func2.o 动态库的使用: ① gcc main.c -L ./ -lmylib 或者 ② gcc main.c libmylib.so 运行的时候报错: ./a.out: error while loading shared libraries: libtestlib.so: cannot open shared object file: No such file or directory 原因:动态库的默认搜索路径不在当前,所以占不到库; 解决: 1.把库拷贝到 /usr/lib 和/lib(一般不这样做) 2.export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:./ 把当前路 径添加到库的默认搜索路径; 是临时的,新建一个终端就不行了; 3.sudo vi /etc/ld.so.conf.d/mylib.conf 如果mylib.conf没 有就创建一个) 在这个文件 当中加上库的绝对路径; sudo ldconfig 刷新; 如果动态库和静态库在一起的时候,优先链接动态库,如果要使用静态 库,那么加上一个 -static eg: gcc main.c -L ./ -lmylib -static
进程线程———————
1.进程的概念
程序: 程序是静态的,它是一些保存在磁盘上的指令的有序集合,没有任何执行的概念; 进程: 是系统分配资源的总称,是程序的一次执行过程(创建、调用、执行、消亡) 进程是程序执行和资源管理的最小单位
2.程序的组成:
正文段 用户数据段
3.进程的组成:
1.正文段(存放的是程序中的代码) 2.用户数据段 3.系统数据段(PCB(进程控制块) task_struct、寄存器、堆栈) 4.pc(程序计数器): 当执行一条指令时,首先需要根据PC中存放的指令地址,将指令由内存取到指令寄存器中,此过程称为“取指令”。与此同时,PC中的地址或自动加1或由转移指针给出下一条指令的地址。此后经过分析指令,执行指令。完成第一条指令的执行,而后根据PC取出第二条指令的地址,如此循环,执行每一条指令。 5.堆栈段(存放的是函数的返回地址、函数的参数)
4.进程的类型
(1)交互进程:跟终端相关,既可以在前台,也可以在后台运行 (2)批处理进程:将一系列进程放在一个工作队列中,按顺序执行 (3)守护进程:跟终端无关,并且是周期性的在后台循环等待处理或者响应某个事件 (一直在后台运行)
5.进程的运行状态
R -- 运行态(就绪态):一个进程正在运行或者等待运行(需要得到时间片) 等待态:等待某种资源,如果有资源则继续运行 S -- 可中断等待态(睡眠态) D -- 不可中断等待态 T -- 暂停态:暂停运行,可以通过信号将暂停的进程唤醒,继续运行 Z -- 僵尸态:进程结束后,没有主动进行资源回收
6.进程的相关命令
ps -ef 查看进程 ps -aux(ps -aus |grep a.out) 查看所有进程的信息 top:动态查看进程的信息 kill 信号 进程ID jobs: 查询后台任务 bg:将后台停的进程在后台运行起来 后台执行: ./a.out & ctrl + z: 将正在运行的前台进程在后台挂起 fg + 任务号:将后台运行的任务,放在前台运行 ---------------------------------------------------- 进程的优先级: NI -- 进程优先级 优先级的取值范围-20 ~ 19 NI值越小,优先级越高 nice -n 优先等级 ./a.out //将准备运行的a.out的优先级设为指定等级 renice -n 优先等级 进程号 //将正在运行的某个进程的优先级设为指定等级 注意:普通用户只能把nice升高;
———————————
1.创建进程fork()
头文件: #include <sys/types.h> #include <unistd.h> 函数原型: pid_t fork(void); 返回值: 成功:父进程返回子进程的ID号,子进程返回0 失败:返回-1;
父子进程关系:
父进程优先于子进程结束: 父子进程没有据对的优先执行关系,谁先抢到资源,先执行谁 子进程完全拷贝父进程的内容(4G虚拟内存) 子进程变成孤儿进程,统一由init进程收养,子进程由前台进程变成后台进程 ------------------------------------------------------ 子进程优先于父进程结束: 如果父进程没有对子进程资源进行回收,子进程会变成僵尸进程 子进程的资源回收一般由父进程统一回收
思考:
1.子进程什么时候开始拷贝父进程? 当fork()执行得下一条语句
fork()执行多次
#include <stdio.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> int main(int argc, char *argv[]) { pid_t pid1 = fork(); pid_t pid2 = fork(); if(pid1 < 0){ perror("fork"); exit(-1); }else if(pid1 == 0){ if(pid2 < 0) { perror("fork"); exit(-1); }else if(pid2 == 0){ printf("pid2 =%d",pid2); }else{ printf("pid2 = %d",pid2); printf("pid = %d",pid2); } } else{ printf("pid1 =%d",pid1); } return 0; }
例子:
#include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> int main() { pid_t pid = fork(); if(pid < 0); { perror("fork"); return -1; }else if(pid == 0){ //子进程 printf("child:pid = %d,getpid() = %d getppid = %d\n",pid,getpid(),getppid()); }else{ //父进程 printf("parrnt:pid = %d getpid =%d getppid = %d\n",pid,getpid(),getppid()); } return 0; } getpid() :获得当前进程的进程号
2.进程回收
一个wait函数只能等待回收一个子进程
wait()
头文件: #include <sys/types.h> #include <sys/wait.h> 函数原型: pid_t wait(int *wstatus); { 功能:阻塞回收子进程的资源 } 参数: wstatus:查看子进程的结束状态,一般给NULL; 返回值: 成功 -- 返回结束子进程的进程号 失败 -- 返回-1 WIFEXITED(wstatus) -- 判断一个进程是否是正常退出,1表示正常退出,0表示非正常退出 WIFSIGNALED(wstatus) -- 判断一个进程是否是由信号终止 , 1表示信号终止,0表示不是信号终止 WTERMSIG(wstatus) -- 返回导致进程结束的信号的编号 WEXITSTATUS(wstatus) -- 正常结束时,返回进程的返回值
waitpid()
函数原型: pid_t waitpid(pid_t pid, int *wstatus, int options); { 功能:回收子进程的资源 } 参数: pid: pid == -1 表示回收任意子进程 pid == 0 表示回收同组的任意子进程 pid > 0 表示回收指定pid这个子进程 pid < -1 表示回收这个进程组号的绝对值得任意子进程 wstatus:进程结束状态 options: 0 -- 以阻塞方式接收 WNOHANG -- 立即结束 例:waitpid(-1, &status, 0) == wait(&status)
例子:
#include <stdio.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> #include <sys/wait.h> int main(int argc, char *argv[]) { pid_t pid = fork(); if(pid < 0){ perror("fork"); exit(-1); }else if(pid == 0){ int i =5; while(1){ sleep(1); printf("child..........i = %d \n",i); i--; } exit(1); }else{ int ret; do{ sleep(1); int ret = waitpid(pid,NULL,WNOHANG); printf("ret = %d\n",ret); }while(ret == 0); while(1); } return 0; }
3.进程结束
头文件: #include <stdlib.h> #include <unistd.h> 函数原型: void exit(int status); ==exit函数结束时会刷新所有缓冲区== void _exit(int status); ==_exit函数结束时不会刷新缓冲区== 参数: status 进程结束状态 0正结束 非0不正常结束
4.exec函数族
在一个进程中启动另一个进程得方法;
int execl(const char *pathname,const char *arg,.....); { 参数: pathname:包含路径得一个可执行文件名; }
例子:
#include <stdio.h> #include <stdio.h> #include <sys/types.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> int main(int argc, char *argv[]) { pid_t pid = fork(); if(pid < 0){ perror("fork"); exit(-1); }else if(pid == 0){ if(-1 == execl("/usr/bin/ls","ls","l","./",NULL));{ perror("execl"); exit(0); } }else{ while(1){ sleep(1); printf("hello world\n"); } } return 0; }
##
5.创建守护进程
创建步骤
1.创建子进程,父进程退出 2.在子进程中创建新的会话 让进程脱离以前会话期,更加灵活; 会话:多个进程组 #include <sys/types.h> #include <unistd.h> pid_t pid setsid(void) 返回值: 成功:成功返回新会话得id 失败:返回-1 并设置错误号 3.改变当前目录为根目录(或者 ./tmp)(修改工作路径) #include <unistd.h> int chdir(const char *path); 成功返回0, 失败返回-1,并设置错误号; 4.重设文件权限掩码 umask值 把umask更改为0;以指定的权限创建文件 #include <sys/stat.h> mode_t umask(mode_t mask); 5.关闭文件描述符 int getdtablesize(void); //获取linux下最多能打开的文 件描述符的个数
练习:
守护进程,新建一个文件把当前的时间写入到文件当中;
#include <stdio.h> #include <string.h> #include <stdlib.h> #include <strings.h> #include <sys/types.h> #include <unistd.h> #include <sys/stat.h> #include <time.h> void mydeamon(); int main() { mydeamon(); FILE *fp = fopen("mytime.txt", "a+"); if(NULL == fp){ perror("fopen"); return -1; } while(1){ sleep(1); time_t t; t = time(&t); struct tm *tp = localtime(&t); fprintf(fp, "%d年%d月%d日-%d时%d分%d秒\n", tp->tm_year + 1900, tp->tm_mon + 1, tp->tm_mday, tp->tm_hour, tp->tm_min, tp->tm_sec); fflush(fp); } return 0; } void mydeamon() { pid_t pid = fork(); if(0 > pid){ perror("fork"); exit(-1); }else if(pid > 0){ printf("parent: %d child = %d\n", getpid(), pid); exit(0); }else{ if(-1 == setsid()){ // 设置新的会话 perror("setsid"); exit(-1); } if(-1 == chdir("/tmp")){//更改工作目录 perror("chdir"); exit(-1); } umask(0);// 设置权限掩码 int i = 0; for(; i < getdtablesize(); i++){ close(i); } return; } }
线程———————————
是轻量型的进程
每个进程相对独立0
每个用户进程都有自己的地址空间
线程被称为最小的任务调度单位
使用多线程的好处:
大大提高任务的切换效率 避免了额外的TLB & cache的刷新
1.线程创建
头文件: #include <pthread.h> 函数原型: int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void),void *arg); { 参数: thread:线程id(线程对象) attr:线程属性(默认属性:NULL (缺省属性、结合属性) 分离属性 start_routine:指向函数的形参和返回值类型要 void (线程处理函数) } 返回值: 成功:0 失败:设置错误号
例子:
#include <stdio.h> #include <stdlib.h> #include <string.h> #include <pthread.h> #include <unistd.h> #include <pthread.h> void *function(void *arg) { printf("pthread_create\n"); } int main() { pthread_t tid; if(0 != pthread_create(&tid,NULL,function,NULL )){ perror("pthread_create"); exit(-1); } printf("hello world\n"); while(1); return 0; }
2.线程结束
pthread_exit(); 头文件: #include <pthread.h> 函数原型: void pthread_exit(void retval); { 参数:retval:结束状态 }
3.线程回收
pthread_join(); 头文件: #include <pthread.h> 函数原型: void pthread_join(pthread_t thread,void**retval); { 功能:阻塞等待回收线程资源 参数:pthreadd_t thread : 进程id retval:线程结束状态 } 主函数里回收
例子:
#include <stdio.h> #include <pthread.h> #include <stdlib.h> #include <unistd.h> void *func(void *arg); void *func(void *arg) { printf("pthread.......\n"); pthread_exit("pthread end.....\n"); } int main(int argc, char *argv[]) { pthread_t tid; if(0 != pthread_create(&tid,NULL,func,NULL)){ //创建线程 perror("pthread_create"); exit(-1); } void *retval; pthread_join(tid,&retval);//阻塞等待 return 0; }
4.设置分离属性
pthread_detach() 头文件: #include <pthread.h> 函数原型: int pthread_detach(pthread_t thread); { 参数:thread:线程id } 返回值: 成功:0 失败:非0;并设置错误号 作用: 当一个线程设置为分离属性,那么当这个线程结束以后,,资源由系统回收;
例子:
#include <stdio.h> #include <unistd.h> #include <pthread.h> #include <stdlib.h> void *function(void *arg){ while(1){ sleep(1); printf("pthread........\n"); } pthread_exit(NULL); } int main(int argc, char *argv[]) { pthread_t tid; if(0!=pthread_create(&tid,NULL,function,NULL)){ perror("pthread_create"); exit(-1); } if(0 != pthread_detach(tid)){ perror("pthread_datach"); exit(-1); } while(1){ sleep(1); printf("main.......\n"); } return 0; }
练习:
定义一个全局数组,从一个线程中写入数据 ,从另一个线程中读取数据
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <string.h> int flag = 0; char buf[64] = {0}; void *funcread(void *arg); void *funcwrite(void *arg); void *funcread(void *arg){ while(1){ if(flag == 1){ // sleep(1); printf("read -> %s\n",buf); flag = 0; } } } void *funcwrite(void *arg){ while(1){ if(flag == 0){ printf("please enter string :\n"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = '\0'; flag =1; } } } int main(int argc, char *argv[]) { pthread_t tid1,tid2; if(0 != pthread_create(&tid1,NULL,funcread,NULL)){ perror("pthread_create"); exit(-1); } if(0 != pthread_detach(tid1)){ perror("pthread_detach"); exit(-1); } if(0 != pthread_create(&tid1,NULL,funcwrite,NULL)){ perror("pthread_create"); exit(-1); } if(0 != pthread_detach(tid2)){ perror("pthread_detach"); exit(-1); } while(1); return 0; }
5.线程的同步
1.概念
同步:指多个任务(线程)按照约定的顺序相互配合完成一件事情
2.信号量
1.信号量代表某一类资源,其值表示系统中该资源的数量 2.信号量的值是指当前可用资源的数量 若信号量的值为 0 表示没有可以的资源 3.信号量是一个受保护的变量,只能通过三种操作来访问: 初始化 p操作 申请资源(信号量-1) v操作:释放资源(信号量+1) 4.信号量的值为非负整数;
3.使用信号量的步骤
1.创建信号量或获得系统已存在的信号量 2.
4.信号量的初始化
头文件: #include <semaphore.h> 函数原型: int sem——init(sem_t sem,int nsems,int semflg); { 参数: sem:信号量 pshared:0 适用于线程 value:信号量的初值 } 返回值: 成功: 0 失败:
例子:
#include <stdio.h> #include <pthread.h> #include <unistd.h> #include <stdlib.h> #include <string.h> #include <semaphore.h> sem_t sem_r; char buf[64] = {0}; void *funcread(void *arg); void *funcwrite(void *arg); void *funcread(void *arg){ while(1){ // sleep(1); sem_wait(&sem_r); printf("read -> %s\n",buf); } } void *funcwrite(void *arg){ while(1){ printf("please enter string :\n"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = '\0'; sem_post(&sem_r); } } int main(int argc, char *argv[]) { pthread_t tid1,tid2; if(-1 == sem_init(&sem_r,0,0)){ perror("sem_init"); exit(-1); } if(0 != pthread_create(&tid1,NULL,funcread,NULL)){ perror("pthread_create"); exit(-1); } if(0 != pthread_detach(tid1)){ perror("pthread_detach"); exit(-1); } if(0 != pthread_create(&tid1,NULL,funcwrite,NULL)){ perror("pthread_create"); exit(-1); } if(0 != pthread_detach(tid2)){ perror("pthread_detach"); exit(-1); } while(1); return 0; }
5.线程的互斥
1.概念:
1.引入互斥(mutual exclusion)锁的目的是用来保证共享数据操作的完 整性。 2.互斥锁主要用来保护临界资源 3.每个临界资源都由一个互斥锁来保护,任何时刻最多只能有一个线程能访 问该资源 4.线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果 无法获得锁,线程会阻塞直到获得锁为止
2.互斥锁的初始化
头文件: include <pthread.h> 函数原型: int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr); { 参数: restrict mutex : 锁 restrict attr:默认属性 NULL } 返回值: 成功:0 失败:设置错误编号
3.加锁
加锁: int pthread_mutex_lock(pthread_mutex_t *mutex); 参数:互斥锁
4.解锁
函数原型: int pthread_mutex_unlock(pthread_mutex_t *mutex); 参数:互斥锁; 返回值: 成功:返回0 失败:设置错误号
进程间通信————————
1.无名管道
1.1概念
1.无名管道和有名管道取决于创建出的管道是否在文件系统中可见 2.无名管道只能用于具有亲缘关系的进程间通信 3.无名管道有固定的读端和写端 4.无名管道是一种半双工的通信方式 5.管道可以看成是一种特殊的文件,对它的读写可以用文件IO(read和write函数)(不能用lseek对管道进行操作) 6.不存在文件系统上,存在内存上;
1.2无名管道的读写特点
1.读特性: 写端存在: 有数据:返回成功读取的字节数 没有数据,阻塞,直到有数据为止 写端不存在: 有数据,返回成功读取的字节数 没有数据,返回 0 2.写特性: 读端存在: 有空间:返回成功写入的字节数 无空间:阻塞,直到有空间为止 读端不存在: 无论有无空间: 管道破裂
1.3管道的创建pipe()
头文件: #include <unistd.h> 函数原型: int pipe(int pipefd[2]); 参数: pipefd:存放读端和写端文件描述符的地址 pipefd[0] -- 读端 pipefd[1] -- 写端 返回值: 成功:0 失败:-1,并设置错误号 注意:当管道没有数据的时候会阻塞等待
例子:
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/stat.h> #include <string.h> int main(int argc, char *argv[]) { int fd[2]; if(-1 == pipe(fd)){ perror("pipe"); exit(-1); } pid_t pid = fork(); if(0 < pid){ perror("fork"); exit(-1); }else if(pid == 0 ){ close(fd[1]); //关闭写管道 while(1){ char buf[32]={0}; int ret = read(fd[0],buf,sizeof(buf)); if(ret < 0){ perror("read"); exit(-1); } printf("read -> %s\n",buf); } }else{ close(fd[0]); while(1){ char buf[32] = {"0"}; printf("请输入:\n"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = '\0'; write(fd[1],buf,sizeof(buf)); } } return 0; }
练习:
计算无名管道的大小
#include <stdio.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #include <sys/stat.h> int main(int argc, char *argv[]) { int count; int fd[2] = {0}; if(-1 == pipe(fd)){ perror("pipe"); exit(-1); } char buf[1]={0}; while(1){ int ret =write(fd[1],buf,1); if(ret < 0){ perror("write"); exit(-1); }else{ count++; } printf("count: %d\n",count); } return 0; }
2.管道破裂
#include <stdio.h> #include <unistd.h> #include <stdlib.h> #include <sys/types.h> #include <sys/stat.h> #include <sys/wait.h> int main(int argc, char *argv[]) { int fd[2]={0}; if(-1 == pipe(fd)){ perror("pipe"); exit(-1); } close(fd[0]); pid_t pid = fork(); if(pid < 0){ perror("fork"); exit(-1); }else if(pid == 0){ char buf[32] = {0}; int ret = write(fd[1],buf,ret); if(ret < 0){ perror("write"); exit(-1); } } else{ int status; wait(&status); printf("%d %d %d\n",WIFEXITED(status),WIFSIGNALED(status),WTERMSIG(status)); } return 0; }
2.有名管道
2.1概念
1.有名管道在创建后会在文件系统中以一个文件的方式存在 2.有名管道没有固定的读端和写端 3.两个进程之间没有亲缘关系
2.2管道创建mkfifo()
头文件: #include <sys/types.h> #include <sys/stat.h> 函数原型: int mkfifo(const char *pathname, mode_t mode); 参数: pathname:有名管道名(可以包含路径) mode:有名管道的初始权限 返回值: 成功“返回0 失败:返回-1
例子:
两个文件之间的通信
写端管道 #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <string.h> #include <unistd.h> int main(int argc, char *argv[]) { if(0 > mkfifo("fifo",0666)){ perror("mkfifo"); // exit(-1); } int fd = open("fifo",O_WRONLY); if(fd < 0){ perror("open"); exit(-1); } printf("open fsuccess........\n"); while(1){ char buf[32] = {0}; printf("please enter string:\n"); fgets(buf,sizeof(buf),stdin); buf[strlen(buf)-1] = '\0'; if(-1 == write(fd,buf,sizeof(buf))){ perror("write"); exit(-1); } if(strcmp(buf,"quit") == 0){ break; } } close(fd); return 0; } ------------------------------------------------------------ ------------------------------------------------------------ #include <stdio.h> #include <stdlib.h> #include <sys/stat.h> #include <sys/types.h> #include <fcntl.h> #include <string.h> #include <unistd.h> int main(int argc, char *argv[]) { int fd = open("fifo",O_RDONLY); if(fd < 0){ perror("open"); exit(-1); } printf("open fsuccess........\n"); while(1){ char buf[32] = {0}; int ret = read(fd,buf,sizeof(buf)); if(ret < 0){ perror("read"); exit(-1); }else if(ret == 0){ break; } printf("ret = %d,read:%s\n",ret,buf); } close(fd); return 0; }
信号———————————
1.概念
信号是中断在软件层次上的一种模拟,是异步的通信方式; kill -l:查看信号 信号的处理方式: 默认 忽略 捕获 -- signal
2.信号种类
查看信号 kill -l 62种 信号 kill +信号编号 常见信号: SIGKILL SIGSTOP 不能被忽略; SIGINT 相当于ctrl + c SIGTSTP 相当于ctrl + z SIGALRM 当定时器接收会发出这个信号; SIGUSR1 用户定义的信号 大多数信号的默认操作是终止,SIGCHLD(子进程状态改变) 默认操作 是忽略;
3.信号接口相关函数
raise()
向自己发送信号
头文件: #include <signal.h> 函数原型: int raise(int sig); { 功能:给当前进程发送信号 } 参数: sig :信号类型 返回值: 成功:0 失败:-1
kill()
向指定进程发送信号
头文件: #include <sys/types.h> #include <signal.h> 函数原型: int kill(pid_t pid, int sig); 参数: pid:指定进程号 sig:指定信号 返回值: 成功:返回0 失败:返回-1;
定时器alarm
头文件: #include <unistd.h> 函数原型: unsigned int alarm(unsigned int seconds); 参数: seconds:定时时间,单位为秒 返回值: 成功:返回0 或者 上一个定时器定时的剩余时间
定时pause()
头文件: #include <unistd.h> 函数原型: int pause(void); 功能:结合alarm函数使用,用于等待定时时间结束
捕获信号signal()
头文件: #include <signal.h> 函数原型: typedef void (*sighandler_t)(int); sighandler_t signal(int signum, sighandler_t handler); 参数: signum:信号编号 handler:信号处理函数或者填SIG_IGN(忽略), SIG_DFL(默认)
进程间通信—IPC通信———
1.共享内存
概念:
共享内存指 (shared memory)在多处理器的计算机系统中,可以被不同中央处理器(CPU)访问的大容量内存。
特点:
1.共享内存是进程间共享数据的一种最快的方法。一个进程向共享的内存区域写入了数据,共享这个内存区域的所有进程就可以立刻看到其中的内容。 2.使用共享内存要注意的是多个进程之间对一个给定存储区访问的互斥。若一个进程正在向共享内存区写数据,则在它做完这一步操作前,别的进程不应当去读、写这些数据。
1.1获取key值
头文件: #include <sys/types.h> #include <sys/ipc.h> 函数原型: key_t ftok(const char *pathname, int proj_id); { 参数: pathname:文件名 proj——id:随便给一个整数 } 返回值: 成功:key值 失败:-1
1.2创建共享内存shmget()
头文件: #include <sys/ipc.h> #include <sys/shm.h> 函数原型: int shmget(key_t key,size_t size,int shmflg) 参数: key:ftok得到的key值或者填IPC_PRIVATE size:共享内存大小 shmflg:一般填 IPC_CREAT | 0664 返回值: 成功:共享内存id 失败:-1
例子:
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { key_t key = ftok(".",'z'); if(key < 0){ perror("ftok"); exit(-1); } int shmid = shmget(key,512,IPC_CREAT | 0664); if(shmid < 0){ perror("shmget"); exit(-1); } printf("shmid = %d\n",shmid); return 0; }
1.3内存映射shmat()
头文件: #include <sys/types.h> #include <sys/shm.h> 函数原型: void *shmat(int shmid, const void *shmaddr, int shmflg); 参数: shmid:共享内存id shmaddr:用户指定的映射地址,如果填NULL,表示系统自动分配 shmflg:访问共享内存的方式 0--表示可读可写 返回值: 成功:映射地址 失败:(void *)-1
例子:
#include <stdio.h> #include <sys/ipc.h> #include <sys/shm.h> #include <stdlib.h> #include <unistd.h> int main(int argc, char *argv[]) { key_t key = ftok(".",'z'); if(key < 0){ perror("ftok"); exit(-1); } int shmid = shmget(key,512,IPC_CREAT | 0664); if(shmid < 0){ perror("shmget"); exit(-1); } printf("shmid = %d\n",shmid); char *addr = (char *) shmat(shmid,NULL,0); if(addr == ((char *)-1)){ perror("shmat"); exit(-1); } printf("addr = %p\n",addr); return 0; }
1.4取消共享内存shmdt()
头文件: #include <sys/types.h> #include <sys/shm.h> 函数原型: int shmdt(const void *shmaddr); 参数: 映射地址 返回值: 成功:0 失败:-1
1.5删除共享内存shmctl()
头文件: #include <sys/ipc.h> #include <sys/shm.h> 函数原型: int shmctl(int shmid,int cmd,struct shmid_ds *buf); 参数: shmid:共享内存id cmd: IPC_STAT:获取共享内存信息 IPC_SET:设置共享内存 IPC_RMID:删除共享内存 返回值: 成功: 失败:-1
练习:
写端代码: #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <stdlib.h> #include <sys/shm.h> #include <unistd.h> #define N 64 int main(int argc, char *argv[]) { key_t key = ftok(".",'z'); if(key < 0){ perror("ftok"); exit(-1); } int shmid = shmget(key,N,IPC_CREAT | 0664); if(shmid < 0){ perror("shmget"); exit(-1); } char *addr =(char *)shmat(shmid,NULL,0); if(addr < 0){ perror("addr"); exit(-1); } while(1){ fgets((char *)addr,N,stdin); } int ret = shmdt(addr); if(ret < 0){ perror("shmdt"); exit(-1); } /* int ret = shmctl(shimd,IPC_RMID,NULL); if(ret < 0){ perror("shmctl"); exit(-1); }*/ return 0; } ------------------------------------------------------------ 读端代码: #include <stdio.h> #include <stdlib.h> #include <sys/shm.h> #include <sys/types.h> #include <sys/ipc.h> #include <unistd.h> #define N 64 int main(int argc, char *argv[]) { key_t key = ftok(".",'z'); if(key < 0){ perror("ftok"); exit(-1); } int shmid = shmget(key,N,IPC_CREAT | 0664); if(shmid < 0){ perror("shmget"); exit(-1); } char *addr =(char *)shmat(shmid,NULL,0); if(addr < 0){ perror("addr"); exit(-1); } while(1){ printf("%s",(char *)addr); } int ret = shmdt(addr); if(ret < 0){ perror("shmdt"); exit(-1); } int reg = shmctl(shmid,IPC_RMID,NULL); if(reg < 0){ perror("shmctl"); exit(-1); } sleep(2); return 0; }
2.信号灯集———————
信号量的集合
信号灯集的操作流程
1.创建信号灯集 --semget() 2.初始化信号灯集--semctl() 3.P/V操作--semop 删除信号灯集--semctl()
1.创建信号灯集-semget()
头文件: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> 函数原型: int semget(key_t key, int nsems, int semflg); { 参数: key:ftok获取 nsems:创建信号灯个数 semflg:信号灯权限 IPC_CREAT |0664 } 返回值: 成功:信号灯id 失败:-1
2.信号灯的初始化-semctl()
头文件: #include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> 函数原型: int semctl(int semid, int semnum, int cmd, ...); { 参数: semid:信号灯集id semnum: 信号灯编号,从0开始 cmd:IPC_RMID 表示删除信号灯,不需要第二个参数,第四个参数给 0; GETVAL:获取信号灯属 SETVAL: 设置信号灯属性 如果要设置信号量初值,那么一定要用共用体; } 返回值: 成功: 失败:
3.P/V操作–semop()
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h> int semop(int semid, struct sembuf *sops, size_t nsops); { 参数: semid:信号灯标识id; sops: struct sembuf{ unsigned short sem_num; //信号灯编号 short sem_op //-1 p操作, 1 v操作 short sem_flg; // 0 阻塞 } nsops:要操作的信号灯集的个数 }
4.删除信号灯集–semctl()
{ semctl(semid, RMID, 0); } ipcs -s 查看信号灯
3.消息队列———————
作用:
从一个进程向另外一个进程发送一个**带有类型的数据块
本质:
是存储在内核中的一个消息的队列(链表)
特点:
1.每个数据块都被认为有一个类型,接受者进程接收的数据块可以有不同的类型值 2.和管道一样,每个消息的最大长度是有上限的(MSGMAX),每个消息队列的总字节数也是有上限的(MSGMNB),系统上的消息队列总数也是有上限的(MSGMNI) 3.是一个全双工通信,可读可写。 4.生命周期随内核
操作流程:
1.创建key值 --ftok() 2.创建、打开消息队列--msgget() 3.发送消息--msgsnd() 4.接收消息--msgrcv() 5.删除消息队列--msg()
1.创建消息队列–msgget()
头文件: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 函数原型: int msgget(key_t key, int msgflg); { 参数: key:ftok()来获取 msgflg:权限 IPC_CREAT | 0664 } 返回值: 成功:消息队列id 失败:-1,设置错误号
2.写入消息–msgsnd()
.添加消息 msgsnd(); 头文件: #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> 函数原型: int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg); { 参数: msqid: 消息队列标识id; msgp:消息正文结构体; struct msgbuf{ long mtype; char mtext[SIZE]; } MSG_t msgsz:发送消息的长度,一定要保证收发一致; msgflg: 0代表当写满以后,就会阻塞等待有空间再写; IPC_NOWAIT 写满的时候直接返回 } 返回值: 成功:0 失败:-1 }
3.读取消息–msgrcv()
.读取消息 msgrcv() { 函数原型: size_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg); 参数: msqid: 消息队列id; msgp:用来保存消息的一个结构体; msgsz: 接收消息的大小,收发一致 msgtyp:消息的类型; 也可以为0, 顺序读取一条消息 (优先、时间。。。) msgflg: 0 当消息队列没有消息的时候会阻塞等待接收消息; IPC_NOWAIT 没消息不阻塞不等待返回错误,告诉当前 进程没有消息; 返回值: 成功:实际读到的字节数, 失败:-1,设置错误号; }
4.控制消息队列–msgctl()
4.控制消息队列 msgctl(); { int msgctl(int msqid, int cmd, struct msqid_ds *buf); { 参数: msqid:消息id cmd: IPC_RMID 删除,buf指针传NULL; IPC_STAT获取消息队列属性; IPC_SET 设置属性; 返回值: 成功返回0, 失败返回-1,并设置错误号; } 查看消息队列: ipcs -q
例子:
双向聊天
send端 #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define typeA 100 #define typeB 200 #define SIZE (sizeof(MSG) - sizeof(long)) typedef struct msgbuf{ long mtype; char mtext[64]; }MSG; int main(int argc, char *argv[]) { MSG m_buf; key_t key =ftok("/",'a'); if(key < 0){ perror("ftok"); exit(-1); } int msgid = msgget(key,IPC_CREAT | 0664); if(msgid < 0){ perror("msgget"); exit(-1); } int ret = 0; char buf[64]={0}; while(1) { char buf[64] = {0}; m_buf.mtype =typeA; printf("input>>:"); fgets(buf,64,stdin); strcpy(m_buf.mtext,buf); ret = msgsnd(msgid,&m_buf,SIZE,0); if(ret < 0){ perror("msgsnd"); exit(-1); } if(strcmp(buf,"quit\n") == 0){ printf("bye~\n"); break; } memset(&m_buf,0,sizeof(MSG)); ret = msgrcv(msgid,&m_buf,SIZE,typeB,0); if(ret < 0){ perror("msgrcv"); exit(-1); } if(strcmp(m_buf.mtext,"quit\n") == 0){ printf("bye~\n"); msgctl(msgid,IPC_RMID,NULL); break; } printf("recv: %s\n",m_buf.mtext); } return 0; } ------------------------------------------------ recv端 #include <stdio.h> #include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h> #include <stdlib.h> #include <unistd.h> #include <string.h> #define typeA 100 #define typeB 200 #define SIZE (sizeof(MSG) - sizeof(long)) typedef struct msgbuf{ long mtype; char mtext[64]; }MSG; int main(int argc, char *argv[]) { MSG m_buf; key_t key =ftok("/",'a'); if(key < 0){ perror("ftok"); exit(-1); } int msgid = msgget(key,IPC_CREAT | 0664); if(msgid < 0){ perror("msgget"); exit(-1); } int ret = 0; char buf[64]; while(1) { memset(buf,0,sizeof(MSG)); ret = msgrcv(msgid,&m_buf,SIZE,typeA,0); if(ret < 0){ perror("msgrcv"); exit(-1); } printf("%s\n",m_buf.mtext); if(strcmp(m_buf.mtext,"quit\n") == 0){ printf("bye~\n"); msgctl(msgid,IPC_RMID,NULL); break; } m_buf.mtype =typeB; printf("input>>:"); fgets(buf,64,stdin); strcpy(m_buf.mtext,buf); ret = msgsnd(msgid,&m_buf,SIZE,0); if(ret < 0){ perror("msgsnd"); exit(-1); } if(strcmp(buf,"quit\n") == 0){ printf("bye~\n"); break; } } return 0; }