zoukankan      html  css  js  c++  java
  • 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,转载请注明出处。

  • 相关阅读:
    在其他对象上同步
    如何在一个线程环境中使用一个线程非安全的java类
    原子类
    Volatile
    Spring中的设计模式2
    Spring中的设计模式
    Struts2中的设计模式
    Struts2中的设计模式----ThreadLocal模式
    享元模式(Flyweight)
    Java类加载器的工作原理
  • 原文地址:https://www.cnblogs.com/Alliswell-WP/p/CPlusPlus_Linux_08.html
Copyright © 2011-2022 走看看