信号量 | 进程共享——无名管道

一、资源竞争解决技术
上篇介绍互斥锁机制用来解决互斥访问的问题;但线程间仍存在顺序问题,因此我们有了信号量的出现。

     P操作:使用这个资源(个数减1)。尝试获取资源;有则用,无则等。

     V操作:产生这个资源(个数加1)。释放一个资源;如果有任务则等,无任务则用。

1、公共资源角色

buf[1024]公共资源。

(1)写线程——写资源
可写数据条件:①开始,有空buf; ②读线程,读完了。

p(写资源) //申请资源

V(读资源)//产生资源

(2)读线程——读资源
        开始,buf中无数据可读,此时充当写资源。

可读数据条件:写线程结束。

P(读资源)

V(写资源)

2、信号量机制


本质:锁(可顺序操作的一把锁);信号量反映的是资源的个数。

功能:实现了一种可以让线程间有序访问临界资源的方式。

步骤:

(1)信号量的定义
        sem_t(信号量类型)   sem(信号量变量)  //造了一类资源;

(2)信号量的初始化
        int sem_init(sem_t *sem , int pshared(使用信号量;0为线程间,非0为进程间) , unsigned int value(资源个数))

无名信号量——线程间通信;有名信号量——进程间通信。

(3)信号量的PV操作
       a. int sem_wait(sem_t *sem);     //P操作

p操作:使用该资源 资源个数-1

表示使用这个资源,资源个数减1

p操作逻辑
尝试获取资源,
有资源可用,直接使用,资源个数减一
如果没有资源可用,此时等待

       b. int sem_post(sem_t *sem);       //V操作

v操作:产生该资源 资源个数+1

操作逻辑:

释放一个资源,此时如果有任务,在等待这个资源,这个资源直接给对应的任务;

(4)信号量的销毁
        int sem_destroy(sem_t *sem)

例:售票系统,实现两个窗口轮流售票。

void *doWin1(void *arg)
{
    while (1)
    {
        sem_wait(&sem1);
        if(ticket <= 0)
        {
            sem_post(&sem2);
            break;
        }
        --ticket;
        printf("win1 sell ticket = %d\n",100-ticket);
        sem_post(&sem2);
    }
    pthread_exit(NULL);
}
void *doWin2(void *arg)
{
    while (1)
    {
        sem_wait(&sem2);
        if(ticket <= 0)
        {
            sem_post (&sem1);
            break;
        }
        --ticket;
        printf("win2 sell ticket = %d\n",100-ticket);
        sem_post (&sem1);

    }
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    pthread_t tid[2];
    sem_init(&sem1,0,1);
    sem_init(&sem2,0,0);
    int ret = pthread_create(&tid[0],NULL,doWin1,NULL);
    if (ret != 0)
    handle_error_en(ret,"pthread_create fail");
    ret = pthread_create(&tid[1],NULL,doWin2,NULL);
    if (ret != 0)
    handle_error_en(ret,"pthread_create fail");
    sem_destroy(&sem1);
    sem_destroy(&sem2);
    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    return 0;
}

例:创建三个线程,有序的打印how are you;

sem_t sem1;
sem_t sem2;
sem_t sem3;
void *doWin1(void *arg)
{
    sem_wait(&sem1);
    puts("how");
    sem_post(&sem2);
    pthread_exit(NULL);
}
void *doWin2(void *arg)
{   
    sem_wait(&sem2);
    puts("are");
    sem_post(&sem3);
    pthread_exit(NULL);
}
void *doWin3(void *arg)
{   
    sem_wait(&sem3);
    puts("you");
    sem_post(&sem1);
    pthread_exit(NULL);
}
int main(int argc, const char *argv[])
{
    pthread_t tid[3];
    sem_init(&sem1,0,2);
    sem_init(&sem2,0,1);
    sem_init(&sem3,0,0);
    int ret = pthread_create(&tid[0],NULL,doWin1,NULL);
    if (ret != 0)
    handle_error_en(ret,"pthread_create fail");
    ret = pthread_create(&tid[1],NULL,doWin2,NULL);
    if (ret != 0)
    handle_error_en(ret,"pthread_create fail");
    ret = pthread_create(&tid[2],NULL,doWin3,NULL);
    if (ret != 0)
    handle_error_en(ret,"pthread_create fail");
    sem_destroy(&sem1);
    sem_destroy(&sem2);
    sem_destroy(&sem3);
    pthread_join(tid[0],NULL);
    pthread_join(tid[1],NULL);
    pthread_join(tid[2],NULL);
    return 0;
}

二、进程间的通信


1、进程间通信方式

同一主机:

(1)古老的通信方式:管道:①无名管道  ②有名管道;

                                      ③信号;

(2)IPC对象通信:System v       BSD       Suse fedora        kernel.org

         ④消息队列;    ⑤共享内存(*)(最高效);  ⑥信号量集(信号量);

不同主机:

(3)socket通信:⑦网络通信。

2、无名管道

(1)创建
        int pipe(int pipefd[2](管道两端))
两端:pipefd[0] 读端;pipefd[1] 写段。   单向管道

(2)管道的特点
        ①管道大小65536字节(64k);②管道操作特点:数据读走之后就清空了。

(3)管道读写规则
        1)写管道:
①//写端存在,读端也存在

        管道若为空,可一直写,直到写满后阻塞。

②//写端存在,读端不存在

        写操作会导致管道破裂。(收到信号SIGPIPE,会使程序结束)

        2)读管道:
①//写端存在,读端也存在

        可读管道,但若管道中无数据,则读操作阻塞。

②//写段不存在,读端存在

        可读管道,但若管道中无数据,则读操作不阻塞。

父进程发送字符给子进程,子进程接受并读取:

int main()
{
    int fd[2];
    if(pipe(fd) < 0)
    {
        perror("pipe error\n");
        return -1;
    }
    pid_t pid = fork();
    if(pid < 0)
    {
        perror("error fork\n");
        return -1;
    }
    char buf[100];
    if(pid > 0)
    {
        close(fd[0]);
        while(1)
        {
            fgets(buf,sizeof(buf),stdin);
            buf[strlen(buf) - 1] ='\0';
            write(fd[1],buf,strlen(buf) + 1);
            if (strcmp(buf,"quit") == 0)
            {
                wait(NULL);
                break;
            }
        }
    }
    else if(pid == 0)
    {
        close(fd[1]);
        while(1)
        {
            read(fd[0],buf,sizeof(buf));
            printf("buf = %s\n",buf);
            if (strcmp(buf,"quit") == 0)
            {
                break;
            }
        }
    }
    return 0;
}

  • 9
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值