zoukankan      html  css  js  c++  java
  • 2019年8月13日星期二(系统编程) 共享内存 信号量 线程 线程退出情况

    2019813日星期二

    . 进程之间的通信方式 - 共享内存

    1. 共享内存作用范围是什么?机制如何?

    可以作用于linux下任意两个进程,机制就是使用同一片共享的内存区域,使得两个任意的进程访问这个区域,实现数据的交换。

    2. 关于共享内存的API函数接口?

    1)由于共享内存属于IPC对象,所以在使用前必须申请key

       key = ftok(".",10);

    2)根据key值申请共享内存的ID号。  -> shmget()  -> man 2 shmget

    功能:allocates a shared memory segment  -> 申请共享内存块

    使用格式:

           #include <sys/ipc.h>

            #include <sys/shm.h>

           int shmget(key_t key, size_t size, int shmflg);

         key:key值

           size:共享内存的总字节数 PAGE_SIZE的倍数   #define PAGE_SIZE 1024

           shmflg:  IPC_CREAT|0666  -> 不存在则创建

                    IPC_EXCL  -> 存在则报错

           返回值:

                  成功:共享内存的ID

                  失败:-1

    3)根据ID号去内存空间中映射一块区域。  -> shmat()  -> man 2 shmat

    使用格式:

           #include <sys/types.h>

            #include <sys/shm.h>

           void *shmat(int shmid, const void *shmaddr, int shmflg);

           shmid:共享内存的ID

           shmaddr: NULL   -> 系统自动分配  99%

                  不为NULL -> 用户自己分配  1%

           shmflg: 普通属性代表内存空间可读可写,填0

           返回值:

                  成功:该内存空间的起始地址

                  失败:(void *)-1

    4)撤销内存空间的区域。  -> shmdt()  -> man 2 shmdt

    使用格式:

           #include <sys/types.h>

            #include <sys/shm.h>

         int shmdt(const void *shmaddr);

           shmaddr: 该内存空间的起始地址

           返回值:

                  成功:0

                  失败:-1

    5)删除共享内存的IPC对象  -> shmctl()   -> man 2 shmctl

    使用格式:

           #include <sys/ipc.h>

            #include <sys/shm.h>

           int shmctl(int shmid, int cmd, struct shmid_ds *buf);

           shmid:共享内存的ID

           cmd:IPC_RMID  -> 删除

           buf:NULL

           返回值:

                  成功:0

                  失败:-1

      练习1:尝试使用共享内存实现进程之间的通信。  -> 单独使用共享内存,容易造成数据的践踏!

    /* 读端 */

    #include "head.h"

     

    int main()

    {

           //1. 申请key值

           key_t key = ftok(".",10);

          

           //2. 根据key值申请共享内存ID号

           int shmid = shmget(key,2048,IPC_CREAT|0666);

          

           //3. 根据ID号申请共享内存的起始地址

           char *p = (char *)shmat(shmid,NULL,0);

          

           //4. 不断读取共享内存的数据

           while(1)

           {

                  printf("from shm:%s",p);

                  usleep(500000);

                  if(strncmp(p,"quit",4) == 0)

                  {

                         break;

                  }

           }

          

           shmdt(p);

           shmctl(shmid,IPC_RMID,NULL);

           return 0;

    }

    /* 写端 */

    #include "head.h"

     

    int main()

    {

           //1. 申请key值

           key_t key = ftok(".",10);

          

           //2. 根据key值申请共享内存ID号

           int shmid = shmget(key,2048,IPC_CREAT|0666);

          

           //3. 根据ID号申请共享内存的起始地址

           char *p = (char *)shmat(shmid,NULL,0);

          

           //4. 往共享内存中写入数据

           bzero(p,2048);

           while(1)

           {

                  fgets(p,2048,stdin);

                  if(strncmp(p,"quit",4) == 0)

                  {

                         break;

                  }

           }

          

           return 0;

    }

    . 处理进程之间通信(共享内存)同步互斥方式 -- 信号量

    1. 什么是信号量?

    信号量虽然属于IPC对象,但是它不属于进程之间的通信,它只是用于处理同步互斥。

    2. 关于信号量的API函数接口?

    1)由于信号量属于IPC对象,所以要申请key值。

       key = ftok(".",10);

    2)根据key值申请信号量的ID号。  -> semget()  -> man 2 semget

    使用格式:

           #include <sys/types.h>

            #include <sys/ipc.h>

            #include <sys/sem.h>

           int semget(key_t key, int nsems, int semflg);

         key:key值

           nsems:信号量的元素个数。 例如: 空间和数据  -> 2

           semflg: IPC_CREAT|0666  -> 不存在则创建

                   IPC_EXCL   -> 存在则报错

           返回值:

                  成功:信号量的ID

                  失败:-1

    3)如何实现信号量P/V操作? (如何使得1->0 0->1)  -> semop()  -> man 2 semop

    使用格式:

           #include <sys/types.h>

            #include <sys/ipc.h>

            #include <sys/sem.h>

           int semop(int semid, struct sembuf *sops, unsigned nsops);

         semid:信号量的ID

           sops:进行P/V操作的结构体

    struct sembuf{

           unsigned short sem_num;  需要操作的成员的下标:  空间  -> 0  数据  ->1   

            short          sem_op;   P操作/V操作            P操作 ->-1  V操作 ->1    

            short          sem_flg;  普通属性,填0

    }

           nsops:信号量操作结构体个数  -> 1

           返回值:

                  成功:0

                  失败:-1

    4)控制信号量参数。  -> semctl()  -> man 2 semctl

    使用格式:

           #include <sys/types.h>

            #include <sys/ipc.h>

            #include <sys/sem.h>

           int semctl(int semid, int semnum, int cmd, ...);

           semid:信号量的ID

           semnum:需要操作的成员的下标:  空间  -> 0  数据  ->1 

           cmd: SETVAL  -> 用于设置信号量元素的起始值

                 IPC_RMID -> 删除信号量

           ...:空间/数据的起始值

    例子: 初始化空间:0  数据:1

    semctl(semid,0,SETVAL,0);

    semctl(semid,1,SETVAL,1);

    . linux最小调用资源单位 - 线程。

    1. 进程与线程的区别?

    进程: ./xxx   -> 开启一个新的进程   -> 是系统中最小的资源分配单位。

    int main()

    {

           /* 进程开始 */

           ....

           /* 进程结束 */

           return 0;

    }

    线程: 是一个进程内部的资源,是系统中最小调度单位。

    2. 线程的函数接口特点?

    1)由于线程函数接口都是被封装在线程库中,所以我们是看不到源码,查看线程函数时,一定是在第3个手册。

    2)所有的线程函数的头文件: #include <pthread.h>

    3. 函数接口有哪些?

    1)如何在正在运行的进程中创建一个新的线程?  -> pthread_create()  -> man 3 pthread_create

    功能: create a new thread  -> 创建子线程。

    使用格式:

           #include <pthread.h>

           int pthread_create(pthread_t *thread, const pthread_attr_t *attr,

                              void *(*start_routine) (void *), void *arg);

           thread:存放线程TID号的变量的地址     

    pthread_t其实是线程TID号的数据类型 

           attr:属性变量   -> 不为NULL,自定义属性

                                 -> 为NULL,普通属性

           start_routine: 线程执行例程函数,类型必须是: void *fun(void *arg)

           arg:传递给例程函数的参数

           返回值:

                  成功:0

                  失败:非0错误码

    注意:
    编译所有包含线程函数接口在内.c文件时,必须链接线程库: Compile and link with -pthread.
    如果不链接,则编译不会通过,会报错: undefined reference to `pthread_create'

    例子: gcc test.c -o test -lpthread

      例题:  尝试创建一个新的线程,使得同时做两件事情。

    #include "head.h"

     

    void *fun(void *arg) //arg = &a

    {

           int a = *(int *)arg;

           printf("a = %d ",a);

          

           int i;

           for(i=0;i<10;i++)

           {

                  printf("child thread:%d ",i);

                  sleep(1);

           }

    }

     

    int main(int argc,char *argv[])

    {

           /* 暂时来讲,都只是一个单线程 */

           printf("helloworld! ");

          

           /* 创建一个新的子线程 */

           pthread_t tid;

           int ret,i;

           int a = 10;

          

           ret = pthread_create(&tid,NULL,fun,(void *)&a);

           printf("ret = %d ",ret);

           printf("tid = %d ",(int)tid);

          

           for(i=0;i<5;i++)

           {

                  printf("main thread:%d ",i);

                  sleep(1);

           }

          

           return 0;

    }

    结论:

    创建线程与调用函数接口有什么不同?  -> 见"调用函数与创建线程的区别.jpg"

    2)如何接合子线程?(等待子线程的退出)  -> pthread_join()  -> man 3 pthread_join

    功能: join with a terminated thread  -> 接合一个结束的子线程

    使用格式:

            #include <pthread.h>

           int pthread_join(pthread_t thread, void **retval);

           thread:需要接合的线程的TID号

           retval:存储子线程退出值的指针  如果填NULL,不关心子线程的退出。

           返回值:

                  成功:0

                  失败:错误码

    例子:主线程主动接合子线程。

    #include "head.h"

     

    void *fun(void *arg) //arg = &a

    {

           int a = *(int *)arg;

           printf("a = %d ",a);

          

           int i;

           for(i=0;i<10;i++)

           {

                  printf("child thread:%d ",i);

                  sleep(1);

           }

    }

     

    int main(int argc,char *argv[])

    {

           /* 暂时来讲,都只是一个单线程 */

           printf("helloworld! ");

          

           /* 创建一个新的子线程 */

           pthread_t tid;

           int ret,i;

           int a = 10;

          

           //fun((void *)&a);

           ret = pthread_create(&tid,NULL,fun,(void *)&a);

           printf("ret = %d ",ret);

           printf("tid = %d ",(int)tid);

          

           for(i=0;i<5;i++)

           {

                  printf("main thread:%d ",i);

                  sleep(1);

           }

          

           // 接合子线程

           ret = pthread_join(tid,NULL);

           printf("ret = %d ",ret);

          

           return 0;

    }

    3)如何退出线程?  -> pthread_exit()   -> man 3 pthread_exit

    该函数的功能:

    1)terminate calling thread  -> 提前结束线程

    2)为了返回一个退出值给主线程。

     

    使用格式:

           #include <pthread.h>

     

           void pthread_exit(void *retval);

     

           retval:子线程的退出值。  -> 退出值不能是局部变量。

     

           返回值:无。

     

    例子: 主线程主动结合子线程,并且关心子线程退出状态。

    #include "head.h"

     

    int exit_state = 5;

     

    void *fun(void *arg) //arg = &a

    {

           int a = *(int *)arg;

           printf("a = %d ",a);

          

           int i;

           for(i=0;i<10;i++)

           {

                  printf("child thread:%d ",i);

                  sleep(1);

           }

          

           pthread_exit((void *)&exit_state);

    }

     

    int main(int argc,char *argv[])

    {

           /* 暂时来讲,都只是一个单线程 */

           printf("helloworld! ");

          

           /* 创建一个新的子线程 */

           pthread_t tid;

           int ret,i;

           int a = 10;

           void *p = NULL;

          

           //fun((void *)&a);

           ret = pthread_create(&tid,NULL,fun,(void *)&a);

           printf("ret = %d ",ret);

           printf("tid = %d ",(int)tid);

          

           for(i=0;i<5;i++)

           {

                  printf("main thread:%d ",i);

                  sleep(1);

           }

          

           // 接合子线程

           ret = pthread_join(tid,&p); // p = (void *)&exit_state

           printf("ret = %d ",ret);

           printf("exit_state = %d ",*(int *)p);

          

           return 0;

    }

    . 什么情况下,线程会退出?

    1. 在线程的内部调用pthread_exit()

    2. 在线程的例程函数中执行return

    3. 该线程被取消掉了。

    4. 只要进程调用exit(),所有的线程都马上退出。

  • 相关阅读:
    739. Daily Temperatures
    556. Next Greater Element III
    1078. Occurrences After Bigram
    1053. Previous Permutation With One Swap
    565. Array Nesting
    1052. Grumpy Bookstore Owner
    1051. Height Checker
    数据库入门及SQL基本语法
    ISCSI的概念
    配置一个IP SAN 存储服务器
  • 原文地址:https://www.cnblogs.com/zjlbk/p/11347552.html
Copyright © 2011-2022 走看看