在学习Linux系统编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。
09-linux-day08(守护进程-线程)
目录:一、学习目标二、守护进程1、守护进程相关的概念2、守护进程创建3、守护进程-扩展了解三、线程1、线程有关的概念2、线程的优点和缺点3、创建一个线程4、线程的退出5、线程的回收6、杀死线程7、线程分离8、线程属性、设置分离9、线程注意事项10、线程同步的概念11、mutex相关的函数
一、学习目标
1、守护进程的特点
2、熟练进行守护进程的创建
3、熟练掌握多线程的创建
4、熟练掌握线程的退出和资源回收
二、守护进程
1、守护进程相关的概念
会话:进程组的更高一级,多个进程组对应一个会话。
(ps ajx | more可以查看会话)
进程组:多个进程在同一个组,第一个进程默认是进程组的组长。
创建会话的时候,组织不可以创建,必须是组员创建。
创建会话的步骤:创建子进程,父进程死去,子进程自当会长。
》setsid——调用后就成为会长
man 2 setsid
pid_t setsid(void);
(例如:如果按照了mysql,可以通过ps aus | grep mysqld查看mysql的守护进程)
前2步骤是必须的!
2、守护进程创建
》需求:创建一个守护进程:每分钟在$HOME/log/ 创建一个文件,程序名.时间戳
>touch daemon.c
>vi daemon.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<fcntl.h>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/stat.h>
7 #include<string.h>
8 #include<signal.h>
9 #include<sys/time.h>
10 #include<time.h>
11
12 #define _FILE_NAME_FORMAT_ "%s/log/mydaemon.%ld" //定义文件格式化
13
14 void touchfile(int num){
15 char *HomeDir = getenv("HOME");
16 char strFilename[256]={0};
17
18 sprintf(strFilename,_FILE_NAME_FORMAT_,HomeDir,time(NULL));
19
20 int fd = open(strFilename,O_RDWR|O_CREAT,0666);
21
22 if(fd < 0){
23 perror("open err");
24 exit(1);
25 }
26 close(fd);
27 }
28
29
30 int main(int argc, char *argv[])
31 {
32 //创建子进程,父进程退出
33 pid_t pid = fork();
34 if(pid > 0){
35 exit(1);
36 }
37 //当会长
38 setsid();
39 //设置掩码
40 umask(0);
41 //切换目录
42 chdir(getenv("HOME"));//切换到家目录
43 //关闭文件描述符
44 //close(0),close(1),close(2);//方便调试,不关
45 //执行核心逻辑
46 //struct itimerval myit = {{60,0},{60,0}};
47 struct itimerval myit = {{60,0},{1,0}};//方便调试,初始改为1s
48 setitimer(ITIMER_REAL,&myit,NULL);
49 struct sigaction act;
50 act.sa_flags = 0;
51 sigemptyset(&act.sa_mask);
52 act.sa_handler = touchfile;
53
54 sigaction(SIGALRM,&act,NULL);
55
56 while(1){
57 //每隔1分钟在/home/wang/log下创建文件
58 sleep(1);
59 //do sth
60 }
61 //退出
62
63 return 0;
64 }
>gcc daemon.c
>./a.out
(打开另一个终端,ps aux查看a.out为守护进程,然后在原终端点叉子关闭a.out,在后打开的终端查看a.out仍然存在。,另外要进入log文件夹下查看目录是否仍然在目录。)
3、守护进程-扩展了解
扩展了解:
通过nohup指令也可以达到守护进程创建的效果。
用法:nohup cmd [ > 1.log ] &
nohup指令会让cmd收不到SIGHUP信号(屏蔽)
& 代表后台运行
>touch daemon1.c
>vi daemon1.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<fcntl.h>
4 #include<stdlib.h>
5 #include<string.h>
6 #include<sys/types.h>
7 #include<time.h>
8
9
10 int main(int argc, char *argv[])
11 {
12 char strfileName[256] = {0};
13
14 while(1){
15 memset(strFileName,0x00,sizeof(strFileName));
16 sprintf(strFileName,"%s/log/zhen2.%ld",getenv("HOME"),time(NULL));
17 int fd = open(strFileName,O_RDONLY|O_CREAT,0664);
18 if(fd < 0){
19 perror("open err");
20 exit(1);
21 }
22 close(fd);
23 sleep(5);
24 }
25
26 return 0;
27 }
>gcc daemon1.c
>./a.out
>nohup ./a.out &
(显示:[1] 3451 忽略输入并把输出追加到'nohup.out';打开另一个终端输入ps aux查看,然后再终端点击叉子关闭,后打开的进程输入ps aux发现a.out仍然活着,另外要进入log文件夹下查看目录是否仍然在创建目录。)
三、线程
1、线程有关的概念
》线程man page 安装
sudo apt-get install manpages-posix-dev
线程的概念:轻量级的进程,一个进程内部可以有多个线程,默认情况下一个进程只能有一个线程。
(pid查看ps aux,如:轻量级桌面管理系统的pid搜索:ps aux | grep lightdm)
2、线程的优点和缺点
》线程共享资源
1)文件描述符表
2)每种信号的处理方式
3)当前工作目录
4)用户ID和组ID
5)内存地址空间(.text/.data/.bss/heap/共享库)
》线程非共享资源
1)线程id
2)处理器现场和栈指针(内核栈)
3)独立的栈空间(用户空间栈)
4)errno 变量(可以查看errno.h -> /usr/include/x86_64-linux-gnu/bits/errno.h)
(获得错误码对应的错误信息:char *strerror(int errnum);)
5)信号屏蔽字
6)调度优先级
》线程优、缺点
优点:1)提高程序并发性;2)开销小;3)数据通信、共享数据方便
缺点:1)库函数,不稳定;2)调试、编写困难;3)对信号支持不好
优点相对突出,缺点均不是硬伤。Linux 下由于实现方法导致进程、线程差别不是很大。
3、创建一个线程
man pthread_create
int pthread_create(pthread_t *thread, const pthread_attr_t *attr, void *(*start_routine)(void *), void *arg) ;
thread 线程id,传出参数
attr 代表线程的属性(大多数情况不用)
第三个参数:函数指针,void *func(void *)
arg 线程执行函数的参数
返回值:成功返回0;失败返回errno
注意:编译和链接需要加上-lpthread。
>man pthread_self
查看线程的pid
pthread_t pthread_self(void);
>touch pthread_create.c
>vi pthread_create.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4
5 void* thr(void *arg){
6 printf("I am a thread!pid=%d,tid=%lu\n",getpid(),pthread_self());
7 return NULL;
8 }
9
10 int main(int argc, char *argv[])
11 {
12 pthread_t tid;
13 pthread_create(&tid,NULL,thr,NULL);
14 printf("I am a main thread,pid=%d,tid=%lu\n",getpid(),pthread_self());
15 sleep(1);//如果不睡1秒,主线程执行return 0;主线程退出后,创建的其他线程不会执行
16 return 0;
17 }
>gcc pthread_create.c -lpthread
>./a.out
(可以对比进程理解:pthread_create相当于进程的fork;pthread_self相当于进程的getpid)
makefile解决?
在家目录~./bashrc文件末尾增加:
alias echomake='cat ~/bin/makefile.template >> makefile'
(makefile.template就是之前写的模板makefile)
1 ### create by wp 20200704
2
3 SrcFiles=$(wildcard *.c)
4 TargetFiles=$(patsubst %.c,%,$(SrcFiles))
5
6 all:$(TargetFiles)
7
8 %:%.c
9 gcc -o $@ $^ -g
10
11 clean:
12 rm -f $(TargetFiles)
>echomake (会在新开目录下增加makefile)
>vi makefile
1 ### create by wp 20200704
2
3 SrcFiles=$(wildcard *.c)
4 TargetFiles=$(patsubst %.c,%,$(SrcFiles))
5
6 all:$(TargetFiles)
7
8 %:%.c
9 gcc -o $@ $^ -lpthread -g
10
11 clean:
12 rm -f $(TargetFiles)
(小技巧:设置shell里vi的快捷键——set -o vi可以查看之前的某个相关的命令,如:输入set -o vi后输入/gcc可以查看之前输入的含有gcc 的命令)
4、线程的退出
解决:如果不睡1秒,主线程执行return 0;主线程退出后,创建的其他线程不会执行?
》pthread_exit——线程退出函数
man pthread_exit
void pthread_exit(void *retval);
>touch pthread_exit.c
>vi pthread_exit.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<stdlib.h>//exit
5
6 void* thr(void *arg){
7 printf("I am a thread!pid=%d,tid=%lu\n",getpid(),pthread_self());
8 return NULL;//线程退出用这个
9 //pthread_exit(NULL);//线程退出也可以更换为这个
10 //exit(1);//线程退出不能用这个,会导致整个进程都退出
11 }
12
13 int main(int argc, char *argv[])
14 {
15 pthread_t tid;
16 pthread_create(&tid,NULL,thr,NULL);
17 printf("I am a main thread,pid=%d,tid=%lu\n",getpid(),pthread_self());
18
19 sleep(10);
20 printf("I will out\n");
21 pthread_exit(NULL);
22 return 0;
23 }
>make
>./pthread_exit
线程退出注意事项:
在线程中使用pthread_exit
在线程中使用return(主控线程return 代表退出进程)
exit代表退出整个进程
5、线程的回收
》pthread_join——线程回收函数(阻塞等待回收)
int pthread_join(pthread_t thread, void **retval);
thread 创建的时候传出的第一个信息
retval 代表的传出线程的退出信息
>touch pthread_rtn.c
>vi pthread_rtn.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<stdlib.h>
5
6 void* thr(void *arg){
7 printf("I am a thread!tid=%lu\n",pthread_self());
8 sleep(5);
9 printf("I am a thread!tid=%lu\n",pthread_self());
10 return (void *)100;//pthread_exit((void*)101);//或者这种
11 }
12
13 int main(int argc, char *argv[])
14 {
15 pthread_t tid;
16 pthread_create(&tid,NULL,thr,NULL);
17 void *ret;
18 pthread_join(tid,&ret);//线程回收
19
20 printf("ret exit with %d\n",(int)ret);
21
22 pthread_exit(NULL);
23 return 0;
24 }
>make
>./pthread_rtn
(可知:线程回收也是阻塞等待回收的!)
6、杀死线程
man pthread_cancel
int pthread_cancel(pthread_t thread);
需要传入tid
返回值:失败返回-1,成功返回0
>touch pthread_cancel.c
>vi pthread_cancel.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<stdlib.h>
5
6 void* thr(void *arg){
7 while(1){
8 printf("I am a thread,very happy!tid=%lu\n",pthread_self());
9 sleep(1);
10 }
11 return NULL;
12 }
13
14 int main(int argc, char *argv[])
15 {
16 pthread_t tid;
17 pthread_create(&tid,NULL,thr,NULL);
18
19 sleep(5);
20 pthread_cancel(tid);//杀死线程
21 void *ret;
22 pthread_join(tid,&ret);//线程回收
23 printf("thread exit with %d\n",(int)ret);
24
25 pthread_exit(NULL);
26 return 0;
27 }
>make
>./pthread_cancel
注意:线程的取消并不是实时的,而是有一定的延时。需要等待线程到达某个取消点(检查点)。
类似于玩游戏存档,必须到达指定的场所(存档点:如:客栈、仓库、城里等)才能存储进度。杀死线程也不是立刻就能完成的,必须要到达取消点。
》取消点:是线程检查是否被取消,并按请求进行动作的一个位置。通常是一些系统调用creat,open,pause,close,read,write...执行命令 man 7 pthreads 可以查看具备这些取消点的系统调用列表。
可粗略认为一个系统调用(进入内核)即为一个取消点。如线程中没有取消点,可以通过调用 pthreadtestcancel() 函数 自行设置一个取消点。
被取消的线程,退出值定义在linux的pthread库中。常用PTHREAD_CANCELED的值是-1。可在头文件pthread.h中找到定义:#define PTHREAD_CANCELED((void *) -1)。因此当我们对一个已经取消的线程使用pthread_join 回收时,得到的返回值为 -1。
》如果把thr函数中while循环中的两行注释掉,就会没有取消点,无法杀死线程。可以通过调用 pthreadtestcancel() 函数 设置一个取消点。
7、线程分离
》pthread_detach——线程分离,此时不需要pthread_join回收资源。
>touch pthread_detach.c
>vi pthread_detach.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<string.h>
5
6 void* thr(void *arg){
7 printf("I am a thread,self=%lu\n",pthread_self());
8 sleep(4);
9 printf("I am a thread,self=%lu\n",pthread_self());
10 return NULL;
11 }
12
13 int main(int argc, char *argv[])
14 {
15 pthread_t tid;
16 pthread_create(&tid,NULL,thr,NULL);
17
18 pthread_detach(tid);//线程分离
19 sleep(5);
20 int ret = 0;
21 if((ret=pthread_join(tid,NULL)) > 0){
22 printf("join err:%d,%s\n",ret,strerror(ret));
23 }
24
25 pthread_exit(NULL);
26 return 0;
27 }
>make
>./pthread_detach
(先运行子线程输出两句,然后报错:join err:22,Invalid argument)
8、线程属性、设置分离
测试:是否共享全局变量
>touch pthread_var.c
>vi pthread_var.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<string.h>
5
6 int var = 100;
7
8 void* thr(void *arg){
9 printf("I am a thread,self=%lu,var=%d\n",pthread_self(),var);
10 sleep(2);
11 var = 1001;
12 printf("I am a thread,self=%lu,var=%d\n",pthread_self(),var);
13 return NULL;
14 }
15
16 int main(int argc, char *argv[])
17 {
18 pthread_t tid;
19 pthread_create(&tid,NULL,thr,NULL);
20
21 pthread_detach(tid);//线程分离
22 printf("I am main thread,self=%lu,var=%d\n",pthread_self(),var);
23 var = 1003;
24 sleep(5);
25 printf("I am main thread,self=%lu,var=%d\n",pthread_self(),var);
26 int ret = 0;
27 if((ret=pthread_join(tid,NULL)) > 0){
28 printf("join err:%d,%s\n",ret,strerror(ret));
29 }
30
31 pthread_exit(NULL);
32 return 0;
33 }
>make
>./pthread_var
》pthread_equal——比较两个线程ID是否相等
man prhread_equal
int pthread_equal(pthread_t t1, pthread_t t2);
有可能Linux在未来线程ID pthread_t 类型被修改为结构体实现。
注意:线程ID在进程内部是唯一的!
>touch npthread.c
>vi npthread.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4
5
6 void* thr(void *arg){
7 int num = (int)arg;
8 printf("I am %d thread,self=%lu\n",num,pthread_self());
9 return (void *)(100+num);
10 }
11
12 int main(int argc, char *argv[])
13 {
14 pthread_t tid[5];
15 int i;
16 for(i = 0; i < 5; i++){
17 pthread_create(&tid[i],NULL,thr,(void*)i);
18 }
19 for(i = 0; i < 5; i++){
20 void *ret;
21 pthread_join(tid[i],&ret);//有序的原因
22 printf("i == %d,ret == %d\n",i,(int)ret);
23 }
24
25 return 0;
26 }
>make
>./npthread
(更改为传地址)
>vi npthread.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4
5
6 void* thr(void *arg){
7 int num = *(int*)arg;
8 printf("I am %d thread,self=%lu\n",num,pthread_self());
9 return (void *)(100+num);
10 }
11
12 int main(int argc, char *argv[])
13 {
14 pthread_t tid[5];
15 int i;
16 for(i = 0; i < 5; i++){
17 pthread_create(&tid[i],NULL,thr,(void*)&i);
18 }
19 for(i = 0; i < 5; i++){
20 void *ret;
21 pthread_join(tid[i],&ret);//有序的原因
22 printf("i == %d,ret == %d\n",i,(int)ret);
23 }
24
25 return 0;
26 }
>make
>./npthread
(结论:传地址极不稳定,所以不能传地址!)
》线程属性(扩展性了解)
》线程属性控制
man pthread_attr_
初始化线程属性:
int pthread_attr_init(pthread_attr_t *attr);
销毁线程属性:
int pthread_attr_destroy(pthread_attr_t *attr);
设置属性分离态:
int pthread_attr_setdetachstate(pthread_attr_t *attr, int detachstate);
attr init初始化的属性
detachstate
PTHREAD_CREATE_DETACHED 线程分离
PTHREAD_CREATE_JOINABLE允许回收
>touch pthread_attr.c
>vi pthread_attr.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<string.h>
5
6 void* thr(void *arg){
7 printf("I am a thread\n");
8 return NULL;
9 }
10
11 int main(int argc, char *argv[])
12 {
13 pthread_attr_t attr;
14 pthread_attr_init(&attr);//初始化属性
15
16 //第一次运行先不加此行
17 pthread_attr_setdetachstate(&attr,PTHREAD_CREATE_DETACHED);//设置属性分离,不需要回收了
18
19 pthread_t tid;
20 pthread_create(&tid,&attr,thr,NULL);
21
22 int ret;
23 if((ret=pthread_join(tid,NULL)) > 0){
24 printf("join err:%d,%s\n",ret,strerror(ret));
25 }
26
27 pthread_attr_destroy(&attr);//摧毁属性
28
29 return 0;
30 }
>make
>./pthread_attr
9、线程注意事项
》NPTL
1)查看当前 pthread 线程库版本: getconf GNU_LIBPTHREAD_VERSION
2)NPTL 实现机制(POSIX),Native POSIX Thread Library
3)使用线程库时 gcc 指定 -lpthread
》线程使用注意事项
1)主线程退出其他线程不退出,主线程应调用 pthread_exit
2)避免僵尸进程
pthread_join
pthread_detach
pthread_create 指定分离属性
被 join线程可能在 join函数返回前就释放完自己的所有内存资源,所以不应当放回被回收线程栈中的值;
3)malloc 和 mmap 申请的内存可以被其他线程释放
4)应避免在多线程模型中调用 fork,除非马上exec,子进程中只有调用fork的线程存在,其他线程在子进程中均 pthread_exit
5)信号的复杂语义很难和多线程共存,应避免在多线程引入信号机制
》创建多少个线程?
CPU核数*2 + 2
作业
(第2题提示:利用mmap)
第1题:
>touch touch_everymain.c
>vi touch_everymain.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<stdlib.h>
4 #include<sys/types.h>
5 #include<sys/stat.h>
6 #include<fcntl.h>
7 #include<string.h>
8 #include<time.h>
9 #include<sys/time.h>
10 #include<signal.h>
11
12 #define _PROGRAM_NAME_ "touchevery"
13 #define _LOG_FORMAT_ "%02d-%02d %02d:%02d:%02d %s [%06d]:%s\n"//mm-dd hh:mmm:ss programname [pid]:msg
14 #define _LOG_FILE_ "%s/log/%s.%04d%02d" //$HOME/log/programname.yyyymm,如果$HOME/log不存在,需要创建
15
16
17 void catch_alarm(int num)
18 {
19 time_t nowtime = time(NULL);
20 struct tm *nowtm = localtime(&nowtime);//localtime获取当前时间
21
22 //拼接文件名
23 char strLogFile[100];
24 memset(strLogFile,0x00,sizeof(strLogFile));
25 sprintf(strLogFile,_LOG_FILE_,getenv("HOME"),_PROGRAM_NAME,nowtm->tm_year+1900,nowtm->tm_mon+1);
26 int fd = open(strLogFile,O_WRONLY|O_CREAT|O_APPEND,0666);
27 if(fd < 0){
28 perror("open file err");
29 printf("file is %s\n",strLogFile);
30 exit(1);
31 }
32 //拼接写入的信息
33 char buf[2014]={0};
34 sprintf(buf,_LOG_FORMAT_,nowtm->tm_mon+1,nowtm->tm_mday,nowtm->tm_hour,nowtm->tm_min,nowtm->sec,_PROGRAM_NAME_,getpid(),"I am alive!");
35 write(fd, buf, strlen(buf));
36 close(fd);
37
38 }
39
40
41
42 int main(int argc, char *argv[])
43 {
44 //初始化需要的环境变量
45 char *strHomeDir = getenv("HOME");
46 printf("homedir is %s\n",strHomeDir);
47 //守护进程创建
48 pid_t pid = fork();
49 if(pid > 0){
50 exit(1);//父进程退出
51 }
52 setsid();//子进程当会长,此上两步必须
53 umask(0);//设置掩码
54 chdir(strHomeDir);
55 close(0);
56
57
58 //设置信号捕捉,捕捉ALARM信号
59 struct sigaction act;
60 sigemptyset(&act.sa_mask);
61 act.sa_flags = 0;
62 act.sa_hander = catch_alarm;
63 sigaction(SIGALRM,&act,NULL);
64
65 //设置时钟参数
66 struct itimerval myit={{60,0},{1,0}};//每隔60s来一次闹钟
67
68 setitimer(ITIMER_REAL,&myit,NULL);
69
70 //循环等待
71 while(1)
72 {
73 sleep(120);
74 }
75
76 return 0;
77 }
>make
>./touch_everymain
第2题:
>touch mp_cp.c
>vi mp_cp.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/stat.h>
7 #include<fcntl.h>
8 #include<string.h>
9 #include<sys/mman.h>
10
11 #define _THR_CNT_ 5 //线程数
12
13 typedef struct _TaskInfo{
14 int num; //线程编号
15 void *src;
16 void *des;
17 int size;//线程大小
18 }TaskInfo;
19
20 void *thr_cp(void *arg)
21 {
22 TaskInfo *info = arg;
23 int num = info->num;
24 int cpsize = info->size/_THR_CNT_;
25 int mod = info->size % cpsize;
26
27 if(num == _THR_CNT_ -1){ //最后一个线程
28 memcpy(info->des+num*cpsize,info->src+num*cpsize,cpsize+mod);
29 }
30 else{ //其他
31 memcpy(info->des+num*cpsize,info->src+num*cpsize,cpsize);
32 }
33 return NULL;
34 }
35
36
37
38 int main(int argc, char *argv[])
39 {
40 if(argc != 3){
41 printf("./a.out srcfile desfile\n");
42 return -1;
43 }
44 int n = _THR_CNT_;//线程个数
45 struct stat sb;
46 if(stat(argv[1],&sb) < 0){ //获得文件大小
47 perror(argv[1]);
48 exit(1);
49 }
50 long lfilesize = sb.st_size;
51
52 //建立两块映射区
53 int fdsrc = open(argv[1],O_RDONLY);
54 int fddes = open(argv[2],O_RDWR,O_CREAT|O_TRUNC,0666);
55 ftruncate(fddes,lfilesize);//第二块映射区文件拓展
56
57 if(fdsrc < 0 || fddes < 0){
58 printf("open file %s %s err\n",argv[1],argv[2]);
59 exit(1);
60 }
61 //建立源的映射区
62 void *srcmem = mmap(NULL,lfilesize,PROT_READ,MAP_PRIVATE,fdsrc,0);
63 if(srcmem == MAP_FAILED){
64 perror("mmap srcfile err");
65 exit(1);
66 }
67 //建立目标的映射区
68 void *desmem = mmap(NULL,lfilesize,PROT_READ|PROT_WRITE,MAP_SHARED,fddes,0);
69 if(desmem == MAP_FAILED){
70 perror("mmap srcfile err");
71 exit(1);
72 }
73
74 //创建线程
75 TaskInfo taskInfos[_THR_CNT_];
76 pthread_t tid[_THR_CNT_];
77 int i;
78 for(i = 0; i < n; i++){
79 taskInfos[i].src = srcmem;
80 taskInfos[i].des = desmem;
81 taskInfos[i].num = i;
82 taskInfos[i].size = lfilesize;
83 pthread_create(&tid[i],NULL,thr_cp,&taskInfos[i]);
84 }
85 //回收线程
86 for(i = 0; i < n; i++){
87 pthread_join(tid[i],NULL);
88 }
89 //释放映射区
90 munmap(srcmem,lfilesize);
91 munmap(desmem,lfilesize);
92
93 return 0;
94 }
10、线程同步的概念
》线程同步:线程访问同一个共享资源,需要协调步骤。
11、mutex相关的函数
》模拟线程共同抢占(屏幕)资源
>touch pthread_print.c
>vi pthread_print.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<stdlib.h>
5
6 void* thr1(void *arg){
7 while(1){
8 printf("hello");//不带换行,没有行缓冲,输出不出来
9 sleep(rand()%3);
10 printf("world\n");
11 sleep(rand()%3);
12 }
13 }
14
15 void* thr2(void *arg){
16 while(1){
17 printf("HELLO");
18 sleep(rand()%3);
19 printf("WORLD\n");
20 sleep(rand()%3);
21 }
22 }
23
24 int main(int argc, char *argv[])
25 {
26 //创建两个线程,回收两次
27 pthread_t tid[2];
28 pthread_create(&tid[0],NULL,thr1,NULL);
29 pthread_create(&tid[1],NULL,thr2,NULL);
30
31 pthread_join(tid[0],NULL);
32 pthread_join(tid[1],NULL);
33
34 return 0;
35 }
>make
>./pthread_print
》模拟线程共同抢占(缓冲区)资源
>touch thr_write.c
>vi thr_write.c
1 #include<stdio.h>
2 #include<unistd.h>
3 #include<pthread.h>
4 #include<stdlib.h>
5 #include<sys/types.h>
6 #include<sys/stat.h>
7 #include<fcntl.h>
8 #include<string.h>
9
10 pthread_mutex_t mutex;
11 char buf[20];
12
13 void* thr1(void *arg){
14 int i = 0;
15 for(;i < 20; i++){
16 usleep(rand()%3);
17 buf[i] = '0';
18 }
19 return NULL;
20 }
21
22 void* thr2(void *arg){
23 int i = 0;
24 for(;i < 20; i++){
25 usleep(rand()%3);
26 buf[i] = '1';
27 }
28 return NULL;
29 }
30
31 int main(int argc, char *argv[])
32 {
33 //创建两个线程,回收两次
34 memset(buf,0x00,sizeof(buf));
35 pthread_t tid[2];
36 pthread_create(&tid[0],NULL,thr1,NULL);
37 pthread_create(&tid[1],NULL,thr2,NULL);
38
39 pthread_join(tid[0],NULL);
40 pthread_join(tid[1],NULL);
41 printf("buf is %s\n",buf);
42
43 return 0;
44 }
>make
>./thr_write
解决?
》mutex 互斥量
初始化:
int pthread_mutex_init(pthread_mutex_t *restrict mutex, const pthread_mutexattr_t *restrict attr);
restrict 约束该块内存区域对应的数据,只能通过后面的变量进行访问和修改。
mutex 互斥量——锁
attr 互斥量的属性,可以不考虑,传NULL
给共享资源加锁:
int pthread_mutex_lock(pthread_mutex_t *mutex);
mutex 为init初始化的锁
如果当前未锁,成功,该线程加锁
如果已经加锁,阻塞等待
摧毁锁:
int pthread_mutex_destroy(pthread_mutex_t *mutex);
mutex 传入的锁
常量初始化:(此时可以不使用init)
pthread_mutex_t mutex = PTHREAD_MUTEX_INITIALIZER;
在学习Linux系统编程总结了笔记,并分享出来。有问题请及时联系博主:Alliswell_WP,转载请注明出处。