访问文件可以通过系统调用和库函数,库函数适用于任何系统,而我这里说的系统调用只是在linux系统下的。
创建函数
int creat(const char *filename,mode_t mode)
filename:要创建的文件名(包含路径,缺省为当前路径) mode:创建模式(S_IRUSR(可读) S_IWUSR (可写) S_IXUSR(可执行) S_IRWXU(可读、写、执行))
int open(const char *pathname,int flags)
int open(const char *pathname,int flags,mode_t mode)(若使用O_CREARE标志时必须使用的函数,这时mode来表示文件的访问权限)
pathname:要打开的文件名(包含路径,缺省为当前路径)
flags:打开标志(O_RDONLY:只读方式打开,O_WRONLY:只写方式打开,O_RDWR:读写方式打开,O_APPEND:追加方式打开,O_CREAT:创建一个文件,O_NOBLOCK:非阻塞方式打开)
又打开就有关闭
所以需要int close (int fd) fd为文件描述符,为open返回的值
int read(int fd,const void *buf,size_t length)
功能:从文件描述符fd所指定的文件中读取length个字节到buf所指向的缓冲区中,返回值为实际读取的字节数
int write(fd,const void *buf,size_t length)
功能:把length个字节从buf所指向的缓冲区中写到文件描述符fd所指向的文件中,返回值为实际写入的字节数
int Iseek(int fd,offset_t offset,int whence)
功能:将文件读写指针相对whence移动offset个字节。操作成功时,返回文件指针相对于文件头的位置
whence可使用下述值
SEEK_SET:相对文件开头
SEEK_CUR:相对文件读写指针的当前位置
SEEK_END:相对文件末尾
offset取负值表示向前移动
int access(const char *pathname,int mode)
pathname:文件名称
mode:要判断的访问权限(R_OK:文件可读,W_OK:文件可写,X_OK:文件可执行,F_OK:文件存在)
测试成功,函数返回0,否则返回-1.
以上是系统调用中关于文件调用的函数,注意一下,Linux不区分二进制文件和文本文件
以下说明一下关于linux编程中的进程之间的通讯:
进程基本概念
1.不是计算机专业的同学可能会对进程这个词有点生疏,简而言之,进程就是一个具有一定独立功能的程序的一次运行活动
特点:动态性 并发性 独立性 异步性
同时进程的三个状态转换需要了解掌握
2.进程ID(PID):标识进程的唯一数字
父进程ID(PPID)
启动进程的用户ID(UID)
3.进程互斥和进程同步
指的是当有若干个进程都要使用某一共享资源时,任何时刻最多允许一个进程使用,其他要使用该资源的进程必须等待直到占用该资源者释放该资源为止。
临界资源:操作系统中将一次只允许一个进程访问的资源称为临界资源
临界区:进程中访问临界资源的程序代码为临界区。
与进程互斥相对应的即进程同步
即一组并发进程按一定顺序执行的过程称为进程间的同步
4.进程调度
按一定算法,从一组待运行的进程中选出一个来占有CPU运行
调度方式分为抢占式和非抢占式
调度算法有:先来先服务调度算法 短进程优先调度算法 高优先级优先调度算法 时间片轮转法
还有死锁等一些进程中必须了解的概念,此处提出一些我认为比较重要的,想详细了解可以再多查资料帮助理解
进程控制程序设计基本函数:
获取进程ID:
#include
#include
pid_t getpid(void)
获取本进程ID
pid_t getppid(void)
获取父进程ID
创建子进程:
#include
pid_t fork(void)//子进程拷贝父进程的数据段、父子执行次序不确定
pid_t vfork(void)//子进程与父进程共享数据段,子进程先运行,父进程后运行
调用一次返回两次,可能有三种不同的返回值
1.在父进程中fork返回新创建的子进程的PID
2.在子进程中,fork返回0
3.出现错误 返回负值
父进程—子进程
使用fork()创建子进程,其数据空间、堆栈空间都会从父进程得到一个拷贝,而不是共享,使用vfork()时是共享
具体区别可通过运行以下代码得出结论:
exec函数族:
具体作用以及应用可参考这篇博文:
https://blog.csdn.net/willib/article/details/15809361
exec用被执行的程序替换调用他的程序
fork()创建一个新的进程会产生新的pid,exec启动一个新程序,替换原有的进程,因此进程的pid不会改变
以下为基本函数:
execv()中的参数:
path:被执行程序名
arg1-argn:被执行程序所需命令行参数,含程序名,以空指针(NULL)结束((char*)0)
argv[ ]被执行程序所需的命令行参数数组
#include
int system(const char * string)
功能:调用fork产生子进程,由子进程来调用/bin/sh -c string来执行参数string所代表的命令
进程等待:
#include
#include
pid_t wait(int *status)
阻塞该进程,直到其某个子进程退出
接下来就要说到进程通信了,这是一个比较重要的概念:
原因:
为什么要进行进程通讯?
1.数据传输: 一个进程需要将它的数据发送给另一个进程
2.资源共享: 多个进程之间共享同样的资源
3.通知事件:一个进程需要向另一个或一组进程发送消息,通知他们发生哪种事件
4.进程控制 : 有些进程希望完全控制另一个进程的执行
进程间通讯方式
1.管道(分为有名管道和无名管道)
无名管道(用于父进程和子进程间的通信)和有名管道(用于运行于同一系统中的任意两个进程间的通信):管道是单向的、先进先出的,将一个进程的输出和另一个进程的输入连接在一起
无名:
创建 int pipe(int filedis[2])
filedis[2]存储管道的头部尾部文件描述符,filedis[0]用于读管道 ,filedis[1]用于写管道
关闭:close(filedis[0]); close(filedis[1]);
父子进程通过read()和write()函数进行数据的读写从而实现管道通信
void *memset(void *s,int ch,size_t n);
将s中当前位置后面的n个字节用ch替换并返回s
s为指针或是数组,
注意:必须在系统调用fork()前调用pipe(),否则子进程将不会继承文件描述符,如果相反,将会创建两个管道
有名:
#include
#include
创建 int mkfifo(const char* pathname,mode_t mode)
pathname FIFO文件名
mode属性(close,read,write)
2.信号通讯
很多人对kill的理解是它是用来杀死进程的,其实不然,它和raise一样是一个放松信号的函数。
kill-send a signal to a process
kill既可以向自身发送信号 也可以向其他进程发送信号
raise是向进程自身发送信号
基本命令在这里简单提一下:
kill -l 查看能发送的信号
kill pid等价于 kill -15 pid
kill命令默认发送信号15给目标进程
kill -9 pid
出现异常情况不能通过信号15正常退出时用上述命令结束进程 临时文件不会被删除
klill -s SIGOUT 3687(pid:通过ps aux查看)
发送信号SIGOUT
#include
#include
int kill(pid_t pid,int signo)
int raise(int signo)
常见信号:(其中SIGKILL 和 SIGSTOP信号不能被忽略)
SIGHUP 从终端上发出的结束信号
SIGINT 来自键盘的中断信号
SIGKILL 该信号结束接收信号的进程
SIGTERM kill命令发出的信号
SIGGHLD 标识子进程停止或结束的信号
SIGSTOP 来自键盘或调试程序的停止执行信号
#include
unsigned int alarm(unsigned int seconds)
使用alarm函数可以设置一个时间值,当经过指定的seconds秒后产生SIGALRM信号,如果不捕捉此信号,则默认动作是终止该进程。
#include
int pause(void)
pause函数使调用进程挂起直至捕捉到一个信号
只有执行了一个信号处理函数后,挂起才结束
信号处理主要方法有两种:一种是使用简单的signal函数,另一种是使用信号集函数组
#include
void(*signal(int signo,void (*func)(int)))(int)
理解:typedef void(*sighandler_t)(int) sighandler_t signal(int signum,sighandler_t handler)
Func可能的值是:
1.SIG_IGN 忽略此信号
2.SIG_DFL 按系统默认方式处理
3.信号处理函数名:使用该函数处理
可以试运行以下代码体会信号通讯处理:
3.消息队列
消息队列目前有两种类型 POSIX消息队列 和 系统V消息队列(随内核持续(因此要求每个消息队列都在系统范围内对应唯一的键值),只有在内核重启或者人工删除时,该消息队列才会被删除)
以下为相关函数:
#include
#include
key_t ftok(char* pathname(文件名),char proj(项目名 不为0即可))
返回文件名对应的键值
#include
#include
#include
打开消息队列:int msgget(key_t key(键值 由ftok获得) ,int msgflg(标志位)) 返回msgtid
发送消息:int msgsnd(int msgid(消息队列描述符,已打开的消息队列id),struct msgbuf* msgp(消息结构指针),int msgsz,int magflg)
接收消息:int msgrcv(int msgid,struct msgbuf* msgp,int msgsz,long msgtyp,int msgflg)
成功读取一条消息后 队列中的这条消息将被删除。
msgsz(消息大小),int msgflg(消息参数,发送标志 有意义的magflg标志为IPC_NOWAIT ,指明在消息队列没有足够的空间容纳要发送的消息时,magsnd是否等待))(向消息队列中发送一条消息)
与键值key相对应的消息队列描述字
IPC_CREAT 创建新的消息队列
IPC_EXCL 与IPC_CREAT一同使用 表示如果要创建的消息队列已经存在
,则返回错误
IPC_NOWAIT 读写消息队列要求无法得到满足时,不阻塞
以下两种情况,将创建一个新的消息队列
1.没有与键值key相对应的消息队列,并且msgflg中包含IPC_CREAT标志位
2.key参数为IPC_PRIVATE
struct magbuf
{
long mtype;//消息类型
char mtext[1];//消息数据的首地址
};
4.共享内存
是被多个进程共享的一部分物理内存,是进程间共享数据最快的方法
共享内存实现分为两个步骤:
1.创建共享内存,使用shmget函数
2.映射共享内存,将这段创建的共享内存映射到具体的进程空间,使用shmat函数。
int shmget(key_t key,int size,int shmflg)
key标识共享内存的键值:0/IPC_PRIVATE
取IPC_PRIVATE值时,函数将创建一块新的共享内存
取0时 又设置IPC_PRIVATE标志 同样会创建一块新的共享内存
返回值:成功 返回共享内存标识符,失败 返回-1
int shmat(int shmid,char *shmaddr(若为0,表示从系统中导入一个地址),int flag)
shmid:shmget函数返回的共享存储标识符
flag:决定以什么方式来确定映射的地址(通常为0)
成功返回共享内存映射到的进程中的地址
当一个进程不再需要共享内存时,需要把它从进程地址空间中脱离
int shmdt(char *shmaddr)
5.信号量
不同于以上的是信号量主要用于进程之间的控制
主要用途:保护临界资源,进程根据信号量判定是否能够访问某些共享资源。除了用于访问控制外,还可用于进程同步
#include
#include
#include
int semget(key_t key,int nsems(指定打开或者新创建的信号灯集中将包含信号灯的数目),int semflg(标识,同消息队列))
int semop(int semid(信号量集的ID),struct sembuf *sops(操作数组,表明要进行什么操作),unsigned nsops(sops所指向的数组的元素个数))
功能:对信号量进行控制
6.套接字(socket)
大致重要的就这些,希望能帮助到大家,有错误的地方希望大家指正。