41、linux系统进程间有哪些通信方式
管道、有名管道、消息队列、信号、共享内存、socket、文件
管道及有名管道:管道可用于具有亲缘关系进程间的通信,例如父子进程,但是有名管道允许无关系的进程间通信。管道其实就是建立一个FIFO文件,一个进程往里面写数据,另外的进程读取数据。
demo如下:
#include <stdio.h>
#include <unistd.h>
#include <string.h>
#include <sys/wait.h>
#include <stdlib.h>
void sys_err(const char *str)
{
perror(str);
exit(1);
}
int main()
{
pid_t pid;
char buf[1024];
int fd[2];
char *p = "test for pipe\n";
if (pipe(fd) == -1)
sys_err("pipe");
pid = fork();
if (pid < 0)
{
sys_err("fork err");
}
else if(pid == 0)
{
//父进程
close(fd[1]);
int len = read(fd[0], buf, sizeof(buf));
write(STDOUT_FILENO, buf, len); //输出到屏幕
close(fd[0]);
}
else
{
//子进程
close(fd[0]);
write(fd[1], p, strlen(p)); //写入test for pipe
wait(NULL);
close(fd[1]);
}
return 0;
}
消息队列:也叫报文队列,消息队列是消息的链接表,包括Posix消息队列和SystemV消息队列,有足够权限的进程可以向队列中添加消息,被赋予读权限的进程则可以读走队列中的信息,消息队列克服了信号承载信息量少,管道只能承载无格式字节流以及缓冲区大小受限等缺点。
发送消息demo如下:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st
{
long int my_msg_type;
char some_text[BUFSIZ];
};
int main(void)
{
int running = 1;
struct my_msg_st some_data;
int msgid = 0;
char buffer[BUFSIZ] = {0};
memset((void*)&some_data, 0, sizeof(some_data));
/*创建消息队列*/
msgid=msgget((key_t)1234,0666 | IPC_CREAT);
if(msgid==-1)
{
fprintf(stderr,"msgget failed with error:%d\n",errno);
exit(EXIT_FAILURE);
}
/*循环向消息队列中添加消息*/
while(running)
{
memset(buffer, 0, sizeof(buffer));
printf("Enter some text:");
fgets(buffer,BUFSIZ,stdin);
some_data.my_msg_type=1;
strcpy(some_data.some_text,buffer);
/*添加消息*/
if(msgsnd(msgid,(void *)&some_data,BUFSIZ,0)==-1)
{
fprintf(stderr,"msgsed failed\n");
exit(EXIT_FAILURE);
}
/*用户输入的为“end”时结束循环*/
if(strncmp(buffer,"end",3)==0)
{
running=0;
}
}
exit(EXIT_SUCCESS);
}
接收消息demo如下:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <errno.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
struct my_msg_st
{
long int my_msg_type;
char some_text[BUFSIZ];
};
int main(void)
{
int running=1;
int msgid = 0;
struct my_msg_st some_data;
long int msg_to_receive=0;
memset((void*)&some_data, 0, sizeof(some_data));
/*创建消息队列*/
msgid=msgget((key_t)1234,0666 | IPC_CREAT);
if(msgid==-1)
{
fprintf(stderr,"msgget failed with error: %d\n",errno);
exit(EXIT_FAILURE);
}
/*循环从消息队列中接收消息*/
while(running)
{
/*读取消息*/
if(msgrcv(msgid,(void *)&some_data,BUFSIZ,msg_to_receive,0)==-1)
{
fprintf(stderr,"msgrcv failed with error: %d\n",errno);
exit(EXIT_FAILURE);
}
printf("接收到的消息为: %s",some_data.some_text);
/*接收到的消息为“end”时结束循环*/
if(strncmp(some_data.some_text,"end",3) == 0)
{
running=0;
}
}
/*从系统内核中移走消息队列*/
if(msgctl(msgid,IPC_RMID,0) == -1)
{
fprintf(stderr,"msgctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
信号:信号是比较复杂的通信方式,用于通知接收进程有某种事情发生,除了用于进程间通信外,进程还可以发送信号给进程本身;linux除了支持Unix早期信息语义函数signal外,还支持语义符合Posix 1标准的信号函数sigaction。
共享内存:共享内存是一种文件映射,使得多个进程可以访问同一块内存空间,是最快的可用IPC形式。是针对其他通信机制运行效率较低而设计的。往往与其他通信机制,如信号量结合使用,来达到进程间的同步及互斥。
demo如下:
写入共享内存:
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>
typedef struct _Teacher
{
char name[64];
int age;
}Teacher;
int main(int argc, char *argv[])
{
int ret = 0;
int shmid = 0;
//创建共享内存 ,相当于打开文件,文件不存在则创建
shmid = shmget(0x2234, sizeof(Teacher), IPC_CREAT | 0666);
if (shmid == -1)
{
perror("shmget err");
return errno;
}
printf("shmid:%d \n", shmid);
Teacher *p = NULL;
//将共享内存段连接到进程地址空间
p = (Teacher*)shmat(shmid, NULL, 0);//第二个参数shmaddr为NULL,核心自动选择一个地址
if (p == (void *)-1 )
{
perror("shmget err");
return errno;
}
strcpy(p->name, "aaaa");
p->age = 33;
//将共享内存段与当前进程脱离
shmdt(p);
printf("键入1 删除共享内存,其他不删除\n");
int num = 0;
scanf("%d", &num);
if (num == 1)
{
//用于控制共享内存
ret = shmctl(shmid, IPC_RMID, NULL);//IPC_RMID为删除内存段
if (ret < 0)
{
perror("rmerrr\n");
}
}
return 0;
}
从共享内存读取:
#include<stdio.h>
#include <unistd.h>
#include<stdlib.h>
#include <sys/ipc.h>
#include <sys/shm.h>
#include<string.h>
#include<errno.h>
typedef struct _Teacher
{
char name[64];
int age;
}Teacher;
int main(int argc, char *argv[])
{
int ret = 0;
int shmid = 0;
//打开获取共享内存
shmid = shmget(0x2234, 0, 0);
if (shmid == -1)
{
perror("shmget err");
return errno;
}
printf("shmid:%d \n", shmid);
Teacher *p = NULL;
//将共享内存段连接到进程地址空间
p = (Teacher*)shmat(shmid, NULL, 0);
if (p == (void *)-1 )
{
perror("shmget err");
return errno;
}
printf("name:%s\n", p->name);
printf("age:%d \n", p->age);
//将共享内存段与当前进程脱离
shmdt(p);
printf("键入1 程序暂停,其他退出\n");
while(1)
{
sleep(1);
}
return 0;
}
socket:socket也就是套接字,最普遍的进程间通信机制,可用于不同机器之间的进程间通信。
42、为什么要使用linux作为服务器。
首先,linux是免费的,而windows需要向微软购买正版授权;
其次,linux比windows灵活,可以实现很多定制化需求,因为linux可以修改系统内核;
再次,很多高端服务器组件对linux支持的更好,windows版本的可能功能都不是很完整;
最后,linux开源,所以很多人为它添砖加瓦,几乎你需要的功能都能找到linux的版本并且都是开源免费的。
43、mysql数据库的引擎
你能用的数据库引擎取决于mysql在安装的时候是如何被编译的。要添加一个新的引擎,就必须重新编译mysql,在缺省情况下,mysql支持三个引擎:ISAM、MYISAM和HEAP,另外两种类型INNODB和BERKLEY,也常常可以使用。
- ISAM
ISAM是一个定义明确且历经时间考验的数据库表格管理方法,它在设计之时就考虑到数据库被查询的次数要远大于更新的次数。因此,ISAM执行读取操作的速度很快,而且不占用大量的内存和存储资源。ISAM的两个主要不足之处在于,它不支持事务处理,也不能够容错,如果你的硬盘崩溃了,那么数据文件就无法恢复了。如果你正在把ISAM用在关键任务应用程序里,那就必须经常备份你所有的实时数据,通过其复制特性,Mysql能够支持这样的备份应用程序。 - MYISAM
MYISAM是MySQL的ISAM扩展格式和缺省的数据库引擎,除了提供ISAM里所没有的索引和字段管理的大量功能,MYISAM还使用一种表格锁定的机制,来优化多个并发的读写操作。其代价是你需要经常运行OPTIMIZE TABLE命令,来恢复被更新机制所浪费的时间。MYISAM还有一些有用的扩展,例如用来修复数据库文件的MYISAMCHK工具和用来恢复浪费空间的MYISAMPACK工具。
MYISAM强调了快速读取操作,这可能就是为什么MYSQL受到了WEB开发如此青睐的主要原因,在WEB开发中你所进行的大量数据操作都是读取操作。所以,大多数虚拟主机提供商和INTERNET平台提供商只允许使用MYISAM格式。 - HEAP
HEAP允许只驻留在内存里的临时表格。驻留在内存使得HEAP比ISAM和MYISAM的速度都快,但是它所管理的数据是不稳定的,而且如果在关机之前没有进行保存,那么所有的数据都会丢失。在数据行被删除的时候,HEAP也不会浪费大量的空间,HEAP表格在你需要使用SELECT表达式来选择和操控数据的时候非常有用。要记住,用完表格后要删除表格。 - INNODB和BERKLEYDB
INNODB和BERKLEYDB数据库引擎都是造就MYSQL的灵活性技术的直接产品,这项技术就是mysql++API。在使用mysql的时候,你所面对的每一个挑战几乎都源于ISAM和MYISAM数据库引擎不支持事务处理也不支持外来键。尽管要比ISAM和MYISAM引擎慢很多,但是INNODB和BERKLEYDB包括了对事务处理和外来键的支持,这两点都是前两个引擎所没有的。如前所述,如果你的设计需要这些特性中的一者或者两者,那你就要被迫使用后两个引擎中的一个了。
44、什么是红黑树?
红黑树,又叫RB树,是一种特殊的二叉查找树,可以自动排序,且红黑树的每个节点都有存储位表示节点的颜色,标识是红或者黑。
红黑树的特性:
每个节点或者是黑色,或者是红色;
跟节点是黑色;
每个叶子节点是黑色(这里叶子节点是指没有子节点的叶子节点);
如果一个节点是红色的,则它的子节点必为黑色的;
从一个节点到该节点子孙节点的所有路径上包含相同数目的黑节点。
45、C程序的内存分配方式
由上到下(地址从高到低):栈、动态链接库、堆、bbs(未初始化的全局变量)、数据段(存放初始化的全局变量)、文本段(存放代码)
46、select和epoll的区别
select函数 int select(int n,fd_set* readfds, fd_set* writefds, fd_set* errorfds, struct timeval* timeout);
第一个参数n代表最大的文件描述符+1
fd_set是一个集合,存放的是文件描述符
readfds表示我们要监视这些文件描述符里读变化
writefds表示我们要监视它所指向的集合里面的文件描述符的写变化
errorfds表示要监视文件描述符是否发生了错误异常
timeout若为NULL,则select置于阻塞状态,直到监视的某个文件描述符发生变化才返回
timeout若等于0秒0毫秒,则select是一个纯粹的非阻塞函数,不管文件描述符是否变化,立即返回。
timeout大于0时,则在timeout时间内阻塞,timeout时间内若文件描述符有变化则返回,如果超时则立即返回
返回值:负值 select发生错误
正值 有文件描述符发生变化
0 等待超时,没有可读写或者发生错误的文件描述符
epoll函数与select函数最大的不同在于它不会随着监听fd数目的增长而降低效率,并且select同时监听的fd数目是有限制的,默认最大是1024.
int epoll_create(int size);//创建epoll句柄,参数size告诉内核这个监听的数目有多大,但最新版本这个参数已经无用了
int epoll_ctl(); //epoll的事件注册函数,它要先注册需要监听的事件类型
int epoll_wait();//等待事件的发生
select是轮询fd,而epoll是先将文件描述符注册到内核,一旦文件描述符发生变化,内核会采用回调机制,激活这个文件描述符,这样epoll_wait便会知道。
epoll相对于select优点:
监视的文件描述符不受限制,具体多少根内存有关
IO的效率不会随着监视fd的数量的增加而降低
mmap加速内核与用户空间的信息传递,避免了多余的内存拷贝
47、linux下inode的说明
每个磁盘空间都有一个inode表,inode里面每一个节点存放该空间每个文件的信息,例如:文件的字节数、文件拥有者、所在的组、权限、时间、位置等。
istat 命令查看单个文件;
df –i查看磁盘空间inode的使用量。
48、IP、TCP、UDP数据包大小
MTU,普通局域网最大传输单元,为1500个字节;
IP数据包首部20个字节,所以IP数据包一般是1480个字节;
TCP数据包首部20个字节,所以数据包大小为1460个字节;
UDP数据包首部8个字节,所以数据包大小我1472个字节;
注意:这里说的首部是固定长度,但后面还有一些可选字段。
49、memcache的特性
服务器接到客户端请求会先检查memcached,如有则直接返回,不再检查DB
如memcache没有要找的数据,则检查DB,查到以后返回给客户端,并同时缓冲一份到memcache
更新DB同时更新memcache,保证数据的一致性
当分配给memcache内存空间用完之后,会根据LRU(最近最少使用)策略和到期失效策略,先替换失效数据,再替换最近最少使用数据
memchche在内存中是一个巨大的hast表,它其实就是读数据库改为直接读内存,提高读取速度
50、什么是sql注入
sql注入:用户可以提交一段数据库查询代码,根据程序返回的结果,获得某些他想得知的数据。
应对办法:可以通过数据库防火墙实现对sql注入攻击的防范,因为sql注入攻击往往是通过应用程序来进攻,可以使用虚拟补丁技术实现,对注入攻击的sql特征识别,实现实时攻击阻断。