复习:
进程间通信方式:信号、管道、共享内存、消息队列、信号量、socket套接字
信号:
种类: kill -l (man 7 signal)
SIGINT
SIGQUIT
SIGALRM
SIGTSTP
信号的处理方式: 捕捉、忽略 、默认
函数: signal kill(pid,SIGINT); raise alarm
管道:
无名管道: pipe int pfd[2]; pfd[0]读端 pfd[1]写端
有名管道: mkfifo("pathname",0664);
新知识:
共享内存 --- 进程间通信最快
为了在多个进程间交换信息,内核专门留出一块内存区。这段内存可以由需要访问的进程
将其映射到自己的私有地址空间,因此进程就可以直接读写这一段内存区,而不需要数据的复制。
从而大大提高了效率。
一、 步骤
1. 创建共享内存对象 shmget
2. 映射 shmat
3. 读写
4. 解除映射 shmdt
5. 删除共享内存对象 shmctl
二、 函数
1. 创建共享内存对象 shmget
函数原型: int shmget(key_t key, size_t size, int shmflg);
头文件: #include <sys/ipc.h>
#include <sys/shm.h>
参数: 参数一: key :IPC_PRIVATE
共享内存的键值。
参数二: size : 共享内存大小
参数三: open() 函数的权限位。 O_RDONLY O_WRONLY O_RDWR O_CREAT 权限位也可以用8进制表示: 0664
*返回值: 成功: 共享内存段标识符 shmid
失败: -1
示例: shmget(IPC_PRIVATE,128,O_CREAT|O_WRONLY);
shmget(key,256,O_RDONLY);
函数2: ftok();
函数原型: key_t ftok(const char *pathname, int proj_id);
头文件: #include <sys/types.h>
#include <sys/ipc.h>
参数: 参数1: pathname 路径。 要求此路径必须是存在的。
参数2: proj_id 1 ~ 255 之间的整数即可。
*返回值: 是一个键值。可以提供给消息队列或者共享内存使用。
示例:
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <unistd.h>
#include <stdio.h>
#include <sys/stat.h>
#include <stdlib.h>
#include <fcntl.h>
int main()
{
key_t key = ftok("/mnt",'a');
int shmid = shmget(key,128,O_CREAT|O_WRONLY);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
printf("OK\n");
return 0;
}
2. 映射 shmat
函数原型: void *shmat(int shmid, const void *shmaddr, int shmflg);
头文件: #include <sys/types.h>
#include <sys/shm.h>
参数: 参数1: shmget 的返回值。
参数2; shmaddr 将共享内存映射到指定地址。 (通常写 NULL)
参数3: SHM_RDONLY 共享内存只读。
0 共享内存可读写
*返回值: 成功: 被映射的段地址
失败: -1
3. 读写
示例:
write.c
///
#include "my.h"
int main()
{
key_t key = ftok("/home",66);
// printf("key : %d\n",key);
int shmid = shmget(key,256,IPC_CREAT|0664); //当参数1为key时,权限使用的是: IPC_CREAT,不是O_CREAT
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
int *p = shmat(shmid,NULL,0);
*p = 999;
return 0;
}
read.c
/
#include "my.h"
int main()
{
key_t key = ftok("/home",66);
int shmid = shmget(key,256,0);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
int *p = shmat(shmid,NULL,0);
printf("%d\n",*p);
return 0;
}
//练习:进程a键盘获取字符串发给进程b 进程b收到后转为大写并输出
write.c
//
#include "my.h"
int main()
{
key_t key = ftok("/home",'r');
int shmid = shmget(key,256,IPC_CREAT|0777);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
char *p = shmat(shmid,NULL,0);
gets(p);
return 0;
}
read.c
///
#include "my.h"
void change(char *p)
{
int i;
for(i=0; p[i]!='\0'; i++)
{
if(p[i]>='a'&& p[i]<='z')
{
p[i] -= 32;
}
}
return;
}
int main()
{
key_t key = ftok("/home",'r');
int shmid = shmget(key,256,0);
if(shmid < 0)
{
perror("shmget");
exit(-1);
}
char *p = shmat(shmid,NULL,0);
change(p);
printf("%s\n",p);
return 0;
}
/ 查看共享内存信息: ipcs -m
/ 删除共享内存信息: ipcrm -m shmid
4. 解除映射 shmdt
函数原型: int shmdt(const void *shmaddr);
头文件: 同 shmat
参数: shmat 的返回值。
返回值: 成功: 0
失败: -1
5. 删除共享内存对象 shmctl
函数原型: int shmctl(int shmid, int cmd, struct shmid_ds *buf);
头文件: #include <sys/ipc.h>
#include <sys/shm.h>
参数: 参数1: shmget 的返回值。
参数2: IPC_STAT 获取共享内存状态信息
IPC_SET 设置共享内存信息
IPC_RMID 删除共享内存
参数3: struct shmid_ds *
struct shmid_ds {
struct ipc_perm shm_perm; /* Ownership and permissions */
size_t shm_segsz; /* Size of segment (bytes) */
time_t shm_atime; /* Last attach time */
time_t shm_dtime; /* Last detach time */
time_t shm_ctime; /* Last change time */
pid_t shm_cpid; /* PID of creator */
pid_t shm_lpid; /* PID of last shmat(2)/shmdt(2) */
shmatt_t shm_nattch; /* No. of current attaches */
...
};
//练习1. 一个进程将数据写入共享内存后 用信号通知另一个进程
另一个进程捕捉信号后 将信息打印
create.c
///
#include"../my.h"
int main()
{
key_t key = ftok("/home",'w');
if(key<0)
{
perror("ftok");
exit(-1);
}
int shmid = shmget(key,256,IPC_CREAT|0777);
if(shmid<0)
{
perror("shmget");
exit(-1);
}
return 0;
}
write.c
#include"../my.h"
int main(int argc,char*argv[])
{
if(argc!=2)
{
printf("%s pid\n",argv[0]);
exit(-1);
}
key_t key = ftok("/home",'w');
if(key<0)
{
perror("ftok");
exit(-1);
}
int shmid = shmget(key,0,0);
if(shmid<0)
{
perror("shmget");
exit(-1);
}
char *p = shmat(shmid,NULL,0);
if(p==(void*)-1)
{
perror("shmat");
exit(-1);
}
pid_t pid = atoi(argv[1]);
while(1)
{
puts("please input a string:");
gets(p);
kill(pid,SIGINT);
}
return 0;
}
read.c
/
#include"my.h"
char *p;
void deal_fun(int sig)
{
printf("%s\n",p);
}
int main()
{
printf("%d\n",getpid());
signal(SIGINT,deal_fun);
key_t key = ftok("/home",'f');
if(key<0)
{
perror("ftok");
exit(-1);
}
int shmid = shmget(key,0,0);
if(shmid<0)
{
perror("shmget");
exit(-1);
}
p = shmat(shmid,NULL,SHM_RDONLY);
if(p==(void*)-1)
{
perror("shmat");
exit(-1);
}
while(1)
{
;
}
return 0;
}
//
执行过程:
gcc create.c -o create
gcc write.c -o write
gcc read.c -o read
./create
./r (打印自己进程号)
./w pid (向进程号为 pid 的进程发送信号)
附: my.h
//
#ifndef _MY
#define _MY 1
#include <stdio.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <strings.h>
#include <string.h>
#include <stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include <signal.h>
#endif
三、函数(第二组)
1. 函数: ftruncate
函数原型: int ftruncate(int fd, off_t length);
头文件: #include <unistd.h>
#include <sys/types.h>
功能:将参数fd 指定的文件大小,改为length指定的大小
参数: fd 文件描述符。
length : 用这个参数,规定了fd 的大小。
2. mmap
函数原型: void *mmap(void *addr, size_t length, int prot, int flags, int fd, off_t offset);
头文件: #include <sys/mman.h>
功能: mmap将文件映射到调用进程的地址空间当中。当用mmap映射文件到进程后,
就可以直接操作这段地址空间进行文件读写操作。
参数:
参数1: 映射位置。 (一般写NULL, 系统会给分配地址)
参数2: 空间的大小。
参数3:PROT_EXEC Pages may be executed.
PROT_READ Pages may be read.
PROT_WRITE Pages may be written.
PROT_NONE Pages may not be accessed.
参数4:
MAP_SHARED 共享
MAP_PRIVATE 私有
参数5:指定要被映射的文件的描述符。
参数6:要映射的存储区,对应文件的起始偏移量。
返回值: 成功:实际的共享内存地址。
失败:MAP_FAILED ( that is (void *)-1 )
使用示例: mmap(NULL, 1024, PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);
3. int munmap(void *addr, size_t length);
功能: 解除映射。
参数: 参数一 : mmap 的返回值。
参数二 : length 共享内存大小。
返回值: 成功: 0
失败: -1
示例:
write.c
1)打开文件
2)设置文件大小
3)映射
4)输入信息到共享内存/输出
5)解除映射
6)关闭文件描述符
read.c
步骤同上。
write.c
//
#include "../my.h"
#include <sys/mman.h>
int main()
{
int fd = open("file",O_CREAT|O_EXCL|O_RDWR,0777);
if(fd < 0)
{
perror("open");
exit(-1);
}
ftruncate(fd,1024);
int *p = mmap(NULL,1024,PROT_WRITE,MAP_SHARED,fd,0);
if(p == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
*p = 12345;
munmap(p,1024);
close(fd);
return 0;
}
read.c
///
#include "../my.h"
#include <sys/mman.h>
int main()
{
int fd = open("file",O_RDWR);
if(fd < 0)
{
perror("open");
exit(-1);
}
ftruncate(fd,1024);
int *p = (int *)mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
printf("%d\n",*p);
munmap(p,1024);
close(fd);
return 0;
}
//练习:进程1从键盘获取一个字符串 传递给进程2 进程2收到串后 转为大写 在输出到屏幕
write.c
/
#include "../my.h"
#include <sys/mman.h>
int main()
{
int fd = open("file",O_CREAT|O_EXCL|O_RDWR,0777);
if(fd < 0)
{
perror("open");
exit(-1);
}
ftruncate(fd,1024);
char *p = mmap(NULL,1024,PROT_WRITE,MAP_SHARED,fd,0);
if(p == MAP_FAILED)
{
perror("mmap");
exit(-1);
}
puts("Input a string:");
gets(p);
munmap(p,1024);
close(fd);
return 0;
}
read.c
//
#include "../my.h"
#include <sys/mman.h>
void change(char *p)
{
int i;
for(i=0; p[i]!='\0'; i++)
{
if(p[i]>='a'&& p[i]<='z')
{
p[i] -= 32;
}
}
return;
}
int main()
{
int fd = open("file",O_RDWR);
if(fd < 0)
{
perror("open");
exit(-1);
}
ftruncate(fd,1024);
char *p = (char *)mmap(NULL,1024,PROT_READ|PROT_WRITE,MAP_SHARED,fd,0);
change(p);
printf("%s\n",p);
munmap(p,1024);
close(fd);
return 0;
}
//作业答案: 有名管道版本的 cat 功能:
create.c
#include "my.h"
int main(int argc,char *argv[])
{
if(argc != 2)
{
printf("%s pipe_name\n",argv[0]);
}
int ret = mkfifo(argv[1],0644);
if(ret < 0)
{
perror("mkfifo");
exit(-1);
}
return 0;
}
write.c
///
#include "my.h"
int main(int argc,char *argv[])
{
if(argc != 3)
{
printf("%s pipe_name src_name:\n",argv[0]);
exit(-1);
}
int fd = open(argv[1],O_WRONLY);
int file_fd = open(argv[2],O_RDONLY);
if(fd < 0||file_fd < 0)
{
perror("open");
exit(-1);
}
char buf[50] = "\0";
while(1)
{
bzero(buf,sizeof(buf)) ;
int ret = read(file_fd,buf,sizeof(buf));
if(ret == 0)
{
break;
}
write(fd,buf,sizeof(buf));
}
close(file_fd);
close(fd);
return 0;
}
read.c
/
#include "my.h"
int main(int argc,char *argv[])
{
if(argc != 2)
{
printf("%s pipe_name:\n",argv[0]);
exit(0);
}
int fd = open(argv[1],O_RDONLY);
if(fd < 0)
{
perror("open");
exit(-1);
}
char buf[50] = "\0";
while(1)
{
bzero(buf,sizeof(buf));
int ret = read(fd,buf,sizeof(buf));
if(ret == 0)
{
break;
}
puts(buf);
}
close(fd);
unlink(argv[1]);
return 0;
}
复习本周内容:
标准IO :
FILE *fp = fopen("file.c",'w+');
fclose(fp);
perror(); %s -- strerror(errno);
c = fgetc(fp); fputc(c,fp);
fgets(buf,n,fp); fputs(buf,fp);
fread(buf,4,100,fp); fwrite(buf,4,100,fp);
fseek(fp,-1,SEEK_SET); num = ftell(fp);
fprintf(fp,"%d,%d\n",a,b); fscanf(fp,"%d%s",&a,s);
fnprintf(fp,50,"%d,%d\n",a,b);
文件IO :
int fd = open("/home/linux/a.c",O_CREAT|O_EXCL|O_RDWR|O_TRUNC,0664);
close(fd);
read(fd,buf,100);
write(fd,buf,100);
lseek(fd,0,SEEK_SET);
bzero(buf,100);
menset(buf,'a',100); //一个字节一个字节 操作。
目录文件:
DIR *dirp = opendir("/home/linux");
closedir(dirp);
struct dirent *p = readdir(dirp);
struct dirent {
d_ino;
d_time;
...
d_name[256];
};
printf("%s\n",p->d_name);
stat("/home/linux",&a);
struct stat {
st_ino;
st_mode;
st_nlink;
...
};
a.st_mode --> S_ISREG() S_ISDIR()
进程线程:
day1: 进程、守护进程
ps -ef
getpid();
getppid();
fork();
if(id == 0)
{
child ;
}
else if(id > 0)
{
father ;
}
exit(0);
_exit(0);
wait(NULL); wait(&a); // a 是输出型参数:函数执行完,变量a 获取一个值。
waitpid(pid,&a,0); // 0 阻塞状态 WNOHANG 非阻塞
execl("/bin/ls","ls","-l",NULL);
execv("/bin/ls",cmd); char *cmd[] = {"ls","-l",NULL};
execvp("ls",cmd); char *cmd[] = {"ls","-l",NULL};
守护进程:
1. fork(); if(id > 0) exit(0);
2. setsid();
3. chdir("/tmp");
4. umask(0);
5. close(0); close(1); close(2);
进程间通信方式: 信号、管道、共享内存、消息队列、信号量、socket套接字
day2: 信号、管道
man 7 signal
signal(SIGINT,fun);
signal(SIGALRM,SIG_IGN);
signal(SIGTSTP,SIG_DFL);
kill(pid, SIGALRM);
raise(SIGALRM);
alarm(45);
管道:
无名管道:
单工、亲缘关系的进程间、不是文件
先创建管道、后创建进程。
pipe(int pfd[2]); pfd[0] 读。 pfd[1] 写。
有名管道:
是文件。任意进程间通信。
mkfifo("./fifo",0664);
day3: 共享内存
key = ftok("/home",88);
shmid = shmget(key,128,IPC_CREAT|O_RDWR|0777);
void *p = shmat(shmid,NULL,0); // NULL 系统会自动分配地址。 0 : 可读写(权限)
p;
shmdt(p);
shmctl(shmid,IPC_RMID,NULL);
//作业:创建子进程代表售票员 父进程代表司机
售票员捕捉信号SIGINT(代表开车) 发SIGUSR1给司机 司机打印"ready-go!"
售票员捕捉SIGQUIT(代表停车) 发SIGUSR2给司机 司机打印 "stop..."
售票员捕捉SIGTSTP(代表车到总站)发SIGUSR1给售票员 售票员打印 get off!
//作业:实现shell组合 ls|grep w
提示:用pipe实现
1 fork
2 dup2(pfd[1],1) //所有输出到屏幕的数据都会输出到管道pfd[1]
3 execlp("ls","ls",NULL)
4 dup2(pfd[0],0);//从标准输入里读 改成从管道中读
5 execlp("grep","grep","w",NULL);