目录
一,线程的相关知识
1,线程的概念
每个进程都是独⽴的私有的空间,是⼀个独⽴的任务,虽然可能多个进程相 互之间需要协同⼯作,但需要系统⾃⼰的对进程间进行切换,因此在进程的上下⽂切换时,系统开销⽐较⼤ 为了提高系统的性能,在操作系统中引⼊了轻量级的进程,也叫做线程。
线程包含在进程内,每条线程都是进程中单一顺序的控制流,一个进程可以并发多条线程,且每个线程执行不同的任务。线程之间的资源是共享的。
2,多线程的概念
在一个程序中,存在许多费时的操作,如果使用单线程,程序只能顺序执行,只有等待当前操作完成后才能执行下一操作,造成资源浪费,影响执行效率,使用多线程就可以将耗时的操作任务放在后台工作,且同时执行其他操作,从而提高工作效率。
多线程就是指一个进程中同时有多个线程正在执行。
二,线程的操作
1,线程的创建
#include <pthread.h>
int pthread_create(pthread_t *thread, const pthread_attr_t*attr,void *(*start_routine) (void *),void *arg);
/*
返回值
成功:返回0;
失败:返回错误码
*/
/*
参数1:
pthread_t *thread:指针,地址,(存储线程id)创建线程后会把线程id存储到这个地址中。
参数2:
const pthread_attr_t *attr:要执⾏的线程的属性,传递的是属性变量值的地址,通常 写 NULL 表⽰使⽤默认属性创建线程。
参数3:
void *(*start_routine) (void *):函数指针,函数地址,线程执⾏的起始函数,线程从哪个函数开始执⾏
参数4:
void *arg:提供给参数3这个函数的参数。
*/
2,线程的关闭
结束当前线程,同时把线程结束状态,返回给创建当前线程的进程或线程 中。
#include <pthread.h>
void pthread_exit(void *retval);
/*
参数:
void *retval:指针,地址,作为线程的结束状态,返回给创建的线程中。
*/
3,线程的等待
等待线程结束,并接收线程结束的状态。
#include <pthread.h>
int pthread_join(pthread_t thread, void **retval);
/*
返回值:
成功:0;
失败:错误码;
*/
/*
参数1:
pthread_t thread:要等待结束的线程id
参数2:
void **retval:⼆级指针,⼀级指针的地址,把线程结束状态(⼀级指针),存储到这个地址中。
*/
4,线程的取消
取消指定线程的执行。
#include <pthread.h>
int pthread_cancel(pthread_t thread);
/*
参数:
pthread_t thread:线程的编号。
*/
三,线程间的同步互斥
1,基本概念
互斥:在同一时刻,有且只有一个任务访问一个资源
同步:在互斥的基础上,访问有着一定的先后顺序,由信号量完成。
信号量:一个非负整数,代表着一类资源。被用来控制对公共资源的访问。编程时可根据操作信号量值的结果判断是否对公共资源具有访问的权限,当信号量值大于0时,则可以访问,否则将阻塞。PV操作即对信号量的操作,-次P操作使信号量减1, - -次V操作使信号量加1。
注:当信号量用于同步时,需要多个信号量同时工作,而用于互斥时只需一个信号量即可。
P操作:(申请资源)只有当信号量大于0时,任务才能进行,否则,任务阻塞。
v操作:(释放资源)
2,(同步)信号量允许的三个操作
2.1,初始化
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
/*
参数1:
sem_t *sem:地址,信号量的地址,把初始化的值存⼊这个变量地址中
参数2:
int pshared:信号量使⽤的范围 0:表⽰在线程间使⽤;⾮0:表⽰在进程间使⽤。
参数3:
unsigned int value:信号量初始化的值
*/
/*
返回值:
成功:返回0
失败:返回-1
*/
2.2,p操作
资源信号量 减减,但sem==0就不能继续 减减 ⽽是阻塞等待
#include <semaphore.h>
int sem_wait(sem_t *sem);
2.3,v操作
#include <semaphore.h>
int sem_post(sem_t *sem);
举例:
#include<stdio.h>
#include<pthread.h>
#include<stdlib.h>
#include<unistd.h>
#include<semaphore.h>
int num;
sem_t rw;
void * thread_write(void * arg) //写
{
int i = *(int *)arg;
while(i--)
{
num = i;
sem_post(&rw); //p操作
sleep(2);
}
return NULL;
}
void * thread_read(void * arg) //读
{
while(1)
{
sem_wait(&rw); //v操作
printf("num==%d\n",num);
}
return NULL;
}
int main()
{
pthread_t tid1,tid2;
sem_init(&rw,0,5); //初始化
int n=10;
pthread_create(&tid1,NULL,thread_write,&n);
pthread_create(&tid2,NULL,thread_read,NULL);
exit(0);
}
3,互斥
互斥锁:就是每个线程在⾃⼰线程中访问资源,当能够获取到锁(加锁)时就访 问,访问完就释放锁(解锁)。当线程⽆法获得锁时,就阻塞等待直到获得锁为 ⽌。
注:加锁后一定要记得解锁。
3.1,互斥锁的初始化
初始化互斥锁,设置互斥锁变量
#include <pthread.h>
int pthread_mutex_init(pthread_mutex_t * mutex,constpthread_mutexattr_t * attr);
/*
参数1:
pthread_mutex_t * mutex:互斥锁变量地址,要初始化的互斥锁变量
参数2:
pthread_mutexattr_t * attr:互斥锁的属性,NULL表⽰默认属性
返回值:
成功:返回0
失败:返回-1
*/
3.2,互斥锁的加锁
#include <pthread.h>
int pthread_mutex_lock(pthread_mutex_t *mutex);
3.3,互斥锁的解锁
#include <pthread.h>
int pthread_mutex_unlock(pthread_mutex_t *mutex);
四,进程间的通信
1,基本概念
1.1,管道
管道是一种半双工的通信方式,数据只能单向流动,进程之间的关系相当于父子进程之间的关系。无名管道(pipe)只能在具有亲属关系之间的进程使用,有名管道(FIFO)可以在无亲属关系的进程之间进行使用。在使用中通常指无名管道。
1.2,信号
信号,一种比较复杂的通信方式,用于通知接收进程某个事件已经发生。
1.3,共享内存
共享内存就是映射一段能被其他进程所访问的内存,这段共享内存由一个进程创建,但多个进程都可以访问。
1.4,消息队列
消息队列,就是一个消息的链表,是一系列保存在内核中消息的列表。用户进程可以向消息队列添加消息,也可以向消息队列读取消息。
1.5,信号灯
信号灯又称信号量。用于实现进程间的互斥与同步。
2,管道的操作
2.1,有名管道的创建
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
/*
返回值:
成功:0;
失败:-1;
*/
/*
参数1:
const char *pathname:创建的管道⽂件的路径
参数2:
mode_t mode:创建管道⽂件的权限
*/
2.2,无名管道的创建
创建打开⽆名管道,同时获取到打开的⽆名管道的⽂件描述符 。
#include <unistd.h>
int pipe(int pipefd[2]);
/*
参数1:
int pipefd[2]:存储⽂件描述符
fd[0]----read打开的⽂件描述符
fd[1]----write打开的⽂件描述符
*/
3,信号的操作
3.1,信号的发送
给指定的进程发送指定的信号
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
/*
返回值:
成功:返回0
失败:返回-1
*/
/*
参数1:
pid_t pid:发送给指定pid的进程
参数2:
int sig:发送的信号
*/
3.2,信号的捕获
进程对信号的响应:
忽略:对信号不做任何处理
缺省(默认):对信号做默认操作
捕获:定义信号处理函数,当产⽣信号后,使⽤对应的函数进⾏处理
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler);
3.3,定时发送信号alarm
在指定的时间后,给当前进程发送SIGALRM信号
#include <unistd.h>
unsigned int alarm(unsigned int seconds);
3.4,阻塞等待信号的接收pause
#include <unistd.h>
int pause(void);
4,共享内存的操作
4.1,创建共享内存
创建或者打开指定的共享内存,存在就打开,不存在就创建
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
/*
返回值:
成功:返回共享内存id
失败:返回-1
*/
/*
参数1:
key_t key:要创建或要打开的内容内存编号
参数2:
size_t size:要创建的共享内存⼤⼩(字节)
参数3:
int shmflg:选项
0666:权限
IPC_CREAT:不存在就创建
*/
4.2,映射共享内存
即把共享内存的地址映射到进程中,可以进⾏使⽤.
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
/*
返回值:
成功:返回映射的地址,操作这个地址对应的空间就是操作共享内存
失败:返回-1
*/
/*
参数1:
int shmid:共享内存id,表⽰要进⾏映射的共享内存
参数2:
const void *shmaddr:指针,地址,表⽰共享内存要映射到进程中的哪个地址位置
NULL:由系统随机映射⼀个地址
参数3:
int shmflg:操作共享内存的⽅式
SHM_RDONLY:只读
0:读写
*/
4.3,解除映射共享内存
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
/*
返回值:
成功:返回0
失败:返回-1
*/
/*
参数1:
const void *shmaddr:要解除映射的内存地址
*/
4.4,关闭共享内存
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
/*
返回值:
成功:返回0;
失败:返回-1
*/
/*
参数1:
int shmid:要操作的共享内存id
参数2:
int cmd:控制⽅式
IPC_STAT:查看获取shmid共享内存的信息
IPC_SET:设置shmid共享内存的信息
IPC_RMID:删除共享内存,第三个参数为NULL
参数3:
struct shmid_ds *buf:结构体指针如果是IPC_STAT,表⽰把shmid信息存储到这个地址中如果是IPC_SET,表⽰把这个地址中的(结构体)信息作为shmid的信息进⾏设置.
*/
5,消息队列的操作
消息队列就是⼀种消息的列表,进程间可以通过消息队列,往消息队列中添 加消息,读取消息
5.1,创建消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
/*
返回值:
成功:返回消息队列的Id
失败:返回-1
*/
/*
参数1:
key_t key:key编号,消息队列的编号
参数2:
int msgflg:选项
*/
5.2,添加 / 读取 消息队列
添加:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
/*
返回值:
成功:返回0;
失败:返回-1;
*/
/*
参数1:
int msqid:消息队列id,操作的消息队列是谁
参数2:
const void *msgp:整个消息数据的⾸地址 包含:类型和正⽂数据
参数3:
size_t msgsz:消息正⽂⼤⼩
参数4:
int msgflg:选项
0:阻塞等待,直到发送完(往消息队列中添加消息完)这个函数才结束
IPC_NOWAIT:消息还没发送完成(往消息队列中添加消息还未完成)就结束这个函数
*/
读取:
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
/*
返回值:
成功:返回获取到的文本大小
失败:返回-1
*/
/*
参数1:
int msqid:要获取的消息队列id
参数2:
void *msgp:获取的消息存放地址
参数3:
size_t msgsz:消息的正⽂⼤⼩
参数4:
long msgtyp:要获取的消息的类型
0:任意消息类型都可以获取
>0:从消息队列中获取第⼀个为对应类型的消息
参数5:
int msgflg:选项
0:阻塞等待,直到接收完(从消息队列中获取消息完)这个函数才结束
IPC_NOWAIT:消息还没接收完成(从消息队列中获取消息还未完成)就结束这个函数
*/
5.3,删除消息队列
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct msqid_ds *buf);
/*
返回值:
成功:返回0;
失败:返回-1;
*/
/*
参数1:
int msqid:消息队列id
参数2:
int cmd:控制⽅式
IPC_STAT:获取消息队列信息
IPC_SET:设置消息队列信息
IPC_RMID:删除消息队列
参数3:
struct msqid_ds *buf:结构体地址,⽤于获取消息队列信息(把信息存储到这个地址);⽤于设置消息队列信息(把地址对应空间信息设置到消息队列)
*/
6,信号灯的操作
在进程间使⽤⼀个特定的信号量值,达到进程间的同步互斥 信号量的操作
6.1,信号量的创建
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
/*
返回值:
成功:返回smid;
失败:返回-1;
*/
/*
参数1:
key_t key:key编号
参数2:
int nsems:信号灯集中信号量的数⽬
参数3:
int semflg:选项,权限
IPC_CREAT:创建
0666:权限
*/
6.2,信号量的操作
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
/*
返回值:
成功:返回0;
失败:返回-1;
*/
/*
参数1:
int semid:要操作的信号灯
参数2:
struct sembuf *sops:
具体的操作⽅式:
struct sembuf sops
{
unsigned short sem_num;//要操作信号量的编号
short sem_op;//操作
0:等待信号量的值变为0
-1:p操作,消费,把信号量值-1
1:v操作,⽣产,把信号量值+1
short sem_flg;//选项
0:阻塞等待
IPC_NOWAIT:⾮阻塞,不等待
}
参数3:
size_t nsops:要操作的信号灯的个数
*/
6.3,信号量的控制
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
/*
返回值:
成功:根据不同的CMD返回不同的值;
失败:返回-1;
*/
/*
参数1:
int semid:控制的信号量id
参数2:
int semnum:要控制的是信号灯集中的哪个信号量编号
参数3:
int cmd:控制⽅式
IPC_RMID:删除信号灯集,同时第⼆个和第四个参数不起作⽤
GETVAL:获取指定编号的信号量的值
SETVAL:设置指定编号的信号量的值
参数4:
根据参数3决定
*/