Linux系统编程——守护进程+线程
在学习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,转载请注明出处。
posted on 2020-07-03 18:42 Alliswell_WP 阅读(397) 评论(0) 编辑 收藏 举报