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(),所有的线程都马上退出。

  • 相关阅读:
    nginx -s reload 时报错 [error] open() "/run/nginx.pid" failed (2: No such file or directory)
    系统调用(四):SSDT
    系统调用(三): 分析KiFastCallEntry(二)
    系统调用(二): 分析KiFastCallEntry(一)
    系统调用(一): 重写WriteProcessMemory
    CVE-2009-0927分析
    CVE-2008-0015分析
    CVE-2006-4868分析
    在linux上使用impdp命令时提示ORA-12154: TNS:could not resolve the connect identifier specified的问题
    破解myeclipse10失败的一个奇葩原因
  • 原文地址:https://www.cnblogs.com/zjlbk/p/11347552.html
Copyright © 2011-2022 走看看