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
",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
",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
",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
",getpid(),pthread_self());
18     
19     sleep(10);
20     printf("I will out
");
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
",pthread_self());
 8     sleep(5);
 9     printf("I am a thread!tid=%lu
",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
",(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
",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
",(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
",pthread_self());
 8     sleep(4);
 9     printf("I am a thread,self=%lu
",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
",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
",pthread_self(),var);
10     sleep(2);
11     var = 1001;
12     printf("I am a thread,self=%lu,var=%d
",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
",pthread_self(),var);
23     var = 1003;
24     sleep(5);
25     printf("I am main thread,self=%lu,var=%d
",pthread_self(),var);
26     int ret = 0;
27     if((ret=pthread_join(tid,NULL)) > 0){
28         printf("join err:%d,%s
",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
",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
",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
",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
",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
");
 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
",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
"//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
",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
",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
");
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
",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
");
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
");
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
",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,转载请注明出处。

原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_08.html