构建一个Linux系统,需要考虑一下几点:
要搭建嵌入式______环境
什么是Bootloader?
U-Boot的编译与使用
U-Boot常用命令
什么是API?
虚拟文件系统(VFS)的概念
通常,一个进程启动时,都要打开三个文件:______ ,______ ,______.
这三个文件对应的文件描述符分别为0,1,2,也就是宏替换STDIN_FILENO,STDOUT_FILENO和STDERR_FILENO.
2.3.1 基本文件操作
课本P42
copy_file.c
拷贝好代码,使用管理员身份创建dest_file和src_file两个文件,在src_file中输入随便一些字符串,运行a.out后会把src中内容复制到dest中:
/* copy_file.c */
#include
#include
#include
#include
#include
#include
#define BUFFER_SIZE 1024 /* 每次读写缓存大小,影响运行效率 */
#define SRC_FILE_NAME "src_file" /* 源文件名 */
#define DEST_FILE_NAME "dest_file" /* 目标文件名 */
#define OFFSET 10240 /* 复制的数据大小 */
int main()
{
int src_file, dest_file;
unsigned char buff[BUFFER_SIZE];
int real_read_len;
/* 以只读方式打开源文件 */
src_file = open(SRC_FILE_NAME, O_RDONLY);
/* 以只写方式打开目标文件,若此文件不存在则创建该文件,访问权限值为644 */
dest_file = open(DEST_FILE_NAME, O_WRONLY | O_CREAT,
S_IRUSR | S_IWUSR | S_IRGRP | S_IROTH);
if(src_file < 0 || dest_file < 0)
{
printf("Open file error\n");
exit(1);
}
/* 将源文件的读写指针移到最后10KB的起始位置 */
lseek(src_file, -OFFSET, SEEK_END);
/* 读取源文件的最后10KB数据并写到目标文件中,每次读写1KB */
while((real_read_len = read(src_file, buff, sizeof(buff))) > 0)
{
write(dest_file, buff, real_read_len);
}
close(dest_file);
close(src_file);
return 0;
}
fcntl()函数
P46
记录锁又可以分为______和______,其中前者又称为______,后者又称为______。
fcntl()函数中lock结构体取值含义
P47
I/O处理的模型有以下五种:
详见P51
select()函数语法要点
poll()函数语法要点
multiplex_poll.c
新建in1和in2两个文件,全部过程都要sudo,否则权限不够
/* multiplex_poll.c */
#include
#include
#include
#include
#include
#include
#include
#include
#define MAX_BUFFER_SIZE 1024 /* 缓冲区大小 */
#define IN_FILES 3 /* 多路复用输入文件数目 */
#define TIME_DELAY 60000 /* 超时时间秒数:60秒 */
#define MAX(a, b) ((a > b) ? (a) : (b))
int main(void)
{
struct pollfd fds[IN_FILES];
char buf[MAX_BUFFER_SIZE];
int i, res, real_read, maxfd;
/*首先按一定的权限打开两个源文件*/
fds[0].fd = 0;
if((fds[1].fd = open ("in1", O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Open in1 error\n");
return 1;
}
if((fds[2].fd = open ("in2", O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Open in2 error\n");
return 1;
}
/*取出两个文件描述符中的较大者*/
for (i = 0; i < IN_FILES; i++)
{
fds[i].events = POLLIN;
}
/* 循环测试是否存在正在监听的文件描述符 */
while(fds[0].events || fds[1].events || fds[2].events)
{
if (poll(fds, IN_FILES, 0) < 0)
{
printf("Poll error or Time out\n");
return 1;
}
for (i = 0; i< IN_FILES; i++)
{
if (fds[i].revents) /* 判断在哪个文件上发生了事件*/
{
memset(buf, 0, MAX_BUFFER_SIZE);
real_read = read(fds[i].fd, buf, MAX_BUFFER_SIZE);
if (real_read < 0)
{
if (errno != EAGAIN)
{
return 1; /* 系统错误,结束运行*/
}
}
else if (!real_read)
{
close(fds[i].fd);
fds[i].events = 0; /* 取消对该文件的监听 */
}
else
{
if (i == 0) /* 如果在标准输入上有数据输入时 */
{
if ((buf[0] == 'q') || (buf[0] == 'Q'))
{
return 1; /*输入"q"或"Q"则会退出*/
}
}
else
{ /* 将读取的数据先是到终端上 */
buf[real_read] = '\0';
printf("%s", buf);
}
} /* end of if real_read*/
} /* end of if revents */
} /* end of for */
} /*end of while */
exit(0);
}
串口属性的配置流程:
标准I/O提供了3种类型的缓冲存储
详见P70
fopen()函数语法要点
字符输出函数语法要点
实验:文件读写及上锁
终端1:
./producer 1 20
终端2:
./customer 5
producer.c
/* producer.c */
#include
#include
#include
#include
#include
#include "mylock.h"
#define MAXLEN 10 /* 缓冲区大小最大值*/
#define ALPHABET 1 /* 表示使用英文字符 */
#define ALPHABET_START 'a' /* 头一个字符,可以用 'A'*/
#define COUNT_OF_ALPHABET 26 /* 字母字符的个数 */
#define DIGIT 2 /* 表示使用数字字符 */
#define DIGIT_START '0' /* 头一个字符 */
#define COUNT_OF_DIGIT 10 /* 数字字符的个数 */
#define SIGN_TYPE ALPHABET /* 本实例选用英文字符 */
const char *fifo_file = "./myfifo"; /* 仿真FIFO文件名 */
char buff[MAXLEN]; /* 缓冲区 */
/* 功能:生产一个字符并写入到仿真FIFO文件中 */
int product(void)
{
int fd;
unsigned int sign_type, sign_start, sign_count, size;
static unsigned int counter = 0;
/* 打开仿真FIFO文件 */
if ((fd = open(fifo_file, O_CREAT|O_RDWR|O_APPEND, 0644)) < 0)
{
printf("Open fifo file error\n");
exit(1);
}
sign_type = SIGN_TYPE;
switch(sign_type)
{
case ALPHABET:/* 英文字符 */
{
sign_start = ALPHABET_START;
sign_count = COUNT_OF_ALPHABET;
}
break;
case DIGIT:/* 数字字符 */
{
sign_start = DIGIT_START;
sign_count = COUNT_OF_DIGIT;
}
break;
default:
{
return -1;
}
}/*end of switch*/
sprintf(buff, "%c", (sign_start + counter));
counter = (counter + 1) % sign_count;
lock_set(fd, F_WRLCK); /* 上写锁*/
if ((size = write(fd, buff, strlen(buff))) < 0)
{
printf("Producer: write error\n");
return -1;
}
lock_set(fd, F_UNLCK); /* 解锁 */
close(fd);
return 0;
}
int main(int argc ,char *argv[])
{
int time_step = 1; /* 生产周期 */
int time_life = 10; /* 需要生产的资源总数 */
if (argc > 1)
{/* 第一个参数表示生产周期 */
sscanf(argv[1], "%d", &time_step);
}
if (argc > 2)
{/* 第二个参数表示需要生产的资源数 */
sscanf(argv[2], "%d", &time_life);
}
while (time_life--)
{
if (product() < 0)
{
break;
}
sleep(time_step);
}
exit(EXIT_SUCCESS);
}
customer.c
/* customer.c */
#include
#include
#include
#include
#define MAX_FILE_SIZE 100 * 1024 * 1024 /* 100M*/
const char *fifo_file = "./myfifo"; /* 仿真FIFO文件名 */
const char *tmp_file = "./tmp"; /* 临时文件名 */
/* 资源消费函数 */
int customing(const char *myfifo, int need)
{
int fd;
char buff;
int counter = 0;
if ((fd = open(myfifo, O_RDONLY)) < 0)
{
printf("Function customing error\n");
return -1;
}
printf("Enjoy:");
lseek(fd, SEEK_SET, 0);
while (counter < need)
{
while ((read(fd, &buff, 1) == 1) && (counter < need))
{
fputc(buff, stdout); /* 消费就是在屏幕上简单的显示 */
counter++;
}
}
fputs("\n", stdout);
close(fd);
return 0;
}
/* 功能:从sour_file文件的offset偏移处开始
将count个字节数据拷贝到dest_file文件 */
int myfilecopy(const char *sour_file,
const char *dest_file, int offset, int count, int copy_mode)
{
int in_file, out_file;
int counter = 0;
char buff_unit;
if ((in_file = open(sour_file, O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Function myfilecopy error in source file\n");
return -1;
}
if ((out_file = open(dest_file,
O_CREAT|O_RDWR|O_TRUNC|O_NONBLOCK, 0644)) < 0)
{
printf("Function myfilecopy error in destination file:");
return -1;
}
lseek(in_file, offset, SEEK_SET);
while ((read(in_file, &buff_unit, 1) == 1) && (counter < count))
{
write(out_file, &buff_unit, 1);
counter++;
}
close(in_file);
close(out_file);
return 0;
}
/* 功能:实现FIFO消费者 */
int custom(int need)
{
int fd;
/* 对资源进行消费,need表示该消费的资源数目 */
customing(fifo_file, need);
if ((fd = open(fifo_file, O_RDWR)) < 0)
{
printf("Function myfilecopy error in source_file:");
return -1;
}
/* 为了模拟FIFO结构,对整个文件内容进行平行移动 */
lock_set(fd, F_WRLCK);
myfilecopy(fifo_file, tmp_file, need, MAX_FILE_SIZE, 0);
myfilecopy(tmp_file, fifo_file, 0, MAX_FILE_SIZE, 0);
lock_set(fd, F_UNLCK);
unlink(tmp_file);
close(fd);
return 0;
}
int main(int argc ,char *argv[])
{
int customer_capacity = 10;
if (argc > 1) /* 第一个参数指定需要消费的资源数目,默认值为10 */
{
sscanf(argv[1], "%d", &customer_capacity);
}
if (customer_capacity > 0)
{
custom(customer_capacity);
}
exit(EXIT_SUCCESS);
}
任务,进程和线程之间的关系
进程的基本概念:
进程具有______ , ______ , ______ , ______和______等主要特性
Linux系统包括以下3种类型的进程
详解见P93
Linux中进程有一下几种状态
详解见P94
获取当前进程号和父进程号的函数分别为:
进程的终止中,父进程得到信息后,开始调用______函数族
线程是系统中函数执行和资源分配的基本单位,它是进程内独立的一条运行路线,是处理器调度的最小单元,也可以称为轻量级进程
进程和线程之间的关系图
线程总共分为以下三种
fork函数语法要点及简单示例程序
P99-P100
fork.c
#include
#include
int main ()
{
pid_t fpid; //fpid表示fork函数返回的值
int count=0;
fpid=fork();
if (fpid < 0)
printf("error in fork!");
else if (fpid == 0) {
printf("i am the child process, my process id is %d/n",getpid());
printf("我是爹的儿子/n");
count++;
}
else {
printf("i am the parent process, my process id is %d/n",getpid());
printf("我是孩子他爹/n");
count++;
}
printf("统计结果是: %d/n",count);
return 0;
}
// i am the child process, my process id is 5574
// 我是爹的儿子
// 统计结果是: 1
// i am the parent process, my process id is 5573
// 我是孩子他爹
// 统计结果是: 1
在进程中调用exec函数族有两种情况:
exec函数族使用示例
execlp.c
/* execlp.c */
#include
#include
#include
int main()
{
if(fork() == 0)
{
/* 调用execlp()函数,这里相当于调用了"ps -ef"命令 */
if((ret = execlp("ps", "ps", "-ef", NULL)) < 0)
{
printf("Execlp error\n");
}
}
}
exit()和_exit()函数
exit()函数使用示例
exit.c
/* exit.c */
#include
#include
int main()
{
printf("Using exit...\n");
printf("This is the content in buffer");
exit(0);
}
_exit()函数使用示例
_exit.c
/* _exit.c */
#include
#include
int main()
{
printf("Using _exit...\n");
printf("This is the content in buffer");
_exit(0);
}
wait()和waitpid()函数格式说明
waitpid()函数示例
waitpid.c
/* waitpid.c */
#include
#include
#include
#include
#include
int main()
{
pid_t pc, pr;
pc = fork();
if( pc < 0 )
{
printf("Error fork\n");
exit(1);
}
else if( pc == 0 ) /* 子进程 */
{
/* 子进程暂停5s */
sleep(5);
/* 子进程正常退出 */
exit(0);
}
else /* 父进程 */
{
/* 循环测试子进程是否退出 */
do
{
/* 调用waitpid,且父进程不阻塞 */
pr = waitpid(pc, NULL, WNOHANG);
/* 若子进程还未退出,则父进程暂停1s */
if( pr == 0 )
{
printf("The child process has not exited\n");
sleep(1);
}
}while( pr == 0 );
/* 若发现子进程退出,打印出相应情况 */
if( pr == pc )
{
printf("Get child exit code: %d\n",pr);
}
else
{
printf("Some error occured.\n");
}
}
}
守护进程概述
编写守护进程的五步
程序阅读题:daemon.c
daemon.c
/* daemon.c创建守护进程实例 */
#include
#include
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
int i, fd;
char* buf = "This is a Daemon\n";
pid = fork(); /* 第一步 */
if(pid < 0)
{
printf("Error fork\n");
exit(1);
}
else if(pid > 0)
{
exit(0); /* 父进程退出 */
}
setsid(); /* 第二步 */
chdir("/"); /* 第三步 */
umask(0); /* 第四步 */
for(i = 0; i < getdtablesize(); i++) /* 第五步 */
{
close(i);
}
/* 这时创建完守护进程,以下开始正式进入守护进程工作 */
while(1)
{
if((fd = open("/tmp/daemon.log", O_CREAT | O_WRONLY | O_APPEND, 0600)) < 0)
{
printf("Open file error\n");
exit(1);
}
write(fd, buf, strlen(buf) + 1);
close(fd);
sleep(10);
}
return 0;
}
僵尸进程的产生条件
实验:编写多进程程序
multi_proc_wrong.c
/* multi_proc_wrong.c */
#include
#include
#include
#include
#include
int main(void)
{
pid_t child1, child2, child;
/*创建两个子进程*/
child1 = fork();
child2 = fork();
/*子进程1的出错处理*/
if (child1 == -1)
{
printf("Child1 fork error\n");
exit(1);
}
else if (child1 == 0) /*在子进程1中调用execlp()函数*/
{
printf("In child1: execute 'ls -l'\n");
if (execlp("ls", "ls", "-l", NULL) < 0)
{
printf("Child1 execlp error\n");
}
}
if (child2 == -1) /*子进程2的出错处理*/
{
printf("Child2 fork error\n");
exit(1);
}
else if( child2 == 0 ) /*在子进程2中使其暂停5s*/
{
printf("In child2: sleep for 5 seconds and then exit\n");
sleep(5);
exit(0);
}
else /*在父进程中等待两个子进程的退出*/
{
printf("In father process:\n");
child = waitpid(child1, NULL, 0); /* 阻塞式等待 */
if (child == child1)
{
printf("Get child1 exit code\n");
}
else
{
printf("Error occured!\n");
}
do
{
child = waitpid(child2, NULL, WNOHANG);/* 非阻塞式等待 */
if (child == 0)
{
printf("The child2 process has not exited!\n");
sleep(1);
}
} while (child == 0);
if (child == child2)
{
printf("Get child2 exit code\n");
}
else
{
printf("Error occured!\n");
}
}
return 0;
}
multi_proc.c
/* multi_proc.c */
#include
#include
#include
#include
#include
int main(void)
{
pid_t child1, child2, child;
/*创建两个子进程*/
child1 = fork();
/*子进程1的出错处理*/
if (child1 == -1)
{
printf("Child1 fork error\n");
exit(1);
}
else if (child1 == 0) /*在子进程1中调用execlp()函数*/
{
printf("In child1: execute 'ls -l'\n");
if (execlp("ls", "ls", "-l", NULL) < 0)
{
printf("Child1 execlp error\n");
}
}
else /*在父进程中再创建进程2,然后等待两个子进程的退出*/
{
child2 = fork();
if (child2 == -1) /*子进程2的出错处理*/
{
printf("Child2 fork error\n");
exit(1);
}
else if(child2 == 0) /*在子进程2中使其暂停5s*/
{
printf("In child2: sleep for 5 seconds and then exit\n");
sleep(5);
exit(0);
}
printf("In father process:\n");
child = waitpid(child1, NULL, 0); /* 阻塞式等待 */
if (child == child1)
{
printf("Get child1 exit code\n");
}
else
{
printf("Error occured!\n");
}
do
{
child = waitpid(child2, NULL, WNOHANG);/* 非阻塞式等待 */
if (child == 0)
{
printf("The child2 process has not exited!\n");
sleep(1);
}
} while (child == 0);
if (child == child2)
{
printf("Get child2 exit code\n");
}
else
{
printf("Error occured!\n");
}
}
return 0;
}
无名管道
有名管道
pipe.c
/* pipe.c */
#include
#include
#include
#include
#include
#define MAX_DATA_LEN 256
#define DELAY_TIME 1
int main()
{
pid_t pid;
int pipe_fd[2];
char buf[MAX_DATA_LEN];
const char data[] = "Pipe Test Program";
int real_read, real_write;
memset((void*)buf, 0, sizeof(buf));
if (pipe(pipe_fd) < 0) /* 创建管道 */
{
printf("pipe create error\n");
exit(1);
}
if ((pid = fork()) == 0) /* 创建一子进程 */
{
/* 子进程关闭写描述符,并通过使子进程暂停1秒等待父进程已关闭相应的读描述符 */
close(pipe_fd[1]);
sleep(DELAY_TIME * 3);
/* 子进程读取管道内容 */
if ((real_read = read(pipe_fd[0], buf, MAX_DATA_LEN)) > 0)
{
printf("%d bytes read from the pipe is '%s'\n", real_read, buf);
}
close(pipe_fd[0]); /* 关闭子进程读描述符 */
exit(0);
}
else if (pid > 0)
{
/* 父进程关闭读描述符,并通过使父进程暂停1秒等待子进程已关闭相应的写描述符 */
close(pipe_fd[0]);
sleep(DELAY_TIME);
if((real_write = write(pipe_fd[1], data, strlen(data))) != -1)
{
printf("Parent wrote %d bytes : '%s'\n", real_write, data);
}
close(pipe_fd[1]); /*关闭父进程写描述符*/
waitpid(pid, NULL, 0); /*收集子进程退出信息*/
exit(0);
}
}
信号概述
相应信号的3种方式
信号发送:kill()和raise()
信号发送与捕捉
kill_raise.c
/* kill_raise.c */
#include
#include
#include
#include
#include
int main()
{
pid_t pid;
int ret;
/* 创建一子进程 */
if((pid = fork()) < 0)
{
printf("Fork error\n");
exit(1);
}
if(pid == 0)
{
/* 在子进程中使用raise()函数发出SIGSTOP信号,使子进程暂停 */
printf("Child(pid : %d) is waiting for any signal\n",getpid());
raise(SIGSTOP);
exit(0);
}
else
{
/* 在父进程中收集子进程发出的信号,并调用kill()函数进行相应的操作 */
if((waitpid(pid, NULL, WNOHANG)) == 0)
{
if((ret == kill(pid, SIGKILL)) == 0)
{
printf("Parent kill %d\n",pid);
}
}
waitpid(pid, NULL, 0);
exit(0);
}
}
alarm_pause.c
alarm_pause.c
/* alarm_pause.c */
#include
#include
#include
int main()
{
/* 调用alarm定时器函数 */
int ret = alarm(5);
pause();
printf("I have been waken up.\n"); /* 此语句不会被执行 */
return 0;
}
信号量概念
PV 原子操作具体定义
使用信号量分为以下4个步骤
共享内存定义
消息队列:一些消息的列表
有名管道通信实验
pipe_select.c
/* pipe_select.c*/
#include
#include
#include
#include
#include
#include
#include
#define FIFO1 "in1"
#define FIFO2 "in2"
#define MAX_BUFFER_SIZE 1024 /* 缓冲区大小*/
#define IN_FILES 3 /* 多路复用输入文件数目*/
#define TIME_DELAY 60 /* 超时值秒数 */
#define MAX(a, b) ((a > b)?(a):(b))
int main(void)
{
int fds[IN_FILES];
char buf[MAX_BUFFER_SIZE];
int i, res, real_read, maxfd;
struct timeval tv;
fd_set inset,tmp_inset;
fds[0] = 0;
/* 创建两个有名管道 */
if (access(FIFO1, F_OK) == -1)
{
if ((mkfifo(FIFO1, 0666) < 0) && (errno != EEXIST))
{
printf("Cannot create fifo file\n");
exit(1);
}
}
if (access(FIFO2, F_OK) == -1)
{
if ((mkfifo(FIFO2, 0666) < 0) && (errno != EEXIST))
{
printf("Cannot create fifo file\n");
exit(1);
}
}
/* 以只读非阻塞方式打开两个管道文件 */
if((fds[1] = open (FIFO1, O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Open in1 error\n");
return 1;
}
if((fds[2] = open (FIFO2, O_RDONLY|O_NONBLOCK)) < 0)
{
printf("Open in2 error\n");
return 1;
}
/*取出两个文件描述符中的较大者*/
maxfd = MAX(MAX(fds[0], fds[1]), fds[2]);
/*初始化读集合inset,并在读文件描述符集合中加入相应的描述集*/
FD_ZERO(&inset);
for (i = 0; i < IN_FILES; i++)
{
FD_SET(fds[i], &inset);
}
FD_SET(0, &inset);
tv.tv_sec = TIME_DELAY;
tv.tv_usec = 0;
/*循环测试该文件描述符是否准备就绪并调用select()函数对相关文件描述符做相应操作*/
while(FD_ISSET(fds[0],&inset)
|| FD_ISSET(fds[1],&inset) || FD_ISSET(fds[2], &inset))
{
/* 文件描述符集合的备份, 免得每次进行初始化 */
tmp_inset = inset;
res = select(maxfd + 1, &tmp_inset, NULL, NULL, &tv);
switch(res)
{
case -1:
{
printf("Select error\n");
return 1;
}
break;
case 0: /* Timeout */
{
printf("Time out\n");
return 1;
}
break;
default:
{
for (i = 0; i < IN_FILES; i++)
{
if (FD_ISSET(fds[i], &tmp_inset))
{
memset(buf, 0, MAX_BUFFER_SIZE);
real_read = read(fds[i], buf, MAX_BUFFER_SIZE);
if (real_read < 0)
{
if (errno != EAGAIN)
{
return 1;
}
}
else if (!real_read)
{
close(fds[i]);
FD_CLR(fds[i], &inset);
}
else
{
if (i == 0)
{ /* 主程序终端控制 */
if ((buf[0] == 'q') || (buf[0] == 'Q'))
{
return 1;
}
}
else
{ /* 显示管道输入字符串 */
buf[real_read] = '\0';
printf("%s", buf);
}
}
} /* end of if */
} /* end of for */
}
break;
} /* end of switch */
} /* end of while */
return 0;
}
共享内存实验
shm_com.h
/* shm_com.h */
#include
#include
#include
#include
#include
#include
#include
#define SHM_BUFF_SZ 2048
struct shm_buff
{
int pid;
char buffer[SHM_BUFF_SZ];
};
producer.c
/* producer.c */
#include "shm_com.h"
#include "sem_com.c"
#include
int ignore_signal(void)
{ /* 忽略一些信号,免得非法退出程序 */
signal(SIGINT, SIG_IGN);
signal(SIGSTOP, SIG_IGN);
signal(SIGQUIT, SIG_IGN);
return 0;
}
int main()
{
void *shared_memory = NULL;
struct shm_buff *shm_buff_inst;
char buffer[BUFSIZ];
int shmid, semid;
/* 定义信号量,用于实现访问共享内存的进程之间的互斥*/
ignore_signal(); /* 防止程序非正常退出 */
semid = semget(ftok(".", 'a'), 1, 0666|IPC_CREAT); /* 创建一个信号量*/
init_sem(semid);/* 初始值为1 */
/* 创建共享内存 */
shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
if (shmid == -1)
{
perror("shmget failed");
del_sem(semid);
exit(1);
}
/* 将共享内存地址映射到当前进程地址空间 */
shared_memory = shmat(shmid, (void*)0, 0);
if (shared_memory == (void*)-1)
{
perror("shmat");
del_sem(semid);
exit(1);
}
printf("Memory attached at %X\n", (int)shared_memory);
/* 获得共享内存的映射地址 */
shm_buff_inst = (struct shared_use_st *)shared_memory;
do
{
sem_p(semid);
printf("Enter some text to the shared memory(enter 'quit' to exit):");
/* 向共享内存写入数据 */
if (fgets(shm_buff_inst->buffer, SHM_BUFF_SZ, stdin) == NULL)
{
perror("fgets");
sem_v(semid);
break;
}
shm_buff_inst->pid = getpid();
sem_v(semid);
} while(strncmp(shm_buff_inst->buffer, "quit", 4) != 0);
/* 删除信号量 */
del_sem(semid);
/* 删除共享内存到当前进程地址空间中的映射 */
if (shmdt(shared_memory) == 1)
{
perror("shmdt");
exit(1);
}
exit(0);
}
customer.c
/* customer.c */
#include "shm_com.h"
#include "sem_com.c"
int main()
{
void *shared_memory = NULL;
struct shm_buff *shm_buff_inst;
int shmid, semid;
/* 获得信号量 */
semid = semget(ftok(".", 'a'), 1, 0666);
if (semid == -1)
{
perror("Producer is'nt exist");
exit(1);
}
/* 获得共享内存 */
shmid = shmget(ftok(".", 'b'), sizeof(struct shm_buff), 0666|IPC_CREAT);
if (shmid == -1)
{
perror("shmget");
exit(1);
}
/* 将共享内存地址映射到当前进程地址空间 */
shared_memory = shmat(shmid, (void*)0, 0);
if (shared_memory == (void*)-1)
{
perror("shmat");
exit(1);
}
printf("Memory attached at %X\n", (int)shared_memory);
/* 获得共享内存的映射地址 */
shm_buff_inst = (struct shm_buff *)shared_memory;
do
{
sem_p(semid);
printf("Shared memory was written by process %d :%s"
, shm_buff_inst->pid, shm_buff_inst->buffer);
if (strncmp(shm_buff_inst->buffer, "quit", 4) == 0)
{
break;
}
shm_buff_inst->pid = 0;
memset(shm_buff_inst->buffer, 0, SHM_BUFF_SZ);
sem_v(semid);
} while(1);
/* 删除共享内存到当前进程地址空间中的映射 */
if (shmdt(shared_memory) == -1)
{
perror("shmdt");
exit(1);
}
/* 删除共享内存 */
if (shmctl(shmid, IPC_RMID, NULL) == -1)
{
perror("shmctl(IPC_RMID)");
exit(1);
}
exit(0);
}
pthread_create()
互斥锁可以分为______ ,______ ,______。
信号量互斥操作和信号量同步操作流程图
绑定属性
分离属性
北平无战事,六章无大题
OSI模型和TCP/IP参考模型对应关系
TCP/IP 分层模型的4个特点以及各自特性
socket定义
套接字类型
sockaddr和sockaddr_in
inet_pton()函数特点
gethostbyaddr()和getaddrinfo()函数
P209程序要读懂
P211两个流程图