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\n",a);

      

       int i;

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

       {

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

              sleep(1);

       }

}

 

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

{

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

       printf("helloworld!\n");

      

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

       pthread_t tid;

       int ret,i;

       int a = 10;

      

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

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

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

      

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

       {

              printf("main thread:%d\n",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\n",a);

      

       int i;

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

       {

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

              sleep(1);

       }

}

 

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

{

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

       printf("helloworld!\n");

      

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

       pthread_t tid;

       int ret,i;

       int a = 10;

      

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

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

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

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

      

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

       {

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

              sleep(1);

       }

      

       // 接合子线程

       ret = pthread_join(tid,NULL);

       printf("ret = %d\n",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\n",a);

      

       int i;

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

       {

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

              sleep(1);

       }

      

       pthread_exit((void *)&exit_state);

}

 

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

{

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

       printf("helloworld!\n");

      

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

       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\n",ret);

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

      

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

       {

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

              sleep(1);

       }

      

       // 接合子线程

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

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

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

      

       return 0;

}

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

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

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

3. 该线程被取消掉了。

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

猜你喜欢

转载自www.cnblogs.com/zjlbk/p/11347552.html
今日推荐