在学习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、守护进程相关的概念



Linux系统编程——守护进程+线程_unix


 



Linux系统编程——守护进程+线程_多线程_02


会话:进程组的更高一级,多个进程组对应一个会话。

(ps ajx | more可以查看会话)

进程组:多个进程在同一个组,第一个进程默认是进程组的组长。

创建会话的时候,组织不可以创建,必须是组员创建。

创建会话的步骤:创建子进程,父进程死去,子进程自当会长。



Linux系统编程——守护进程+线程_多线程_03


》setsid——调用后就成为会长

man 2 setsid

pid_t setsid(void);



Linux系统编程——守护进程+线程_linux_04


 (例如:如果按照了mysql,可以通过ps aus | grep mysqld查看mysql的守护进程)



Linux系统编程——守护进程+线程_unix_05




Linux系统编程——守护进程+线程_操作系统_06


前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



Linux系统编程——守护进程+线程_linux_07


线程的概念:轻量级的进程,一个进程内部可以有多个线程,默认情况下一个进程只能有一个线程。



Linux系统编程——守护进程+线程_操作系统_08


 (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回收资源。



Linux系统编程——守护进程+线程_linux_09


>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

(结论:传地址极不稳定,所以不能传地址!)

》线程属性(扩展性了解)



Linux系统编程——守护进程+线程_linux_10




Linux系统编程——守护进程+线程_多进程_11


》线程属性控制

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

作业



Linux系统编程——守护进程+线程_操作系统_12


(第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、线程同步的概念

》线程同步:线程访问同一个共享资源,需要协调步骤。



Linux系统编程——守护进程+线程_linux_13




Linux系统编程——守护进程+线程_unix_14


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​​,转载请注明出处。