公司建站费用网站组成元素
线程:
回顾线程(一):
1.线程间通信问题
线程间共享同一个资源(临界资源)
互斥:
排他性访问
linux系统 -- 提供了Posix标准的函数库 -- 互斥量(互斥锁)
原子操作:---
机制:
加锁 -- 解锁
锁 --- 操作系统 --- (实现的机制,需要操作系统来
| - 加锁 -- 用户态 -- 切换到(耗时) -- 内核态 -- 获得了 -- 内核态 -- 用户态 -- 解锁 |
函数:
pthread_mutex_t mutex;
pthread_mutex_init();
pthread_mutex_lock();
pthread_mutex_trylock(); // 尝试获得锁,若没获得则返回非0值。
                
pthread_mutex_unlock();
pthread_mutex_destroy();
线程的同步:
同步 ==》有 一定先后顺序的 对资源的排他性访问。
        要同步的原因:互斥锁可以控制排他访问但没有次序。
     
         信号量 --- 实现线程间的同步.
     
         来源  生活 --- 交通信号灯
信号量的分类:
         1、无名信号量 ==》线程间通信
         2、有名信号量 ==》进程间通信
同步机制:
信号量(个数) --- 反映的是资源的数量
考虑的时候,站在使用这的角度考虑
站在a的角度考虑。。。。。。
框架:
        1. 信号量的定义       sem_t  sem  //造了一类资源
         2. 信号量的初始化   sem_init 
         3. 信号量的PV操作 (核心) sem_wait()/ sem_post()
         4. 信号量的销毁。   sem_destroy
  
信号量函数:
1、定义
sem_t 名字;
2、初始化
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:
将已经定义好的信号量赋值。
参数:
@sem 要初始化的信号量
@pshared
pshared = 0 ;表示线程间使用信号量(一般填这个)
!=0 ;表示进程间使用信号量
@value:
信号量的初始值,一般无名信号量(一开始的资源的个数)
都是二值信号量,0 1
0 表示红灯,进程暂停阻塞
1 表示绿灯,进程可以通过执行
也可以是多个,变成计数信号量
返回值:
成功 0,失败 -1;
3、PV操作
int sem_wait(sem_t *sem); //p操作
功能:
判断当前sem信号量是否有资源可用。
如果sem有资源(==1),则申请该资源,程序继续运行
如果sem没有资源(==0),则线程阻塞等待,一旦有资源
则自动申请资源并继续运行程序。
消耗了这个sem,就没有了
注意:sem 申请资源后会自动执行 sem = sem - 1;
参数:
@sem 要判断的信号量资源
返回值:
成功 0 ,失败 -1
int sem_post(sem_t *sem); //V操作
功能:
函数可以将指定的sem信号量资源释放
并默认执行,sem = sem+1;
线程在该函数上不会阻塞。
产生了这个sem就有了,可以由wait(sem)接受去消耗
参数:
@sem 要释放资源的信号量
返回值:
成功 0,失败 -1;
4、销毁
int sem_destroy(sem_t *sem);
练习: hello world
#include<stdio.h>
#include<semaphore.h>
#include<errno.h>
#include<pthread.h>
#include<stdlib.h>sem_t sem_h;
sem_t sem_w;void *do_hello(void *arg)
{while(1)	{sem_wait(&sem_h);printf("hello ");sem_post(&sem_w);}return NULL;
}void *do_world(void *arg)
{while(1)	{sem_wait(&sem_w);printf("world\n");sem_post(&sem_h);}return NULL;}
typedef void *(*threadF_t)(void *);
int main(int argc, const char *argv[])
{int i;pthread_t tid[i];threadF_t pFunc[2] = {do_hello,do_world};sem_init(&sem_h,0,1);sem_init(&sem_w,0,0);for(i = 0;i < 2;++i){int ret = pthread_create(&tid[i],NULL,pFunc[i],NULL);if(ret != 0){errno = ret;perror("pthread_create fail");exit(EXIT_FAILURE);}}sem_destroy(&sem_h);sem_destroy(&sem_w);pthread_detach(tid[0]);pthread_detach(tid[1]);printf("---main----exit\n");pthread_exit(NULL);return 0;
} 
 
 
进程间的通信方式:

三大类:
1.同主机   ---- 基于内存的 
          
 
 古老的通信方式 
                     //管道  ---- 
                              无名管道  
                              有名管道
                     //信号  
     
           IPC对象通信(改进)
                      消息队列(用的相对少,这里不讨论)
                      共享内存(*) //最高效 
                      信号量集() //信号量  
 
 2.     
 
        //不同主机 、多台主机
           socket //网络部分 
   
         //同一主机
         2.1、古老的通信方式
                 管道:
                            无名管道  
                            有名管道  
                            信号
        2.2、IPC对象通信 system v    BSD     suse fedora   kernel.org
                 消息队列(用的相对少,这里不讨论)
                 共享内存(*) //最高效 
                 信号量集() //信号量  
         //不同主机 
 3、socket通信
 
        网络通信
  
1、pipe 无名管道
使用框架:
创建管道 ==》读写管道 ==》关闭管道
1、无名管道 ===》管道的特例 ===>pipe函数
     特性:
             1.1  亲缘关系进程使用
             1.2  有固定的读写端
流程:
     创建并打开管道: pipe函数
     
函数:
    #include <unistd.h>
     int pipe(int pipefd[2]);
     int pipe(int *pipefd);
     int fd[2];
             功能:创建并打开一个无名管道
             参数:  @pipefd[0] ==>无名管道的固定读端//0 -- 标准输入
                          @pipefd[1] ==>无名管道的固定写端//1 -- 标准输出 
             返回值: 成功 0
                             失败 -1;
注意事项:
1、无名管道的架设应该在fork之前进行。
关闭管道: close();
练习:父进程输入、子进程打印
#include<stdio.h>
#include<unistd.h>
#include<string.h>
#include<stdlib.h>
#include <sys/wait.h>int main(int argc, const char *argv[])
{int fd[2];if(pipe(fd) < 0){perror("pipe fail");return -1;}char buf[100] = {0};int ret = 0;pid_t pid = fork();while(1){if(pid < 0){perror("fork fail");return -1;}else if(pid > 0){close(fd[0]);printf(">");fflush(stdout);ret = read(0,buf,sizeof(buf));buf[ret] = '\0';write(fd[1],buf,strlen(buf) + 1);if(strncmp(buf,"quit",4) == 0){wait(NULL);close(fd[1]);return 0;}}else if(pid == 0){close(fd[1]);ret = read(fd[0],buf,sizeof(buf));printf("date = %s\n",buf);if(strncmp(buf,"quit",4) == 0){close(fd[0]);exit(0);}}}return 0;
} 
 
管道的读写规则:
    
 
    1.读端存在,写管道
          管道空:可以写数据
          管道满:会造成-->写阻塞 
       
     2.读端不存在,写管道
          系统会给进程发一个信号SIGPIPE(管道破裂)
    3.写端存在,读管道
          管道空,读不到数据,
          这时会造成读操作阻塞
    4.写端不存在,读管道 
          如果管道中有数据,则读取这些数据!
          如果没有数据,读操作不阻塞,立即返回!
2、fifo有名管道
有名管道===》fifo ==》有文件名称的管道。
                                                                       文件系统中可见

框架:
    (1).创建有名管道 -- 类似 文件 (管道文件) 
     (2).打开有名管道 -- open 
     (3).读写管道     -- read/write 
     (4).关闭管道  ==》卸载有名管道 //close  
1、创建:mkfifo //创建了一个有名管道
#include <sys/types.h>
 #include <sys/stat.h>
  remove();
int mkfifo(const char *pathname, mode_t mode);
        功能:
                       在指定的pathname路径+名称下创建一个权限为
                       mode的有名管道文件。
         参数:@pathname要创建的有名管道路径+名称
                       mode  8进制文件权限。
         返回值:  成功 0
                         失败  -1;
2、打开有名管道 open
注意:该函数使用的时候要注意打开方式,
     因为管道是半双工模式,所有打开方式直接决定

     当前进程的读写方式。
     一般只有如下方式:
     int fd-read = open("./fifo",O_RDONLY); ==>fd 是固定读端    //阻塞,只有双方以对应的方式打开的时候才会
     int fd-write = open("./fifo",O_WRONLY); ==>fd 是固定写端   
     不能是 O_RDWR 方式打开文件。
     不能有 O_CREAT 选项,因为创建管道有指定的mkfifo函数
     
     有名管道打开:
     注意,
     如果一端是以只读,或者只写方式打开的。
     程序会阻塞,
     阻塞在打开操作。
     直到另一端,以只写或只读方式打开。
     A.c --- 只读 
     B.c --- 只写 
练习:实现双向通信:
打开两个有名通道文件

// a
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>int main(int argc, const char *argv[])
{if (mkfifo("a_2_b",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if (mkfifo("b_2_a",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd_w = open("a_2_b",O_WRONLY);if (fd_w< 0){perror("open fail");return -1;}int fd_r = open("b_2_a",O_RDONLY);if (fd_r< 0){perror("open fail");return -1;}pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}char buf[1024] = {0};if (pid > 0){while (1){printf(">");fflush(stdout);fgets(buf,sizeof(buf),stdin);write(fd_w,buf,strlen(buf)+1);if (strncmp(buf,"quit",4) == 0){close(fd_w);close(fd_r);}}}else if (pid == 0){while (1){printf("<");int ret = read(fd_r,buf,sizeof(buf));printf("ret = %d: %s\n",ret,buf);}}return 0;
}// b
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <string.h>
#include <unistd.h>
#include <errno.h>int main(int argc, const char *argv[])
{if (mkfifo("a_2_b",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}if (mkfifo("b_2_a",0666) < 0 && errno != EEXIST){perror("mkfifo fail");return -1;}int fd_r = open("a_2_b",O_RDONLY);if (fd_r< 0){perror("open fail");return -1;}int fd_w = open("b_2_a",O_WRONLY);if (fd_w< 0){perror("open fail");return -1;}pid_t pid = fork();if (pid < 0){perror("fork fail");return -1;}char buf[1024] = {0};if (pid > 0){while (1){printf(">");fflush(stdout);fgets(buf,sizeof(buf),stdin);write(fd_w,buf,strlen(buf)+1);}}else if (pid == 0){while (1){printf("<");int ret = read(fd_r,buf,sizeof(buf));printf("ret = %d: %s\n",ret,buf);if (strncmp(buf,"quit",4) == 0){close(fd_w);close(fd_r);}}}return 0;
} 
