1、进程的创建fork()
#include
pid_t fork(void);
pid_t vfork(void);
返回值: > 0 父进程 parent process
==0 子进程 child process
< 0(-1) 失败
fork的特点:
第一个特点: 一次调用,两次返回,返回大于0(值就是子进程的id号)表示父进程,等于0表示子进程
子进程会复制父进程的所有资源(代码段,数据段)
第二个特点:父进程总共分成三个部分
第一个部分在fork之前
第二个部分fork成功之后,在id>0情况下的那部分代码
第三个部分fork后面的
总结:
第一点:fork()的套路
if(>0){ 父进程代码 }else if(==0){ 程序员创建子进程需要并发执行的任务代码 }else{ 错误 }
第二点:vfork创建的子进程共享父进程的资源,vfork创建的子进程一定优先于父进程运行
2、进程的退出跟回收exit()/wait()
#include
void exit(int status); //退出进程的同时刷新缓冲区
void _exit(int status);//退出进程的时候不刷新缓冲区
参数:status -->进程退出时候的值
对比return: 区别一:return是C语言中关键字,exit()是函数
区别二:return是返回函数调用的结果,exit()是结束整个进程
#include
pid_t wait(int *stat_loc);--->收子进程结束返回值(收尸)
返回值:成功 返回值回收到的那个子进程的ID,失败 -1
参数:stat_loc -->用来存放进程退出时候的状态信息
不是存放退出值,退出值仅仅只是状态信息中的一部分
特点:会让父进程一直阻塞,直到成功回收到子进程为止
pid_t waitpid(pid_t pid, int *stat_loc, int options);
返回值:成功 返回值回收到的那个子进程的ID,失败 -1
参数: pid --> 小于-1 waitpid(-1200,&status,options);回收进程组id是1200的其中一个
等于-1 waitpid(-1,&status,options);回收任意一个子进程(不要求同组)
等于0 waitpid(0,&status,options); 回收本进程组中任意一个子进程
大于0 waitpid(1200,&status,options); 指定回收id是1200的子进程
stat_loc -->跟wait一样
options -->WN0HANG 非阻塞等待,等得到就等,等不到就直接退出
0 阻塞等待
3、获取当前进程的id以及父进程的id
#include
#include
pid_t getpid(void); //获取子进程ID
pid_t getppid(void); //获取父进程ID
gid_t getgid(void); //获取进程组ID
4、使用函数执行shell命令
#include
#include
int system(const char *command);
返回值:失败 -1
参数: command -->你要执行的shell命令或者你要执行程序完整的命令名
int execl(const char *path, const char *arg, ...);
参数: path -->你要执行的程序/命令所在的路径
arg -->你要执行的命令/程序
int execlp(const char *file, const char *arg, ...);
int execle(const char *path, const char *arg,..., char * const envp[]);
int execv(const char *path, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],char *const envp[]);
参数: file-->即将被加载执行的文件名
argv-->保存即将被执行的参数的数组
envp-->用户自定义的环境变量数组
总结: l -->参数以列表的形式逐一列举
e -->参数中可以设置环境变量
v -->参数用一个指针数组存放
p -->传递程序/命令的名字
(一)无名管道和有名管道
1、创建无名管道
#include
int pipe(int fildes[2]);
返回值:成功 0 失败 -1
参数: fildes[2] -->存放的是两个文件描述符
fildes[0]读端的文件描述符
fildes[1]写端的文件描述符
特点:
有固定的读端(fd[0])跟写端(fd[1])
当管道中没有数据可以读的时候,调用read会阻塞
只能用于具有血缘关系的进程之间(父子进程,兄弟进程之间)
2、创建有名管道
#include
#include
int mkfifo(const char *pathname, mode_t mode);
返回值:成功 0 失败 -1
参数: pathname -->你要创建的有名管道的名字(带路径)
mode -->0777
特点:
有名字,生成一个管道文件,用于通信
当管道中没有数据可以读的时候,调用read会阻塞
任意两个不同进程之间都能通信
3、判断文件是否存在
#include
int access(const char *path, int amode);
功能:判断一个文件是否存在,参数用来判断文件是否可读、是否可写、是否可执行
返回值:符合你要求的判断条件返回0 否则 -1
参数: path -->文件的路径
amode -->R_OK, W_OK, X_OK ,F_OK
(二)信号:
作用是当一个进程发送信号给另外一个进程的时候,可以通过该信号去控制另外一个进程执行程序员想干的事情
1、发送信号
#include
int kill(pid_t pid, int sig);
参数: pid -->你要发送信号的那个进程的id
sig -->你要发送的信号
int sigqueue(pid_t pid, int sig, const union sigval value);
参数:union sigval {
int sival_int;
void *sival_ptr; //void *万能指针
};//存放你想发送的额外数据
跟kill的区别:sigqueue买一送一(发送信号的同时可以额外发送其他数据给到进程)
2、捕捉信号并改变信号的响应动作
#include
void (*signal(int sig, void (*func)(int)))(int);
返回值:最近一次调用时第二个参数的值(函数指针)
参数:sig -->你想要捕捉的信号
第二个参数有三种情况:
情况一:
void (*func)(int) -->函数指针,你想要改变的信号动作就靠该函数实现
情况二:
SIG_DFL -->按照信号默认的动作响应
情况三:
SIG_IGN -->忽略信号,收到信号之后不做任何响应,直接舍弃
注意:在所有的信号中有两个信号是既不能改变默认动作也不能忽略,SIGKILL和SIGSTOP
int sigaction(int sig, const struct sigaction *restrict act,struct sigaction *restrict oact);
参数: struct sigaction
{
void(*)(int) sa_handler //跟signal中函数指针一模一样
sigset_t sa_mask //信号阻塞掩码??
int sa_flags //设置0选择sa_handler
//设置SA_SIGINFO表示选择sa_sigaction
void(*)(int,siginfo_t *,void *) sa_sigaction//另外一个信号响应函数,接收额外数据
}
void(*)(int,siginfo_t *,void *)
siginfo_t
{
si_int -->存放union sigval里面的sival_int
si_ptr -->存放union sigval里面的sival_ptr
si_pid -->存放发送信号的那个进程的id
}
oact-->原有信号的处理参数,一般为NULL
3、其它简单函数
#include
int pause(void);//阻塞当前进程等待信号到来
int raise(int sig);//自己给自己发送信号
unsigned alarm(unsigned seconds);//定时器,alarm(5);过5秒之后自己给自己发送SIGALRM
4、信号的阻塞或屏蔽
#include
int sigprocmask(int how, const sigset_t *restrict set,sigset_t *restrict oset);//设置信号阻塞的函数
返回值:成功 0 失败 -1
参数:how -->SIG_BLOCK //设置阻塞 将set对应信号添加到原本的信号集合中
SIG_SETMASK //设置阻塞 用set替换原本的信号集合中的信号
SIG_UNBLOCK //解除阻塞
sigset_t --> 系统定义的一种变量类型,专门用来存放你想要阻塞的信号
称之为信号阻塞掩码集
操作信号阻塞掩码集合的函数
int sigemptyset(sigset_t *set); //清空掩码集
int sigfillset(sigset_t *set); //将所有的linux信号添加到集合中
int sigaddset(sigset_t *set, int signum);//将具体的某个信号添加到集合
int sigdelset(sigset_t *set, int signum);//将具体的某个信号从集合删除
int sigismember(const sigset_t *set, int signum);//判断某个信号在不在集合 返回1是成员 返回0不是成员
注意:信号设置阻塞仅仅只是将信号暂时挂起,信号依然存在(等到解除阻塞又能重新响应)
**********************************************************************************************************************************************
《system-V IPC通信 》:指的就是共享内存,消息队列,信号量
linux命令: ipcs -s 查看当前系统所有的信号量
ipcs -m 查看当前系统所有的共享内存
ipcs -q 查看当前系统所有的消息队列
ipcs -a 查看当前系统所有的IPC对象
ipcrm -s 信号量id 删除信号量
ipcrm -m 共享内存id 删除共享内存
ipcrm -q 消息队列id 删除消息队列
(三)信号量:用来协调多个进程对应共享资源的访问
特点:当信号量的值为0,你还想p操作,会阻塞当前进程
信号量的值是不可能为负数的
v操作永远不会阻塞
1、创建信号量
#include
#include
int semget(key_t key, int nsems, int semflg);
返回值:成功 信号量的id 失败 -1
参数:key -->键值,确保唯一性
产生键值两种方法
第一种:自己随便写一个(正整数)
第二种:使用系统提供的ftok()生成一个键值
#include
key_t ftok(const char *pathname, int proj_id);
返回值:成功 返回键值 失败 -1
参数:pathname -->合法的linux路径
proj_id -->随便写个整数
ftok(".",200); ftok(".",199);
nsems -->你打算创建多少个信号量
semflg -->IPC_CREAT信号量不存在则创建
IPC_EXCL信号量已存在则报错
0777 信号量的访问权限
2、获取或设置信号量的相关属性
#include
#include
int semctl(int semid, int semnum, int cmd, ...);
参数: semid -->信号量的ID,semget的返回值
semnum -->信号量的序号,从0开始
cmd -->GETVAL //获取信号量值
int value=semctl(id,0,GETVAL);返回值给value
SETVAL //设置信号量值
semctl(id,0,SETVAL,10);//将第一个信号量值设置为10
IPC_RMID //删除信号量
3、信号量的PV操作
#include
#include
int semop(int semid, struct sembuf *sops, size_t nsops);
返回值:
参数:struct sembuf
{
short sem_num 信号量的序号
short sem_op 决定你究竟是想P操作还是V操作,负数P操作,正数V操作
short sem_flg SEM_UNDO(操作完信号量之后,恢复成原本值)
}
nsops -->信号量struct sembuf个数
(四)共享内存:效率最高的IPC
1、申请共享内存
#include
#include
int shmget(key_t key, size_t size, int shmflg);
返回值:成功 共享内存的ID 失败 -1
参数:size -->你打算申请多少的内存,字节,一般设置成512的整数倍
shmflg->IPC_CREAT共享内存不存在则创建
IPC_EXCL共享内存已存在则报错
0777 共享内存的访问权限
2、对共享内存进行映射或解除映射
#include
#include
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmdt(const void *shmaddr);
参数:shmid-->共享内存id
shmaddr -->一般为NULL
shmflg -->一般为0
返回值:成功返回共享内存的首地址
失败 -1
3、获取、设置共享内存相关属性或删除共享内存
#include
#include
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
参数:cmd -->IPC_STAT //获取共享内存的属性信息
IPC_SET //设置共享内存的属性信息
IPC_RMID //删除共享内存
struct shmid_ds -->用来存放共享内存的属性信息
(五)消息队列
1、创建消息队列
#include
#include
int msgget(key_t key, int msgflg);
参数:shmflg->IPC_CREAT消息队列不存在则创建
IPC_EXCL消息队列已存在则报错
0777 消息队列的访问权限
2、收发信息
#include
#include
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
返回值:成功 0 失败 -1
参数:msgflg -->默认设置0 //阻塞
IPC_NOWAIT //非阻塞
msgp--->要发送数据的存储区域指针
msgsz-->要发送数据的大小
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
参数:msgtyp -->你要接收的信息类型
msgp--->要接收数据的存储区域指针
msgsz-->要接收数据的大小
重点:发送消息的格式是有要求的,要求用户自定数据类型
struct msgbuf
{
long msgtype; //表明消息类型
char truemsg[50];//真实的信息内容
};
struct singlelist
{
int num;//真实的数据
char buf[10]; //next指针
};
3、删除消息队列
#include
#include
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
参数:cmd-->IPC_STAT 获取MSG的信息
IPC_SET 设置MSG的信息
IPC_RMID 删除MSG
buf-->存放信息结构体缓冲区