目录
一,守护进程
2.创建守护进程的步骤:
二,线程-重点
1.概念
2.进程和线程的区别
3.线程资源
三,线程函数
1.线程创建
2.线程退出与线程回收
3.线程同步(信号量)
4.线程互斥与线程条件变量
1.线程互斥
2.线程条件变量
一,守护进程
1.特点:守护进程是后台进程,它的生命周期比较长,从系统启动时开启,在系统关闭时结束;它是脱离控制终端且周期执行的进程。
2.创建守护进程的步骤:
(1)使用fork函数,创建子进程,让父进程退出,使子进程成为孤儿进程(后台进程);
(2)使用setsid函数,在子进程中创建新会话,让子进程成为会话组组长,让子进程完全脱离终端;
(3)使用chdir函数,改变进程运行路径为根目录,因为守护进程的路径不能被删除或卸载;
(4)使用umask函数,重设文件权限掩码,目的是增大守护进程的权限;
(5)使用close函数关闭文件描述符,将不需要的文件关闭。
代码演示:
#include#include #include #include #include #include int main(int argc, char const *argv[]) { pid_t pid; if((pid = fork()) < 0) { perror("fork err"); return -1; }else if(pid == 0) { int i; setsid(); //在子进程中创建会话 chdir("/"); //改变当前路径为根目录 umask(0); //重设文件权限掩码 for(i = 0;i < 3;i++) { close(i); //关闭文件描述符 } //守护进程文件路径 int fd = open("/tmp/error.log",O_WRONLY|O_CREAT|O_APPEND,0777); while(1) { write(fd,"hello",5); sleep(1); } } else { exit(0); } return 0; }
执行程序后,可以在目录/temp下找到error.log文件。
二,线程-重点
1.概念
线程相当于一个轻量级的进程,可以提高系统的性能;在Linux系统中,同样使用结构体task_struct来描述一个线程;线程和进程都参加统一的调度。
在同一个进程中创建的线程共享该进程的地址空间。
2.进程和线程的区别
共同点:都为操作系统提供了并发执行的能力;
不同点:(1)线程是系统调度的最小单位;进程是资源分配的最小单位;
(2)同一个进程创建的多个线程共享进程的资源;进程的地址空间相互独立;
(3)线程通信相对简单,可通过全局变量实现,但是需要考虑临界资源访问的问题;进程通信比较复杂,需要借助进程间的通信机制(3G-4G的内核空间);
(4)线程安全性较差,当进程结束时会导致所有的线程退出,一个线程的结束可能会影响到其他线程;进程相对安全。
3.线程资源
共享的资源:可执行的指令,静态数据,进程中打开的文件描述符,信号处理函数,当前工作目录,用户ID,用户组ID。
私有的资源:线程ID(TID),PC(程序计数器)和相关寄存器,堆栈,错误码,信号掩码,优先级,执行状态和属性。
三,线程函数
1.线程创建
函数:int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void *),void *arg);
函数:int pthread_create(pthread_t *thread,const pthread_attr_t *attr,void*(*start_routine)(void *),void *arg);
功能:创建线程;
参数:thread->线程标识;
attr->线程属性,常设置为默认属性NULL;
start_toutine->函数名,代表线程函数,函数的参数应为void *arg;
arg->用来给线程函数传参;
返回值:成功->0;失败->错误码;
代码演示:
#include#include #include //线程函数 void *handler(void *arg) { printf("in threadn"); } int main(int argc, char const *argv[]) { //定义pthread_t类型的tid pthread_t tid; //判断,返回值不为0则创建失败 if(pthread_create(&tid,NULL,handler,NULL) != 0) { perror("thread create err"); return -1; } printf("in mainn"); //不一定会进入线程,所以使用循环保证线程运行 while(1); return 0; }
2.线程退出与线程回收
(1) 函数:int pthread_exit(void *value_ptr);
(1) 函数:int pthread_exit(void *value_ptr);
功能:执行线程退出的功能;
参数:value_ptr->线程退出时返回的值,一般设置为NULL;
返回值:成功->0;失败->错误码;
(2) 函数:int pthread_join(pthread_t thread,void **value_ptr);
功能:用于等待一个指定的线程结束;
参数:thread->创建的线程对象;
value_ptr->指针*value_ptr指向线程返回的参数,一般设置为NULL;
返回值:成功->0;失败->错误码。
代码演示:
#include#include #include void *handler(void *arg) { printf("in threadn"); //pthread_exit(NULL);//结束线程 pthread_exit("hello"); } int main(int argc, char const *argv[]) { pthread_t tid; if(pthread_create(&tid,NULL,handler,NULL) != 0) { perror("thread create err"); return -1; } printf("in mainn"); //接收线程返回的参数hello void *ch; pthread_join(th,&ch); printf("%sn",(char *)ch); //pthread_join(th,NULL);//回收线程资源 return 0; }
3.线程同步(信号量)
(1)概念: 同步(synchronization)指的是多个任务(线程)按照约定的顺序相互配合完成一件事情;
(2)信号量:通过信号量实现同步操作;由信号量来决定线程是继续运行还是阻塞等待;
信号量代表某一类资源,其值表示系统中该资源的数量;
信号量是一个受保护的变量,只能通过三种操作来访问:初始化、P操作(申请资源)、V操作(释放资源);
信号量的值为非负整数。
(3)函数接口
①int sem_init(sem_t *sem, int pshared, unsigned int value)
功能:初始化信号量;
参数:sem->初始化的信号量对象;
pshared->信号量共享的范围 (0: 线程间使用 非0:1进程间使用);
value->信号量初值;
返回值:成功为0,失败为-1;
sem_t sem;
if(sem_init(&sem,0,0) < 0)
{
perror("sem init err");
return -1;
}
②int sem_wait(sem_t *sem)
功能:申请资源,P操作;
参数:sem->信号量对象;
返回值:成功为0,失败为-1;
注:此函数执行过程,执行到wait,先对信号量进行减1,若减完的信号量的值大于等于0,表示有资源可以用,则继续执行;当信号量的值小于0时,表示没有资源可以使用,函数阻塞。
③int sem_post(sem_t *sem)
功能:释放资源,V操作;
参数:sem->信号量对象;
返回值:成功为0,失败为-1;
代码演示:
#include#include #include #include #include char ch[32] = ""; sem_t sem; void *handler(void *arg) { while(1) { //给信号量 -1 sem_wait(&sem); if((strcmp(ch,"quit") == 0)) { pthread_exit(NULL); } printf("threadn"); printf("output:%sn",(char *)arg); } } int main(int argc, char const *argv[]) { //创建线程 pthread_t tid; //给线程函数传递参数ch,先强转为void *类型 if(pthread_create(&tid,NULL,handler,(void *)&ch) != 0) { perror("thread create err"); return -1; } //初始化信号量(信号量对象,0->线程间使用,信号量初值) if(sem_init(&sem,0,0) < 0) { perror("sem init err"); return -1; } while(1) { scanf("%s",ch); //信号量+1 sem_post(&sem); if(strcmp(ch,"quit")==0) break; } pthread_join(tid,NULL); printf("mainn"); return 0; }
执行结果:输入一串字符串后,信号量先增加1,如果字符串不是“quit”,则线程中的wait判断信号量不为0,字符串也不为“quit”,就输出字符串,然后如果线程继续循环,wait检测到信号量为0时,将会继续运行主进程的循环,继续输入字符串,信号量再+1。
4.线程互斥与线程条件变量
1.线程互斥
1.相关概念
(1)临界资源:一次仅允许一个进程所使用的资源;
(2)临界区:指的是一个访问共享资源的程序片段;
(3)互斥:多个线程在访问临界资源时,同一时间只能被一个线程访问;
(4)互斥锁:通过互斥锁可以实现互斥机制,使用互斥锁的目的是用来保护临界资源,每个临界资源都应该由一个互斥锁来保护,线程必须先获得互斥锁才能访问临界资源,访问完资源后释放该锁。如果无法获得锁,线程会阻塞直到获得锁为止。
2.函数接口
(1)int pthread_mutex_init(pthread_mutex_t *mutex, pthread_mutexattr_t *attr)
功能:初始化互斥锁;
参数:mutex ->互斥锁,用pthread_mutex_t来定义该参数;
pthread_mutex_t lock;
attr ->互斥锁属性 ,一般设置为NULL;
返回值:成功为0,失败为-1;
(2)int pthread_mutex_lock(pthread_mutex_t *mutex)
功能:申请互斥锁;
参数:mutex->互斥锁;
//初始化锁
pthread_nutex_t lock;
if(pthread_mutex_init(&lock,NULL)<0)
{
perror("mutex init err");
return -1;
}
//上锁
pthread_mutex_lock(&lock);
返回值:成功为0,失败为-1;
注:pthread_mutex_lock和pthread_mutex_trylock区别:pthread_mutex_lock申请不到锁是会阻塞函数的;pthread_mutex_trylock不阻塞,如果申请不到锁会立刻返回。
(3)int pthread_mutex_unlock(pthread_mutex_t *mutex)
功能:释放互斥锁;
参数:mutex->互斥锁;
//初始化锁
pthread_nutex_t lock;
if(pthread_mutex_init(&lock,NULL)<0)
{
perror("mutex init err");
return -1;
}
//上锁
pthread_mutex_lock(&lock);
//解锁
pthread_mutex_unlock(&lock);
返回值:成功为0,失败为-1。
(4)int pthread_mutex_destroy(pthread_mutex_t *mutex)
功能:销毁互斥锁;
参数:mutex->互斥锁;
代码演示:
#include#include #include //互斥锁参数 pthread_mutex_t lock; int a[10] = {0,1,2,3,4,5,6,7,8,9}; int i; //线程函数 void *handler_swap(void *arg) { int temp; while(1) { //上锁 pthread_mutex_lock(&lock); for(i = 0;i < 5;i++) { temp = a[i]; a[i] = a[9-i]; a[9-i] = temp; } //解锁 pthread_mutex_unlock(&lock); } } //线程函数 void *handler_printf(void *arg) { while(1) { //上锁 pthread_mutex_lock(&lock); for(i = 0;i < 9;i++) { printf("%d ",a[i]); } putchar(10); //解锁 pthread_mutex_unlock(&lock); sleep(1); } } int main(int argc, const char *argv[]) { //创建两个线程 pthread_t tid1,tid2; if(pthread_create(&tid1,NULL,handler_swap,NULL)!=0) { perror("thread create err"); return -1; } if(pthread_create(&tid2,NULL,handler_printf,NULL)!=0) { perror("thread create err"); return -1; } //初始化锁 if(pthread_mutex_init(&lock,NULL)<0) { perror("mutex init err"); return -1; } //回收线程资源 pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
执行结果为:在终端中循环正序输出0到9,或9到0,不能确定输出的次数,但是不会打乱一次输出中的顺序。
2.线程条件变量
条件变量需要与
互斥锁搭配使用,实现
同步机制;
(1)int
pthread_cond_init
(pthread_cond_t *restrict cond,const pthread_condattr_t *restrict attr);
功能:
初始化
条件变量;
参数:cond->是一个指向结构pthread_cond_t
的指针;
pthread_cond_t cond;
pthread_cond_init(&cond,NULL);
restrict attr->是一个指向结构pthread_condattr_t的指针,一般设为NULL;
返回值:成功为0,失败为非0。
初始化时,通过判断返回值是否小于0,来确定是否初始化成功,小于0则初始化失败
功能: 等待信号 的产生; 参数:restrict cond->要等待的条件; restrict mutex->对应的锁; 返回值:成功为0 ,失败不为0。 注:当没有条件产生时->函数阻塞,锁解开; 等待到条件产生->函数结束阻塞,同时上锁。(2)int pthread_cond_wait(pthread_cond_t *restrict cond,pthread_mutex_t *restrict mutex);
pthread_cond_wait(&cond,&lock);
功能:给条件变量发送信号, 产生信号; 参数:cond->条件变量值;(3)int pthread_cond_signal(pthread_cond_t *cond);
pthread_cond_signal(&cond);
返回值:成功为0,失败为非0;
注:必须等待pthread_cond_wait函数先执行,再产生条件信号。
此函数和pthread_cond_broadcast区别是:
pthread_cond_broadcast函数相当于是广播,会将所有等待此条件的线程唤;
pthread_cond_signal只能唤醒单个等待此条件的线程。
(4)int pthread_cond_destroy (pthread_cond_t *cond);功能:将条件变量销毁; 参数:cond->条件变量值; 返回值:成功为 0 ,失败为非0; 代码演示:
#include#include #include //函数pthread_mutex_init()中的参数 pthread_mutex_t lock; //函数pthread_cond_init()中的参数 pthread_cond_t cond; int money = 0; void *handler_get(void *arg) { while(1) { pthread_mutex_lock(&lock);//上锁 if(money < 100) { //当没有条件cond产生时阻塞,同时将锁打开; //等待到条件产生时,函数立即返回同时进行上锁; pthread_cond_wait(&cond,&lock); } if(money >= 100) { money -= 100; printf("money:%dn",money); } pthread_mutex_unlock(&lock);//解锁 } } void *handler_save(void *arg) { while(1) { sleep(1);//睡眠一秒确保get线程先执行 pthread_mutex_lock(&lock);//上锁 scanf("%d",&money); pthread_mutex_unlock(&lock);//解锁 pthread_cond_signal(&cond);//产生cond条件(信号); } } int main(int argc, char const *argv[]) { pthread_t tid1,tid2; //创建线程:tid->pthread_t,NULL,线程函数名,函数参数 if(pthread_create(&tid1,NULL,handler_get,NULL)!=0) { perror("create tid1 err"); return -1; } if(pthread_create(&tid2,NULL,handler_save,NULL)!=0) { perror("create tid2 err"); return -1; } //初始化锁 //参数:取地址-定义的pthread_mutex_t lock,状态NULL //返回值小于0时,创建失败 if(pthread_mutex_init(&lock,NULL) < 0) { perror("mutex init err"); return -1; } //初始化条件变量 //参数:取地址-定义的pthread_cond_t cond,状态NULL //返回值小于0时,创建失败 if(pthread_cond_init(&cond,NULL) < 0) { perror("cond init err"); return -1; } //回收线程资源 pthread_join(tid1,NULL); pthread_join(tid2,NULL); return 0; }
执行结果:
如果本文中存在概念错误或代码错误的情况,请批评指正。


