linux系统和网络编程
2021-07-06 10:13:34 9 举报
AI智能生成
linux系统编程和网络编程思维导图
作者其他创作
大纲/内容
L09-10 进程间通信
无名管道
一种最近本的IPC机制,有pipe函数创建
int pipe(int pipefd[2]);
4种特殊情况
1.如果所有管道写端都关闭了,有读端,read返回0,就像文件末尾一样
2.如果写端没关闭,没有写数据,读端read会阻塞,指导有数据读取才返回
3.所有读端都关了,写端write会异常终止,sigpipe信号
4.如果读端没有关闭,写端一直写,写满后write会阻塞
练习:process/pipeCom/unnamed_pipe/pipeOpt.c
作业2: 实现shell
子主题
无名管道(popen和pclose)
创建一个管道,fork一个子进程,关闭管道的不使用端,exec一个cmd命令,等待命令终止
FILE *popen(const char *command, const char *type);
int pclose(FILE *stream);
int pclose(FILE *stream);
练习:process/pipeCom/unnamed_pipe/popenOpt.c popenOptWr.c
命名管道FIFO
mkfifo tube命令创建一个有名管道
练习:process/pipeCom/named_pipe/readfifo.c writefifo.c
共享内存
共享存储允许两个或多个进程共享以给定的存储区
key_t ftok(const char *pathname, int proj_id);
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
int shmget(key_t key, size_t size, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
void *shmat(int shmid, const void *shmaddr, int shmflg);
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
使用命令行 ipcs -m/-q/q命令查看,使用命令行ipcrm -m/-q/q删除
练习:process/pipeCom/shared_memory/shmopt.c
消息队列
系统内核维护了一个存放消息的队列,不同用户可以向队列中发送消息,或者从队列中读取消息
int msgget(key_t key, int msgflg); //创建或获取消息队列
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp,int msgflg);
练习:process/pipeCom/msgque/getMsg.c sendMsg.c
L11-13 信号
目录
1 信号基本概念
2 如何产生信号
3 如何阻塞信号
4 如何捕捉信号
kill -l命令查看信号
1-31为非实时信号
34-64为实时信号
man 7 signal查看信号说明
信号操作方式分类
Term
终止进程
Ign
忽视信号
Core
down信息
Stop
停止进程
Cont
继续进程
常见信号
1 SIGHUP
2 SIGINT --CTL+C
3 SIGQUIT--CTL+\
4 SIG ILL--非法指令
6 SIGABRT
8 SIGFPE 浮点错误,除0
9 SIGKILL kill信号
11SIGSEGV 非法段信号
13 SIGPIPE管道错误
14 SIGALRM 闹钟信号
15 SIGTERM
20 SIGCHLD 子进程停止信号
19 SIGCONT 子进程继续信号
SIGTSTP CTL+Z
常见信号举例
信号相关函数
kill
kill函数给一个指定的进程发送指定信号
int kill(pid_t pid, int sig);
练习:signal/killOpt.c
raise
raise函数给当前进程发送指定的信号
int raise(int sig);
练习:signal/raiseOpt.c
abort
abort函数使当前进程接收到SIGABRT信号而异常终止
void abort(void);
练习:signal/raiseOpt.c
管道错误信号
练习:signal/pipeSignal.c
闹钟信号
练习:alarmOpt.c
sigchld信号
子进程在终止时会给父进程发SIGCHLD信号,该信号默认处理动作是忽略,父进程可以自定义SIGCHLD信号处理函数,函数中用wait处理,不需要主进程使用wait/waitpid函数阻塞等待
练习:signal/sigchild.c
阻塞信号
信号处理方式
信号递达
信号未决
信号阻塞
信号屏蔽
信号集操作函数
int sigemptyset(sigset_t *set);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
int sigfillset(sigset_t *set);
int sigaddset(sigset_t *set, int signum);
int sigdelset(sigset_t *set, int signum);
int sigismember(const sigset_t *set, int signum);
信号屏蔽字读写函数
int sigprocmask(int how, const sigset_t *set, sigset_t *oldset);
练习:signal/sigMask.c
信号未决集
sigpending读取当前进程的未决信号集,通过set参数传出
int sigpending(sigset_t *set);
练习:signal/sigMask.c
捕捉信号
如果信号的处理动作是用户自定义函数,在信号递达时就调用这个函数,成为捕捉信号
int sigaction(int signum, const struct sigaction *act,struct sigaction *oldact);
练习:signal/sigHandler.c
pause函数使调用进程挂起指导有信号递达
int pause(void);
练习:signal/mysleep.c
竞态条件与sigsuspend函数
原子操作:临时设置sigmask屏蔽字,并挂起进程,等待信号到来
练习:signal/suspend.c
作业3: 编写一个程序,实现父进程创建两个子进程,然后父进程通过signal()来捕捉键盘发出的CTL+C键,当按下CTL+C键后,父进程分别向两个子进程发出SIGUSR1和SIGUSR2,两个子进程收到信号后分别打印:
“child1 is killed by parent !” "child2 is killed by parent!"
“child1 is killed by parent !” "child2 is killed by parent!"
L14-15 线程
概念:由于同一个进程的多个线程共享同一地址空间,因此Text Segment、Data Segment都是共享的,如果定义一个函数,在各线程中都是可以调用的,如果定义一个全局变量,在各线程中都可以访问,除此之外,各线程还共享以下进程资源和环境:
1.文件描述符表
2.每种信号的处理方式
3.当前的工作目录
4.用户ID和组ID
但有些资源是每个线程各一份的
1.线程id
2.上下文,包括各种寄存器的值,程序计数器和栈指针
3.栈空间
4.errno变量
5.信号屏蔽字
6.调度优先级
1.文件描述符表
2.每种信号的处理方式
3.当前的工作目录
4.用户ID和组ID
但有些资源是每个线程各一份的
1.线程id
2.上下文,包括各种寄存器的值,程序计数器和栈指针
3.栈空间
4.errno变量
5.信号屏蔽字
6.调度优先级
线程控制
创建线程
int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
void *(*start_routine) (void *), void *arg);
void *(*start_routine) (void *), void *arg);
获取当前线程id
pthread_t pthread_self(void);
终止线程
1.从线程函数return,这种方法对主线程不适用,从main函数return相当于调用exit
2.一个线程可以调用pthread_cancel终止统一进程中的另一个线程
int pthread_cancel(pthread_t thread);
3.线程可以调用pthread_exit终止自己
void pthread_exit(void *retval);
练习:thread/creatThread.c creatThread_02.c
线程收尸
调用pthread_join函数的线程将挂起等待,直到id为thread的线程终止
练习: thread/exitThread.c exitThread_02.c
线程间同步
练习:thread/addnum_thread.c
互斥锁
#include <pthread.h>
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INI‐TIALIZER_NP;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MU‐TEX_INITIALIZER_NP;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
pthread_mutex_t fastmutex = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t recmutex = PTHREAD_RECURSIVE_MUTEX_INI‐TIALIZER_NP;
pthread_mutex_t errchkmutex = PTHREAD_ERRORCHECK_MU‐TEX_INITIALIZER_NP;
int pthread_mutex_init(pthread_mutex_t *mutex, const pthread_mutexattr_t *mutexattr);
int pthread_mutex_lock(pthread_mutex_t *mutex);
int pthread_mutex_trylock(pthread_mutex_t *mutex);
int pthread_mutex_unlock(pthread_mutex_t *mutex);
int pthread_mutex_destroy(pthread_mutex_t *mutex);
练习:thread/addnum_thread.c
条件变量
#include <pthread.h>
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mu‐tex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex, const struct timespec *ab‐stime);
int pthread_cond_destroy(pthread_cond_t *cond);
pthread_cond_t cond = PTHREAD_COND_INITIALIZER;
int pthread_cond_init(pthread_cond_t *cond,pthread_condattr_t *cond_attr);
int pthread_cond_signal(pthread_cond_t *cond);
int pthread_cond_broadcast(pthread_cond_t *cond);
int pthread_cond_wait(pthread_cond_t *cond, pthread_mu‐tex_t *mutex);
int pthread_cond_timedwait(pthread_cond_t *cond,pthread_mutex_t *mutex, const struct timespec *ab‐stime);
int pthread_cond_destroy(pthread_cond_t *cond);
练习:thread/condi.c
信号量
int sem_init(sem_t *sem, int pshared, unsigned int value);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);
int sem_wait(sem_t *sem);
int sem_trywait(sem_t *sem);
int sem_timedwait(sem_t *sem, const struct timespec *abs_timeout);
int sem_post(sem_t *sem);
int sem_destroy(sem_t *sem);
练习:thread/sem.c
作业4:哲学家和筷子
L16 总结补充
文件锁
多进程对同一个文件进行读写访问时,为保证数据的完整性,有时需要对文件进行锁定。可以通过fcntl()函数对文件进行锁定和解锁。
对于写锁(F_WRLCK),只有一个进程可以在文件的任一特定区域上享有独占锁。对于读锁(F_RDLCK),许多不同的进程可以同时拥有文件上同一区域上的共享锁。为了拥有共享锁,文件必须以读或者读/写方式打开。只要任一进程拥有共享锁,那么其他进程就无法再获得独占锁
对于写锁(F_WRLCK),只有一个进程可以在文件的任一特定区域上享有独占锁。对于读锁(F_RDLCK),许多不同的进程可以同时拥有文件上同一区域上的共享锁。为了拥有共享锁,文件必须以读或者读/写方式打开。只要任一进程拥有共享锁,那么其他进程就无法再获得独占锁
int flock(int fd, int operation);
练习:summary/readlock.c writelock.c
终端、作业、守护进程
终端
练习:summary/ttyOpt.c
作业(JOB)
shell分前后台来控制的不是进程而是作业(job)或者进程组(Process Group)。一个前台作业可以有多个进程组成,作业也可以由多个进程组成,sheel可以同时运行一个前台作业和任意多个后台作业,这成为作业控制
proc1 | proc2 & ---后台
proc3 | proc4 | proc5 ---前台
练习:ps -o pid,ppid,pgrp,session,tpgid,comm | cat
守护进程
linux系统启动时会启动很多系统服务进程,这些系统服务进程没有控制终端,不与用户交互。其他进程都是在用户登录或者运行程序时创建,在运行结束或用户注销时终止,但系统服务进程不受用户登录注销的影响,他们一直在运行着。这种进程有一个名称叫守护进程(Daemon
创建守护进程
pid_t setsid(void);
作业:summary/deamon.c
链接库
L17-21 网络理论知识
网络结构
查看ip
ifconfig
ip addr
mac地址
DHCP
osi七层模型/TCP/IP五层模型
L22-24
预备知识
网络字节序
#include <arpa/inet.h>
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
uint32_t htonl(uint32_t hostlong);
uint16_t htons(uint16_t hostshort);
uint32_t ntohl(uint32_t netlong);
uint16_t ntohs(uint16_t netshort);
练习:net/test.c
socket地址的数据类型
TCP
流程
练习: net/tcp/server.c client.c
UDP
流程
练习:net/udp/server.c client.c
网络高并发:线程池并发处理
1.系统编程入门
linux系统编程目录
文件IO、缓冲IO
文件的打开关闭读写
阻塞与非阻塞IO
同步IO
内核内幕,虚拟文件系统
标准IO
流的打开关闭读写
控制缓冲
线程安全,对文件加锁
多进程编程
进程体系,进程ID,父子进程关系
运行新进程,fork、exec
终止进程
等待子进程退出
特殊进程
僵尸进程
孤儿进程
守护进程
高级进程管理
进程调度
完全公平调度算法
实时系统的原理与实现
进程间通信IPC
基于文件的简单进程间通信
共享内存:互斥锁、条件变量
管道:匿名管道与命名管道
共享存储映射
消息队列
信号量
套接字
多线程编程
线程与进程的对比
线程模型
线程API
线程模式
一个连接对应一个线程
事件驱动
并发、并行和竞争
同步:互斥与死锁
线程池的实现
信号
基本信号管理
发送信号
信号集
高级信号管理
时间
时间的表示
睡眠与等待
定时器
终端IO编程
终端驱动程序的模式
ncurses库的使用
linux系统编程特点
无法跨平台
速度慢
更加底层,接口更加复杂
网络编程
计算机网络知识补充
计算机网络概论
应用层协议
运输层协议、TCP与UDP
网络层协议数据链路层协议
TCP网络通信剖析
三次握手与四次握手
TCP流量控制
TCP拥塞避免
TCP状态转换
socket网络编程接口
socket的创建与关闭
client端:connect
数据的发送与接收:send、recv
UDP编程实现
在UDP中使用connect
基于CS模型的TCP客户端与服务端设计与实现
P2P模型网络服务的实现
HTTP与BS模型
简单的网络并发模型
基于多进程与多线程
简单的IO多路复用机制
L01 标准库函数和系统调用
标准库函数
fopen
FILE *fopen(const char *pathname, const char *mode);
fgetc
int fgetc(FILE *stream);
fputc
int fputs(const char *s, FILE *stream);
fclose
int fclose(FILE *stream);
标准库函数在用户空间的缓冲区
全缓冲
缓冲区满了触发调用系统调用
行缓冲
缓冲区满了、或者遇到换行,调用系统调用
标准输出是行缓冲
无缓冲
stderr是无缓冲
测试:fileio/hello.c
程序启动自动打开三个文件
STDIN_FILENO
0
STDOUT_FILENO
1
STDERR_FILENO
2
L02 文件与IO
系统调用函数
open
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
练习:fileio/fileio.c
close
int close(int fd);
read
ssize_t read(int fd, void *buf, size_t count);
练习:fileio/read.c、readstdin.c
write
ssize_t write(int fd, const void *buf, size_t count);
练习:fileio/nonblockread.c
umask
阻塞与非阻塞
状态
阻塞状态
sleep
运行状态
正在被调度执行
就绪状态
fopen时增加nonblock标志
int fd=open("/dev/tty",O_RDONLY|O_NONBLOCK);
非阻塞没有读到时判断errno!=EAGAIN
练习:fileio/nonblockread.c
L03-04 文件与IO--高级控制
标准库函数
fopen
FILE *fopen(const char *pathname, const char *mode);
FILE *fdopen(int fd, const char *mode);
r/r+/w/w+/a/a+
fgetc
int fgetc(FILE *stream);
fputc
int fputc(int c, FILE *stream);
fclose
int fclose(FILE *stream);
fseek
int fseek(FILE *stream, long offset, int whence);
whence基准,offset偏移量
long ftell(FILE *stream);
void rewind(FILE *stream);
od -tx1 -tc hello.txt
查看文件内容,含不可见标记
练习fileOpt/fileopt.c、fileopt/mycat.c、fileopt/mycp.c
系统调用函数
lseek
off_t lseek(int fd, off_t offset, int whence);
内核总的文件读写位置,不是用户区
练习:fileopt/lseek.c
fcntl
int fcntl(int fd, int cmd, ... /* arg */ );
改变open文件时的属性,比如读写、阻塞等
练习:fileopt/fcntl.c
改变已打开文件的阻塞非阻塞
练习:fileopt/fcntlGetstatus.c
取得文件标识符的状态,包括读写、阻塞等
./a.out 0
./a.out <dev/tty
./a.out >good.txt
ioctl
int ioctl(int fd, unsigned long request, ...);
练习:fileopt/ioctlopt.c
获取终端的窗口尺寸
mmap
void *mmap(void *addr, size_t length, int prot, int flags,
int fd, off_t offset);
int fd, off_t offset);
练习:fileopt/mmap.c
L05-06 文件系统与目录操作
ext2文件系统
启动块
1KB,由PC标准规定,用来存储磁盘分区信息和启动信息,任何文件系统都不能使用该块
超级块
描述整个分区的文件系统信息,例如块大小、文件系统版本号,上次mount时间等等,超级快在每个块组的开头都有一份拷贝
块组描述符表
由很多块组描述符组成,整个分区分成多少个块组就对应有多少个块组描述符,包括inode表哪里开始,数据块从哪里开始,空闲的inode和数据块还有多少个等。块组描述符在每个块组的开头也都有一份拷贝
块位图
用来描述整个块组中哪些块已经使用,哪些空闲
inode位图
标识一个inode是否空闲可用
inode表
文件类型(常规、目录、符号链接等),权限,文件大小,创建、修改、访问时间等信息,每个文件都有一个inode
数据块
常规文件
文件的数据存储在数据快中
目录
该目录下所有文件名和目录名存储在数据块中(注意,文件名保存在它所在的目录的数据块中,其他信息都保存在该文件的inode中)
符号链接
如果目标路径名比较短则直接保存在inode中,否则分配一个数据块保存
设备文件、FIFO、和socket等特殊文件
没有数据块,设备文件的主设备号和次设备号保存在inode中
系统调用
stat
返回文件信息
int stat(const char *pathname, struct stat *statbuf);
int fstat(int fd, struct stat *statbuf);
int lstat(const char *pathname, struct stat *statbuf);
命令行可以直接查询--stat test.txt
练习:vfs/stat/statOpt.c
其他类似
access
chmod、fchmod
chown、fchown、lchown
utime
truncate、ftruncate
link
unlink
rename
readlink
mkdir
rmdir
opendir/readdir/closedir
返回目录信息
练习:vfs/stat/dirOpt.c
dup/dup2
赋值现存的文件描述符
练习:vfs/dup/dupOpt.c
作业1:实现ls -al
L07-08 进程体系与进程管理
目录
进程控制块PCB
进程控制fork
exec函数族
wait和waitpid
进程间通信
系统调用
fork
根据一个现有的进程复制出一个新的进程
查看进程 ps aux、pstree
pid_t fork(void);
getpid
获取进程id
getppid
获取父进程id
练习:process/forkOpt.c、multiProc.c
exec函数族
取代当前进程
int execl(const char *pathname, const char *arg, ...
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execle(const char *pathname, const char *arg, ...
/*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
/* (char *) NULL */);
int execlp(const char *file, const char *arg, ...
/* (char *) NULL */);
int execle(const char *pathname, const char *arg, ...
/*, (char *) NULL, char *const envp[] */);
int execv(const char *pathname, char *const argv[]);
int execvp(const char *file, char *const argv[]);
int execvpe(const char *file, char *const argv[],
char *const envp[]);
char *getenv(const char *name);
练习:process/envOpt.c execOpt.c execOpt2.c
重定向
重定向输入输出
练习 process/redirect/callback.c
wait waitpid
一个进程在终止时会关闭所有文件描述符,释放在用户控件分配的内存,但它的PCB还保留着,内核在其中保存了一些信息如果正常终止则保存着退出状态,如果异常终止则保存着导致进程终止的信号是哪个
pid_t wait(int *wstatus);
pid_t waitpid(pid_t pid, int *wstatus, int options);
pid_t waitpid(pid_t pid, int *wstatus, int options);
练习:process/zomb.c waitOpt.c
0 条评论
下一页