Linux系统编程是介于驱动层和应用层之间的编程方法,一般编程形式为:
int main(int argc, char *argv[])
{
//argc为参数个数
//argv为命令行中的参数
return 0;
}
文件IO函数
文件open函数
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
int open(const char *pathname, int flags);
int open(const char *pathname, int flags, mode_t mode);
/*
pathname为文件名;
flag为打开文件的状态:0_RDONLY-可读,0_WRONLY-可写,0_RDWR-可读可写等;
当flag为O_CREAT创建文件时,需加入第三个参数mode-文件的权限
*/
函数返回值为int型文件描述符:0-标准输入;1-标准输出;3-标准出错;-1-读不到文件。若创建新文件返回值将从3递增。
文件close函数
#include <unistd.h>
int close(int fd);
//关闭一个文件描述符-open函数的返回值
文件read函数
#include <unistd.h>
ssize_t read(int fd, void *buf, size_t count);
//从文件描述符fd中读取count长度的字符到buf缓冲区中,返回值为实际读取到的字节数
文件write函数
#include <unistd.h>
ssize_t write(int fd, const void *buf, size_t count);
/*
把count长度的字符从buf缓冲区中写到文件描述符为fd的文件中,
返回值为实际写入的字节数,写入失败返回-1
*/
设置文件位置指针lseek函数
#include <sys/types.h>
#include <unistd.h>
off_t lseek(int fd, off_t offset, int whence);
/*
fd-文件描述符;
offset-相对于whence的偏移量,负数向前移动,正数向后移动;
whence-当前位置的基点
SEEK_SET:相对于文件开头
SEEK_CUR:相对于当前文件读写指针的位置
SEEK_END:相对于文件末尾
返回值,成功后范围位移值,失败返回-1
*/
目录IO函数
创建目录mkdir函数
#include <sys/stat.h>
#include <sys/types.h>
int mkdir(const char *pathname, mode_t mode);
//pathname-创建的路径;mode-文件夹的权限
//创建成功返回0,失败返回-1
打开目录opendir函数
#include <dirent.h>
#include <sys/types.h>
DIR *opendir(const char *name);
//name-打开的路径目录,返回值为目录流指针
关闭目录opendir函数
#include <dirent.h>
#include <sys/types.h>
int closedir(DIR *dirp);
//dirp-对应目录流指针,成功返回0,失败返回-1
读目录readdir函数
#include <dirent.h>
struct dirent *readdir(DIR *dirp);
//dirp-对应目录流指针,返回dirent结构体
//如果读取到文件尾或读取失败返回NULL
struct dirent{
ino_t d_ino; // 文件inode
off_t d_off;
unsigned short d_reclen;
unsigned char d_type;
char d_name[256]; // 文件名
}
Linux下的库
种类分为静态库和动态库
静态库:静态库在程序编译时会被链接到目标代码里。程序运行时不再需要动态库了。因此编译出来的体积较大。
特点:以lib开头,以.a结尾
动态库(共享库):动态库态库在程序编译时不会被链接到目标代码里。程序运行时还需要动态库。因此编译出来的体积较小
特点:以lib开头,以.so结尾
静态库制作步骤:
①编写库的源代码
②将源代码的.c文件编译生成.o文件
gcc -c mylib.c
③使用ar命令创建静态库
ar cr libmylib.a mylib.o # libmylib.a-库文件名,mylib-库名
④使用静态库中的函数后,在编译时要链接静态库
gcc test.c -lmylib -L . # -l指定库名,-L指定路径
动态库制作步骤:
①编写库的源代码
②将源代码的.c文件编译生成.o文件
gcc -c -fpic mylib.c # -fpic:产生位置无关代码
③使用gcc命令创建动态库
gcc -shared -o libmylib.so mylib.o # libmylib.so-库文件名,mylib-库名
④使用动态库中的函数后,我们在编译时需要链接库。系统默认会在/lib或者/usr/lib中找库文件,或者在编译的时候指定库文件的路径
编译时链接到库的三种方法:
1.将生成的动态库拷贝到/lib或者/usr/lib中
2.把动态库所在的路径添加到环境变量中
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/path/to/lib
3.修改/etc/id.so.conf,在配置文件中加入动态库路径,然后使用ldconfig更新目录
Linux和进程
每个进程都有唯一的标识符,即进程ID,简称pid
进程间通信:
管道通信:有名管道,无名管道
信号通信:信号的发送,信号的接收,信号的处理
IPC通信:共享内存,消息队列,信号灯
socket通信
进程的三种基本状态:就绪态、执行态、阻塞态
创建进程fork函数
#include <unistd.h>
pid_t fork(void)
//在父进程中,返回值为子进程的pid,在子进程中,返回值为0,错误返回值为负数
获得pid函数
getpid(); // 获得当前进程的pid
getppid(); // 获得当前进程的父进程的pid
使子进程执行不同于父进程的程序exec函数族
作用:①当一个进程不能再作出贡献时,可以使用exec函数使自己重生②如果一个进程想执行另一个程序,就可以调用exec函数使子进程重生。
#include <unistd.h>
int execl(const char *path, const char *arg, ...)
//path-文件路径,arg-进程名,“...”-进程的参数,以NULL结尾
ps命令
ps命令可以列出系统中当前运行的进程
格式:ps [参数](常用aux)
kill命令
kill命令用于杀死进程
格式:kill -9 pid
孤儿进程
父进程结束后,子进程还未结束,这个子进程就叫孤儿进程,它会被系统的init进程“领养”。
僵尸进程
子进程结束以后,父进程还在运行,但父进程未释放进程控制块,这个子进程就叫僵尸进程,可以通过kill命令杀死。
回收子进程wait函数
#include <sys/wait.h>
pid_t wait(int *status)
/*
返回值:成功返回子进程pid,失败返回-1
参数status有两个宏定义:
WIFEXITED(status):如果子进程正常退出,该宏定义为真
WEXITSTATUS(status):如果子进程正常退出,该宏定义为子进程的退出值
*/
守护进程
1.是init进程的子进程
2.不和控制终端交互
创建守护进程:1.使用fork函数创建一个进程,再推出父进程
2.调用setsid函数,摆脱控制终端
3.调用chdir函数,将当前工作目录改为根目录
4.重设umask文件掩码
5.关闭文件描述符
管道通信pipe函数:
无名管道-只能实现有亲缘关系进程之间的通信
#include <unistd.h>
int pipe(int pipefd[2]);
//得到的文件描述符pipefd[0]为读端,pipefd[1]为写端
有名管道-可以实现所有进程通信,通过对fifo文件的读和写进行通信
#include <sys/types.h>
#include <sys/stat.h>
int mkfifo(const char *pathname, mode_t mode);
//创建成功返回0,创建失败返回-1;pathname-创建文件名;mode-文件权限
信号发送函数
kill函数
#include <sys/types.h>
#include <signal.h>
int kill(pid_t pid, int sig);
//pid-进程号;sig-信号的种类
raise函数(等价于kill(getpid(), sig),给自己发信号)
#include <signal.h>
int raise(int sig);
信号中断函数
pause函数:使目前的进程暂停,直到被信号中断
#include <unistd.h>
int pause(void);//只返回-1
信号处理函数
#include <signal.h>
typedef void (*sighandler_t)(int);
sighandler_t signal(int signum, sighandler_t handler)
//signum-要处理的信号;handler-处理的方式(SIG_DFL-系统默认、SIG_IGN-忽略、捕获)
捕获信号并自定义处理
void myfun(int sig)
{
if(sig == SIGINT)
{
return -1;
}
}
void main(void)
{
signal(SIGINT, myfun);
whlie(1){
printf("wait\n");
sleep(1);
}
}
共享内存
创建共享内存shmget函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmget(key_t key, size_t size, int shmflg);
/*
key_t key:IPC_PRIVATE或ftok函数返回值
size_t size:共享内存大小
int shmflg:权限
成功返回共享内存标识符,失败返回-1
*/
key_t ftok(const char *pathname, int proj_id);
/*
pathname-文件名
proj_id-字符
成功返回key值,失败返回-1
*/
将共享内存映射到进程shmat函数
#include <sys/types.h>
#include <sys/shm.h>
void *shmat(int shmid, const void *shmaddr, int shmflg);
/*
shmid:共享内存标识符
shmaddr:映射到的地址,NULL-系统自动映射
shmflg:权限,0-可读可写
成功返回共享内存映射到进程中的地址,失败返回-1
*/
删除共享地址映射shmdt函数
#include <sys/types.h>
#include <sys/shm.h>
int shmdt(const void *shmaddr);
/*
shmaddr:映射地址
成功返回0,失败返回-1
*/
操作共享内存shmctl函数
#include <sys/ipc.h>
#include <sys/shm.h>
int shmctl(int shmid, int cmd, struct shmid_ds *buf);
/*
shmid:要操作的共享内存标识符
cmd:IPC_STAT(获取对象属性),IPC_sET(设置对象属性),IPC_RMID(删除对象)
buf:指定要操作的对象属性
*/
消息队列
创建消息队列msgget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgget(key_t key, int msgflg);
/*
key-消息队列的key值
msgflg-访问权限
成功返回消息队列ID,失败返回-1
*/
操作消息队列msgctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgctl(int msqid, int cmd, struct shmsq_ds *buf);
/*
msqid:要操作的消息队列ID
cmd:IPC_STAT(获取消息队列属性),IPC_sET(设置消息队列属性),IPC_RMID(删除消息队列)
buf:消息队列缓冲区
成功返回0,失败返回-1
*/
发送消息msgsnd函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
int msgsnd(int msqid, const void *msgp, size_t msgsz, int msgflg);
/*
msqid:消息队列ID
msgp:指向消息类型的指针
msgsz:发送消息的字节数
msgfig:0-直到发送完成才返回(阻塞发送);IPC_NOWAIT-消息没发送完成也会返回(非阻塞发送)
成功返回0,失败返回-1
*/
//msgp指向的格式
struct msgbuf{
long mtype; //消息的类型
char mtext[1]; //消息的内容
};
接收消息msgrcv函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/msg.h>
ssize_t msgrcv(int msqid, void *msgp, size_t msgsz, long msgtyp, int msgflg);
/*
msqid:消息队列ID
msgp:接收消息的缓冲区
msgsz:接收消息的字节数
msgtyp:接收消息的标识
msgfig:0-阻塞读取;IPC_NOWAIT-非阻塞读取
成功返回接收消息的长度,失败返回-1
*/
信号量
获取信号量IDsemget函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semget(key_t key, int nsems, int semflg);
/*
key:信号量的键值
nsems:信号量的数量
semflg:标识
成功返回信号量的ID,失败返回-1
*/
操作信号量semctl函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semctl(int semid, int semnum, int cmd, union semun arg);
/*
semid:信号量ID
semnum:信号量编号
cmd:IPC_STAT(获取信号量属性),IPC_sET(设置信号量属性),
IPC_RMID(删除信号量),SETVAL(设置信号量的值)
arg:信号量结构体
*/
union semun{
int val;
struct semid_ds *buf;
unsigned short *array;
struct seminfo *__buf;
};
操作信号量数量semop函数
#include <sys/types.h>
#include <sys/ipc.h>
#include <sys/sem.h>
int semop(int semid, struct sembuf *sops, size_t nsops);
/*
semid:信号量ID
sops:信号量结构体数组
nsops:要操作的信号量数量
*/
struct sembuf{
unsigned short sem_num; //要操作的信号量编号
short sem_op; // P/V操作。
//1为V操作,释放资源;-1为P操作,分配资源,0为等待,知到信号量变为0
short sem_fig; //0-阻塞,IPC_NOWAIT-非阻塞
};