Unix高级编程:线程的同步之信号量、进程通信之信号量集、system函数、简单web服务器代码

来源:互联网 发布:北京大兴区行知小学 编辑:程序博客网 时间:2024/06/05 07:07
一、线程的同步之信号量
信号量用于多个资源的情况下的同步。
需要使用下面函数:
"sem_init"(3)
#include <semaphore.h>
int sem_init(sem_t *sem, int pshared, unsigned int value);
功能:初始化一个信号量
参数:
"sem" 在sem指定的空间里初始化一个信号量
"pshared" 0 用于多线程之间共享;非 0 用于多进程之间共享
"value" 信号量的初始值
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置




"sem_destroy"(3)
#include <semaphore.h>
int sem_destroy(sem_t *sem);
功能:销毁一个信号量
参数:"sem" 要销毁的信号量
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置


"sem_post"(3)
#include <semaphore.h>
int sem_post(sem_t *sem);
功能:使信号量的值加 1
参数:"sem" 要操作的信号量
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置


"sem_wait"(3)
#include <semaphore.h>
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
功能:使信号量的值减少 1
 如果在减1之前信号量的值>0,立即返回;如果值==0,阻塞等待。
参数:"sem" 要操作的信号量
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置


"生产者和消费者模型"/** 举例验证:使用信号量,来实现生产者和消费者的例子。基于固定大小的环形队列。semaphore.c **/#include <stdio.h>#include <pthread.h>#include <semaphore.h>#include <time.h>#define NUM 5int queue[NUM]; //使用数组描述环形队列sem_t c_number, p_number;void *consumer(void *arg) {    int c = 0;    while(1) {        sem_wait(&p_number);        printf("consumer %d\n", queue[c]);        queue[c] = 0;        sem_post(&c_number);        c = (c+1) % NUM;        sleep(rand() % 5);     }       return NULL;}void *producer(void *arg) {    int p = 0;    while(1) {        sem_wait(&c_number);        queue[p] = rand() % 1000 + 1;        printf("producer %d\n", queue[p]);        sem_post(&p_number);        p = (p+1) % NUM; //数组下标实现环形队列        sleep(rand() % 5);     }       return NULL;}int main(void) {    pthread_t pid, cid;    srand(time(NULL));    //对信号量初始化    sem_init(&c_number, 0, NUM);    sem_init(&p_number, 0, 0);     //创建生产者和消费者的线程    pthread_create(&pid, NULL, producer, NULL);    pthread_create(&cid, NULL, consumer, NULL);    pthread_join(pid, NULL);    pthread_join(cid, NULL);    sem_destroy(&c_number);    sem_destroy(&p_number);    return 0;}

web服务器
"apache" 更多的是用多线程
"nigix" 使用的是多进程 (并发 "select"(2) 在笔记 1214+1215)
都是纯C写的。

二、进程间通讯之信号量集
信号量集就是信号量的集合。
使用"信号量集实现进程间通讯"的步骤:
<1> 获取键值 ("ftok"(3))
<2> 获取跟键值相关的信号量集的id ("semget"(2))

"semget"(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
功能:获取一个信号量集的id
参数:
"key" ftok(3)的返回值
"nsems" 信号量集中包含的信号量的个数
"semflg" 
IPC_CREAT 这个信号量集不存在就创建|考虑权限;存在就获取
IPC_EXCL 如果信号量集不存在就创建|考虑权限;存在就报错
mode 等同于文件的mode
返回值:
成功 - 返回信号量集的id
失败 - 返回 -1,errno被设置
/** 举例验证:
创建信号量集。semaphores.c **/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int main(void) {
    key_t key;
    //获取键值
    key = ftok(".", 41);
    if(-1 == key) {
        perror("ftok");
        return 1;
    }   
    //获取和键值相关的semid
    int semid = semget(key, 1, IPC_CREAT|0664);
    if(-1 == semid) {
        perror("semget");
        return 2;
    }   
    printf("semaphores creat success...\n");
    printf("semid is %d\n", semid);
    return 0;
}


"对某一个信号量的pv操作"
"semop"(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, unsigned nsops);
功能:对信号量的操作
参数:
"semid" 信号量集的id,semget(2)的返回值
"sops" 信号量的具体操作
"nsops" 操作信号量集中信号量的个数
返回值:
成功 - 返回 0
失败 - 返回 -1,errno被设置
Each semaphore in a semaphore set has the following associated values:
unsigned short  semval;   /* semaphore value */
unsigned short  semzcnt;  /* # waiting for zero */
unsigned short  semncnt;  /* # waiting for increase */
pid_t           sempid;   /* process that did last op */


struct sembuf {
unsigned short sem_num;  /* semaphore number */
short          sem_op;   /* semaphore operation */
short          sem_flg;  /* operation flags */
}
"sem_num" 这个信号量在信号量集里的索引/下标
"sem_op" p/v操作
> 0 (正整数)将这个正整数加上
==0 
< 0 将sem_op加到semval上,semval是信号量的值
如果semval-semop >= 0,semop立即执行
如果semval-semop <  0,IPC_NOWAIT被设置,semop执行失败,errno被设置为EAGAIN
"sem_flg"
IPC_NOWAIT 非阻塞
SEM_UNDO 撤销


对信号量设置初值的操作,对信号量集的操作:
"semctl"(2)
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, ...);
功能:对信号量的控制操作
参数:
"semid" 要操作的信号量集的id,semget(2)的返回值
"semnum" 信号量在信号量集里的索引/下标
"cmd" 指定了操作的命令
GETVAL
SETVAL
...(略)
"..." 
返回值:
成功 - cmd使用GETVAL时,返回semval;cmd使用SETVAL时,返回 0
失败 - 返回 -1,errno被设置
union semun {
int              val;    /* Value for SETVAL */
struct semid_ds *buf;    /* -- */
unsigned short  *array;  /* -- */
struct seminfo  *__buf;  /* -- */
};


