【1】守护进程
什么是守护进程?
在后台做服务的一个程序。有一定的生命周期,从系统启动的时候开始运行,到系统结束的时候终止,并且守护进程不受终端的控制,要想从实际上摆脱终端的控制,必须脱离会话组组长和进程组组长,然后让自己成为会话组的组长和进程族的组长。
(2)创建守护进程的步骤:
a、创建父子进程,结束父进程,让子进程继续运行
fork exit
b、要让你的后台进程完全的摆脱终端的控制
setsid()
功能: 创建新会话,并且成为会话组组长和进程组组长,完全的摆脱终端的控制
c、改变当前目录为根目录 chdir()
d、修改文件权限掩码 uamsk()
e、关闭打开的文件描述符,节省资源
int i;
for(i = 0; i < getdtablesize(); i++)
{
close(i)
}
while(1)
{
服务操作
}
【2】线程
因为线程间切需要对系统分配的资源,进行保护工作(保存task_struct结构体),
而不需要保存地址空间,因为线程是在进程当中创建,共享进程的地址空间,而进程不一样
进程切换切换时,需要保存更多的资源,(保存task_struct结构体和地址空间当中的数据),
进行上下文切换时所需要的时间较多,采用多任务进行处理时一般现在采用线程的方式。
让线程共享进程的地址空间。
线程跟进程的关系:
线程依附于进程,当一个进程结束的时候,线程也就结束了。
线程跟线程实现通信:
线程的实现: 在编程中使用一个函数来实现,但是这个必须通过通过创建线程时,才能获得使用
使用全局变量实现线程间通信,可以基本数据类型也可以是构造数据类型
操作:
1、创建线程 pthread_create
2、对线程资源的回收 pthread_join
3、结束线程 pthread_exit
在使用线程,由多个线程都可以访问共享资源(全局变量),需要对
共享资源做保护工作,操作系统提供对共享资源保护的一些机制,需要
用户自己去调用,(信号量、互斥锁、条件变量),通过这些方式就保护好了
共享资源
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
功能: 创建一个线程,开始启动线程从start_routine启动
参数:
pthread 创建好线程纸偶,会返回一个线程标识符,唯一的表示一个线程
attr 包含是线程的属性,而线程的实现用函数,所有说回味每个函数开辟一个占空间
存放线程(当中变来那个的内容等),设置空间大小默认为8M
平常采用默认属性,NULL
start_routine 函数指针,用来指向一个函数,函数的类型是 void *pthread_fun(void * arg)
arg 是一个变量,这个变量的值,传递给了start_routine的形参
返回值:
成功 0
失败 错误码
【注意】: 在编译和链接式需要加 -pthread
pthread_exit(char *)
功能: 结束线程,并且会传递形参的值给pthread_join函数的第二个参数
pthread_join(pthread_t tid, void ** ret)
功能:阻塞等待一个线程tid的退出,并且会清理线程所占用的task_struct结构体,由pthread_exit函数的形参传递给了ret变量,需要实现定义一个void *,然后对指针变量取地址,在把指针变量转换成确定的数据类型访问数据。
对于线程同时访问共享资源时,会出现各种各样的错误,我们在使用线程或者进程写程序时,一定要注意对共享资源的保护工作。
【3】同步机制
(1)信号量
(2)互斥锁
(3)条件变量
采用上面的三种方式实现线程对共享资源访问的同步与互斥
直接制约:一组在异步环境下并发的进程,各自的执行结果互为对方执行的条件,从而限制各并发进程执行速度的过程,成为直接制约。
同步:异步环境下一组并发进程,因直接制约互相发送消息而进行互相合作,互相等待,使各进程按照一定速度执行成为同步。(把同一个事情拆分几件事情,让他们按照约定的先后顺序共同完成一个任务)
使用信号量来实现同步,他们都是抽象的概念,需要把他们假定为一种资源
是一种资源,就有一定的数量,然后通过申请和释放的方式来达到同步或者互斥的目的
共享资源: 可以被一个(多个)进程或者线程共同访问的资源
临界资源: 在某个时刻只允许一个进程或者线程访问的资源称作临界资源
临界区: 访问临界资源的那段代码称作临界区
使用信号量:
P(申请操作)
进入临界区
V(释放操作)
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化信号量
参数:
sem:信号量
pshared:0表示信号量只在本进程的线程间有效;非0表示信号量在进程之间共享,此时必须将信号量放在共享内存区域
value:信号量的初始值
返回值:成功返回0;失败返回-1,同时perror被自动设置
int sem_wait(sem_t *sem);
功能:申请信号量,如果信号量为0则阻塞
参数:
sem:信号量指针
返回值:成功返回0;失败返回-1,同时perror被自动设置
int sem_post(sem_t *sem);
功能:释放信号量
参数:
sem:信号量指针
返回值:成功返回0;失败返回-1,同时perror被自动设置
【4】互斥机制
间接制约:由于共享某一共有资源而引起的在临界区内不允许并发进程交叉执行的现象,称为由共享共有资源造成的对并发进程执行速度的间接制约,简称间接制约。
互斥:一组并发进程中的一个或者多个程序段,因为共享共有资源而导致它们必须以一个不允许交叉执行的单位执行,即不允许一个以上的共享该资源的并发进程同时进入临界区称为互斥。
使用互斥锁:
如果无法找到相关文档,执行以下命令
sudo apt-get install glibc-doc
sudo apt-get install manpages-posix-dev
lock(加锁操作)
进入临界区
unlock(解锁操作)
互斥锁是操作系统中提供的一种互斥机制,在创建好一把锁之后,线程1申请到锁,会执行临界区中的代码,而线程2或者其他线程想要申请说的时候,会处于阻塞状态,直到线程1释放锁之后,其他的线程才可以申请锁。
【注意】互斥锁只是实现了cpu的独占使用,也就是一个线程在申请到所之后,其他的线程不能在申请到锁。由于cpu的调度策略,可能一个线程被调度多次。
int pthread_mutex_destroy(pthread_mutex_t *mutex);
功能: 对初始化好的互斥锁,进行毁坏操作,也就是把互斥锁的相关信息清除掉
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
功能:初始化互斥量
参数:
mutex:互斥量
mutexattr:用于初始化mutex的属性信息,如果为NULL,则使用默认属性
返回值:总是返回0
int pthread_mutex_lock(pthread_mutex_t *mutex);
功能:给一个mutex加锁,如果mutex被其它线程锁定,则阻塞
参数:
mutex:互斥量
返回值:成功返回0;失败返回非0值,同时perror被自动设置
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能:给一个mutex解锁
参数:
mutex:互斥量
返回值:成功返回0;失败返回非0值,同时perror被自动设置
int pthread_mutex_unlock(pthread_mutex_t *mutex);
功能: 释放申请到的互斥锁,释放之后,可以被其他的线程申请,但是这个申请,是由操作系统的调度策略决定
【5】条件变量
条件变量内部是一个等待队列,等待队列里边存放的是等待的线程,在线程等待函数里面指定等待哪一个线程。使用了互斥锁锁定的线程,然后条件变量通过信号通知的方式通知阻塞的线程(使用了互斥锁的线程)
需要一个线程给另外一个线程发送信号,如果接收到信号,阻塞的线程会解除阻塞,从阻塞位置继续往下运行。
int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
pthread_cond_wait在执行的时候会先unlock mutex,然后再等待条件变量成立,因此当wait函数执行下去后,一定要重新尝试lock mutex,从而保证没有其它进程也进入临界区。