一、线程的同步之信号量
信号量用于多个资源的情况下的同步。
需要使用下面函数:
"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被设置
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服务器端的程序,获取浏览器发送过来的信息
运行起来,浏览器输入"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
/* 第二个内容为路径 */
信号量用于多个资源的情况下的同步。
需要使用下面函数:
"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 5
int 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
/* 第二个内容为路径 */