第十八章 linux-应用层open->软中断指令->底层驱动xxx_open
第十八章 linux-应用层open->软中断指令->底层驱动xxx_open
文章目录
前言
驱动程序中实现file_operations中的open函数,在应用层中使用open来调用到内核中实现的file_operations中的open函数,那么如何实现的呢?
inode
内核中字符设备相应文件实现file_operations后生成ko文件,每个文件有一个特定的inode号,每个文件会对应一个inode结构体
struct inode {
umode_t i_mode; // 打开方式
kuid_t i_uid; // 用户id
kgid_t i_gid; // 组id
unsigned int i_flags; // 权限标志位,阻塞,非阻塞... 标志位信息
const struct inode_operations*i_op; // inode 操作方法集
struct super_block*i_sb;
struct address_space*i_mapping;
unsigned longi_ino; // inode 号码
union {
const unsigned int i_nlink;
unsigned int __i_nlink; // 链接数
};
dev_ti_rdev; // 设备号
struct timespeci_atime; // 访问时间
struct timespeci_mtime; // 修改时间
struct timespeci_ctime; // 创建时间
// 存储信息
unsigned short i_bytes;
unsigned int i_blkbits;
blkcnt_t i_blocks;
const struct file_operations*i_fop; /* 文件操作方法集合 */
struct list_headi_devices; // 内核循环双链表节点
union {
struct pipe_inode_info*i_pipe;
struct block_device*i_bdev;
struct cdev *i_cdev; // 字符设备对象指针
};
...
};
inode结构体包含很多信息,如文件的权限信息,文件的时间信息等等。在驱动文件中,我们主要关注标红的信息,inode号,设备号,文件操作集合。使用mknod创建设备节点时,会将inode号和设备号关联起来。若此时内核中已经注册了该设备号的对象,此时会将设备对象的操作方法集合赋值到此处。
应用层open->底层驱动xxx_open
文件操作集合i_fop指向了def_chr_fops,这个结构中的open函数指向了chrdev_open。chrdev_open完成的主要工作是:首先根据设备号找到添加在内核中代表字符设备的cdev。找到对应的cedv对象后,用cedv关联的操作方法集和赋值给新建的file结构体中的文件操作集合。并调用操作集合中的open函数,完成真正的打开操作。
一个驱动可能会被多应用打开,那多次打开时是生成多个inode号吗?答案是否定的。inode号是唯一的,那如何表示多次打开一个驱动文件呢?
系统中每个打开的文件在内核空间都有一个对应的file结构体。
struct file {
struct path f_path; // 路径名称
struct inode *f_inode; /*inode 指针*/
const struct file_operations *f_op; // 文件操作集合
unsigned int f_flags; // 标志位信息
fmode_t f_mode; // 操作方式
loff_t f_pos;
struct fown_struct f_owner; // 异步通知 ,需要用到此结构体
void *private_data;
...
};
应用层open->底层驱动xxx_open
应用层open->软中断指令->底层驱动xxx_open
- 应用层中使用open时,触发软中断(int $0x80)
int open(const char *pathname, int flags, mode_t mode);
- 触发软中断后更根据参数的数量触发不同的函数,利用0x80中断劫持system_call->sys_call_table(系统调用表)
static inline long stub_syscall3(long syscall, long arg1, long arg2, long arg3)
{
long ret;
__asm__ volatile ("int $0x80" : "=a" (ret) : "0" (syscall), "b" (arg1),
"c" (arg2), "d" (arg3));
return ret;
}
sys_call_table(系统调用表)
#include <linux/syscalls.h>
#include <linux/signal.h>
#include <linux/unistd.h>
#include <asm/syscall.h>
#undef __SYSCALL
#define __SYSCALL(nr, call) [nr] = (call),
void *sys_call_table[__NR_syscalls] = {
#include <asm/unistd.h>
};
/*
* arch/sh/kernel/syscalls_64.S
*
* Copyright (C) 2000, 2001 Paolo Alberelli
* Copyright (C) 2004 - 2007 Paul Mundt
* Copyright (C) 2003, 2004 Richard Curnow
*
* This file is subject to the terms and conditions of the GNU General Public
* License. See the file "COPYING" in the main directory of this archive
* for more details.
*/
#include <linux/sys.h>
.section .data, "aw"
.balign 32
/*
* System calls jump table
*/
.globl sys_call_table[]
sys_call_table:
.long sys_restart_syscall /* 0 - old "setup()" system call */
.long sys_exit
.long sys_fork
.long sys_read
.long sys_write
.long sys_open /* 5 */
.long sys_close
.long sys_waitpid
.long sys_creat
.long sys_link
.long sys_unlink /* 10 */
.long sys_execve
.long sys_chdir
.long sys_time
.long sys_mknod
.long sys_chmod /* 15 */
.long sys_lchown16
.long sys_ni_syscall /* old break syscall holder */
.long sys_stat
.long sys_lseek
.long sys_getpid /* 20 */
.long sys_mount
.long sys_oldumount
.long sys_setuid16
.long sys_getuid16
.long sys_stime /* 25 */
.long sh64_ptrace
.long sys_alarm
.long sys_fstat
.long sys_pause
.long sys_utime /* 30 */
.long sys_ni_syscall /* old stty syscall holder */
.long sys_ni_syscall /* old gtty syscall holder */
.long sys_access
.long sys_nice
.long sys_ni_syscall /* 35 */ /* old ftime syscall holder */
.long sys_sync
.long sys_kill
.long sys_rename
.long sys_mkdir
.long sys_rmdir /* 40 */
.long sys_dup
.long sys_pipe
.long sys_times
.long sys_ni_syscall /* old prof syscall holder */
.long sys_brk /* 45 */
.long sys_setgid16
.long sys_getgid16
.long sys_signal
.long sys_geteuid16
.long sys_getegid16 /* 50 */
.long sys_acct
.long sys_umount /* recycled never used phys( */
.long sys_ni_syscall /* old lock syscall holder */
.long sys_ioctl
.long sys_fcntl /* 55 */
.long sys_ni_syscall /* old mpx syscall holder */
.long sys_setpgid
.long sys_ni_syscall /* old ulimit syscall holder */
.long sys_ni_syscall /* sys_olduname */
.long sys_umask /* 60 */
.long sys_chroot
.long sys_ustat
.long sys_dup2
.long sys_getppid
.long sys_getpgrp /* 65 */
.long sys_setsid
.long sys_sigaction
.long sys_sgetmask
.long sys_ssetmask
.long sys_setreuid16 /* 70 */
.long sys_setregid16
.long sys_sigsuspend
.long sys_sigpending
.long sys_sethostname
.long sys_setrlimit /* 75 */
.long sys_old_getrlimit
.long sys_getrusage
.long sys_gettimeofday
.long sys_settimeofday
.long sys_getgroups16 /* 80 */
.long sys_setgroups16
.long sys_ni_syscall /* sys_oldselect */
.long sys_symlink
.long sys_lstat
.long sys_readlink /* 85 */
.long sys_uselib
.long sys_swapon
.long sys_reboot
.long sys_old_readdir
.long old_mmap /* 90 */
.long sys_munmap
.long sys_truncate
.long sys_ftruncate
.long sys_fchmod
.long sys_fchown16 /* 95 */
.long sys_getpriority
.long sys_setpriority
.long sys_ni_syscall /* old profil syscall holder */
.long sys_statfs
.long sys_fstatfs /* 100 */
.long sys_ni_syscall /* ioperm */
.long sys_socketcall /* Obsolete implementation of socket syscall */
.long sys_syslog
.long sys_setitimer
.long sys_getitimer /* 105 */
.long sys_newstat
.long sys_newlstat
.long sys_newfstat
.long sys_uname
.long sys_ni_syscall /* 110 */ /* iopl */
.long sys_vhangup
.long sys_ni_syscall /* idle */
.long sys_ni_syscall /* vm86old */
.long sys_wait4
.long sys_swapoff /* 115 */
.long sys_sysinfo
.long sys_ipc /* Obsolete ipc syscall implementation */
.long sys_fsync
.long sys_sigreturn
.long sys_clone /* 120 */
.long sys_setdomainname
.long sys_newuname
.long sys_cacheflush /* x86: sys_modify_ldt */
.long sys_adjtimex
.long sys_mprotect /* 125 */
.long sys_sigprocmask
.long sys_ni_syscall /* old "create_module" */
.long sys_init_module
.long sys_delete_module
.long sys_ni_syscall /* 130: old "get_kernel_syms" */
.long sys_quotactl
.long sys_getpgid
.long sys_fchdir
.long sys_bdflush
.long sys_sysfs /* 135 */
.long sys_personality
.long sys_ni_syscall /* for afs_syscall */
.long sys_setfsuid16
.long sys_setfsgid16
.long sys_llseek /* 140 */
.long sys_getdents
.long sys_select
.long sys_flock
.long sys_msync
.long sys_readv /* 145 */
.long sys_writev
.long sys_getsid
.long sys_fdatasync
.long sys_sysctl
.long sys_mlock /* 150 */
.long sys_munlock
.long sys_mlockall
.long sys_munlockall
.long sys_sched_setparam
.long sys_sched_getparam /* 155 */
.long sys_sched_setscheduler
.long sys_sched_getscheduler
.long sys_sched_yield
.long sys_sched_get_priority_max
.long sys_sched_get_priority_min /* 160 */
.long sys_sched_rr_get_interval
.long sys_nanosleep
.long sys_mremap
.long sys_setresuid16
.long sys_getresuid16 /* 165 */
.long sys_ni_syscall /* vm86 */
.long sys_ni_syscall /* old "query_module" */
.long sys_poll
.long sys_ni_syscall /* was nfsservctl */
.long sys_setresgid16 /* 170 */
.long sys_getresgid16
.long sys_prctl
.long sys_rt_sigreturn
.long sys_rt_sigaction
.long sys_rt_sigprocmask /* 175 */
.long sys_rt_sigpending
.long sys_rt_sigtimedwait
.long sys_rt_sigqueueinfo
.long sys_rt_sigsuspend
.long sys_pread64 /* 180 */
.long sys_pwrite64
.long sys_chown16
.long sys_getcwd
.long sys_capget
.long sys_capset /* 185 */
.long sys_sigaltstack
.long sys_sendfile
.long sys_ni_syscall /* getpmsg */
.long sys_ni_syscall /* putpmsg */
.long sys_vfork /* 190 */
.long sys_getrlimit
.long sys_mmap2
.long sys_truncate64
.long sys_ftruncate64
.long sys_stat64 /* 195 */
.long sys_lstat64
.long sys_fstat64
.long sys_lchown
.long sys_getuid
.long sys_getgid /* 200 */
.long sys_geteuid
.long sys_getegid
.long sys_setreuid
.long sys_setregid
.long sys_getgroups /* 205 */
.long sys_setgroups
.long sys_fchown
.long sys_setresuid
.long sys_getresuid
.long sys_setresgid /* 210 */
.long sys_getresgid
.long sys_chown
.long sys_setuid
.long sys_setgid
.long sys_setfsuid /* 215 */
.long sys_setfsgid
.long sys_pivot_root
.long sys_mincore
.long sys_madvise
/* Broken-out socket family (maintain backwards compatibility in syscall
numbering with 2.4) */
.long sys_socket /* 220 */
.long sys_bind
.long sys_connect
.long sys_listen
.long sys_accept
.long sys_getsockname /* 225 */
.long sys_getpeername
.long sys_socketpair
.long sys_send
.long sys_sendto
.long sys_recv /* 230*/
.long sys_recvfrom
.long sys_shutdown
.long sys_setsockopt
.long sys_getsockopt
.long sys_sendmsg /* 235 */
.long sys_recvmsg
/* Broken-out IPC family (maintain backwards compatibility in syscall
numbering with 2.4) */
.long sys_semop
.long sys_semget
.long sys_semctl
.long sys_msgsnd /* 240 */
.long sys_msgrcv
.long sys_msgget
.long sys_msgctl
.long sys_shmat
.long sys_shmdt /* 245 */
.long sys_shmget
.long sys_shmctl
/* Rest of syscalls listed in 2.4 i386 unistd.h */
.long sys_getdents64
.long sys_fcntl64
.long sys_ni_syscall /* 250 reserved for TUX */
.long sys_ni_syscall /* Reserved for Security */
.long sys_gettid
.long sys_readahead
.long sys_setxattr
.long sys_lsetxattr /* 255 */
.long sys_fsetxattr
.long sys_getxattr
.long sys_lgetxattr
.long sys_fgetxattr
.long sys_listxattr /* 260 */
.long sys_llistxattr
.long sys_flistxattr
.long sys_removexattr
.long sys_lremovexattr
.long sys_fremovexattr /* 265 */
.long sys_tkill
.long sys_sendfile64
.long sys_futex
.long sys_sched_setaffinity
.long sys_sched_getaffinity /* 270 */
.long sys_ni_syscall /* reserved for set_thread_area */
.long sys_ni_syscall /* reserved for get_thread_area */
.long sys_io_setup
.long sys_io_destroy
.long sys_io_getevents /* 275 */
.long sys_io_submit
.long sys_io_cancel
.long sys_fadvise64
.long sys_ni_syscall
.long sys_exit_group /* 280 */
/* Rest of new 2.6 syscalls */
.long sys_lookup_dcookie
.long sys_epoll_create
.long sys_epoll_ctl
.long sys_epoll_wait
.long sys_remap_file_pages /* 285 */
.long sys_set_tid_address
.long sys_timer_create
.long sys_timer_settime
.long sys_timer_gettime
.long sys_timer_getoverrun /* 290 */
.long sys_timer_delete
.long sys_clock_settime
.long sys_clock_gettime
.long sys_clock_getres
.long sys_clock_nanosleep /* 295 */
.long sys_statfs64
.long sys_fstatfs64
.long sys_tgkill
.long sys_utimes
.long sys_fadvise64_64 /* 300 */
.long sys_ni_syscall /* Reserved for vserver */
.long sys_ni_syscall /* Reserved for mbind */
.long sys_ni_syscall /* get_mempolicy */
.long sys_ni_syscall /* set_mempolicy */
.long sys_mq_open /* 305 */
.long sys_mq_unlink
.long sys_mq_timedsend
.long sys_mq_timedreceive
.long sys_mq_notify
.long sys_mq_getsetattr /* 310 */
.long sys_ni_syscall /* Reserved for kexec */
.long sys_waitid
.long sys_add_key
.long sys_request_key
.long sys_keyctl /* 315 */
.long sys_ioprio_set
.long sys_ioprio_get
.long sys_inotify_init
.long sys_inotify_add_watch
.long sys_inotify_rm_watch /* 320 */
.long sys_ni_syscall
.long sys_migrate_pages
.long sys_openat
.long sys_mkdirat
.long sys_mknodat /* 325 */
.long sys_fchownat
.long sys_futimesat
.long sys_fstatat64
.long sys_unlinkat
.long sys_renameat /* 330 */
.long sys_linkat
.long sys_symlinkat
.long sys_readlinkat
.long sys_fchmodat
.long sys_faccessat /* 335 */
.long sys_pselect6
.long sys_ppoll
.long sys_unshare
.long sys_set_robust_list
.long sys_get_robust_list /* 340 */
.long sys_splice
.long sys_sync_file_range
.long sys_tee
.long sys_vmsplice
.long sys_move_pages /* 345 */
.long sys_getcpu
.long sys_epoll_pwait
.long sys_utimensat
.long sys_signalfd
.long sys_timerfd_create /* 350 */
.long sys_eventfd
.long sys_fallocate
.long sys_timerfd_settime
.long sys_timerfd_gettime
.long sys_signalfd4 /* 355 */
.long sys_eventfd2
.long sys_epoll_create1
.long sys_dup3
.long sys_pipe2
.long sys_inotify_init1 /* 360 */
.long sys_preadv
.long sys_pwritev
.long sys_rt_tgsigqueueinfo
.long sys_perf_event_open
.long sys_recvmmsg /* 365 */
.long sys_accept4
.long sys_fanotify_init
.long sys_fanotify_mark
.long sys_prlimit64
.long sys_name_to_handle_at /* 370 */
.long sys_open_by_handle_at
.long sys_clock_adjtime
.long sys_syncfs
.long sys_sendmmsg
.long sys_setns /* 375 */
.long sys_process_vm_readv
.long sys_process_vm_writev
.long sys_kcmp
.long sys_finit_module
-
然后执行汇编指令跳转到内核态,最终调用到do_sys_open函数
-
在do_sys_open函数中,经过一系列的调用最终会打开对应驱动,调用到对应驱动中的xxx_open函数
do_sys_open
long do_sys_open(int dfd, const char __user *filename, int flags, int mode)
{
/*把用户空间的存在内存的文件名字符串复制到内核空间*/
char *tmp = getname(filename);
int fd = PTR_ERR(tmp);
if (!IS_ERR(tmp)) {
/*寻找一个暂时没用的文件描述符,或者没有的话返回错误*/
fd = get_unused_fd();
if (fd >= 0) {
/*如果找到的话,就执行打开操作*/
struct file *f = do_filp_open(dfd, tmp, flags, mode);
if (IS_ERR(f)) {
/*打开失败,就释放文件描述符*/
put_unused_fd(fd);
fd = PTR_ERR(f);
} else {
fsnotify_open(f->f_path.dentry);
fd_install(fd, f);
}
}
putname(tmp);
}
return fd;
}
do_filp_open
/*
* for the internal routines (ie open_namei()/follow_link() etc). 00 is
* used by symlinks.
*/
static struct file *do_filp_open(int dfd, const char *filename, int flags,
int mode)
{
int namei_flags, error;
struct nameidata nd;
/*内外标记不一样*/
namei_flags = flags;
if ((namei_flags+1) & O_ACCMODE)
namei_flags++;
/*顺着文件名打开操作*/
error = open_namei(dfd, filename, namei_flags, mode, &nd);
if (!error)/*根据得到的文件数据,填充file结构体*/
return nameidata_to_filp(&nd, flags);
return ERR_PTR(error);
}
open_namei
int open_namei(int dfd, const char *pathname, int flag,
int mode, struct nameidata *nd)
{
int acc_mode, error;
struct path path;
struct dentry *dir;
int count = 0;
/*从flags得到打开模式*/
acc_mode = ACC_MODE(flag);
/* O_TRUNC表示我们需要检查写的权限 */
if (flag & O_TRUNC)
acc_mode |= MAY_WRITE;
/*O_APPEND需要MAY_APPEND权限 */
if (flag & O_APPEND)
acc_mode |= MAY_APPEND;
/*如果是不是创建文件的话,就调用path_lookup_open函数获得文件的dentry和vfsmount填充nameidata结构体,找到后就直接返回*/
if (!(flag & O_CREAT)) {
error = path_lookup_open(dfd, pathname, lookup_flags(flag),
nd, flag);
/*获得后就直接返回函数*/
if (error)
return error;
goto ok;
}
/*如果是要创建文件,我们需要知道父目录,所以加上LOOKUP_PARENT的flag*/
error = path_lookup_create(dfd,pathname,LOOKUP_PARENT,nd,flag,mode);
if (error)
return error;
/*已经拥有了父目录,我们检查下返回结果,如果最后找到的是目录就返回错误,我们只创建文件不创建目录 */
error = -EISDIR;
if (nd->last_type != LAST_NORM || nd->last.name[nd->last.len])
goto exit;
/**/
dir = nd->dentry;
nd->flags &= ~LOOKUP_PARENT;
mutex_lock(&dir->d_inode->i_mutex);
/*填充path结构体*/
path.dentry = lookup_hash(nd);
path.mnt = nd->mnt;
do_last:
/*如果获得的参数有问题就返回错误*/
error = PTR_ERR(path.dentry);
if (IS_ERR(path.dentry)) {
mutex_unlock(&dir->d_inode->i_mutex);
goto exit;
}
if (IS_ERR(nd->intent.open.file)) {
mutex_unlock(&dir->d_inode->i_mutex);
error = PTR_ERR(nd->intent.open.file);
goto exit_dput;
}
/* 错误的dentry,直接创建文件 */
if (!path.dentry->d_inode) {
error = open_namei_create(nd, &path, flag, mode);
if (error)
goto exit;
return 0;
}
/*
* 说明文件已经存在
*/
mutex_unlock(&dir->d_inode->i_mutex);
audit_inode(pathname, path.dentry->d_inode);
/*O_EXCL标志代表如果文件存在就退出*/
error = -EEXIST;
if (flag & O_EXCL)
goto exit_dput;
/*找到真正的挂载点*/
if (__follow_mount(&path)) {
error = -ELOOP;
if (flag & O_NOFOLLOW)
goto exit_dput;
}
error = -ENOENT;
/*说明创建失败*/
if (!path.dentry->d_inode)
goto exit_dput;
/*处理连接*/
if (path.dentry->d_inode->i_op && path.dentry->d_inode->i_op->follow_link)
goto do_link;
/*path结构体转成nameidata结构体*/
path_to_nameidata(&path, nd);
error = -EISDIR;
/*再次检查*/
if (path.dentry->d_inode && S_ISDIR(path.dentry->d_inode->i_mode))
goto exit;
ok:
/*写之前的一些检查和准备工作*/
error = may_open(nd, acc_mode, flag);
if (error)
goto exit;
return 0;
exit_dput:
/*退出前释放path*/
dput_path(&path, nd);
exit:
if (!IS_ERR(nd->intent.open.file))
release_open_intent(nd);
path_release(nd);
return error;
do_link:
/*如果flag有O_NOFOLLOW,说明禁止链接,直接返回错误*/
error = -ELOOP;
if (flag & O_NOFOLLOW)
goto exit_dput;
/*首先找到父目录*/
nd->flags |= LOOKUP_PARENT;
/*安全操作,暂时不管*/
error = security_inode_follow_link(path.dentry, nd);
if (error)
goto exit_dput;
/*搜寻,并创建*/
error = __do_follow_link(&path, nd);
if (error) {
release_open_intent(nd);
return error;
}
nd->flags &= ~LOOKUP_PARENT;
/*检验返回结果*/
if (nd->last_type == LAST_BIND)
goto ok;
error = -EISDIR;
if (nd->last_type != LAST_NORM)
goto exit;
if (nd->last.name[nd->last.len]) {
__putname(nd->last.name);
goto exit;
}
error = -ELOOP;
/*超出循环最大次数*/
if (count++==32) {
__putname(nd->last.name);
goto exit;
}
/*填充返回结果*/
dir = nd->dentry;
mutex_lock(&dir->d_inode->i_mutex);
path.dentry = lookup_hash(nd);
path.mnt = nd->mnt;
__putname(nd->last.name);
goto do_last;
}
open_namei_create
static int open_namei_create(struct nameidata *nd, struct path *path,
int flag, int mode)
{
int error;
struct dentry *dir = nd->dentry;
/*POSIX权限标准检查*/
if (!IS_POSIXACL(dir->d_inode))
mode &= ~current->fs->umask;
/*vfs层调用文件创造*/
error = vfs_create(dir->d_inode, path->dentry, mode, nd);
mutex_unlock(&dir->d_inode->i_mutex);
dput(nd->dentry);
/*创建后放到nameidata里*/
nd->dentry = path->dentry;
if (error)
return error;
/*权限检查,不检查写权限和truncate权限*/
return may_open(nd, 0, flag & ~O_TRUNC);
}
vfs_create
int vfs_create(struct inode *dir, struct dentry *dentry, int mode,
struct nameidata *nd)
{
/*打开前的权限检查*/
int error = may_create(dir, dentry, nd);
if (error)
return error;
/*inode的inode_operation函数表的create函数*/
if (!dir->i_op || !dir->i_op->create)
return -EACCES; /* shouldn't it be ENOSYS? */
mode &= S_IALLUGO;
mode |= S_IFREG;
/*安全检查操作*/
error = security_inode_create(dir, dentry, mode);
if (error)
return error;
DQUOT_INIT(dir);
/*创建*/
error = dir->i_op->create(dir, dentry, mode, nd);
if (!error)
fsnotify_create(dir, dentry);
return error;
}
相关文章
- 嵌入式Linux网关:迈向无缝连接的未来(嵌入式linux网关)
- Linux下解压WAR文件的方法(linux解压war文件)
- 程序深入 Linux 系统中的驱动程序过滤(linux过滤驱动)
- 解决Linux图片显示问题(linux图片不显示)
- 机驱动Linux内核支持打印机驱动安装与使用(linux内核打印)
- Linux系统启动过程简介(linux启动过程简述)
- 监控Linux系统状态监控:实时掌控系统健康状况(linux系统状态)
- Linux环境下使用SSH指令操作(ssh指令linux)
- 实现Linux系统安全:守护服务(linux守护服务)
- Linux安装触摸板驱动的指南(linux安装触摸板驱动)
- Linux极速设置无线网络快速上网(linux无线网络设置)
- 指令清理,Linux虚拟机一键清屏(linux虚拟机清屏)
- Linux分支:丰富的发行版本选择(linux的分支)
- Linux 管理员的指令攻略(linux管理员命令)
- Linux操作系统下的线程机制分析(linux有线程吗)
- 对比Linux文件的方法(linux文件比较)
- Linux下配置串口驱动程序:必要步骤.(linux添加串口驱动)
- Linux实用技巧:轻松去除重复文件和内容(linux去掉重复)
- 命令玩转Linux——学习Awk命令(linux的awk)
- Linux下增加默认网关指令实操(linux增加网关)
- 驱动Linux服务器:建立串口连接.(linux串口服务器)
- Linux系统下的USB驱动技术(linux驱动usb)
- 龙井:Linux上的新生(龙井linux)
- Linux下图形卡驱动程序的安装指南(linux装显卡驱动)
- 探秘Linux显存:优化性能,提高效率(linux显存)
- 如何在Linux系统上编程高效运行(怎么用linux编程)
- 路由器轻松安装Linux系统,实现强大功能!(路由器安装linux系统)
- 深入理解Linux内核:驱动源码分析(linux驱动源码分析)