益阳公司网站建设,qq小程序怎么开发,金华网站建设解决方案,成都网站制作机构系列文章目录 文章目录 系列文章目录前言一、unix, linux, GNU, POSIXLinux程序 二、shellshell语法1.变量2.语句 函数命令命令的执行dialog工具-- 三、文件操作1. Linux 文件结构2. 系统调用和设备驱动程序3. 库函数4. 底层文件访问5. 标准I/O库6.格式化输入输出7. 文件和目录…系列文章目录 文章目录 系列文章目录前言一、unix, linux, GNU, POSIXLinux程序 二、shellshell语法1.变量2.语句 函数命令命令的执行dialog工具-- 三、文件操作1. Linux 文件结构2. 系统调用和设备驱动程序3. 库函数4. 底层文件访问5. 标准I/O库6.格式化输入输出7. 文件和目录维护8. 扫描目录9. 错误处理10. /proc文件系统11. fcntl, mmap 四、linux 环境1. 命令行解析函数2. 环境变量3. 时间和日期4. 临时文件5. 用户信息6. 主机信息7. 日志8. 资源和限制 五、终端/dev/tty终端驱动程序与其接口输入模式输出模式控制模式本地模式特殊字符控制- 终端输出终端控制程序示例检测击键动作 虚拟控制台伪终端AI 六、curses函数库管理基于文本的屏幕七、数据管理内存管理文件锁dbm数据库 八、MySQL十五、套接字1. 套接字属性2. 套接字地址3. 套接字命名4. 创建套接字队列5. 接受连接1. 请求连接2. 使用完成后应关闭sockfd网络信息因特网守护进程xinetd/inetd套接字选项select系统调用多客户服务器程序数据报UDP 前言 一、unix, linux, GNU, POSIX
通用许可证(GPL)条款下发布的一些主要GNU项目软件
GCC: GNU编译器集包括GNU C编译器。G: C编译器是GCC的一部分。GNU make: UNIX make命令的免费版本。Bison: 与UNIX yacc兼容的语法分析程序生成器。bash 命令解释器(shell的一种具体实现)。GNU Emacs: 文本编辑器及环境。
通过以上一些列的可用的自由软件Linux kernel可以说创建一个GNU的、自由的类UNIX系统的目标已经通过Linux系统实现了。
Linux内核一系列工具程序Linux发行版
POSIX: POSIX(Portable, Operating System Interface)是基于UNIX或类UNIX操作系统的一系列操作系统接口标准。标准定义了常用接口(open, write…)和通用工具(cd, ls…)。POSIX在源代码级别支持应用程序的可移植性因此应用程序可以构建为在任何符合POSIX的操作系统上运行。
可用的编程语言
Linux程序
可执行文件 计算机可以直接运行脚本文件 由解释器执行
echo $PATH
/bin: 二进制文件目录用于存放系统启动时用到的程序。/usr/bin: 用户二进制文件目录用于存放用户使用的标准程序。/usr/local/bin: 本地二进制文件目录用于存放软件的安装的程序。
可选的操作系统组件和第三方应用程序可能被安装在 /opt 目录下
二、shell
shell是一个普通的用户程序它解析命令行中的字符串并执行命令。shell是用户程序并非内核的一部分。经典的shell是 bash一般Linux都会预装。csh也是个命令解释器。 ls out.txt # 重定向覆盖文件
ls out.txt # 重定向追加
# 将标输出误和标准错误分别重定向到不同的文件中
ls out.txt 2 out2.txt# 如果想把两组输出都重定向到一个文件中可以用操作符来结合两个输出
# 将12都重定向到同一个 out.txt 需注意操作符出现的顺序。
# “将标准错误重定向到与标准输出相同的地方”
ls out.txt 21 # # Linux通用“回收站” /dev/null 将所有输出丢弃
ls /dev/null 21查看系统中运行的所有进程的名字但包括shelll本身
ps -xo comm|sort |uniq | grep -v sh | less
# sort按照字母顺序排序uniq去除同名的 grep -v sh删除名为sh的进程bash通配符
*匹配任意个字符?单个字符[set]匹配方括号中任何一个字符[^set]上面取反{}: ls my_{finger, toe}s将显示 my_fingers my_toes
shell语法
#!/bin/bash
#!/bin/env python 是更为通用的写法for i in *
doif grep -q POSIX $ithenecho $filefi
doneexit 0 # optional exit normalchmod x test.bin # 所有用户都有该文件的可执行权限
1.变量
aa # 1.等号两边不能有空格
ba b #2.有空格必须用引号echo $b $0 $1 ...read val # 等待用户输入字符直到遇到... you known what that mean
echo $val
echo $val
echo $val
echo \$val单引号不会将$b替换为它的值双引号就会 转移字符 \ 这shell中也有效
环境变量 参数变量
2.语句
检查一个文件是否存在
if [ -f file.c ]; then # [ 命令前后要空格 ( [ 相当于 test 关键字) 这样写then前要加 ;
# 结果为bool形...
fi# or
if [ -f file.c ]
then...
fi# or
if test -f file.c
then
...
fitest命令可使用的条件类型可归为3类字符串比较、算术比较和与文件有关的条件测试
echo -n agbc # 去除换行符
read timeofday
if [ $timeofday yes ]; thenecho yes
elif [ $timeofday no ]; thenecho no
elseecho ???exit 1
fiexit 0for
for i in a b cd e
doecho $i
done# or
for i in a b cd; doecho $i
done# $(command)语法
for file in $(ls f*.sh); dolpr $file
donewhile
while condition do...
done# or
while [ -d LOGS ] ;doecho absleep 1
doneuntil
until condition # 循环将反复执行直到条件为真
do...
done
#---------------
until who | grep $1 /dev/null
dosleep 60
done
# ring the bell
echo -e \acase
case var inpattern [ | pattern] ...) statements;;pattern [ | pattern] ...) statements;;
esac
#-------------------
read timeofdaycase $timeofday inyes) echo sdf;;no ) echo sd;;n ) echo sdf;;* ) echo sjdlfkj;;
esac
#-------------------case $timeofday inyes | y | Yes | YES ) echo slkdjf;;n* | N* ) echo asdf;;* ) echo jsdlkfj;;
esac
#-------------------
case $timeofday inyes | y | Yes | YES)echo askjdfecho sjldkfj;;[nN]*)echo sjldkfjsdfsdfecho sjldkfjlksjdfljsldkfjlsdjf;;[Yy] | [Yy][Ee][Ss])echo haha;;*)echo jecho sdlexit 1;;
esac命令列表 逻辑结构 statement1 statement2 statement3 … 的作用是检查前一条命令的返回值 左边为true才执行右边
statement1 || statement2 || statement3 || … 执行一系列命令直到有一条命令成功 || 只有左边为false才执行右边遇到true立即停止
if [ -f file1 ] echo hello you [ -f file2] echo hehe
thenecho in if
elseecho in else
fi#--------------------
if [ -f file1 ] || echo hello you || [ -f file2] || echo hehe
thenecho in if
elseecho in else
fi#-------------------
[ -f file1 ] command for true || command for false语句块 将多条语句当成一条
get_confirm {grep -v $cdcatnum tracks_file $temp_filecat $temp_file $tracks_fileechoadd_record_tracks
}函数
function_name(){statements
}
#------------
foo(){echo fucntion too is executing
}
echo script starting
foo
echo script ended
sleep 1
exit 0函数的用法
#!/bin/bash
#!/bin/env python 是更为通用的写法yes_or_no(){echo is your name $while truedoecho -n Enter yes or no: read xcase $x in[Yy] | [Yy]es ) return0;;[Nn] | [Nn]o ) return1;;*) echo Answer yes or noesacdone
}echo Original parameter are $*
if yes_or_no $1
thenecho Hi $1, nice name
elseecho Never mind
fi
exit 0命令 break for dir in fred*
doif [ -d $dir ]; thenbreak;fi
doneecho first directory starting fred was $dir: 命令 冒号(:)命令是一个空命令。偶尔用于简化 条件逻辑相当于true的一个别名。由于它是内置命令所以它运行的比true快。 while:相当于while true无限循环 if [ -f fred ]; then:
elseecho file nnooo
ficontinue 类似C .命令 source命令和.命令差不多是同义词 source ./shell_script . ./shell_script 通常当前shell在执行命令是会fork然后waiti子进程执行完毕而.命令是不让shell fork echo echo -n 结尾没有换行 echo -e 转移字符有效\t\a\n等 eval 对参数求值。有点像一个额外的$它给出一个变量的值 foo10
xfoo
y$$x #输出$foo
echo $yfoo10
xfoo
eval y$$x
echo $y #输出10exec 将当前shell替换为一个不同的程序 exec wall Thanks for all the fish修改当前文件描述符 exec 3afile # 文件描述符3被打开以从afile文件中读取exit n命令 使脚本程序以退出码n结束运行。 shell脚本编程中0表示成功1~125是脚本程序可以使用的错误代码。其余数字具有保留含义 退出码说明126文件不可执行127命令未找到128及以上出现一个信号 export export命令将作为它参数的变量导出到子shell中并使之在子shell中有效。 set -a或set -allexport命令 将导出它之后声明的所有变量 expr 将它的参数当做一个表达式来求值。 # 反引号字符使x取值为命令expr $x 1的执行结果
xexpr $x 1
# or
x$(expr $x 1)printf printf format string param1 param2 printf %s %d\t%s Hi there 15 people
# Hi there 15 peoplereturn set 为shell设置参数变量 echo the date is $(date)
set $(date)
echo The month is $2shift 把所有参数变量左移一个位置使$2变为$1$3变为$2。$0不变 while [ $1 ! ]; doecho $1shift
donetrap 接收到信号后所采取的行动 trap -l列出信号 trap commond signal #!/bin/bash
#!/bin/env python 是更为通用的写法trap rm -f /tmp/my_tmp_file_$$ INT
echo creating file
date /tmp/my_tmp_file_$$echo press interrupt (ctrl-c) to interrupt ...
while [ -f /tmp/my_tmp_file$$ ]; doecho File exitssleep 1
done
echo the file no longer exists
trap INT # unset 从环境中删除变量或函数不能删除shell本身定义的只读变量 foohello
echo $foo
unset foo
echo $foo搜索相关 find / -name test find [path] [options] [tests] [actions]grep general regular expression parser正则 字符含义^指向一行的开头$指向一行的结尾.任意单个字符[]方括号同通配符
匹配模式含义[:alnum:]字母与数字[:alpha:]字母[:ascii:]ASCII字符[:blank:]空格或制表符[:digit:]数字
命令的执行
简单示例
for i in 1 2
domy_file_${i}
donedialog工具
– 命令行图形化工具
类型选项选项含义
dialog --title Check me --checklist Pick Numbers 15 25 3 1 one off 2 two on 3 three off#!/bin/bash
#!/bin/env python 是更为通用的写法dialog --title Qestionnaire --msgbox welcom to my simple survey 9 18dialog --title Confirm --yesno are you willing to take part? 9 18
# 用环境变量$?来检查用户是否选择了yes
if [ $? ! 0 ]; thendialog --infobox Thank you anyway 5 20sleep 2dialog --clearexit 0
fi# 使用一个输入框来询问用户的姓名。重定向标准错误流2到临时文件_1.txt然后再将其放入变量Q_NAME中
dialog --title Questionnaire --inputbox please enter your name 9 30 2_1.txt
Q_NAME$(cat _1.txt)# 用户选择的菜单项编号将被保存到临时文件_1.txt中同时这个结果被放入变量Q_MUSIC中
dialog --menu $Q_NAME, what music do you like best? 15 30 4 1 Classical 2 Jazz\3 Country 4 Other 2_1.txt
Q_MUSIC$(cat _1.txt)if [ $Q_MUSIC 1 ]; thendialog --title Likes Classical --msgbox Good choice! 12 25
elsedialog --title Doesnt like Classical --msgbox Shame 12 25
fisleep 2
dialog --clear
exit 0– 三、文件操作
文件和设备系统调用库函数底层文件访问管理文件标准I/O库格式化输入和输出文件和目录的维护扫描目录错误及其处理/proc文件系统fcntl和mmap
1. Linux 文件结构
Linux一切皆文件 访问不同类型的设备和访问文件一样
对于文件操作大多数情况下需要知道5个基本的函数——open, close, read, write, ioctl
文件除了本身包含的内容以外还包含一个名字和一些属性信息(即“管理信息”包括修改时间访问权限等)。这些属性被保存在文件的inode中它是文件系统中的一个特殊的数据块。 目录是用于保存其它文件的节点号和名字的文件。
UNIX和Linux中比较重要的设备文件有3个/dev/console, /dev/tty, /dev/null
/dev/console 这个设备代表的是系统控制台 /dev/tty 如果一个进程有控制终端的话那么特殊文件/dev/tty就是这个控制终端(键盘和显示屏或键盘和窗口)的别名(逻辑设备)。如由系统自动运行的进程和脚本就没有控制终端所以它们不能打开/dev/tty。 /dev/tty/目录下存放所有的逻辑终端 /dev/null /dev/null文件是空(null)设备。所有写向这个设备的输出都将被丢弃而读取这个设备会立刻返回一个文件尾标志。
/dev目录中的其他设备包括硬盘、软盘、通信端口、声卡以及一些代表系统内部工作状态的设备。
2. 系统调用和设备驱动程序
操作系统内核由一组设备驱动程序组成。
ioctl: 把控制信息传递给设备驱动程序。用于提供一些与特定硬件设备有关的必要控制所以它的用法随设备的不同而不同。
3. 库函数
函数库封装的系统调用提供更高层次的用法。如stdio.h用户程序、函数库、系统调用、内核、设备驱动以及硬件之间的关系 4. 底层文件访问
文件描述符相当与一个索引它指向操作系统的一个对象
write 的作用是把缓冲区buf的年n个字节写入与文件描述符fildes关联的文件中。它返回实际写入的字节数。如果文件描述符有错误或者底层的设备驱动程序对数据块长度比较敏感该返回值可能会小于n。如果返回0则表示未写入任何数据如果返回-1表示调用出错错误码会保存在全局变量errno里。
#include unistd.h
size_t wirte(int fd, const void* buf, size_t nbytes);read 的作用是从与文件描述符fd关联的文件里读入n个字节的数据并把它们放到数据区buf中。它返回实际读入的字节数这可能会小于请求的字节数。如果返回0则表示未读入任何数据已达到了文件尾如果返回-1表示调用出错错误码会保存在全局变量errno里。
open 建立了一条到文件或设备的访问路径。如果调用成功它将返回一个可以被read、write和其他系统调用使用的文件描述符。这个文件描述符是唯一的它不会与任何其他运行中的进程共享 。如果两个进程同时打开一个文件它们会分别得到两个不同的文件描述符。如果它们都对文件进行写操作那么它们会各写各的它们分别接着上次离开的位置继续往下写。它们的数据不会交织在一起而是彼此互相覆盖。两个程序对文件的读写位置(偏移值)不同。可以通过使用文件锁来防止出现冲突。
#include fcntl.h
#incldue sys/types.h
#incldue sys/stat.hint open(const char* path, int oflags);
int open(const char* path, int oflags, mode_t mode);
--oflags: 文件访问模式- O_RDONLY- O_WRONLY- O_RDWR- O_APPEND- O_TRUNC: 覆盖- O_CREAT: 如果需要就按参数mode中给出的访问模式创建文件- O_EXCL: 与O_CREAT一起使用确保调用者创建出文件。open调用是一个原子操作也即是在说
它只执行一个函数调用。使用这个可选模式可以防止两个程序同时创建同一个文件。如果文件已存在open调用将失败。
当使用O_CREAT时必须使用三个参数格式的open调用。第三个参数mode是几个标志位按位或后得到的,这些标志位在头文件sys/stat.h中S_IRUSR: 读属主S_IWUSR: 写属主S_IXUSR: 执行属主S_IRGRP: 读属组S_IWGRP:写属组S_IXGRP:执行属组S_IROTH: 读其他S_IWOTH: 写其他S_IXOTH: 执行其他 在程序中创建的文件的权限是有mode参数和umask值共同决定的umask是权限掩码。这样做并不能组织程序或用户在随后使用chmod命令或chmod系统调用来改变文件权限。
close 终止文件描述符与其对应文件之间的关联并释放资源。成功0失败-1。
ioctl 大杂烩。它提供了一个用于控制设备及其描述符行为和配置底层服务的接口。终端、文件描述符、套接字甚至磁带都可有为它们定义的ioctl。
#include unistd.h
int ioctl(int fd, int cmd, ...);理论上下面这两段程序所花费的时长将不同其主要耗费在了系统调用上所以对其使用进行优化是有必要的。
int main()
{int fd_src;fd_src open(txt.txt , O_RDONLY);if (fd_src 0) perror(open, txt.txt);char buf[1024] {\0};size_t rdsz 0;int fd_dst;fd_dst open(txt2.txt, O_WRONLY | O_CREAT, 0777);while ( (rdsz read(fd_src, buf, 1024)) 0 ){if (write(fd_dst, buf, rdsz) rdsz){perror(write);exit(0);}}close(fd_src);close(fd_dst);
}
int main()
{int fd_src;fd_src open(txt.txt , O_RDONLY);if (fd_src 0) perror(open, txt.txt);char buf[1] {\0};size_t rdsz 0;int fd_dst;fd_dst open(txt2.txt, O_WRONLY | O_CREAT, 0777);while ( (rdsz read(fd_src, buf, 1)) 0 ){if (write(fd_dst, buf, rdsz) rdsz){perror(write);exit(0);}}close(fd_src);close(fd_dst);
}
lseek 对文件描述符的读写指针进行设置。
#include unistd.h
#include sys/types.hoff_t lseek(int fd, off_t offset, int whence);fstat, stat, lstat 返回与打开的文件描述符相关的文件的状态信息该信息将会写到一个buf结构中buf的地址以参数形式传递给fstat
#include unistd.h
#include sys/stat.h
#include sys/types.hint fstat(int fd, struct stat *buf);
int stat(const char* path, struct stat *buf);
int lstat(const char* path, struct stat *buf);dup, dup2 复制文件描述符使得多个fd指向同一个对象
#include unistd.h
int dup(int fd);
int dup2(int fd, int fd2);5. 标准I/O库
fopen, fclosefread, fwritefflush-fseek-fgetc, getc, getcharfputc, putc, putcharfgets, getsprintf, fprintf, sprintfscanf, fscanf, sscanf
fopenFILE* fopen(const char* filename, const char* mode);freadsize_t fread(void*ptr, size_t size, size_t nitems, FILE* stream);fwritesize_t fwrite(const void*ptr, size_t size, size_t nitems, FILE* stream);fcloseint fclose(FILE*stream);fflushint fflush(FILE* stream);fseekint fseek(FILE* stream, long int offset, int whence);fgetc, getc, getcharfputc, putc, putcharfgets, gets fgets从输入文件流读取一个字符串直到遇到换行符或已经传输了n-1个字符或到达文件尾。 gets类似上面只不过它从标准输入读取数据并丢弃换行符char* fgets(char*s, int n, FILE*stream);
char* gets(char* s);6.格式化输入输出
printf, fprintf, sprintf scanf, fscanf, sscanf fgetpos 获得文件流的当前位置 fsetpos 设置文件流的当前位置 ftell 返回文件流当前位置的偏移值 rewind 重置文件流里的读写位置 freopen重新使用一个文件流 setvbuf设置文件流的缓冲机制 remove相当于unlink函数但如果它的path参数是一个目录的话其作用就相当于rmdir
int ferror(FILE* stream);
int feof(FILE* stream);
void clearerr(FILE* stream);int fileno(FILE* stream);
FILE* fdopen(int fd, const char*mode);7. 文件和目录维护 chmod – syscall int chmod(const char* path, mode_t mode);chown – syscall int chown(const char* path, uid_t owner, gid_t group);unlink, link, symlink – syscall unlink系统调用删除一个文件的目录项并减少它的连接数。如果一个文件的连接数减少到0并且没有进程打开它这个文件就会被删除。目录项总是被立刻删除但文件所占用的空间要等到最后一个进程关闭它之后才会被系统回收先用open创建一个文件然后对其调用unlink可以用来创建临时文件这些文件只有在被打开的时候才能被程序使用当程序退出并且文件关闭时它们就会被自动删除掉 link将创建一个指向已有文件path1的新链接。新目录项由path2给出。可以通过symlik创建符号链接。 int unlink(const char* path);
int link(const char* path1, const char* path2);
int symlink(const char* path1, const char* path2);mkdir, rmdir – syscall #include sys/types.h
#include sys/stat.h
int mkdir(const char* path, mode_t mode);
#include unistd.h
int rmdir(const char* path);chdir – syscall, getcwd – function chdir用于切换目录 getcwd用于显示当前工作目录 #include unistd.h
int chdir(const char* path);
char* getcwd(char* buf, size_t size);8. 扫描目录
文件流(FILE*) 目录流(DIR*)
opendir, closedirreaddirtelldirseekdir opendir – function #include sys/types.h
#include dirent.h
DIR* opendir(const char* name);readdir – function 每次调用该函数都会返回目录项中的下一项如果发生错误或到达目录尾放回NULL #include sys/types.h
#include dirent.h
struct dirent* readdir(DIR* dirp);telldir – function telldir函数的返回值记录着一个目录流里的当前位置可以再随后的seekdir中利用这个值来重置目录扫描到当前位置。 long int telldir(DIR* dirp);seekdir – function 设置目录流的目录项指针。loc的值用来设置指针位置它应该通过前一个telldir获得 void seekdir(DIR* dirp, long int loc);closedir int closedir(DIR* dirp);9. 错误处理
#include errno.h
strerror函数把错误代码映射为一个字符串该字符串对发生的错误类型进行说明
perror函数也把errno变量中报告的当前错误映射到一个字符串并把它输出到标准错误输出流
char* strerror(int errnum);void perror(const char* s);10. /proc文件系统
proc是伪文件系统它提供了与内核数据结构的接口。通常挂载在/proc下。一般由系统自动挂载也可手动挂载: mount -t proc proc /proc。/proc中的大多数文件是只读的有些是可写的用以修改内核变量的值。
11. fcntl, mmap
fcntl对底层文件描述符提供了更多的操作方法
#include fcntl.h
int fcntl(int fd, int cmd);
int fcntl(int fd, int cmd, long arg);mmap函数创建一个指向一段内存区域的指针该内存区域与可以通过一个打开的文件描述符访问的文件的内容相关联。
#include sys/mman.h
// 以下两个函数线程安全// success 有效地址; failure: (void*)-1, errno be set
void *mmap(void *addr, size_t len, int prot, int flags, int fd, off_t offset);
// sucess: 0; failure:-1, errno be set
int munmap(void* addr, size_t length);return: 映射起始地址 addr: 创建的新映射的起始地址; 如果为NULL内核会自动选择合适的地址(推荐的方式)如果不为NULL则内核选择它作为映射的起始地址(不推荐) len: 映射的长度必须大于0 prot: 映射内存的权限。以下可安位取或不能与文件的打开模式冲突 PROT_EXEC: PROT_READ: PROT_WRITE: PROT_NONE: 不能访问不能和以上取或 flags: 参数确定映射的更新对于映射同一区域的其他进程是否可见以及更新是否会传递到底层文件。此行为由以下因素决定 在标志中恰好包含以下值之一 MAP_SHARED: fd: 文件描述符(文件映射与匿名映射相反)。调用mmap后fd可直接关闭而不影响mmap offset: 被映射fd的起始地址偏移量必须为页大小( sysconf(_SC_PAGE_SIZE)返回值 )的整数倍
NOTE 文件以页面大小的整数被进行映射映射尾部多余的补0并且对多余部分的修改不会写入文件。此时增删文件的大小是未定义行为。
#include sys/mman.h
#include sys/stat.h
#include fcntl.h
#include stdio.h
#include stdlib.h
#include unistd.h#define handle_error(msg) \do { perror(msg); exit(EXIT_FAILURE); } while (0)int
main(int argc, char *argv[])
{char *addr;int fd;struct stat sb;off_t offset, pa_offset;size_t length;ssize_t s;if (argc 3 || argc 4) {fprintf(stderr, %s file offset [length]\n, argv[0]);exit(EXIT_FAILURE);}fd open(argv[1], O_RDONLY);if (fd -1)handle_error(open);if (fstat(fd, sb) -1) /* To obtain file size */handle_error(fstat);offset atoi(argv[2]);pa_offset offset ~(sysconf(_SC_PAGE_SIZE) - 1);/* offset for mmap() must be page aligned */if (offset sb.st_size) {fprintf(stderr, offset is past end of file\n);exit(EXIT_FAILURE);}if (argc 4) {length atoi(argv[3]);if (offset length sb.st_size)length sb.st_size - offset;/* Cant display bytes past end of file */} else { /* No length arg display to end of file */length sb.st_size - offset;}addr (char*)mmap(NULL, length offset - pa_offset, PROT_READ,MAP_PRIVATE, fd, pa_offset);if (addr MAP_FAILED)handle_error(mmap);s write(STDOUT_FILENO, addr offset - pa_offset, length);if (s ! length) {if (s -1)handle_error(write);fprintf(stderr, partial write);exit(EXIT_FAILURE);}munmap(addr, length offset - pa_offset);close(fd);exit(EXIT_SUCCESS);
}#include sys/mman.h
int msync(void* addr, size_t len, int flags);把在该内存段的某个部分或整段中的修改写回到被映射的文件中或者从被映射的文件里读出
flags: MS_ASYVN(异步写), M_SYNC(同步写), MS_INVALIDATE(从文件中读回数据)
#include stdio.h
#include unistd.h
#include sys/types.h
#include sys/stat.h
#include fcntl.h
#include sys/mman.htypedef struct
{int integer;char string[24];
} RECORD;#define NRECORDS (100)int main(int argc, const char* argv[])
{RECORD record, *mapped;FILE *fp;fp fopen(records.dat, w);for (int i 0; i NRECORDS; i) {record.integer i;sprintf(record.string, RECORD-%d, i);fwrite(record, sizeof(record), 1, fp);}fclose(fp);fp fopen(records.dat, r);fseek(fp, 43*sizeof(RECORD), SEEK_SET);fread(record, sizeof(RECORD), 1, fp);record.integer 143;sprintf(record.string, RECORD-%d, record.integer);fseek(fp, 43*sizeof(RECORD), SEEK_SET);fwrite(record, sizeof(RECORD), 1, fp);fclose(fp);int fd open(records.dat, O_RDWR);mapped (RECORD*)mmap(NULL, NRECORDS*sizeof(RECORD), PROT_READ|PROT_WRITE, MAP_SHARED, fd, 0);close(fd);mapped[43].integer 243;sprintf(mapped[43].string, RECORD-%d, mapped[43].integer);msync(mapped, NRECORDS*sizeof(RECORD), MS_ASYNC);munmap(mapped, NRECORDS*sizeof(RECORD));}四、linux 环境
向程序传递参数环境变量查看时间临时文件获得有关用户和主机的信息生成和配置日志信息了解系统各项资源的限制
1. 命令行解析函数
main的参数由shell传递
命令行解析函数
#include unistd.hint getopt(int argc, char* const argv[], const char* optstring);
extern char* optarg;
extern int optind, opterr, optopt;#include getopt.h
int getopt_long(int argc, char* const argv[], const char*optstring, const struct option* longopts, int *longindex);
int getopt_long_only(int argc, char* const argv[], const char* optstring,const struct option* longopts, int* longindex);optarg选项后面的值会被赋给optargoptind所有选项值的个数optopt有问题的字符选项返回值为选项字符
eg:
# -n -t val#include unistd.h
#include stdlib.h
#include stdio.hint
main(int argc, char *argv[])
{int flags, opt;int nsecs, tfnd;nsecs 0;tfnd 0;flags 0;while ((opt getopt(argc, argv, nt:)) ! -1) {switch (opt) {case n:flags 1;break;case t:nsecs atoi(optarg);tfnd 1;break;default: /* ? */fprintf(stderr, Usage: %s [-t nsecs] [-n] name\n,argv[0]);exit(EXIT_FAILURE);}}printf(flags%d; tfnd%d; nsecs%d; optind%d\n,flags, tfnd, nsecs, optind);if (optind argc) {fprintf(stderr, Expected argument after options\n);exit(EXIT_FAILURE);}printf(name argument %s\n, argv[optind]);/* Other code omitted */exit(EXIT_SUCCESS);
}int main(int argc, const char* argv[])
{int opt -1;while ( (opt getopt(argc, argv, alhf:d:)) ! -1 ){printf(-----st-------);printf(optind:%d, opt:%c\n, optind, optopt);switch (opt){case a:case l:case h:printf(option:%c\n, opt);break;case f:printf(-f val:%s\n, optarg);break;default:printf(optind:%d, opt:%c\n, optind, optopt);break;}printf(-----ed-------);printf(optind:%d, opt:%c\n, optind, optopt);}printf(optind:%d, opt:%c\n, optind, optopt);}getopt_long
struct option longopts[] {{initialize, 0, NULL, i},{file, 1, NULL, f},{list, 0, NULL, l},{restart, 0, NULL, r},{0,0,0,0}
};
while( (opt getopt_long(argc, argv, if:lr)) ! -1 );2. 环境变量
#include stdlib.hchar* getenv(const char* name);
int putenv(const char* string);getenv函数以给定的名字搜索环境中的一个字符串并返回与该名字相关的值。若变量不存在或变量没有值则返回null。它返回的字符串存储在静态区 putenv函数以一个格式为“名字值”的字符串作为参数并将该字符串加到当前环境变量中。如果失败返回-1。
NOTE 环境仅对程序本身有效。变量的值不会从子进程传播到父进程
#include stdlib.h
#include stdio.hint main(int argc, char *argv[])
{char* home_val getenv(HOME);printf(%s\n, home_val);putenv(HELLOYES);home_val getenv(HELLO);printf(%s\n, home_val);
}程序经常使用环境变量来改变它们的工作方式。
用户可在以下方式设置环境变量的值在默认环境中设置、通过登录shell读取的.profile文件来设置、使用shell专用的启动文件(rc)或在shell命令行上对变量进行设置
#incldue unistd.h
extern char** environ;3. 时间和日期
GMT格林尼治时间 1970年1月1日0点
date命令可以获取时间和日期
UTC时间1970年1月1日零时作为 第0秒
获取时间点
#include time.h
/* Return the current time and put it in *TIMER if TIMER is not NULL. */
// 返回秒数跟随系统时间
time_t time (time_t *__timer) __THROW;/* Get current value of clock CLOCK_ID and store it in TP. */
extern int clock_gettime (clockid_t __clock_id, struct timespec *__tp) __THROW;
__clock_idCLOCK_REALTIME系统时间CLOCK_MONOTONIC单调递增时钟可用于计算时间间隔
时间转换
struct tm {int tm_sec; /* Seconds (0-60) */int tm_min; /* Minutes (0-59) */int tm_hour; /* Hours (0-23) */int tm_mday; /* Day of the month (1-31) */int tm_mon; /* Month (0-11) */int tm_year; /* Year - 1900 */int tm_wday; /* Day of the week (0-6, Sunday 0) */int tm_yday; /* Day in the year (0-365, 1 Jan 0) */int tm_isdst; /* Daylight saving time */
};// *_r thread safe
// 不带 _r 的函数返回值存储在静态区
char *asctime(const struct tm *tm);
char *asctime_r(const struct tm *tm, char *buf);char *ctime(const time_t *timep);
char *ctime_r(const time_t *timep, char *buf);struct tm *gmtime(const time_t *timep);
struct tm *gmtime_r(const time_t *timep, struct tm *result);struct tm *localtime(const time_t *timep);
struct tm *localtime_r(const time_t *timep, struct tm *result);// 线程安全
time_t mktime(struct tm *tm);格式化输出时间
// 将struct tm 格式化为 字符串的形式
size_t strftime(char*s, size_t maxsize, const char*format, struct tm* timeptr);// 读取日期和时间,
/* Parse buf according to FORMAT and store binary time information in timeptr.
The return value is a pointer to the first unparsed character in S. */
char* strptime(const char* buf, const char* format, struct tm* timeptr);4. 临时文件
#include stdio.h// 返回一个不与任何已存在文件同名的有效文件名可能与其他程序调用该函数产生的名字同名
// 关于线程安全等请参考手册
char* tmpnam(char*s); // 返回值存储在静态存储区// 直接打开一个唯一的临时文件当对它的引用全部关闭时该文件会被自动删除
FILE* tmpfile(void);#include stdlib.h// unix中类似函数
char* mktemp(char* template);
int mkstemp(char* template); // 返回文件描述符template参数必须是一个以6个X字符结尾的字符串以上函数以给定的模板为基础创建一个唯一的文件名。用有效文件名字符的一个唯一组合来替换这些X字符
5. 用户信息
init进程PID1除了该进程外所有Linux程序都是由其他程序或用户启动的。 程序能够通过检查环境变量和读取系统时钟来在很大程度上了解它所处的运行环境。程序也能够发现它的使用者的相关信息。 当一个用户要登录Linux时通过用户名和密码验证用户就可以进入一个shell。每个用户都有一个唯一的UIDLinux运行的每个程序都是以某个用户的名义在运行都有一个关联的UID。
UID 为uid_t类型在sys/types.h中定义一般情况下用户的UID值都大于100.
#include sys/types.h
#include unistd.h
// 返回启动程序的用户的UID
uid_t getuid(void);
// 返回与当前用户关联的登录名
char* getlogin();系统文件/etc/passwd 包含一个用户账号数据库。如果要获取相关信息建议使用如下相关函数
sdf: x :1000:1000:sdf,,,:/home/sdf:/bin/bash
用户名加密口令UIDGID全名家目录默认shellsdfx10001000sdf,,,/home/sdf/bin/bash
获取用户信息的编程接口
#include sys/types.h
#include pwd.h
struct passwd* getpwuid(uid_t uid);
struct passwd* getpwnam(const char* name);// 依次取出文件数据项
struct passwd *getpwent();
// 终止处理过程
void endpwent(void);
// 再次调佣getpwent时将返回首x
void setpwent(void);#include unistd.h
/* Get the real user ID of the calling process. */
extern __uid_t getuid (void) __THROW;
/* Get the real group ID of the calling process. */
extern __gid_t getgid (void) __THROW;
/* Return the login name of the user.This function is a possible cancellation point and therefore notmarked with __THROW. */
extern char *getlogin (void);#include pwd.h
/* A record in the user database. */
struct passwd
{char *pw_name; /* Username. */char *pw_passwd; /* Hashed passphrase, if shadow databasenot in use (see shadow.h). */__uid_t pw_uid; /* User ID. */__gid_t pw_gid; /* Group ID. */char *pw_gecos; /* Real name. */char *pw_dir; /* Home directory. */char *pw_shell; /* Shell program. */
};6. 主机信息
#include unistd.h
// These system calls are used to access or to change the system hostname.
int gethostname(char* name, size_t namelen);
int sethostname(const char* name, size_t len);
long gethostid(void);#include sys/utsname.h
// get name and information about current kernel
int uname(struct utsname *name); /* Structure describing the system and machine. */
struct utsname{/* Name of the implementation of the operating system. */char sysname[_UTSNAME_SYSNAME_LENGTH];/* Name of this node on the network. */char nodename[_UTSNAME_NODENAME_LENGTH];/* Current release level of this implementation. */char release[_UTSNAME_RELEASE_LENGTH];/* Current version level of this release. */char version[_UTSNAME_VERSION_LENGTH];/* Name of the hardware type the system is running on. */char machine[_UTSNAME_MACHINE_LENGTH];#if _UTSNAME_DOMAIN_LENGTH - 0/* Name of the domain of this node on the network. */
# ifdef __USE_GNUchar domainname[_UTSNAME_DOMAIN_LENGTH];
# elsechar __domainname[_UTSNAME_DOMAIN_LENGTH];
# endif
#endif};7. 日志
/var/log/#include syslog.h
void syslog(int priority, const char* message, arguments...);void closelog(void); void openlog(const char* ident, int logopt, int facility);
int setlogmask(int maskpri);// 实测会被记录到 /var/log/syslog下:
// syslog(LOG_USER, hi, this is log... - %m\n);
// Oct 28 09:29:34 MyComputer main: hi, this is log... - Successpid_t getpid(void);
pid-t getppid(void); // 获得父进程pid8. 资源和限制
#include sys/resource.h
int getpriority(int which, id_t who);
int getpriority(int which, id_t who, int priority);
int getrlimit(int resource, struct rlimit *r_limit);
int setrlimit(int resource, const struct rlimit *r_limit);
int getrusage(int who, struct rusage* r_usage);一个程序耗费的CPU时间 用户时间程序执行自身指令所耗费的时间系统时间操作系统为执行程序所耗费的时间即输入输出、系统调用时间。
// 确定当前进程优先级
priority getpriority(PRIO_PROCESS, getpid());
ulimit -a
# 查看操作系统对软件的资源限制五、终端
检查文件描述符是否连接到一个终端
#include unistd.h
int isatty(int fd);输出缓冲是由libc实现的那么输入缓冲应该也是由libc实现的
/dev/tty
Linux提供了一个特殊设备/dev/tty(点击页内跳转) 该设备始终执行当前终端或当前的登录会话。
终端驱动程序与其接口 通过控制接口能够控制的主要功能
行编辑是否允许用退格键进行编辑缓存是立即读取字符还是等待一段可配置的延迟之后再读取它们。回显允许控制字符的回显例如读取密码时回车/换行(CR/LF)定义如何在输入/输出是映射回车/换行符比如打印\n字符时应该如何处理线速~
硬件模型 termios结构
#include termios.h
int tcgetattr(int fd, struct termios *termios_p);
int tcsetattr(int fd, int actions, const struct* termios_p);struct termios{tcflag_t c_iflag; /* input mode flags */tcflag_t c_oflag; /* output mode flags */tcflag_t c_cflag; /* control mode flags */tcflag_t c_lflag; /* local mode flags */cc_t c_line; /* line discipline */cc_t c_cc[NCCS]; /* control characters */speed_t c_ispeed; /* input speed */speed_t c_ospeed; /* output speed */
#define _HAVE_STRUCT_TERMIOS_C_ISPEED 1
#define _HAVE_STRUCT_TERMIOS_C_OSPEED 1};输入模式
输入模式控制输入数据终端驱动程序从串口或键盘接收到的字符在被传递给程序之前的处理方式。 c_iflag成员宏
BRKINT: 当在输入行中检测到一个终止状态链接丢失时产生一个中断IGNBRK: 忽略输入行中的终止状态ICRNL: 将接收到的回车符转换为新行符IGNCR: 忽略接收到的回车符INLCR: 将接收到的新行符转换为回车符IGNPAR: 忽略奇偶校验错误的字符INPCK: 对接收到的字符执行奇偶校验PARMRK: 对奇偶校验错误做出标记ISTRIP: 将所有接收到的字符裁减为7比特IXOFF: 对输入启用软件流控IXON: 对输出启用软件流控
输出模式
控制输出字符的处理方式即由程序发送出去的字符在传递到串口或屏幕之前是如何处理的。 c_oflag成员宏
OPOST: 打开输出处理功能ONLCR: 将输出中的换行符转换为回车/换行符OCRNL: 将输出中的回车符转换为新行符ONOCR: 在第0列不输出回车符ONLRET: 不输出回车符OFILL: 发送填充字符以提供延时OFDEL: 用DEL而不是NULL字符作为填充字符NLDLY: 新行符延时选择CRDLY: 回车符延时选择TABDLY制表符延时选择BSDLY: 退格符延时选择VTDLY: 垂直制表符延时选择FFDLY: 换页符延时选择
控制模式
控制中断的硬件特性 c_cflag成员的宏
CLOCAL: 忽略所有调制解调器的状态行CREAD: 启用字符接收器CS5: 发送或接收字符时用5比较CS6: 发送或接收字符时用6比较CS7: 发送或接收字符时用7比较CS8: 发送或接收字符时用8比较CSTOPB: 每个字符使用两个停止位而不是一个HUPCL: 关闭时挂断调制解调器PARENB: 启用奇偶校验码的生成和检测功能PARODD: 使用奇校验而不是偶校验
本地模式
控制终端的各种特性 c_lflag宏
ECHO: 启用输入字符的本地回显功能ECHOE: 接收到ERASE时执行退格、空格、退格的动作组合ECHOK: 接收到KILL字符时执行行删除操作ECHONL: 回显新行符ICANON: 启用标准输入处理IEXTEN: 启用基于特性实现的函数ISIG: 启用信号NOFLSH: 禁止清空队列TOSTOP: 在试图进行写操作之前给后台进程发送一个信号
特殊字符控制
特殊字符是一些字符组合如CtrlC
-
关闭回显修改终端速度等等
终端输出
终端类型 echo $TERM
xterm-256colorshell通过xterm-256color程序一个X视窗系统中的终端仿真程序或是提供类似功能的程序运行的
查看终端当前信息属性 stty -a
speed 38400 baud; rows 75; columns 274; line 0;
intr ^C; quit ^\; erase ^?; kill ^U; eof ^D; eol undef; eol2 undef; swtch undef; start ^Q; stop ^S; susp ^Z; rprnt ^R; werase ^W; lnext ^V; discard ^O; min 1; time 0;
-parenb -parodd -cmspar cs8 -hupcl -cstopb cread -clocal -crtscts
-ignbrk -brkint -ignpar -parmrk -inpck -istrip -inlcr -igncr icrnl ixon -ixoff -iuclc -ixany -imaxbel iutf8
opost -olcuc -ocrnl onlcr -onocr -onlret -ofill -ofdel nl0 cr0 tab0 bs0 vt0 ff0
isig icanon iexten echo echoe echok -echonl -noflsh -xcase -tostop -echoprt echoctl echoke -flusho -extproc
输出terminfo数据项的可读版本 infocmp xterm
#include term.h
// 将当前终端类型设置为参数term指向的值
int setupterm(char* term, int fd, int *errret);
- -1 terminfo数据库不存在
- 0terminfo数据库中没有匹配的数据项
- 1成功
该函数成功返回常量OK失败返回ERR。如果errret被设置为空指针setupterm函数会在失败时输出一条诊断信息并导致程序直接退出 just like this:
#include stdio.h
#include term.h
#include curses.h
#include stdlib.hint main()
{setupterm(unlisted, fileno(stdout), (int*)0);printf(Done.\n);exit(0);
}终端控制程序示例 https://gitee.com/overcast-to-clear/linux-programming-/tree/master/%E4%BA%94%E3%80%81%E7%BB%88%E7%AB%AF%E6%8E%A7%E5%88%B6%E7%A8%8B%E5%BA%8F%E7%A4%BA%E4%BE%8B https://gitee.com/overcast-to-clear/linux-programming-/blob/master/%E4%BA%94%E3%80%81%E7%BB%88%E7%AB%AF%E6%8E%A7%E5%88%B6%E7%A8%8B%E5%BA%8F%E7%A4%BA%E4%BE%8B/example.c 检测击键动作
MS-DOS中有kbhit函数用于在没有实际进行读操作之前检测是否某个键被按过而Linux中没有次函数。
虚拟控制台
特殊文件“/dev/tty”总是指向当前终端
Linux提供了虚拟控制台的功能一组终端设备共享PC电脑的屏幕、键盘和鼠标。通常一个Linux安装将配置多个虚拟控制台。虚拟控制台通过字符设备文件/dev/ttyN使用
使用命令who 和ps即可看到目前登录进系统的用户以及在这个虚拟控制台上运行的shell和执行的程序。
sdfMyComputer:/dev$ who
sdf tty2 2023-10-28 08:20 (tty2)
sdf tty3 2023-10-28 21:31sdfMyComputer:/dev$ ps -e | grep tty2355 tty2 00:00:00 gdm-wayland-ses2361 tty2 00:00:00 gnome-session-b22039 tty3 00:00:00 login22122 tty3 00:00:00 bash
可以通过CtrlAlt_FN在不同的ttyN之间切换字符界面可以通过AltFN在不同的虚拟控制台之间进行切换。
可以同时在Linux系统上运行多个X视窗会话
startx -- :1/usr/lib/xorg/Xorg.wrap: Only console users are allowed to run the X server在其他方面虚拟控制台的行为都与普通硬件终端一样。
伪终端
PTY
伪终端的行为与虚拟控制台的行为非常相似唯一的区别是伪终端没有对应的硬件设备。它们可以用来为其他程序提供终端形式的接口。
AI
chatgpt 3.5 伪终端Pseudo TerminalPTY 伪终端是一种虚拟设备用于模拟物理终端的功能。它是一个用于进程之间的通信的接口使得一个进程可以像在终端上一样与另一个进程交互。伪终端通常以一对的方式出现一个用于接收用户输入另一个用于输出给应用程序。伪终端用于在不同进程之间建立交互式会话例如SSH会话或在终端窗口中运行shell或其他终端应用程序。 虚拟终端Virtual Terminal 虚拟终端是一个概念它不对应实际的硬件设备而是指在Linux系统上提供的多个虚拟控制台终端。这些虚拟终端允许用户在同一台计算机上同时运行多个命令行会话或终端应用程序。用户可以通过切换虚拟终端来管理多个命令行界面每个虚拟终端通常对应一个文本控制台。虚拟终端通常通过键盘快捷键例如CtrlAltF1到F6进行切换。 区别 伪终端是一种虚拟设备用于在不同进程之间建立交互式会话而虚拟终端是在同一台计算机上管理多个命令行会话的虚拟控制台。伪终端通常用于进程之间的通信例如通过SSH建立远程会话或在终端窗口中运行终端应用程序而虚拟终端用于在同一台计算机上管理多个本地会话。伪终端是一个设备接口虚拟终端是一个用户界面的概念不对应硬件设备
Bing chat 在Linux系统中伪终端也被称为pty是一对字符设备包括伪终端主设备master和伪终端从设备slave。其中slave对应/dev/pts/目录下的一个文件而master则在内存中标识为一个文件描述符12。
伪终端的主要作用是模拟物理终端的行为。它由运行在用户态的终端模拟器提供。Master端更接近用户显示器、键盘的一端而slave端则运行在虚拟终端上的命令行接口CLI程序。Linux的伪终端驱动程序会把master端如键盘写入的数据转发给slave端供程序输入同时把程序写入slave端的数据转发给master端供显示器驱动等读取12。
虚拟终端是Linux系统中的一个概念它是通过软件在物理机上虚拟出来的终端3。Linux默认存在6个虚拟终端可以使用快捷键Ctrl Alt F1~F6切换3。这些虚拟终端使用ttyteletypewriter显示3。虚拟终端并不直接对应硬件设备但它们模拟了物理设备如键盘和显示器的行为13。
总的来说伪终端和虚拟终端都是Linux系统中用于提供用户交互界面的重要工具。它们使得用户可以在同一时间运行多个会话并且可以方便地在这些会话之间切换
六、curses函数库管理基于文本的屏幕
curses库简介
上一章介绍了如何加强对字符输入的控制以及如何以终端无关的方式提供字符输出。使用通用终端接口GTI或termios和通过tparm以及相关函数控制escape转义系列都会导致大量的底层代码。 而curses库函数提供了高层接口它位于简单的文本行程序和完全图形化界面的X视窗系统程序如GTK/GNOME和qt/KED之间。
stdscr与终端屏幕的尺寸完全一样。窗口可以互相重叠可以拥有自己的多个子窗口但每个子窗口必须总是被包含在它的父窗口内。
stdscr数据结构对应的是“标准屏幕”与stdout非常相似是个缓存curscr数据结构和stdscr相似但它对应的是当前屏幕的样子。在程序调用refresh之前输出到stdscr上的内容不会显示在屏幕上。curses函数库会在refresh函数被调用时比较stdscr与curscr屏幕当前的样子之间的不同之处然后用这两个数据结构之间的差异来刷新屏幕。
移动和更新窗口
int touchwin(WINDOW *window_ptr);
int scrollok(WINDOW *window_ptr, bool scroll_flag);
int scroll(WINDOW *window_ptr);touchwin非常特殊他的作用是通知curses函数库其指针指向的窗口内容已经发生改变在下次调用wrefresh函数是curses必须重新绘制该窗口即使用户实际并未修改其内容。当屏幕上重叠着多个窗口时可以用过该函数来安排要显示的窗口。
优化屏幕刷新 目的尽量减少在屏幕上绘制的字符数目
#include curses.h
int wnoutrefresh(WINDOW* window_ptr);
int doupdate(void);子窗口
#include curses.h
WINDOW* subwin(WINDOW*parent, int num_of_lines, int num_of_cols, int start_y, int start_x);
int delwin(WINDOW* win_to_del);pad 可以控制尺寸大于正常窗口的逻辑屏幕。
#include curses.h
WINDOW *newpad(int num_of_lines, int num_of_cols);
// 将pad从坐标(pad_row, pad_col)开始的区域写到屏幕上指定的显示区域该显示区域
// 从(scr_row_min,scr_col_min)到(scr_row_max,scr_col_max)
int prefresh(WINDOW *pad_ptr, int pad_row, int pad_col, int scr_row_min, int scr_col_min, int scr_row_max, int scr_col_max
);
pnoutrefresh;类似wnoutrefresh七、数据管理
内存管理
当物理内存耗尽时内核便会使用交换空间(swap space)在Linux中交换空间是一个在安装系统是分配的独立的磁盘区域。
文件锁
open系统调用并且带上O_CREAT和O_EXCL标志这样能够以一个原子操作同时完成两项工作确定文件不存在然后创建它。
#incldue unistd.h
#include stdlib.h
#include stdio.h
#include fcntl.h
#include errno.h
const char* lock_file /tmp/LCK.test2;
int main(){int fd;int tries 10;while(tries--){fd open(lock_file, O_RDWR|O_CREAT|O_EXCL, 0444);if (fd -1) {printf(%d -lock already present\n, getpid());sleep(3);} else {printf(%d - i have exclusive access\n, getpid());sleep(1);close(fd);unlink(lock_file);}}return 0;
}区域锁定
#include fcntl.h
int fcntl(int fd, int command, ...); F_GETLKF_SETLKF_SETLKW
int fcntl(int fd, int command, struct flock *flock_st);flock(文件锁)结构依赖具体的实现但它至少包含下属成员
short l_type定义了文件中的一个区域 short l_whence SEEK_SET(文件头)SEEK_CUR(当前位置)SEEK_END(文件尾) off_t l_start 相对于l_whence的起始位置 off_t l_len 区域长度 pid_t l_pid
文件中的每个字节在任意时刻只能拥有易总类型的锁共享锁、独占锁、解锁
command参数
F_GETLKF_SETLKF_SETLKW
在使用文件锁时应该使用系统调用read和write读写而不是用libc读写。
文件锁测试 P246
dbm数据库 八、MySQL
十五、套接字
套接字工作原理套接字的属性、地址和通讯网络信息和互联网守护进程客户和服务器
socket是syscall
套接字是一种通讯机制凭借这种机制客户/服务器系统的开发工作即可以在本地单机上进行也可以跨网络进行。Linux所提供的功能如打印、连接数据库和提供web页面和网络工具如用于远程登录的rlogin和用于文件传输的ftp通常都是通过套接字来进行通信的。
套接字程序是如何通过套接字来维持一个连接的 代码地址 https://gitee.com/overcast-to-clear/linux-programming-.git 用完一个套接字后就应该把它删除即使在程序因接收到一个信号而异常终止的情况下也应该这么做。
client:
1. 创建socket
2. 根据socket以及addr建立connect
3. 读写socket文件描述符
4. closeserver
1. 创建socket
2. 指定地址
3. 将地址与sfd 绑定(bind)
4. 监听该sfd
5. 调用accept等待客户端链接
6. 读写客户端对应的文件描述符
7. close1. 套接字属性 https://blog.csdn.net/surfaceyan/article/details/125341896 套接字特性由3个属性确定域(domain又称协议族protocal family)类型type和协议protocal。
extern int socket (int domain, int type, int protocol);
参数- domain: AF_INET, ipv4AF_INET6, ipv6AF_UNIX, unix文件系统域- type: - 流套接字SOCK_STREAM, TCP/IP- 数据报套接字, SOCK_DGRAM, UPD/IP- protocal: 填0默认即可X/Open规范中在同文件netdb.h中定义了一个常量IPPORT_RESERVED代表保留端口号的最大值。
2. 套接字地址
#define __SOCKADDR_COMMON(sa_prefix) \sa_family_t sa_prefix##family/* Structure describing the address of an AF_LOCAL (aka AF_UNIX) socket. */
struct sockaddr_un{sa_family_t sun_family; // AF_UNIXchar sun_path[108]; /* Path name. */};/* Structure describing an Internet socket address. */
struct sockaddr_in{__SOCKADDR_COMMON (sin_);in_port_t sin_port; /* Port number. */struct in_addr sin_addr; /* Internet address. *//* Pad to size of struct sockaddr. */unsigned char sin_zero[sizeof (struct sockaddr)- __SOCKADDR_COMMON_SIZE- sizeof (in_port_t)- sizeof (struct in_addr)];};
/* Internet address. */
typedef uint32_t in_addr_t;
struct in_addr{in_addr_t s_addr;};/* Structure describing a generic socket address. */
struct sockaddr{sa_family_t sa_family; /* Common data: address family and length. */char sa_data[14]; /* Address data. */};3. 套接字命名
要想让通过socket调用创建的套接字可以被其他进程使用服务器程序就必须给该套接字命名。这样AF_UNIX套接字就会关联到一个文件系统的路径名。AF_INET套接字就会关联到一个IP端口号。
#include sys/socket.h
int bind(int socket, const struct sockaddr* address, size_t address_len);4. 创建套接字队列
为了能够在套接字上接收进入的链接服务器程序必须创建一个队列来保存未处理的请求。它用listen系统调用来完成该工作。
#include socket.h
int listen(int socket, int backlog);backlog代表允许的队列中的最大数量超过这个数量后再往后的连接将被拒接导致客户端连接请求失败。
5. 接受连接
通过accept系统调用来等待客户端建立对该套接字的连接
#include sys/socket.h
int accept(int socket, struct sockaddr*address, size_t*address_len);accept系统调用只有当客户程序试图连接到有socket参数指定的套接字上时才返回。这里的客户指在套接字队列中排在第一个的未处理连接。accept函数将创建一个新套接字来与该客户进行通讯并且返回新套接字的描述符。新套接字的类型和服务器监听套接字类型是一样的。
int flags fcntl(socket, F_GETFL, 0);
fcntl(socket, F_SETFL, O_NONBLOCK|flags);1. 请求连接
客户端程序通过在一个未命名套接字和服务器监听套接字之间建立连接的方法来连接到服务器。
int connect(int socket, const struct sockaddr*addr, size_t addr_len);2. 使用完成后应关闭sockfd
小于1024的端口号默认是为系统保留的 关于/etc/services文件 1作用 /etc/services文件是记录网络服务名和它们对应使用的端口号及协议。 2格式 文件中的每一行对应一种服务它由4个字段组成中间用TAB或空格分隔分别表示“服务名称”、“使用端口”、“协议名称”以及“别名”。
服务名tab 端口号/协议名 “tab” 别名
kermit 1649/udp
l2tp 1701/tcp l2f
l2tp 1701/udp l2f
h323gatedisc 1718/tcp
http 80/tcp www # WorldWideWeb HTTP
https 443/tcp # http protocol over TLS/SSL
https 443/udp # HTTP/3回路loopbackq网络只包含一台计算机传统上它被称为localhost标准IP: 127.0.0.1即为本地主机。可在网络主机文件/etc/hosts找到
用netstat命令可查看网络连接状况 netstat -A inet
网络信息
因特网守护进程xinetd/inetd
其配置文件。。。
套接字选项
setsockopt: https://blog.csdn.net/surfaceyan/article/details/125341896
select系统调用
https://blog.csdn.net/surfaceyan/article/details/125341896 select example
多客户服务器程序
通过selelct调用避免使用子进程 multicli_server.c
数据报UDP
udp.c