/** 举例验证:
使用信号量集实现进程间通讯。semA.c semB.C **/
#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
union semun {
    int val;
};
int main(void) {
    union semun semopts;
    struct sembuf sbuf = {0, -1, IPC_NOWAIT}; //每次减1
    key_t key;
    //获取键值
    key = ftok(".", 41);
    if(-1 == key) {
        perror("ftok");
        return 1;
    }
    //获取和键值相关联的semid
    int semid = semget(key, 1, IPC_CREAT|0664);
    if(-1 == semid) {
        perror("semget");
        return 2;
    }
    //初始化semval的值
    semopts.val = 30;
    //设置第一个信号量的值,下标为0
    int ret = semctl(semid, 0, SETVAL, semopts);
    if(-1 == ret) {
        perror("semctl");
        return 3;
    }
    while(1) {
        //将第一个信号量的值-1
        int sp = semop(semid, &sbuf, 1);
        if(-1 == sp) {
            perror("semop");
            return 4;
        }
        sleep(2);
    }
    return 0;
}


#include <stdio.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>


int main(void) {
    key_t key;
    //获取键值
    key = ftok(".", 41);
    if(-1 == key) {
        perror("ftok");
        return 1;
    }   
    //获取和键值相关的semid
    int semid = semget(key, 1, IPC_CREAT|0664);
    if(-1 == semid) {
        perror("semget");
        return 2;
    }   
    while(1) {
        int semval = semctl(semid, 0, GETVAL, 0); 
        if(-1 == semval) {
            perror("semctl");
            return 3;
        }   
        if(!semval) {
            printf("no sources..\n");
            break;
        } else {
            printf("sources %d...\n", semval);
        }   
        sleep(1);
    }   
    return 0;
}




三、system函数的使用
"system"(3)
#include <stdlib.h>
int system(const char *command);
功能:执行一个shell命令
参数:"command" 要执行的命令
返回值:
成功 - 返回command命令的退出状态码
失败 - 返回 -1
/** 举例验证: system.c **/
#include <stdio.h>
#include <stdlib.h>
int main(void) {
    system("bash"); 
    return 0;
}




四、编写一个web服务器端的程序,获取浏览器发送过来的信息
/** 代码: myweb.c **/#include <stdio.h>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h>#include <unistd.h>#include <strings.h>#include <arpa/inet.h>typedef struct sockaddr SA;typedef struct sockaddr_in SA4;int main(void) {    SA4 server;    char buf[1024] = {0};    int s_fd, conn_fd;    //创建通讯端套接字    s_fd = socket(AF_INET, SOCK_STREAM, 0);    if(-1 == s_fd) {        perror("soket");        return 1;    }    //初始化服务器的地址    bzero(&server, sizeof(server));    server.sin_family = AF_INET;    server.sin_port = htons(8080);    server.sin_addr.s_addr = htonl(INADDR_ANY);    //绑定套接字和服务器的地址    int b = bind(s_fd, (SA *)&server, sizeof(server));    if(-1 == b) {        perror("bind");        return 2;    }    //监听通讯端    listen(s_fd, 10);    while(1) {        conn_fd = accept(s_fd, NULL, NULL);        if(-1 == conn_fd) {            perror("accept");            return 3;        }        int r = read(conn_fd, buf, 1024);        write(1, buf, r);        write(1, "\n", 2);        close(conn_fd);    }    return 0;}

运行起来,浏览器输入"http://127.0.0.1:8080"


GET / HTTP/1.1
Host: 127.0.0.1:8080
User-Agent: Mozilla/5.0 (X11; Ubuntu; Linux i686; rv:29.0) Gecko/20100101 Firefox/29.0
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,* / *;q=0.8
Accept-Language: zh-cn,zh;q=0.8,en-us;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Connection: keep-alive
/* 此内容为http协议 */


http://127.0.0.1:8080/index.html
GET /index.html HTTP/1.1
/* 第二个内容为路径 */

0 1
原创粉丝点击