进程的五种状态
1:new 进程正在被创建,尚未达到ready状态
2:ready状态,进程已经获得除了cpu的一切其他资源----万事具备,只欠东风
3:running状态
4:waiting状态---正在等待获得其他资源--io资源等
5:结束状态
image.png
进程与线程的区别
进程是资源分配的基本单位,线程是任务执行的基本单位
线程的程序计算器、本地方法区、虚拟机栈是独立的,堆和方法区是共享的
image.png
进程通信
匿名管道
一般是父子进程或兄弟进程之间的通信,半双工模式,有固定的读端(fd[0])和写端(fd[1])
image.png
image.png
#include
#include
int main()
{
int fd[2]; // 两个文件描述符
pid_t pid;
char buff[20];
if(pipe(fd) < 0) // 创建管道
printf("Create Pipe Error!\n");
if((pid = fork()) < 0) // 创建子进程
printf("Fork Error!\n");
else if(pid > 0) // 父进程
{
close(fd[0]); // 关闭读端
write(fd[1], "hello world\n", 12);
}
else
{
close(fd[1]); // 关闭写端
read(fd[0], buff, 20);
printf("%s", buff);
}
return 0;
}
命名管道
不具有亲缘关系的进程也可以使用
消息队列
消息的链接表,独立于发送和接收线程,有特定的优先级,可以随机进行访问
#include
// 创建或打开消息队列:成功返回队列ID,失败返回-1
int msgget(key_t key, int flag);
// 添加消息:成功返回0,失败返回-1
int msgsnd(int msqid, const void *ptr, size_t size, int flag);
// 读取消息:成功返回消息数据的长度,失败返回-1
int msgrcv(int msqid, void *ptr, size_t size, long type,int flag);
// 控制消息队列:成功返回0,失败返回-1
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
#include
#include
#include
// 用于创建一个唯一的key
#define MSG_FILE "/etc/passwd"
// 消息结构
struct msg_form {
long mtype;
char mtext[256];
};
int main()
{
int msqid;
key_t key;
struct msg_form msg;
// 获取key值
if((key = ftok(MSG_FILE,'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 打印key值
printf("Message Queue - Server key is: %d.\n", key);
// 创建消息队列
if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
{
perror("msgget error");
exit(1);
}
// 打印消息队列ID及进程ID
printf("My msqid is: %d.\n", msqid);
printf("My pid is: %d.\n", getpid());
// 循环读取消息
for(;;)
{
msgrcv(msqid, &msg, 256, 888, 0);// 返回类型为888的第一个消息
printf("Server: receive msg.mtext is: %s.\n", msg.mtext);
printf("Server: receive msg.mtype is: %d.\n", msg.mtype);
msg.mtype = 999; // 客户端接收的消息类型
sprintf(msg.mtext, "hello, I'm server %d", getpid());
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
}
return 0;
}
#include
#include
#include
// 用于创建一个唯一的key
#define MSG_FILE "/etc/passwd"
// 消息结构
struct msg_form {
long mtype;
char mtext[256];
};
int main()
{
int msqid;
key_t key;
struct msg_form msg;
// 获取key值
if ((key = ftok(MSG_FILE, 'z')) < 0)
{
perror("ftok error");
exit(1);
}
// 打印key值
printf("Message Queue - Client key is: %d.\n", key);
// 打开消息队列
if ((msqid = msgget(key, IPC_CREAT|0777)) == -1)
{
perror("msgget error");
exit(1);
}
// 打印消息队列ID及进程ID
printf("My msqid is: %d.\n", msqid);
printf("My pid is: %d.\n", getpid());
// 添加消息,类型为888
msg.mtype = 888;
sprintf(msg.mtext, "hello, I'm client %d", getpid());
msgsnd(msqid, &msg, sizeof(msg.mtext), 0);
// 读取类型为777的消息
msgrcv(msqid, &msg, 256, 999, 0);
printf("Client: receive msg.mtext is: %s.\n", msg.mtext);
printf("Client: receive msg.mtype is: %d.\n", msg.mtype);
return 0;
}
信号量
信号量是利用PV原语来实现进程同步
共享内存
效率最高,多个进程去对某一个内存进行访问
信号
信号:作为一种软中断,一个进程可以发出信号给操作系统,操作系统然后去通知另一个进程,然后该进程中断一下去执行
kill()函数主要用于向指定的进程或进程组发送信号
image.png
线程同步
线程同步:多个线程在竞争过程中,能保证保证代码顺序执行
image.png
1:互斥锁
pthread_mutex_lock就是上锁的函数,pthread_mutex_unlock就是解锁的函数
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
int gTotalTickerNum = 20;
void *sellTicket(void *arg) {
for (int i = 0; i < 20; i++) {
pthread_mutex_lock(&mutex);
if (gTotalTickerNum > 0) {
sleep(1);
printf("sell the %dth ticket\n", 20 - gTotalTickerNum + 1);
gTotalTickerNum--;
}
pthread_mutex_unlock(&mutex);
}
return nullptr;
}
在多线程同步中的用到的比较多
image.png
Mutex相当于一把钥匙
image.png
2:条件变量
和互斥锁一起用
3:读写锁
写锁独占,读锁共享
4:信号量
信号量类似一个容纳n个人的房间,限制同时访问的量
image.png
管程
为什么引入monitor?
信号量保证不了执行的顺序,也可能导致错误
将共享变量和操作原语封装在一个对象中,保证去顺序执行这些操作
image.png