##文件IO和标准IO
对一个文件有两种操作方式
文件IO---(linux系统提供)
系统调用:当我们的应用程序要使用一些底层功能时,不应自行访问底层,而应该向操作系统发出请求
特点:1.不带缓冲区 2.操作系统直接提供的函数接口 3.调用系统调用是很耗费资源的
标准IO----(C库提供)
C库函数:在系统调用接口之上封装的接口,一个C库函数可以封装多个系统调用函数
特点:1.增强了代码的可移植性,复用性 2.提高了效率 (标准IO增加了一个缓冲机制)
一.标准IO
函数:fopen/fclose/fgetc/fputc/fgets/fputs/fread/fwrite
fopen:用于打开文件, 其调用格式为
FILE *fp = fopen(路径名,操作模式)
例如:FILE *fp = fopen("./1/txt","r");
FILE:系统自动为使用的文件在内存中开辟一片空间,存储文件的详细信息,这个控件类型为FILE结构体类型,该结构体由系统设计。
FILE *:流指针,在标准IO中,每次成功打开一个文件,都会返回一个流指针,这个流指针就描述了一个文件,所有的标准IO都围绕流指针来进行。
fclose(fp);
关闭文件的目的:1.防止其他进程操作这个文件 2.释放结构体占用的资源
每一个终端都是一个文件:ptx/xxx 这个就是终端对应的文件,这个文件名字以数字命名且存储在:/dev/pts/xxx 这些文件由linux系统自动创建。当打开一个终端时,会重建一个新的文件与之对应stdin、stdout、stderr都是指向同一个文件(终端文件)
freopen:以指定模式重新指定到另一个文件,模式用于指定新文件的访问方式。(例如可以把在终端上打印的东西传到另一个路径文件中)
FILE *fp = freopen(路径名,操作模式,流指针)
功能:改变流指针的指向
例如:FILE *fp = freopen("./1.txt","r",FILE *stream);
fgetc:从输入流中获取下一个字符,并将文件指针加1
fgetc(FILE *filename)
功能:读取一个字节的数据,并将文件指针加1,且有了缓冲区
例如:fgetc(fp);
fputc:写一个字节到文件流中
fputc(输入的字符,FILE *stream)
功能:写入一个字节数据
例如:fputc('c',fp)
注意:使用fputc就有了缓冲区
fgets:从 指针流中读取 sizeof个字符存储到字符指针变量所指向的内存空间。
返回值:它的返回值是一个指针,指向字符串中第一个字符的地址。
fgets(内存空间首地址,sizeof个大小的指针变量字符,标准输入流 stdin,也可以是文件流)
例如:char *p = fgets(buf, sizeof(buf), fp);
fputs:把字符串或数组写入指定的stream(即指向文件的指针)中,但不包括空字符。
返回值:函数返回一个非负值。
fputs(数组,FILE *stream)
例如:fputs(str,sp);
fseek:重定位文件指针
fseek(文件指针,偏移量,移到的位置)
例如:fseek(fp,0,SEEK_SET);//移动到文件最开头
fseek(fp,-50,SEEK_END);//从文件末尾向前偏移50字节处
fseek(fp,50,SEEK_CUR);//从当前指针位置向后偏移50字节处
perror:将上一个函数错误的原因输出,
fwrite:向指定文件中写入数据
size_t fwrite(const void *str,size_t size,size_t nmemb,FILE *stream)
str:内存地址
size:字节数
nmemb:块数
stream:流指针
成功返回块数,失败返回-1
例如:fwrite(&a,sizeof(a),1,fp)意思是向取a中的地址向fp所指向的文件中写入一块sizeof(a)大小的数据
fread:从一个文件流中读取数据读取size*nmemb个字节数据,并将它们替换到buf(缓冲区)所指定的数组中,函数的返回值就是读取的数据量
size_t fread(void *str,size_t size,size_t nmemb,FILE *stream)
成功返回块数
str是读取的数据存放的内存的指针,可以是数组,也可以是新开辟的空间,str就是一个索引。
例如:fread(buf,100,10,fp)从
sprintf:按照格式把数据放在内存中(数据->内存)
int sprintf(char *str,const char *format,...)
sprintf(buf,%d-%d-%d,2023,2,15);
fprintf:按照格式把数据放在流指针指向的文件中(数据->流指针指向的文件)
int fprintf(FILE *stream,const char *format,....)
stream:流指针
format:格式控制符
....:不定参数
例如:fprintf(fp,%d-%d-%d,2023,2,15);
fscanf:把文件中的数据提取到不定参数中(文件->不定参数)
int fscanf(FILE *stream,const char *format,....)
例如:fscanf(fp,%d-%d-%d,&a,&b,&c)
sscanf:把内存中的数据提取到不定参数中(内存->参数)
int sscanf(const char *str,const char *format,...)
例如:sscanf(buf,%d-%d-%d,&a,&b,&c);
##查单词
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <strings.h>
int main()
{
FILE *fp = fopen("./dict.txt","r");//找到单词文件
if(NULL == fp){
perror("fopen");
return -1;
}
char buf[32] = {0};//接受单词
char str[1000] = {0};//定位单词
while(1)
{
printf("please input word:");
scanf("%s",buf);//输入单词
while(1)
{
char *p = fgets(str,sizeof(str),fp);//定义一个指针获取单词表
if(NULL == p){
perror("fgets");
printf("no such word\n");
break;//如果没有输入的单词,则跳出
}
if(strncmp(p,buf,strlen(buf)) == 0 && p[strlen(buf)+1] ==' ' )//判断条件
{
printf("%s\n",p);//打印此单词
break;
}
memset(str,0,sizeof(str);//清空单词表,因为p指向的文件中指针不在开头也就是里面有数,所有要清空
}
rewind(fp);//将文件指针置为开头
}
fclose(fp);
return 0;
}
time时间相关函数
#include <time.h>
#include <unistd.h>
time_t t;//初始化t
time_t time(time_t *t):从1970-1-1 0:0:0开始到现在的秒数
int n = time(NULL or &t);//返回值是秒数,从1970年开始
time_t t; time(&t);
struct tm *localtime(const time_t *timep):从1970-1-1 0:0:0开始到现在的秒数转换成日历
struct tm *p = localtime(&t);
struct tm{
int tm_sec;
int tm_min;
int tm_hour;
int tm_mday;
int tm_mon;
int tm_year;
}
p->tm_year + 1900; //从1900年开始
p->tm_mon + 1;//从0月开始
p->tm_hour;
p->tm_mday;
p->tm_sec;
char *q = ctime(&t);
功能:获取现在时间
printf("%s\n",q);
文件IO:系统调用函数
无缓存
文件描述符 :表示打开文件的符号,每打开一个文件 系统会自动分配一个fd
非负整数一般最大打开的文件描述符数量:0~1023
0:stdin 1: stdout 2:stderr
fdopen:将文件IO转换成标准IO,转换成对应文件指针返回。
FILE *fdopen(int fd,const char *mode)
参数:fd 文件描述符 mode 打开方式
FILE *fp = fdopen(1,"w")用写的方式以输出流方式指向文件
文件IO函数 (open/close/read/write/lseek)
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
open
int fd = open("文件路径名",打开方式)//文件已经存在
int fd = open("文件路径名",打开方式,文件权限)//文件不存在,则创建
例如: int fd = open("./",O_RDONLY,0666);
打开方式:O_CREAT 创建 O_RDONLY只读 O_WRONLY可写
O_RDWR读写 O_TRUNC清零 O_ APPEND追加
标准IO->文件IO
r ->O_RDONLY w+ ->O_RDWR | O_CREAT | O_TRUNC
r+ ->O_RDWR a ->O_WRONLY | O_CREAT | O_APPEND
w ->O_RDWR a+ ->O_RDWR | O_CREAT | O_APPEND
实际创建的文件权限需要经过一个公式计算得到:mode & (~umask)
mode:文件权限:0666
成功返回:文件描述符fd
close
#include <unistd.h>
int close(int fd);
write
ssize_t write(int fd,const void *buf,size_t count);
功能:把内存中的数据写入到文件中
read
ssize_t read(int fd,void *buf,size_t count);
功能:从文件中读取count字节的数据到内存
lseek
off_t lseek(int fd,off_t offset,int whence);
功能:定位文件指针
offset:偏移量
whence:基点
SEEK_SET:文件开头
SEEK_SET:文件末尾
SEEK_SET:文件当前位置
例如:lseek(fd,0,SEEK_END);
目录操作函数
opendir
#include<sys/types.h>
#include<dirent.h>
opendir:打开目录
DIR *opendir(const char *name);
功能:打开目录
name:目录的路径名
成功返回:DIR*
DIR *k = opendir("");
readdir:读取目录信息
struct dirent *readdir(DIR *dirp);
功能:读取目录信息
dirp:opendir的返回值
返回成功: struct dirent * 失败:NULL(一直遍历到目录完成)
DIR *k = opendir("");
struct dirent *p = readdir(k);
struct dirent{
d_ino; /* inode number 索引节点号 */
off_t d_off; /* offset to this dirent 在目录文件中的偏移 */
unsigned short d_reclen; /* length of this d_name 文件名长 */
unsigned char d_type; /* the type of d_name 文件类型 */
char d_name [NAME_MAX+1]; /* file name (null-terminated) 文件名,最长255字符 */
}
closedir:关闭目录
int closedir(DIR *dirp);
DIR *k = opendir("");
struct dirent *p = readdir(k);
closedir(k);
chdir:用户将当前的工作目录改变成以参数路径所指的目录
int chdir(const char * path);
文件信息函数
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
stat
struct stat mybuf:_stat函数用来获取指定路径的文件或者文件夹的信息。
struct stat mybuf;
int stat(const char *path,struct stat *buf);
int stat("文件路径名",结构体地址&mybuf)
struct stat{
dev_t st_dev; /* ID of device containing file -文件所在设备的ID*/
ino_t st_ino; /* inode number -inode节点号*/
mode_t st_mode; 文件的权限、类型
nlink_t st_nlink; /* number of hard links -链向此文件的连接数(硬连接)*/
uid_t st_uid; 所属用户ID
gid_t st_gid; 所属组ID
dev_t st_rdev; /* device ID (if special file) -设备号,针对设备文件*/
off_t st_size; /* total size, in bytes -文件大小,字节为单位*/
blksize_t st_blksize; /* blocksize for filesystem I/O -系统块的大小*/
blkcnt_t st_blocks; /* number of blocks allocated -文件所占块数*/
time_t st_atime; 最后一次访问时间
time_t st_mtime; 最后一次修改时间
time_t st_ctime; 最后一次文件属性修改时间
}
getpwuid
struct passwd *getpwuid(uid_t uid):通过用户uid查找用户的相关信息,并以passwd的结构体返回其数据
struct passwd *getpwuid(uid_t uid);
struct passwd{
char *pw_name; /* 用户登录名 */
char *pw_passwd; /* 密码(加密后) */
__uid_t pw_uid; /* 用户ID */
__gid_t pw_gid; /* 组ID */
char *pw_gecos; /* 详细用户名 */
char *pw_dir; /* 用户目录 */
char *pw_shell; /* Shell程序名 */
}
getgrgid
struct group *getgrgid(gid_t gid):获取用户组的相关信息
struct group *getgrgid(gid_t gid);
struct group{
char *gr_name; /* 组名 */
char *gr_passwd; /* 密码 */
__gid_t gr_gid; /* 组ID */
char **gr_mem; /* 组成员名单 */
}
文件类型:
{
常规文件:S_ISREG ‘-’
目录:S_ISDIR 'd'
字符设备:S_ISCHR 'c'
块设备:S_ISBLK 'b'
管道:S_ISFIFO 'p'
套接字:S_ISSOCK 's'
符号链接:S_ISLNK 'i'
}
##实现ls-s功能
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <pwd.h>
#include <time.h>
#include <dirent.h>
#include <grp.h>
int main()
{
DIR *p_dir = opendir("./");//打开当前路径目录
if(NULL == p_dir)
{
perror("opendir");
return -1;
}
struct dirent *rid_dir = p_dir;//读取目录信息,dirent型结构体指针指向打开的目录文件
struct group *gr_gid = NULL;//定义一个group型指针,用来接收用户组信息
struct passwd *pw_uid = NULL;//定义一个passwd型指针,用来接收用户信息
struct tm *dir_time = NULL; //定义一个tm型指针,用于读取最后一次时间
while(1)
{
//1.判断隐藏文件
if(strncmp(rid_dir->d_name,'.',1) == 0)//d_name是文件名,rid_dir读到的文件判断,隐藏文件则跳出本次循环
{
continue;
}
else if(rid_dir == NULL){
break;
}
//2.获取文件信息
struct stat mybuf;//定义一个stat结构体型
stat(rid_dir->d_name,&mybuf);//获取每一个文件的信息
//3.判断文件的类型
if(S_ISREG(mybuf.st_mode))//S_ISREG是常规文件的意思不是函数,mybuf.st_mode是在stat结构体中获取每一个文件的类型
{
printf("-");//判断普通文件
}
。。。。。。依次执行判断完文件类型
//3.打印出文件权限
printf("%c",mybuf.st_mode & 0x1<<8?'r':'-');//把1左移几位判断该位置是否有权限
printf("%c",mybuf.st_mode & 0x1<<7?'w':'-');
printf("%c",mybuf.st_mode & 0x1<<6?'x':'-');
printf("%c",mybuf.st_mode & 0x1<<5?'r':'-');
printf("%c",mybuf.st_mode & 0x1<<4?'w':'-');
printf("%c",mybuf.st_mode & 0x1<<3?'x':'-');
printf("%c",mybuf.st_mode & 0x1<<2?'r':'-');
printf("%c",mybuf.st_mode & 0x1<<1?'w':'-');
printf("%c",mybuf.st_mode & 0x1<<0?'x':'-');
//4.打印硬链接
printf("%d",mybuf.st_nlink);//打印文件个数即硬链接数
//5.获取用户组和用户id
gr_gid = getgrgid(mybuf.st_gid);//获取用户组id
pw_uid = getpwuid(mybuf.st_uid);//获取用户id
//6.求文件大小
printf("%ld",sizeof(mybuf.st_size));//求文件大小
//7.获取时间
dir_time = localtime(&mybuf.st_ctime);//获取文件属性最后一次修改时间
printf("%d月",r->tm_mon +1);//获取月数
printf("%d日",r->tm_mday);//获取日
printf("%d:%d",r->tm_hour,r->tm_min);//获取时和分
printf("%s\n",rid_dir->d_name);//打印文件名
}
closedir(p_dir);//关闭目录文件
return 0;
}
##库的制作
静态库*.a
静态库的制作和使用
制作:$gcc -c xxx.c -o xxx.o
$ar -crs libxxx.a xxx.o
静态库的命名规范:
必须以lib开头,紧跟库的名字,跟扩展名.a 例如:libxxx.a
使用:
$gcc main.c -L路径 -lxxx -L指定静态库所在目录 -l指定静态库的名字 xxx部分
运行:$./a.out
优点:a.out运行后不需要库,程序在编译链接的时候把库的代码复制到可执行文件当中的,可以直接运行
缺点:每个a.out都需要包含库,体积大,耗资源,对程序更新,部署,发布带来麻烦。
动态库*.so
动态库的制作和使用
制作:
$gcc -fPIC -c xxx.c -o xxx.o
$gcc -shared -o libxxx.so xxx.o
动态库的命名规范:
必须以lib开头,紧跟库的名字,跟扩展名.so 例如:libxxx.so
使用:
$gcc main.c -L路径 -lxxx
$ldd a.out #用于查看可执行程序以来的动态库有哪些
运行:
$./a.out #会报错
动态库的搜索方式:
1.将动态库拷贝到/lib/或者/usr/lib/
$sudo cp libxxx.so /lib/
export LD_LIBRARY_PATH=.或者so所在的路径(临时情况)
特点:在编译时不会链接到可执行文件中,只是在其中保存一个索引,在运行时,才真正的链接(动态),因此可执行程序体积小。
优点:a.out体积小,节省资源
缺点:a.out运行后需要库,不能直接运行
##进程
进程
进程是一个程序的依次执行的过程
每一个进程都分配一个虚拟的4G (0-3G:用户 3G-4G:内核)
进程和程序的区别
程序是静态的 进程是动态的
进程的内存管理
正文段 用户数据段 系统数据段
进程号PID
唯一的标识一个过程
进程的类型
交互进程(ctrl+z/jobs-l/bg/fg/kill-l/kill-9 PID/ps -ajx)
ctrl+z:把前台进程转成后台
jobs -l:查看作业
bg %作业号:把后台作业重新放在前台,且不能ctrl+c结束
fg %作业号:把后台作业重新放在前台,能被ctrl+c结束
kill -l:查看信号种类
kill-9 PID:杀死该进程
ps -ajx:查看进程
进程的运行状态
ps -ajx查看
运行态R:此时进程或者正在进行或准备运行
内核调度程序到CPU上执行running
等待态:此时进程需要满足一些条件,如果不满足就等待
可中断S:如果进程收到信号会醒来ctrl+c
不可中断D:如果进程收到信号不会醒来
停止态T:此时进程被中止SIGSTOP
死亡态Z:已终止的进程(僵尸进程)
但还在进程向量数组中占有一个task_struct结构
task_struct{
pid_t pid;
R;
}
<高优先级
N低优先级
L有些页被锁进内存
s会话组组长
+位于前台的进程组
I多线程,克隆线程
##进程相关函数
fork/【exit库函数有缓存,会刷新缓存区】(_exit系统调用无缓冲,不会刷新)
创建进程/退出进程
#include <unistd.h>
pid_t pid;//进程号
pid = fork();//分配进程
if(pid < 0){
perror("fork");
return -1;//创建进程失败
}
if(pid == 0){//子进程创建成功
int n = 5;
while(n--)
{
printf("son is running/n");
sleep(1);
}
exit(0);//结束进程,返回0
}
else if(pid > 0){//创建父进程成功
int status;//用来返回子进程结束的状态,父进程通过status可以得到一些子进程结束时的状态信息,一般为NULL
wait(&status);//父进程等待子进程结束回收它的资源
printf("%d",WEXITSTATUS(status));//获取子进程返回值
printf("%d",WIFEXITED(status));//判断子进程是否正常结束
while(1)
{
printf("father is running\n");
sleep(1);
}
return 0;
}
wait/waitpid
回收进程资源
pid_t pid;//进程号
pid = fork();//分配进程
if(pid < 0){
perror("fork");
return -1;//创建进程失败
}
if(pid == 0){//子进程创建成功
int n = 5;
while(n--)
{
printf("son is running/n");
sleep(1);
}
exit(0);//结束进程,返回0
}
else if(pid > 0){//创建父进程成功
int status;//用来返回子进程结束的状态,父进程通过status可以得到一些子进程结束时的状态信息,一般为NULL
waitpid(-1,NULL,WNOHANG);//父进程自动回收子进程结束后的资源
while(1)
{
printf("father is running\n");
sleep(1);
}
return 0;
#include<sys/types.h>
#include <sys/wait.h>
pid_t wait(int *status);
功能:父进程等待子进程结束,回收它的资源
参数:status:一般为NULL
WEXITSTATUS(status) 获取子进程返回值 WIFEXITED(status) 判断子进程是否正常结束
pid_t waitpid(pid_t pid,ing *status,int options);
功能:父进程自动回收子进程结束后的资源
参数:pid:-1 任意进程 status:NULL
options:0 阻塞(即waitpid(-1,NULL,0) == wait(NULL)) WNOHANG 非阻塞
子进程先于父进程退出--父进程为回收资源--子进程会变成僵尸进程
危害:占用进程号、内存空间、PCB进程控制块等
解决:wait/waitpid
注意:任何进程结束都会变成僵尸进程,只是时间有长有短
父进程先于子进程退出--子进程会变成孤儿进程--被init进程接管
init进程:系统启动后运行的第一个用户空间进程,pid=1,会定期扫描系统,收养孤儿进程
注:孤儿进程一般没什么危害
##exec函数族
概念:函数族提供了一种在进程中启动另一个程序执行的方法,它可以根据指定的文件名或目录名找到可执行文件,并用它来取代原调用进程的数据段、代码段和堆栈段,再执行完之后,原调用进程的内容除了进程号外,其他全部被新程序的内容替换了。另外,这里的可执行文件既可以是二进制文件,也可以是linux下任何可执行脚本文件。
#include <unistd.h>
int execl(const char *path,const char *arg,...);
功能:进程重载0-3G的用户空间,可与fork函数搭配使用
参数:int execl("绝对路径","标识符(可以理解成文件名)",不定参数(一般为NULL));
int execv(const char *path,char *const argv[]);
int execlp(const char *file,const char *arg,...);
int execvp(const char *file,char *const argv[]);
int execle(const char *path,const char *arg,...,char *const envp[]);
int execve(const char *path,char *const argv[],char *const envp[]);
##守护进程
守护进程
在linux中与用户交互的界面叫终端,从终端运行起来的程序都依附于这个终端,当终端关闭时,相应的进程都会被关闭,守护进程可以突破这个限制。
特点
在后台服务的进程
生存期很长
守护进程独立于控制终端
守护进程创建流程
创建子进程,父进程退出 fork(void);
在子进程中创建新会话setsid(void);
修改工作目录chdir("");
修改umask,增加安全性 umask();
关闭文件描述,即回收资源 close();
##线程
线程函数
#include <pthread.h>
pthread_t tid;//线程号
int pthread_create(pthpread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void *),void *arg);
功能:创建线程
参数:
thread: &tid 线程号的地址
attr: NULL 线程函数默认的属性
start_routine: 线程函数的函数名
arg: 给线程函数传递的参数NULL
例如:pthread_t tid;//初始化线程号
int ret = pthread_create(&tid,NULL,fun,NULL);
int pthread_join(pthread_t thread,void **retval)
功能:主线程等待子线程结束,回收资源(阻塞)
参数:
thread : tid
retval : NULL (接受子线程结束的返回值)
返回值: 成功0 失败-1
例如:pthread_join(tid,NULL);
int pthread_detach(pthread_t thread);
功能:主线程和子线程分离开来,系统自动回收资源(非阻塞)
参数:
thread : tid
返回值: 成功0 失败-1
sem_t sem;//初始化信号量
int sem_init(sem_t *sem,int pshared,unsignd value)
功能:初始化信号量
参数:
sem : &sem取地址
pshared : 0:线程 1:进程
value : 初值 0 or 1
例如:sem_init(&sem,0,0);
int sem_wait(sem_t *sem);//相当于value-1
int sem_post(sem_t *sem);//相当于value+1
pthread_mytex_mutex;//互斥锁
pthread_mutex_init(&mutex,NULL);//初始化
pthread_mutex_lock(&mutex);//上锁
pthread_mutex_unlock(&mutex);//解锁
##传统的进程间通信
无名管道
查看命令: man 2 pipe
头文件:#include <unistd.h>
函数原型: int pipe(int pipefd[2])
pipefd[2]:无名管道的两个文件描述符,int型数组,大小为2,pipefd[0]为读端,pipefd[1]为写端
无名管道的特点:
没有名字,所以无法用open()打开
只能用于亲缘进程间(如父子进程、兄弟进程、祖孙进程等)通信
半双工工作模式,读写端是分开的,pipefd[0]为读端,pipefd[1]为写端,无名管道两端每一端都可以读和写
是一种特殊的文件,只存在内存中,由内核进行管理
对于它的读写可以以使用文件IO如read、write函数
无名管道的操作属于一次性操作,如果对无名管道进行读操作,数据会全被读走
注意事项:
当管道中无数据时,执行读操作,读操作阻塞
无名管道的大小是固定的,管道一旦满,写操作就会导致进程阻塞
对无名管道的操作,类似一个队列,后写入的数据不会覆盖之前的数据,会在其后面存储,读取完的数据会从管道里面移除。
将读端关闭,向无名管道中写数据,管道破裂,进程收到信号(SIGPIPE),默认这个信号会将进程杀死
当管道中有数据,将写端关闭,读操作可以执行,之后数据读完,可以继续读取(非阻塞),直接返回0
单工:固定一种方向通信,如广播
半双工:同一时间只能由一端发送到另一端
全双工:通信方向都可以,同时可以发送也可以接受电话
2.有名管道
有名管道也叫命名管道,在文件系统目录中存在一个管道文件
管道文件仅仅是文件系统中标识,并不在磁盘上占据空间。在使用时,内存上开辟空间,作为两个进程数据交互通道
查看命令:man 3 mkfifo
头文件:#include <sus/types.h> #include <sys/stat.h>
函数原型: int mkfifo(const char*pathname,mode_t mode);
int mkfifo(const char*pathname,mode_t mode);
功能:创建有名管道
参数:
pathname:文件路径名
mode:权限
在Shell中使用mkfifo命令:mkfifo filename创建管道
特点:
1.有名管道存在文件系统中,数据存在内存中
2.可以用于无亲缘关系的进程
3.只有读端和写端同时存在管道才能成功打开
数据传输特点:
读端不存在时,写端写入数据将会阻塞
读端意外结束,写端在写数据将会管道破裂,该进程结束
有名管道数据存储在内存中,数据交互在内核中
有名管道和无名管道的异同点:
相同点:
open打开管道文件以后,在内存中开辟了一块空间,管道内容在内存中存放,有两个指针——头指针(指向写的位置)和尾指针(指向读的位置)指向它。读写数据都是在给内存的操作,并且都是半双工通讯。
区别
有名在任意进程之间使用,无名在父子进程之间使用
##信号
简单概念:信号在软件层次上对中断机制的一种模拟
kill把信号发送给进程或进程组;raise把信号发送给进程自身。
#include <signal.h>
int kill(pid_t pid,int signo);
int raise(int signo); raise(signo) == kill(getpid(),signo);//等价于
alarm();//设置闹钟
#include <unistd.h>
unsignd int alarm(unsigned int seconds);
功能:可以为当前进程定义闹钟,时间到了会发出SIGALRM信号
注:每个进程只能有一个alarm,当重新定义时,会重新计时,如果之前定义了一个闹钟,则这次定义返回的是上次闹钟剩余的时间,否则返回0
pause();//程序暂停
pause函数的作用,是让当前进程暂停运行,交出CPU给其他进程去执行,当前进程进入pause状态后,当前进程会表现为”卡住、阻塞住“,要退出pause状态,当前进程需要被信号唤醒。
#信号的三种处理方式
忽略 2.默认
#include <signal.h>
void (*signal (int signo,void (*func)(int)))(int);
3.自定义信号处理函数
#include <signal.h>
typedef void(*sighandler_t)(int);
sighandler_t signal(int signum,sighandler_t handler);
参数:
捕获信号,设置信号的处理方式
handler:SIG_IGN:忽略 SIG_DFL:默认
SIGINT:ctrl+c SIGQUIT:ctrl+\ SIGTSTP:ctrl+z
##共享内存
进程间通信方式:
传统进程间通信方式: 无名管道 有名管道 信号
system V 的IPC(信号间通信方式)对象 : 共享内存 消息队列 信号灯集
ipcs
ipcs -m:查询显示当前系统的共享内存
ipcs -q:查询显示当前系统的消息队列
ipcs -s:查询显示当前系统的信号灯集
ipcrm -m shmid:删除某个共享内存
ipcrm -q msgid:删除某个消息队列
ipcrm -s semid:删除某个信号灯集
IPC步骤:ftok->shm_get/msg_get/sem_get->shmmat/shmdt/shmctrl/msgctrl/msgsend/msgrecv/semctrl/semop
##共享内存
是一种通信效率最高的进程间通信方式,进程间通信直接访问内存,不需要进行数据的拷贝
#include <sys/types.h> #include <sys/ipc.h> #include <sys/shm.h>
步骤:1.ftok 2.shmget 3.shmat 4.进程间通信fork 5.shmat 6.shmctl
key_t key;
key_t ftok(const char *pathname,int proj_id);
功能:得到key的值
参数:pathname:路径名 proj_id:1-255(子序号)
int shmget(key_t key,size_t size,int shmflg);
功能:创建共享内存
参数:key:key值 或者IPC_PRIVATE
size:共享内存大小 1024bytes
shmflg:打开方式:IPC_CREAT|0666
成功返回shmid 失败-1
void *shmat(int shmid,const void *shmaddr,int shmflg);
功能:创建映射区域
参数:shmid:shmid值
shamaddr:NULL(默认:自动映射)
shmflg:SHM_RDONLY:只读 0:读写(重要!)
成功返回内存地址 失败返回(void *)-1
int shmdt(const void *shmaddr);
功能:解除映射
参数:shmaddr:共享内存地址shmat的返回值,即内存地址
成功0 失败-1
int shmctl(int shmid,int cmd,struct_ds *buf);
功能:控制函数
参数:shmid:shmid值
cmd:IPC_RMIDsh删除共享内存
buf:NULL
成功0 失败-1
##消息队列
#include <sys/types.h> #include <sys/ipc.h> #include <sys/msg.h>
步骤:1.ftok 2.msgget 3.进程间通信 4.msgsnd 5. msgrcv 6.msgctl
int msgget(key_t key,int msgflg);
功能:创建消息队列
参数:key:key值
msgflg:IPC_CREAT|0666
成功返回msgid值 失败-1
struct msgbuf msg{
long mtype;//是一个正的长整型量,通过它来区分不同的消息数据类型大于0,过设定mtype 值,可以进行单个消息队列的多向通讯
char mtext[N];//消息数据的内容
};
struct msgbuf msg;
int msgsnd(int msgid,const void *msgp,size_t msgsz,int msgflg);
功能:发送数据
参数:msgid:msgid值
msgp:&msg(结构体变量的地址)
msgsz:消息正文的长度 sizeof(msg)-sizeof(long)
msgflg:IPC_NOWAIT 非阻塞 0 阻塞
ssize_t msgrcv(int msgid,void *msgp,size_t msgsz,long msgtyp,int msgflg);
功能:接受数据
参数:msgid:msgid值
msgp:&msg(结构体变量地址)
msgsz:消息正文长度 sizeof(msg)-sizeof(long)
msgtyp:消息类型
msgflg:IPC_NOWAIT 非阻塞 0 阻塞
int msgctl(int msgid,int cmd,struct msqid_ds *buf);
功能:控制函数
参数:msgid:msgid值
cmd:IPC_RMID(删除消息队列)
buf:NULL
##信号灯集
#include <sys/types.h> #include <sys/ipc.h> #include <sys/sem.h>
步骤:1.ftok 2.shmget 3.shmat 4.semget 5.semctl 6.进程间通信 7.semop 8.semctl 9.shmdt 10.shmctl(黑体部分是与共享内存增加部分)
int semget(key_t key,int nsems,int semflg);
功能:创建信号灯集
参数:key:ftok的返回值orIPC_PRIVATE
nsems:设置的信号灯数目
semflg:IPC_CREAT|0666
成功返回semid
union semun{
int val;//设置初值
struct semid_ds *buf;//使用缓冲区
unsigned short *array;//使用的数组
struct seminfo *_buf;//(linux特有的)使用缓冲区
}
int semctl(int semid,int semnum,int cmd,...);
功能:信号灯集控制函数
参数:semid:semget的返回值
semnum:要设置的信号灯编号
cmd:IPC_RMID删除信号灯集 SETVAL:设置信号灯
...:共用体的变量or不写
int semop(int semid,struct sembuf *sops,unsigned nsops);
功能:对信号灯进行PV操作(+1 -1)
参数:semid:semget的返回值
sops:结构体变量的地址
struct sembuf{
unsigned short sem_num;//信号灯的编号
shrot sem_op;//信号灯操作+1 -1
shrot sem_flg;//0阻塞
}
nsops:信号灯个数,默认为1