劳动生产率定义:1--共享内存的实践到内核--共享内存的创建 - 如何从应用程序进入linux内核 - 无...
我是无名小卒,昨天在留言里看到说我日志抄袭的评论,我非常气愤,气愤那些抄袭我的文章而不做声明的人,这和明着偷窃有什么区别?假如有一天你的作品也被别人抄袭了你会如何?还有没有继续作品的信心呢,做为人的第一原则就应该诚实,所以希望阅读我日志的朋友不要把抄袭做成一种爱好和虚荣的表现,还是踏实的学习知识比抄袭强万倍,就象我以前认识的一个网友整天以与LINUX内核的专家在一起拍照与荣,动不动在网上炫耀与外国哪一个LINUX名人的合影,那兴奋劲感觉就象是他成了LINUX专家成了名人似的,或者在他的脑子里能与别人合影就会吸收别人的知识,我不知道他有没有这种特异功能,我只怀疑他是在热爱LINUX的学习呢还是热爱LINUX给他带来的虚荣,对,是虚荣心在作怪,他觉得利用LINUX的效应可以让他在事业上混个一官半职,天知道他脑袋里装的是学问还是水啊,就是学问我相信也是严重掺水了。所以希望抄袭我文章的朋友在看到这里时,请你把脑袋的水挤净,学知识不是为我学的,是为你自己学的,抄袭文章去炫耀只是你的虚荣心在张扬,一旦别人知道你抄袭后,或者有一天朋友们追究起来时,你的颜面何在? 今天我们继续linux从实践到内核的学习,我们开始看一下应用实践程序,他是关于共享内存的,程序来自于linux程序设计书中例子,利用他的例子是为了让大家知道什么是共享内存以及他有什么好处,相信朋友们跟我看我这个练习并走进内核就会清楚了,我们边分析边介绍,遇河搭桥,引领分析方法 首先我们建立一个描述共享内存的数据结构,我们把他放在一个共享的头文件shm_com.h中
#define TEXT_SZ 2048
struct shared_use_st {
int written_by_you;
char some_text[TEXT_SZ];
};
我们看到some_text数组是封装共享内存文字的的一个数组,另一个written_by_you是控制写进程(我们称它为生产进程吧)读进程(我们称它为消费进程吧),我们的比喻就象生产与消费的过程一样。我们先来看消费进程方向看一个代码,它包括创建共享内存的函数
#include
#include
#include
#include
#include
#include "shm_com.h"
int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
int shmid;
srand((unsigned int)getpid());
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
if (shmid == -1) {
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n", (int)shared_memory);
shared_stuff = (struct shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;
while(running) {
if (shared_stuff->written_by_you) {
printf("You wrote: %s", shared_stuff->some_text);
sleep( rand() % 4 );
shared_stuff->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0) {
running = 0;
}
}
}
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
创建一个共享内存,进一步使用shmat(shmid, (void *)0, 0);与建立的共享内存建立联系并把共享内存的地址赋值给void类型的指针shared_memory,此后将这个指针转变成struct shared_use_st *结构指针并把地址赋值给了shared_stuff,此后对shared_stuff 的操作就是对我们建立的共享内存的操作,我们分段看一下
shared_stuff = (struct shared_use_st *)shared_memory;
shared_stuff->written_by_you = 0;
while(running) {
if (shared_stuff->written_by_you) {
printf("You wrote: %s", shared_stuff->some_text);
sleep( rand() % 4 );
shared_stuff->written_by_you = 0;
if (strncmp(shared_stuff->some_text, "end", 3) == 0) {
running = 0;
}
}
}
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
if (shmctl(shmid, IPC_RMID, 0) == -1) {
fprintf(stderr, "shmctl(IPC_RMID) failed\n");
exit(EXIT_FAILURE);
}
最后共享内存与进程断开联系shmdt(shared_memory),并且删除共享内存shmctl(shmid, IPC_RMID, 0)。
我们再看另一个生产进程的程序代码,这个进程允许我们输入数字给上面的消费进程
#include
#include
#include
#include
#include
#include "shm_com.h"
int main()
{
int running = 1;
void *shared_memory = (void *)0;
struct shared_use_st *shared_stuff;
char buffer[BUFSIZ];
int shmid;
shmid = shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);
if (shmid == -1) {
fprintf(stderr, "shmget failed\n");
exit(EXIT_FAILURE);
}
shared_memory = shmat(shmid, (void *)0, 0);
if (shared_memory == (void *)-1) {
fprintf(stderr, "shmat failed\n");
exit(EXIT_FAILURE);
}
printf("Memory attached at %X\n", (int)shared_memory);
shared_stuff = (struct shared_use_st *)shared_memory;
while(running) {
while(shared_stuff->written_by_you == 1) {
sleep(1);
printf("waiting for client...\n");
}
printf("Enter some text: ");
fgets(buffer, BUFSIZ, stdin);
strncpy(shared_stuff->some_text, buffer, TEXT_SZ);
shared_stuff->written_by_you = 1;
if (strncmp(buffer, "end", 3) == 0) {
running = 0;
}
}
if (shmdt(shared_memory) == -1) {
fprintf(stderr, "shmdt failed\n");
exit(EXIT_FAILURE);
}
exit(EXIT_SUCCESS);
}
$gcc -o shm1 share1.c
$gcc -o shm2 share2.c
[root@localhost wumingxiaozu]#./shm1 &
[1] 3289
[root@localhost wumingxiaozu]# Memory attached at B7FAE000
[root@localhost wumingxiaozu]# ./shm2
Memory attached at B7FB2000
Enter some text: wumingxiaozu
You wrote: wumingxiaozu
waiting for client...
waiting for client...
waiting for client...
Enter some text: wumingxiaozu
You wrote: wumingxiaozu
waiting for client...
waiting for client...
Enter some text: end
[root@localhost wumingxiaozu]# You wrote: end
可以看到我们的程序是完全按照上面的分析运行的,今天先到这里明天继续。。。。。。
我们今天继续探讨共享内存的到底是如何进入内核及内核如何处理的,以前我们讲过消息队列的应用程序,没有看过这篇文章的朋友可以返回仔细阅读一下,我们这里不重复关于sys_ipc()函数的分析和介绍了,直接进入其关键的代码,在开始之前我们还是看一下应用程序中的调用界面,调用界面的叫法是为了说明函数的调用方式,上面有一个shmget((key_t)1234, sizeof(struct shared_use_st), 0666 | IPC_CREAT);这个是创建共享内存函数,我们顺着他进入sys_ipc()函数看关键部分:
case SHMGET:
return sys_shmget (first, second, third);
对照应用程序中的第一个参数传递下来的是1234,第三个参数是数据结构的大小,第三个结构是创建标志和权限。我们进入sys_shmget()
asmlinkage long sys_shmget (key_t key, size_t size, int shmflg)
{
struct ipc_namespace *ns;
struct ipc_ops shm_ops;
struct ipc_params shm_params;
ns = current->nsproxy->ipc_ns;
shm_ops.getnew = newseg;
shm_ops.associate = shm_security;
shm_ops.more_checks = shm_more_checks;
shm_params.key = key;
shm_params.flg = shmflg;
shm_params.u.size = size;
return ipcget(ns, &shm_ids(ns), &shm_ops, &shm_params);
}
static int newseg(struct ipc_namespace *ns, struct ipc_params *params)
{
key_t key = params->key;
int shmflg = params->flg;
size_t size = params->u.size;
int error;
struct shmid_kernel *shp;
int numpages = (size + PAGE_SIZE -1) >> PAGE_SHIFT;
struct file * file;
char name[13];
int id;
if (size < SHMMIN || size > ns->shm_ctlmax)
return -EINVAL;
if (ns->shm_tot + numpages > ns->shm_ctlall)
return -ENOSPC;
shp = ipc_rcu_alloc(sizeof(*shp));
if (!shp)
return -ENOMEM;
shp->shm_perm.key = key;
shp->shm_perm.mode = (shmflg & S_IRWXUGO);
shp->mlock_user = NULL;
shp->shm_perm.security = NULL;
error = security_shm_alloc(shp);
if (error) {
ipc_rcu_putref(shp);
return error;
}
sprintf (name, "SYSV%08x", key);
if (shmflg & SHM_HUGETLB) {
/* hugetlb_file_setup takes care of mlock user accounting */
file = hugetlb_file_setup(name, size);
shp->mlock_user = current->user;
} else {
int acctflag = VM_ACCOUNT;
/*
* Do not allow no accounting for OVERCOMMIT_NEVER, even
* if it's asked for.
*/
if ((shmflg & SHM_NORESERVE) &&
sysctl_overcommit_memory != OVERCOMMIT_NEVER)
acctflag = 0;
file = shmem_file_setup(name, size, acctflag);
}
error = PTR_ERR(file);
if (IS_ERR(file))
goto no_file;
id = ipc_addid(&shm_ids(ns), &shp->shm_perm, ns->shm_ctlmni);
if (id < 0) {
error = id;
goto no_id;
}
shp->shm_cprid = task_tgid_vnr(current);
shp->shm_lprid = 0;
shp->shm_atim = shp->shm_dtim = 0;
shp->shm_ctim = get_seconds();
shp->shm_segsz = size;
shp->shm_nattch = 0;
shp->shm_file = file;
/*
* shmid gets reported as "inode#" in /proc/pid/maps.
* proc-ps tools use this. Changing this will break them.
*/
file->f_dentry->d_inode->i_ino = shp->shm_perm.id;
ns->shm_tot += numpages;
error = shp->shm_perm.id;
shm_unlock(shp);
return error;
no_id:
fput(file);
no_file:
security_shm_free(shp);
ipc_rcu_putref(shp);
return error;
}
struct shmid_kernel /* private to the kernel */
{
struct kern_ipc_perm shm_perm;
struct file * shm_file;
unsigned long shm_nattch;
unsigned long shm_segsz;
time_t shm_atim;
time_t shm_dtim;
time_t shm_ctim;
pid_t shm_cprid;
pid_t shm_lprid;
struct user_struct *mlock_user;
};
static struct file_system_type tmpfs_fs_type = {
.owner = THIS_MODULE,
.name = "tmpfs",
.get_sb = shmem_get_sb,
.kill_sb = kill_litter_super,
};
每一个文件系统都要有这么一个类型声明,可以看出这里声明为tmpfs,注意我们分析的代码是目前发行的最新的2.6.26,如果朋友们想读代码一定要注意版本,linux在系统初始化时会通过init_tmpfs()来安装这个共享内存文件系统
static int __init init_tmpfs(void)
{
int error;
error = bdi_init(&shmem_backing_dev_info);
if (error)
goto out4;
error = init_inodecache();
if (error)
goto out3;
error = register_filesystem(&tmpfs_fs_type);
if (error) {
printk(KERN_ERR "Could not register tmpfs\n");
goto out2;
}
shm_mnt = vfs_kern_mount(&tmpfs_fs_type, MS_NOUSER,
tmpfs_fs_type.name, NULL);
if (IS_ERR(shm_mnt)) {
error = PTR_ERR(shm_mnt);
printk(KERN_ERR "Could not kern_mount tmpfs\n");
goto out1;
}
return 0;
out1:
unregister_filesystem(&tmpfs_fs_type);
out2:
destroy_inodecache();
out3:
bdi_destroy(&shmem_backing_dev_info);
out4:
shm_mnt = ERR_PTR(error);
return error;
}
struct file *shmem_file_setup(char *name, loff_t size, unsigned long flags)
{
int error;
struct file *file;
struct inode *inode;
struct dentry *dentry, *root;
struct qstr this;
if (IS_ERR(shm_mnt))
return (void *)shm_mnt;
if (size < 0 || size > SHMEM_MAX_BYTES)
return ERR_PTR(-EINVAL);
if (shmem_acct_size(flags, size))
return ERR_PTR(-ENOMEM);
error = -ENOMEM;
this.name = name;
this.len = strlen(name);
this.hash = 0; /* will go */
root = shm_mnt->mnt_root;
dentry = d_alloc(root, &this);
if (!dentry)
goto put_memory;
error = -ENFILE;
file = get_empty_filp();
if (!file)
goto put_dentry;
error = -ENOSPC;
inode = shmem_get_inode(root->d_sb, S_IFREG | S_IRWXUGO, 0);
if (!inode)
goto close_file;
SHMEM_I(inode)->flags = flags & VM_ACCOUNT;
d_instantiate(dentry, inode);
inode->i_size = size;
inode->i_nlink = 0; /* It is unlinked */
init_file(file, shm_mnt, dentry, FMODE_WRITE | FMODE_READ,
&shmem_file_operations);
return file;
close_file:
put_filp(file);
put_dentry:
dput(dentry);
put_memory:
shmem_unacct_size(flags, size);
return ERR_PTR(error);
}