https://www.bilibili.com/video/BV1kk4y117Tu
4【第四章】文件I/O
4_6_综合实验_处理表格
hexdump -C main.c
可以用来查看二进制数据,并且在右侧显示相应的文本对照。
4_7_文件IO系统调用内部机制
/proc/pid/fd
对应进程打开的 fd,默认有 0,1,2 三个,对应 stdin, stdout, stderr.
内部调用
fs/open.c
SYSCALL_DEFINE3(open, const char __user *, filename, int, flags, umode_t, mode)
->do_sys_open(AT_FDCWD, filename, flags, mode)
do_sys_open(AT_FDCWD, filename, flags, mode)
->fd = get_unused_fd_flags(flags)
获取未使用的文件描述符,struct file *f = do_filp_open(dfd, tmp, &op)
打开文件结构体 ,fd_install(fd, f)
把文件结构体放入文件描述符数组中。fs/namei.c
struct file *do_filp_open(int dfd, struct filename *pathname, const struct open_flags *op)
fs/file.c
int get_unused_fd_flags(unsigned flags)
->__alloc_fd(current->files, 0, rlimit(RLIMIT_NOFILE), flags)
->fdt = files_fdtable(files)
->fd = find_next_fd(fdt, fd)
,files->next_fd = fd + 1
fs/file.c
void fd_install(unsigned int fd, struct file *file)
->__fd_install(current->files, fd, file)
->fdt = rcu_dereference_sched(files->fdt)
,rcu_assign_pointer(fdt->fd[fd], file)
current
来源于include/asm-generic
#define current get_current()
->#define get_current() (current_thread_info()->task)
->thread_info.h
里面的struct thread_info
->sched.h
struct task_struct *task
->fdtable.h
struct files_struct *files
->struct fdtable fdtab
4_8_dup函数的使用
int dup(int old);
, 返回一个指向 old 的新的最小未使用 fd.int dup2(int old,int new);
, 先检查 new 有没有被打开,如果打开,就先关闭。 然后让 new 指向 old.
第7章 输入系统应用编程
7.2 现场编程获取输入设备信息
drivers\input\evdev.c
,static long evdev_do_ioctl(struct file *file, unsigned int cmd, void __user *p, int compat_mode)
内核中 ioctl 对应的函数-
evdev_do_ioctl
中 switch 判断分支EVIOCGVERSION
在include\uapi\linux\input.h
中#define EVIOCGVERSION _IOR('E', 0x01, int) /* get driver version */ #define EVIOCGID _IOR('E', 0x02, struct input_id) /* get device ID */ #define EVIOCGREP _IOR('E', 0x03, unsigned int[2]) /* get repeat settings */ #define EVIOCSREP _IOW('E', 0x03, unsigned int[2]) /* set repeat settings */ // #define EVIOCGBIT(ev,len) _IOC(_IOC_READ, 'E', 0x20 + (ev), len) /* get event bits */
- 获取设备 id 代码
struct input_id id; err = ioctl(fd, EVIOCGID, &id); if (err == 0) { printf("bustype = 0x%x\n", id.bustype ); printf("vendor = 0x%x\n", id.vendor ); printf("product = 0x%x\n", id.product ); printf("version = 0x%x\n", id.version ); }
-
获取设备 evbits 代码,
EVIOCGBIT(ev,len)
中 ev 为 0 表示获取 evbit, 为 1 表示获取 keybit,按照include\linux\input.h
里面的struct input_dev
中的偏移来算。unsigned int evbit[2]; len = ioctl(fd, EVIOCGBIT(0, sizeof(evbit)), &evbit); if (len > 0 && len <= sizeof(evbit)) { printf("support ev type: "); for (i = 0; i < len; i++) { byte = ((unsigned char *)evbit)[i]; for (bit = 0; bit < 8; bit++) { if (byte & (1<<bit)) { printf("%s ", ev_names[i*8 + bit]); } } } printf("\n"); }
7_3 查询 休眠唤醒 读取输入数据
- read 对应的驱动中的函数在
drivers\input\evdev.c
static ssize_t evdev_read(struct file *file, char __user *buffer, size_t count, loff_t *ppos)
, 通过input_event_to_user(buffer + read, &event)
给用户空间传数据,struct input_event event
. - 应用中读取数据的代码:
while (1) { len = read(fd, &event, sizeof(event)); if (len == sizeof(event)) { printf("get event: type = 0x%x, code = 0x%x, value = 0x%x\n", event.type, event.code, event.value); } else { printf("read err %d\n", len); } }
7_6 电阻屏和电容屏
- 上报数据中的
ABS_MT_TOUCH_MAJOR
和ABS_MT_WIDTH_MAJOR
指的是接触区域的椭圆尺寸,正常没啥用。7_7 tslib框架分析
tslib,暂时不太需要,以后需要的时候,再回头看。
第8章 网络通信
8_2 tcp 编程
tSocketServerAddr.sin_port = htons(SERVER_PORT); /* host to net, short */
, 自己给出来的数据,需要进行字节序转换。iRet = bind(iSocketServer, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
把方便的struct sockaddr_in
转为不方便的struct sockaddr
iSocketClient = accept(iSocketServer, (struct sockaddr *)&tSocketClientAddr, &iAddrLen);
- 服务端接收数据
if (-1 != iSocketClient) { iClientNum++; printf("Get connect from client %d : %s\n", iClientNum, inet_ntoa(tSocketClientAddr.sin_addr)); if (!fork()) { /* 子进程的源码 */ while (1) { /* 接收客户端发来的数据并显示出来 */ iRecvLen = recv(iSocketClient, ucRecvBuf, 999, 0); if (iRecvLen <= 0) { close(iSocketClient); return -1; } else { ucRecvBuf[iRecvLen] = '\0'; printf("Get Msg From Client %d: %s\n", iClientNum, ucRecvBuf); } } } }
signal(SIGCHLD,SIG_IGN);
显式的忽略子进程退出信号,防止fork
子进程退出后变为僵尸进程。 当然也可以根据需要设置一个信号处理函数。iRet = connect(iSocketClient, (const struct sockaddr *)&tSocketServerAddr, sizeof(struct sockaddr));
-
客户端发送数据
if (-1 == iRet) { printf("connect error!\n"); return -1; } while (1) { if (fgets(ucSendBuf, 999, stdin)) { iSendLen = send(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0); if (iSendLen <= 0) { close(iSocketClient); return -1; } } }
8_3 udp 编程
- 服务端接收数据
while (1) { iAddrLen = sizeof(struct sockaddr); iRecvLen = recvfrom(iSocketServer, ucRecvBuf, 999, 0, (struct sockaddr *)&tSocketClientAddr, &iAddrLen); if (iRecvLen > 0) { ucRecvBuf[iRecvLen] = '\0'; printf("Get Msg From %s : %s\n", inet_ntoa(tSocketClientAddr.sin_addr), ucRecvBuf); } }
- 客户端发送数据
while (1) { if (fgets(ucSendBuf, 999, stdin)) { iAddrLen = sizeof(struct sockaddr); iSendLen = sendto(iSocketClient, ucSendBuf, strlen(ucSendBuf), 0, (const struct sockaddr *)&tSocketServerAddr, iAddrLen); if (iSendLen <= 0) { close(iSocketClient); return -1; } } }
串口
10_3 tty 体系设备节点
- tty 终端,ubuntu 直接切换的就是 tty,
tty0
特指前台终端,如果说当前切换到了tty3
,那么tty0
也是指向tty3
.- 可以使用
echo hello > /dev/tty3
来向tty3
发消息,也可以用echo hello > /dev/tty0
向前台发消息。 while [ 1 ]; do echo msg_to_front > /dev/tty0; sleep 3; done
来间隔3秒向前台发信息。就算切换 tty,也可以自动在当前前台显示出来。/dev/tty
不含数字的 tty,表示当前 shell 所对应的终端,如果当前是 tty3, 那么 访问/dev/tty
就是访问/dev/tty3
.- 串口对应的 tty,一般是
ttyS0
或者ttymxc0
等等。 console
表示控制台,比 tty 权限大。 正常情况下,一般是选择某个 tty 作为 console.- 内核启动时,可以用
cmdline
来指定,比如console=tty1
, 如果有多个值的时候,以最后一个为准。 **注意:console=tty0
和console=tty
都表示使用前台作为 console. - 可以通过
cat /proc/cmdline
来获取cmdline
的参数设置。 如果cmdline
中没有指定 console,那么默认就是第一个tty
,根据驱动安装的先后,可能是第一个串口,也有可能是第一个虚拟终端。 - 在 reboot 之后,可以按住
esc
进入系统选择界面,选择 advance 选项,把 linux 这一行中的 quiet 去掉,然后增加console=tty0
, 然后退出选择第一个进入系统。 系统启动过程中 console 是等于前台,等到系统启动完成,才会按照指定的的 console 来对应设备。 - console 也是可以用
echo hello > /dev/console
来打印信息。
10_4 tty 驱动程序框架
- The TTY demystified, 解密TTY
- 行编辑。大多数用户都会在输入时犯错,所以退格键会很有用。这当然可以由应用程序本身来实现,但是根据UNIX设计“哲学”,应用程序应尽可能保持简单。为了方便起见,操作系统提供了一个编辑缓冲区和一些基本的编辑命令(退格,清除单个单词,清除行,重新打印),这些命令在行规范(line discipline)内默认启用。高级应用程序可以通过将行规范设置为原始模式(raw mode)而不是默认的成熟或准则模式(cooked and canonical)来禁用这些功能。大多数交互程序(编辑器,邮件客户端,shell,及所有依赖curses或readline的程序)均以原始模式运行,并自行处理所有的行编辑命令。行规范还包含字符回显和回车换行(译者注:\r\n 和 \n)间自动转换的选项。如果你喜欢,可以把它看作是一个原始的内核级sed(1)